Compare commits

...
Author SHA1 Message Date
nacabaro 3fa072ce1e Updated the store
- Items can now be purchased
- Changed settings to make use of flows/stateflows from the database, which update the screen in real time after any changes.
- Separated functions from files
2025-11-13 23:52:14 +01:00
nacabaro 69245be8dd Updated README.md 2025-11-03 13:20:16 +01:00
Nacho 73c393df64 Fixed bug syncing watch adventure missions progress with the app 2025-09-14 00:34:05 +02:00
nacabaroandGitHub 0b3ce486cb Merge pull request #39 from nacabaro/card/import_more
Adventure Missions progress
2025-09-10 02:10:22 +02:00
Nacho 7160eb792a Adventure Missions progress
- Viewing the adventure missions completed in a card is now possible.
2025-09-10 02:09:15 +02:00
43 changed files with 999 additions and 342 deletions
+66 -35
View File
@@ -1,38 +1,69 @@
# VBHelper # VBHelper
## Developer Setup Application to interact with the Vital series, VB, VH, VBC and VBBE.
1. Clone vb-nfc-reader (https://github.com/cfogrady/lib-vb-nfc) ## Current state of the project
2. Run vb-nfc-reader/publishToMavenLocal gradle task in the lib-vb-nfc project.
3. Clone vb-dim-reader (https://github.com/cfogrady/vb-dim-reader) Right now the project is still under development, and until further notice, any database updates will result in having to erase application data.
4. Run publishToMavenLocal gradle task in the vb-dim-reader project.
5. Create res/values/keys.xml within the app module. This document will be updated once the application does not need any more database resets.
6. Populate with:
``` ## Features
<?xml version="1.0" encoding="utf-8"?>
<resources> As of now, the project allows you to read characters, view characters stats, and send them back to your watch.
<string name="password1">beHmacKey1</string>
<string name="password2">beHmacKey2</string> You can also apply items to the characters read, such as special missions, or change timers, and store characters in the storage section.
<string name="decryptionKey">aesKey</string>
<integer-array name="substitutionArray"> You also earn new items every time an item such as a special mission (VB only) or a character completes an in-app adventure mission.
<item>0</item>
<item>1</item> App also comes with a dex that will update every time a new character is added, and allows you to see evolution requirements and current adventure stage in the watch.
<item>2</item>
<item>3</item> ## How to set up
<item>4</item>
<item>5</item> 1. Download the latest version for VB Arena APK from a trustworthy source. If your download is a standalone APK, continue to step 2. Otherwise, if your download is an XAPK, do the following:
<item>6</item>
<item>7</item> 1. Using your phone file manager, rename the XAPK file to ZIP, and extract its contents. You can also do this with any other device, such as Windows, macOS and Linux.
<item>8</item>
<item>9</item> 2. Once the files are extracted, look for an APK called `com.bandai.vitalbraceletarena.apk`. Copy it somewhere else, you will need it.
<item>10</item>
<item>11</item> 2. Install an APK release for VB Arena. You will find the releases [here](http://github.com/nacabaro/vbhelper/releases). Download the latest release and install its APK.
<item>12</item>
<item>13</item> Note, in the current stage of the project, you will have to delete the old application from your device. If the app keeps crashing after installing, clear application data and storage.
<item>14</item>
<item>15</item> 3. Import secrets in the app. These secrets will allow the app to talk to the watch. On the main screen, click on the gear icon, then `Import secrets`.
</integer-array>
</resources> You will be prompted to choose a file. Choose the APK file that was previously obtained.
```
7. Replace the values in the keys.xml file with those extracted from the original app. 4. Import cards. Due to copyright laws, we cannot offer the characters and sprites themselves in the application. In order to import the cards do the following.
8. Run
1. Using your own DiM/BEm cards, dump the cards to your device. You can get an in-depth tutorial in [here](http://mrblinky.net/digimon/vb/dimcardtool/dimcardtool.html). You can download the dump tool from [here](http://mrblinky.net/digimon/vb/dimcardtool/)
2. Once installed the tool and drivers, open the tool, connect your DiM/BEm reader hardware to yout computer and click on Read card.
3. Transfer the resulting file to your mobile device. You can put them anywhere, as long as they are accessible. My recommendation is to put them under a folder called `Cards` in your `Internal storage` or `SD Card`
4. In the app, click on import card. Next choose the BIN file corresponding to the card you want to import.
**Note: if you do not import the card, whenever you attempt to read a character from th watch, the character you read will get deleted.**
5. App will now be ready to be used.
## Planned features
- Online battles, undegoing development by `lightheel`.
- VitalWear compatibility, undergoing development by `cfogrady`.
- Support for multiple languages, not yet started.
- Database backup/restore.
## Credits
- `cyanic` for helping us understand more about the VB connection protocol.
- `cfogrady` for making both [`VB-DIM-Reader`](https://github.com/cfogrady/VB-DIM-Reader) and [`lib-vb-nfc`](https://github.com/cfogrady/lib-vb-nfc)
- `lightheel` for working on the online component in the application, both server and battle client.
- `shvstrz` for the app icon.
+2 -1
View File
@@ -15,7 +15,7 @@ android {
minSdk = 28 minSdk = 28
targetSdk = 35 targetSdk = 35
versionCode = 1 versionCode = 1
versionName = "Alpha 0.6" versionName = "Alpha 0.6.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -91,4 +91,5 @@ dependencies {
implementation("com.google.android.material:material:1.2.0") implementation("com.google.android.material:material:1.2.0")
implementation(libs.protobuf.javalite) implementation(libs.protobuf.javalite)
implementation("androidx.compose.material:material") implementation("androidx.compose.material:material")
implementation("androidx.datastore:datastore-preferences:1.1.7")
} }
@@ -31,7 +31,7 @@ interface AdventureDao {
a.finishesAdventure AS finishesAdventure, a.finishesAdventure AS finishesAdventure,
a.originalDuration AS originalTimeInMinutes a.originalDuration AS originalTimeInMinutes
FROM UserCharacter uc FROM UserCharacter uc
JOIN CharacterData c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
JOIN Card d ON c.cardId = d.id JOIN Card d ON c.cardId = d.id
JOIN Adventure a ON uc.id = a.characterId JOIN Adventure a ON uc.id = a.characterId
@@ -0,0 +1,58 @@
package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Query
import com.github.nacabaro.vbhelper.dtos.CardDtos
@Dao
interface CardAdventureDao {
@Query("""
INSERT INTO
CardAdventure (cardId, characterId, steps, bossAp, bossHp, bossDp, bossBp)
SELECT
:cardId,
cc.id,
:steps,
:bossAp,
:bossHp,
:bossDp,
:bossBp
FROM
CardCharacter cc
WHERE
cc.charaIndex = :characterId AND
cc.cardId = :cardId
""")
suspend fun insertNewAdventure(
cardId: Long,
characterId: Int,
steps: Int,
bossAp: Int,
bossHp: Int,
bossDp: Int,
bossBp: Int?
)
@Query("""
SELECT
cc.nameSprite as characterName,
cc.nameWidth as characterNameWidth,
cc.nameHeight as characterNameHeight,
s.spriteIdle1 as characterIdleSprite,
s.width as characterIdleSpriteWidth,
s.height as characterIdleSpriteHeight,
ca.bossAp as characterAp,
ca.bossBp as characterBp,
ca.bossDp as characterDp,
ca.bossHp as characterHp,
ca.steps as steps
FROM CardCharacter cc
JOIN Sprite s ON cc.spriteId = s.id
JOIN CardAdventure ca ON cc.id = ca.characterId
WHERE
cc.cardId = :cardId
""")
suspend fun getAdventureForCard(
cardId: Long
): List<CardDtos.CardAdventureWithSprites>
}
@@ -21,7 +21,7 @@ interface CardDao {
""" """
SELECT ca.* SELECT ca.*
FROM Card ca FROM Card ca
JOIN CharacterData ch ON ca.id = ch.cardId JOIN CardCharacter ch ON ca.id = ch.cardId
JOIN UserCharacter uc ON ch.id = uc.charId JOIN UserCharacter uc ON ch.id = uc.charId
WHERE uc.id = :id WHERE uc.id = :id
""" """
@@ -0,0 +1,32 @@
package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao
import androidx.room.Query
@Dao
interface CardFusionsDao {
@Query("""
INSERT INTO
CardFusions (
fromCharaId,
attribute1Fusion,
attribute2Fusion,
attribute3Fusion,
attribute4Fusion
)
SELECT
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :fromCharaId),
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr1),
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr2),
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr3),
(SELECT id FROM CardCharacter WHERE cardId = :cardId AND charaIndex = :toCharaIdAttr4)
""")
suspend fun insertNewFusion(
cardId: Long,
fromCharaId: Int,
toCharaIdAttr1: Int,
toCharaIdAttr2: Int,
toCharaIdAttr3: Int,
toCharaIdAttr4: Int
)
}
@@ -1,17 +1,28 @@
package com.github.nacabaro.vbhelper.daos package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import androidx.room.Upsert
import com.github.nacabaro.vbhelper.domain.card.CardProgress import com.github.nacabaro.vbhelper.domain.card.CardProgress
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
@Dao @Dao
interface CardProgressDao { interface CardProgressDao {
@Upsert @Query("""
fun updateDimProgress(vararg cardProgresses: CardProgress) UPDATE CardProgress
SET
currentStage = :currentStage,
unlocked = :unlocked
WHERE cardId = :cardId AND
currentStage < :currentStage
""")
fun updateCardProgress(currentStage: Int, cardId: Long, unlocked: Boolean)
@Query( @Query(
"SELECT currentStage FROM CardProgress WHERE cardId = :cardId" "SELECT currentStage FROM CardProgress WHERE cardId = :cardId"
) )
fun getCardProgress(cardId: Int): Int fun getCardProgress(cardId: Long): Int
@Insert
fun insertCardProgress(cardProgress: CardProgress)
} }
@@ -3,17 +3,17 @@ package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import com.github.nacabaro.vbhelper.domain.card.CharacterData import com.github.nacabaro.vbhelper.domain.card.CardCharacter
import com.github.nacabaro.vbhelper.domain.characters.Sprite import com.github.nacabaro.vbhelper.domain.characters.Sprite
import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.dtos.CharacterDtos
@Dao @Dao
interface CharacterDao { interface CharacterDao {
@Insert @Insert
suspend fun insertCharacter(vararg characterData: CharacterData) suspend fun insertCharacter(vararg characterData: CardCharacter)
@Query("SELECT * FROM CharacterData WHERE charaIndex = :monIndex AND cardId = :dimId LIMIT 1") @Query("SELECT * FROM CardCharacter WHERE charaIndex = :monIndex AND cardId = :dimId LIMIT 1")
fun getCharacterByMonIndex(monIndex: Int, dimId: Long): CharacterData fun getCharacterByMonIndex(monIndex: Int, dimId: Long): CardCharacter
@Insert @Insert
suspend fun insertSprite(vararg sprite: Sprite) suspend fun insertSprite(vararg sprite: Sprite)
@@ -24,10 +24,12 @@ interface CharacterDao {
d.cardId as cardId, d.cardId as cardId,
c.charaIndex as charId, c.charaIndex as charId,
c.stage as stage, c.stage as stage,
c.attribute as attribute c.attribute as attribute,
FROM CharacterData c cp.currentStage as currentStage
FROM CardCharacter c
JOIN UserCharacter uc ON c.id = uc.charId JOIN UserCharacter uc ON c.id = uc.charId
JOIN Card d ON c.cardId = d.id JOIN Card d ON c.cardId = d.id
JOIN CardProgress cp ON d.id = cp.cardId
WHERE c.id = :charId WHERE c.id = :charId
""" """
) )
@@ -37,14 +39,14 @@ interface CharacterDao {
""" """
INSERT INTO PossibleTransformations (charaId, requiredVitals, requiredTrophies, requiredBattles, requiredWinRate, changeTimerHours, requiredAdventureLevelCompleted, toCharaId) INSERT INTO PossibleTransformations (charaId, requiredVitals, requiredTrophies, requiredBattles, requiredWinRate, changeTimerHours, requiredAdventureLevelCompleted, toCharaId)
SELECT SELECT
(SELECT id FROM CharacterData WHERE charaIndex = :fromChraraIndex AND cardId = :cardId), (SELECT id FROM CardCharacter WHERE charaIndex = :fromChraraIndex AND cardId = :cardId),
:requiredVitals, :requiredVitals,
:requiredTrophies, :requiredTrophies,
:requiredBattles, :requiredBattles,
:requiredWinRate, :requiredWinRate,
:changeTimerHours, :changeTimerHours,
:requiredAdventureLevelCompleted, :requiredAdventureLevelCompleted,
(SELECT id FROM CharacterData WHERE charaIndex = :toChraraIndex AND cardId = :cardId) (SELECT id FROM CardCharacter WHERE charaIndex = :toChraraIndex AND cardId = :cardId)
""" """
) )
suspend fun insertPossibleTransformation( suspend fun insertPossibleTransformation(
@@ -76,7 +78,7 @@ interface CharacterDao {
pt.requiredAdventureLevelCompleted as requiredAdventureLevelCompleted pt.requiredAdventureLevelCompleted as requiredAdventureLevelCompleted
FROM FROM
PossibleTransformations pt PossibleTransformations pt
JOIN CharacterData c on pt.toCharaId = c.id JOIN CardCharacter c on pt.toCharaId = c.id
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
LEFT JOIN Dex d ON d.id = pt.toCharaId LEFT JOIN Dex d ON d.id = pt.toCharaId
WHERE WHERE
@@ -11,7 +11,7 @@ interface DexDao {
""" """
INSERT OR IGNORE INTO Dex(id, discoveredOn) INSERT OR IGNORE INTO Dex(id, discoveredOn)
VALUES ( VALUES (
(SELECT id FROM CharacterData WHERE charaIndex = :charIndex AND cardId = :cardId), (SELECT id FROM CardCharacter WHERE charaIndex = :charIndex AND cardId = :cardId),
:discoveredOn :discoveredOn
) )
""" """
@@ -34,7 +34,7 @@ interface DexDao {
c.baseAp as baseAp, c.baseAp as baseAp,
c.stage as stage, c.stage as stage,
c.attribute as attribute c.attribute as attribute
FROM CharacterData c FROM CardCharacter c
JOIN Sprite s ON c.spriteId = s.id JOIN Sprite s ON c.spriteId = s.id
LEFT JOIN dex d ON c.id = d.id LEFT JOIN dex d ON c.id = d.id
WHERE c.cardId = :cardId WHERE c.cardId = :cardId
@@ -50,8 +50,8 @@ interface DexDao {
c.logo as cardLogo, c.logo as cardLogo,
c.logoWidth as logoWidth, c.logoWidth as logoWidth,
c.logoHeight as logoHeight, c.logoHeight as logoHeight,
(SELECT COUNT(*) FROM CharacterData cc WHERE cc.cardId = c.id) AS totalCharacters, (SELECT COUNT(*) FROM CardCharacter cc WHERE cc.cardId = c.id) AS totalCharacters,
(SELECT COUNT(*) FROM Dex d JOIN CharacterData cc ON d.id = cc.id WHERE cc.cardId = c.id AND d.discoveredOn IS NOT NULL) AS obtainedCharacters (SELECT COUNT(*) FROM Dex d JOIN CardCharacter cc ON d.id = cc.id WHERE cc.cardId = c.id AND d.discoveredOn IS NOT NULL) AS obtainedCharacters
FROM Card c FROM Card c
""" """
) )
@@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.daos
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos
import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface ItemDao { interface ItemDao {
@@ -13,7 +14,7 @@ interface ItemDao {
ORDER BY Items.itemIcon ASC ORDER BY Items.itemIcon ASC
""" """
) )
suspend fun getAllItems(): List<ItemDtos.ItemsWithQuantities> fun getAllItems(): Flow<List<ItemDtos.ItemsWithQuantities>>
@Query( @Query(
""" """
@@ -22,7 +23,7 @@ interface ItemDao {
WHERE quantity > 0 WHERE quantity > 0
""" """
) )
suspend fun getAllUserItems(): List<ItemDtos.ItemsWithQuantities> fun getAllUserItems(): Flow<List<ItemDtos.ItemsWithQuantities>>
@Query( @Query(
""" """
@@ -5,7 +5,7 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import androidx.room.Upsert import androidx.room.Upsert
import com.github.nacabaro.vbhelper.domain.card.CharacterData import com.github.nacabaro.vbhelper.domain.card.CardCharacter
import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter import com.github.nacabaro.vbhelper.domain.device_data.UserCharacter
import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData import com.github.nacabaro.vbhelper.domain.device_data.BECharacterData
import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions import com.github.nacabaro.vbhelper.domain.device_data.SpecialMissions
@@ -47,7 +47,7 @@ interface UserCharacterDao {
c.charaIndex AS monIndex, c.charaIndex AS monIndex,
t.transformationDate AS transformationDate t.transformationDate AS transformationDate
FROM TransformationHistory t FROM TransformationHistory t
JOIN CharacterData c ON c.id = t.stageId JOIN CardCharacter c ON c.id = t.stageId
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
WHERE monId = :monId WHERE monId = :monId
""" """
@@ -70,7 +70,7 @@ interface UserCharacterDao {
d.isBEm as isBemCard, d.isBEm as isBemCard,
a.characterId = uc.id as isInAdventure a.characterId = uc.id as isInAdventure
FROM UserCharacter uc FROM UserCharacter uc
JOIN CharacterData c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON d.id = c.cardId JOIN Card d ON d.id = c.cardId
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
LEFT JOIN Adventure a ON a.characterId = uc.id LEFT JOIN Adventure a ON a.characterId = uc.id
@@ -94,7 +94,7 @@ interface UserCharacterDao {
d.isBEm as isBemCard, d.isBEm as isBemCard,
a.characterId = uc.id as isInAdventure a.characterId = uc.id as isInAdventure
FROM UserCharacter uc FROM UserCharacter uc
JOIN CharacterData c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON c.cardId = d.id JOIN Card d ON c.cardId = d.id
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
LEFT JOIN Adventure a ON a.characterId = uc.id LEFT JOIN Adventure a ON a.characterId = uc.id
@@ -131,7 +131,7 @@ interface UserCharacterDao {
d.isBEm as isBemCard, d.isBEm as isBemCard,
a.characterId as isInAdventure a.characterId as isInAdventure
FROM UserCharacter uc FROM UserCharacter uc
JOIN CharacterData c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON c.cardId = d.id JOIN Card d ON c.cardId = d.id
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
LEFT JOIN Adventure a ON a.characterId = uc.id LEFT JOIN Adventure a ON a.characterId = uc.id
@@ -153,13 +153,13 @@ interface UserCharacterDao {
@Query( @Query(
""" """
SELECT c.* SELECT c.*
FROM CharacterData c FROM CardCharacter c
join UserCharacter uc on c.id = uc.charId join UserCharacter uc on c.id = uc.charId
where uc.id = :charId where uc.id = :charId
LIMIT 1 LIMIT 1
""" """
) )
suspend fun getCharacterInfo(charId: Long): CharacterData suspend fun getCharacterInfo(charId: Long): CardCharacter
@Query( @Query(
@@ -167,7 +167,7 @@ interface UserCharacterDao {
INSERT INTO TransformationHistory(monId, stageId, transformationDate) INSERT INTO TransformationHistory(monId, stageId, transformationDate)
VALUES VALUES
(:monId, (:monId,
(SELECT id FROM CharacterData WHERE charaIndex = :stage AND cardId = :dimId), (SELECT id FROM CardCharacter WHERE charaIndex = :stage AND cardId = :dimId),
:transformationDate) :transformationDate)
""" """
) )
@@ -195,7 +195,7 @@ interface UserCharacterDao {
d.isBEm as isBemCard, d.isBEm as isBemCard,
a.characterId = uc.id as isInAdventure a.characterId = uc.id as isInAdventure
FROM UserCharacter uc FROM UserCharacter uc
JOIN CharacterData c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON d.id = c.cardId JOIN Card d ON d.id = c.cardId
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
LEFT JOIN Adventure a ON a.characterId = uc.id LEFT JOIN Adventure a ON a.characterId = uc.id
@@ -220,7 +220,7 @@ interface UserCharacterDao {
d.isBEm as isBemCard, d.isBEm as isBemCard,
a.characterId = uc.id as isInAdventure a.characterId = uc.id as isInAdventure
FROM UserCharacter uc FROM UserCharacter uc
JOIN CharacterData c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON d.id = c.cardId JOIN Card d ON d.id = c.cardId
JOIN Sprite s ON s.id = c.spriteId JOIN Sprite s ON s.id = c.spriteId
LEFT JOIN Adventure a ON a.characterId = uc.id LEFT JOIN Adventure a ON a.characterId = uc.id
@@ -3,17 +3,21 @@ package com.github.nacabaro.vbhelper.database
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import com.github.nacabaro.vbhelper.daos.AdventureDao import com.github.nacabaro.vbhelper.daos.AdventureDao
import com.github.nacabaro.vbhelper.daos.CardAdventureDao
import com.github.nacabaro.vbhelper.daos.CharacterDao import com.github.nacabaro.vbhelper.daos.CharacterDao
import com.github.nacabaro.vbhelper.daos.DexDao import com.github.nacabaro.vbhelper.daos.DexDao
import com.github.nacabaro.vbhelper.daos.CardDao import com.github.nacabaro.vbhelper.daos.CardDao
import com.github.nacabaro.vbhelper.daos.CardFusionsDao
import com.github.nacabaro.vbhelper.daos.CardProgressDao import com.github.nacabaro.vbhelper.daos.CardProgressDao
import com.github.nacabaro.vbhelper.daos.ItemDao import com.github.nacabaro.vbhelper.daos.ItemDao
import com.github.nacabaro.vbhelper.daos.SpecialMissionDao import com.github.nacabaro.vbhelper.daos.SpecialMissionDao
import com.github.nacabaro.vbhelper.daos.SpriteDao import com.github.nacabaro.vbhelper.daos.SpriteDao
import com.github.nacabaro.vbhelper.daos.UserCharacterDao import com.github.nacabaro.vbhelper.daos.UserCharacterDao
import com.github.nacabaro.vbhelper.domain.card.Background import com.github.nacabaro.vbhelper.domain.card.Background
import com.github.nacabaro.vbhelper.domain.card.CharacterData import com.github.nacabaro.vbhelper.domain.card.CardCharacter
import com.github.nacabaro.vbhelper.domain.card.Card import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.card.CardAdventure
import com.github.nacabaro.vbhelper.domain.card.CardFusions
import com.github.nacabaro.vbhelper.domain.card.CardProgress import com.github.nacabaro.vbhelper.domain.card.CardProgress
import com.github.nacabaro.vbhelper.domain.card.PossibleTransformations import com.github.nacabaro.vbhelper.domain.card.PossibleTransformations
import com.github.nacabaro.vbhelper.domain.characters.Sprite import com.github.nacabaro.vbhelper.domain.characters.Sprite
@@ -32,7 +36,9 @@ import com.github.nacabaro.vbhelper.domain.items.Items
entities = [ entities = [
Card::class, Card::class,
CardProgress::class, CardProgress::class,
CharacterData::class, CardCharacter::class,
CardAdventure::class,
CardFusions::class,
Sprite::class, Sprite::class,
UserCharacter::class, UserCharacter::class,
BECharacterData::class, BECharacterData::class,
@@ -57,4 +63,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun adventureDao(): AdventureDao abstract fun adventureDao(): AdventureDao
abstract fun spriteDao(): SpriteDao abstract fun spriteDao(): SpriteDao
abstract fun specialMissionDao(): SpecialMissionDao abstract fun specialMissionDao(): SpecialMissionDao
abstract fun cardAdventureDao(): CardAdventureDao
abstract fun cardFusionsDao(): CardFusionsDao
} }
@@ -1,9 +1,11 @@
package com.github.nacabaro.vbhelper.di package com.github.nacabaro.vbhelper.di
import com.github.nacabaro.vbhelper.database.AppDatabase import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.source.CurrencyRepository
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
interface AppContainer { interface AppContainer {
val db: AppDatabase val db: AppDatabase
val dataStoreSecretsRepository: DataStoreSecretsRepository val dataStoreSecretsRepository: DataStoreSecretsRepository
val currencyRepository: CurrencyRepository
} }
@@ -1,20 +1,27 @@
import android.content.Context import android.content.Context
import androidx.datastore.core.DataStore import androidx.datastore.core.DataStore
import androidx.datastore.dataStore import androidx.datastore.dataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
import androidx.room.Room import androidx.room.Room
import com.github.nacabaro.vbhelper.database.AppDatabase import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.di.AppContainer import com.github.nacabaro.vbhelper.di.AppContainer
import com.github.nacabaro.vbhelper.source.CurrencyRepository
import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository import com.github.nacabaro.vbhelper.source.DataStoreSecretsRepository
import com.github.nacabaro.vbhelper.source.SecretsSerializer import com.github.nacabaro.vbhelper.source.SecretsSerializer
import com.github.nacabaro.vbhelper.source.proto.Secrets import com.github.nacabaro.vbhelper.source.proto.Secrets
private const val SECRETS_DATA_STORE_NAME = "secrets.pb" private const val SECRETS_DATA_STORE_NAME = "secrets.pb"
private const val USER_PREFERENCES_NAME = "user_preferences"
val Context.secretsStore: DataStore<Secrets> by dataStore( val Context.secretsStore: DataStore<Secrets> by dataStore(
fileName = SECRETS_DATA_STORE_NAME, fileName = SECRETS_DATA_STORE_NAME,
serializer = SecretsSerializer serializer = SecretsSerializer
) )
val Context.currencyStore: DataStore<Preferences> by preferencesDataStore(
name = USER_PREFERENCES_NAME
)
class DefaultAppContainer(private val context: Context) : AppContainer { class DefaultAppContainer(private val context: Context) : AppContainer {
override val db: AppDatabase by lazy { override val db: AppDatabase by lazy {
@@ -29,5 +36,6 @@ class DefaultAppContainer(private val context: Context) : AppContainer {
override val dataStoreSecretsRepository = DataStoreSecretsRepository(context.secretsStore) override val dataStoreSecretsRepository = DataStoreSecretsRepository(context.secretsStore)
override val currencyRepository = CurrencyRepository(context.currencyStore)
} }
@@ -0,0 +1,32 @@
package com.github.nacabaro.vbhelper.domain.card
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
foreignKeys = [
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["characterId"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = Card::class,
parentColumns = ["id"],
childColumns = ["cardId"],
onDelete = ForeignKey.CASCADE
)
]
)
data class CardAdventure(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val cardId: Long,
val characterId: Long,
val steps: Int,
val bossHp: Int,
val bossAp: Int,
val bossDp: Int,
val bossBp: Int?
)
@@ -28,7 +28,7 @@ import com.github.nacabaro.vbhelper.domain.characters.Sprite
* and monIndex. * and monIndex.
* TODO: Customs will mean this should be unique per cardName and monIndex * TODO: Customs will mean this should be unique per cardName and monIndex
*/ */
data class CharacterData ( data class CardCharacter (
@PrimaryKey(autoGenerate = true) val id: Long = 0, @PrimaryKey(autoGenerate = true) val id: Long = 0,
val cardId: Long, val cardId: Long,
val spriteId: Long, val spriteId: Long,
@@ -0,0 +1,48 @@
package com.github.nacabaro.vbhelper.domain.card
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
foreignKeys = [
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["fromCharaId"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["attribute1Fusion"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["attribute2Fusion"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["attribute3Fusion"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["attribute4Fusion"],
onDelete = ForeignKey.CASCADE
)
]
)
data class CardFusions(
@PrimaryKey(autoGenerate = true) val id: Long,
val fromCharaId: Long,
val attribute1Fusion: Long?,
val attribute2Fusion: Long?,
val attribute3Fusion: Long?,
val attribute4Fusion: Long?
)
@@ -7,13 +7,13 @@ import androidx.room.PrimaryKey
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
ForeignKey( ForeignKey(
entity = CharacterData::class, entity = CardCharacter::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["charaId"], childColumns = ["charaId"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
), ),
ForeignKey( ForeignKey(
entity = CharacterData::class, entity = CardCharacter::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["toCharaId"], childColumns = ["toCharaId"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
@@ -3,12 +3,12 @@ package com.github.nacabaro.vbhelper.domain.characters
import androidx.room.Entity import androidx.room.Entity
import androidx.room.ForeignKey import androidx.room.ForeignKey
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.github.nacabaro.vbhelper.domain.card.CharacterData import com.github.nacabaro.vbhelper.domain.card.CardCharacter
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
ForeignKey( ForeignKey(
entity = CharacterData::class, entity = CardCharacter::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["id"], childColumns = ["id"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
@@ -3,7 +3,7 @@ package com.github.nacabaro.vbhelper.domain.device_data
import androidx.room.Entity import androidx.room.Entity
import androidx.room.ForeignKey import androidx.room.ForeignKey
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.github.nacabaro.vbhelper.domain.card.CharacterData import com.github.nacabaro.vbhelper.domain.card.CardCharacter
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
@@ -14,7 +14,7 @@ import com.github.nacabaro.vbhelper.domain.card.CharacterData
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
), ),
ForeignKey( ForeignKey(
entity = CharacterData::class, entity = CardCharacter::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["stageId"], childColumns = ["stageId"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
@@ -5,12 +5,12 @@ import androidx.room.ForeignKey
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.utils.DeviceType import com.github.nacabaro.vbhelper.utils.DeviceType
import com.github.nacabaro.vbhelper.domain.card.CharacterData import com.github.nacabaro.vbhelper.domain.card.CardCharacter
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
ForeignKey( ForeignKey(
entity = CharacterData::class, entity = CardCharacter::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["charId"], childColumns = ["charId"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
@@ -10,4 +10,18 @@ object CardDtos {
val totalCharacters: Int, val totalCharacters: Int,
val obtainedCharacters: Int, val obtainedCharacters: Int,
) )
data class CardAdventureWithSprites (
val characterName: ByteArray,
val characterNameWidth: Int,
val characterNameHeight: Int,
val characterIdleSprite: ByteArray,
val characterIdleSpriteWidth: Int,
val characterIdleSpriteHeight: Int,
val characterAp: Int,
val characterBp: Int?,
val characterDp: Int,
val characterHp: Int,
val steps: Int,
)
} }
@@ -35,10 +35,11 @@ object CharacterDtos {
) )
data class CardCharacterInfo( data class CardCharacterInfo(
val cardId: Int, val cardId: Long,
val charId: Int, val charId: Int,
val stage: Int, val stage: Int,
val attribute: NfcCharacter.Attribute val attribute: NfcCharacter.Attribute,
val currentStage: Int
) )
data class TransformationHistory( data class TransformationHistory(
@@ -34,6 +34,7 @@ import com.github.nacabaro.vbhelper.screens.itemsScreen.ItemsScreenControllerImp
import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl import com.github.nacabaro.vbhelper.screens.settingsScreen.SettingsScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreen import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreen
import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl import com.github.nacabaro.vbhelper.screens.adventureScreen.AdventureScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.cardScreen.CardAdventureScreen
import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl import com.github.nacabaro.vbhelper.screens.cardScreen.CardScreenControllerImpl
import com.github.nacabaro.vbhelper.screens.settingsScreen.CreditsScreen import com.github.nacabaro.vbhelper.screens.settingsScreen.CreditsScreen
import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl import com.github.nacabaro.vbhelper.screens.spriteViewer.SpriteViewerControllerImpl
@@ -145,7 +146,7 @@ fun AppNavigation(
if (cardId != null) { if (cardId != null) {
CardViewScreen( CardViewScreen(
navController = navController, navController = navController,
dimId = cardId.toLong() cardId = cardId.toLong()
) )
} }
} }
@@ -177,6 +178,17 @@ fun AppNavigation(
navController = navController navController = navController
) )
} }
composable(NavigationItems.CardAdventure.route) {
val cardId = it.arguments?.getString("cardId")
if (cardId != null) {
CardAdventureScreen(
navController = navController,
cardId = cardId.toLong(),
cardScreenController = applicationNavigationHandlers
.cardScreenController
)
}
}
} }
} }
} }
@@ -11,6 +11,7 @@ sealed class NavigationItems (
object Battles : NavigationItems("Battle", R.drawable.baseline_swords_24, "Battle") object Battles : NavigationItems("Battle", R.drawable.baseline_swords_24, "Battle")
object Home : NavigationItems("Home", R.drawable.baseline_cottage_24, "Home") object Home : NavigationItems("Home", R.drawable.baseline_cottage_24, "Home")
object Dex : NavigationItems("Dex", R.drawable.baseline_menu_book_24, "Dex") object Dex : NavigationItems("Dex", R.drawable.baseline_menu_book_24, "Dex")
object CardAdventure : NavigationItems("CardAdventure/{cardId}", R.drawable.baseline_fort_24, "Card adventure")
object Storage : NavigationItems("Storage", R.drawable.baseline_catching_pokemon_24, "Storage") object Storage : NavigationItems("Storage", R.drawable.baseline_catching_pokemon_24, "Storage")
object Settings : NavigationItems("Settings", R.drawable.baseline_settings_24, "Settings") object Settings : NavigationItems("Settings", R.drawable.baseline_settings_24, "Settings")
object Viewer : NavigationItems("Viewer", R.drawable.baseline_image_24, "Viewer") object Viewer : NavigationItems("Viewer", R.drawable.baseline_image_24, "Viewer")
@@ -6,6 +6,7 @@ import androidx.lifecycle.lifecycleScope
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.random.Random import kotlin.random.Random
@@ -77,6 +78,7 @@ class AdventureScreenControllerImpl(
val randomItem = database val randomItem = database
.itemDao() .itemDao()
.getAllItems() .getAllItems()
.first()
.random() .random()
val random = ((Random.nextFloat() * character.stage) + 3).roundToInt() val random = ((Random.nextFloat() * character.stage) + 3).roundToInt()
@@ -0,0 +1,122 @@
package com.github.nacabaro.vbhelper.screens.cardScreen
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Card
import androidx.compose.material3.CardColors
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.github.nacabaro.vbhelper.dtos.CardDtos
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.utils.getImageBitmap
@Composable
fun CardAdventureEntry(
cardAdventureEntry: CardDtos.CardAdventureWithSprites,
obscure: Boolean
) {
val charaImageBitmapData = BitmapData(
bitmap = cardAdventureEntry.characterIdleSprite,
width = cardAdventureEntry.characterIdleSpriteWidth,
height = cardAdventureEntry.characterIdleSpriteHeight
).getImageBitmap(
context = LocalContext.current,
multiplier = 4,
obscure = obscure
)
val nameImageBitmapData = BitmapData(
bitmap = cardAdventureEntry.characterName,
width = cardAdventureEntry.characterNameWidth,
height = cardAdventureEntry.characterNameHeight
).getImageBitmap(
context = LocalContext.current,
multiplier = 3,
obscure = obscure
)
Card (
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
) {
Row (
modifier = Modifier
.padding(8.dp)
){
Card (
colors = CardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant,
contentColor = MaterialTheme.colorScheme.contentColorFor(
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
),
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
disabledContentColor = MaterialTheme.colorScheme.contentColorFor(
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
)
)
) {
Image(
bitmap = charaImageBitmapData.imageBitmap,
contentDescription = "Icon",
modifier = Modifier
.size(charaImageBitmapData.dpWidth)
.padding(8.dp),
colorFilter = when (obscure) {
true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
false -> null
},
filterQuality = FilterQuality.None
)
}
Spacer(modifier = Modifier.padding(8.dp))
Column {
if (!obscure) {
Image(
bitmap = nameImageBitmapData.imageBitmap,
contentDescription = "Icon",
modifier = Modifier
.width(nameImageBitmapData.dpWidth)
.height(nameImageBitmapData.dpHeight),
filterQuality = FilterQuality.None
)
Spacer(modifier = Modifier.padding(4.dp))
Text(
text = "HP: ${cardAdventureEntry.characterHp}, DP: ${cardAdventureEntry.characterDp}, AP: ${cardAdventureEntry.characterAp}"
)
if (cardAdventureEntry.characterBp != null) {
Text(text = "BP: ${cardAdventureEntry.characterBp}")
}
Text(text = "Steps: ${cardAdventureEntry.steps}")
} else {
Text(text = "????????????????")
Text(
text = "HP: -, BP: -, AP: -"
)
if (cardAdventureEntry.characterBp != null) {
Text(text = "DP: -")
}
Text(text = "Steps: -")
}
}
}
}
}
@@ -0,0 +1,66 @@
package com.github.nacabaro.vbhelper.screens.cardScreen
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.dtos.CardDtos
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun CardAdventureScreen(
navController: NavController,
cardScreenController: CardScreenControllerImpl,
cardId: Long
) {
val cardAdventureMissions = remember { mutableStateOf(emptyList<CardDtos.CardAdventureWithSprites>()) }
var currentCardAdventure by remember { mutableIntStateOf(0) }
LaunchedEffect(cardId) {
withContext(Dispatchers.IO) {
cardAdventureMissions.value =
cardScreenController
.getCardAdventureMissions(cardId)
currentCardAdventure =
cardScreenController
.getCardProgress(cardId)
}
}
Scaffold (
topBar = {
TopBanner(
text = "Adventure missions",
onBackClick = {
navController.popBackStack()
}
)
}
) { contentPadding ->
Column (
modifier = Modifier
.padding(top = contentPadding.calculateTopPadding())
.verticalScroll(state = rememberScrollState())
) {
cardAdventureMissions.value.mapIndexed { index, it ->
CardAdventureEntry(
cardAdventureEntry = it,
obscure = index > currentCardAdventure - 1
)
}
}
}
}
@@ -1,6 +1,10 @@
package com.github.nacabaro.vbhelper.screens.cardScreen package com.github.nacabaro.vbhelper.screens.cardScreen
import com.github.nacabaro.vbhelper.dtos.CardDtos
interface CardScreenController { interface CardScreenController {
fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit) fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit)
fun deleteCard(cardId: Long, onDeleted: () -> Unit) fun deleteCard(cardId: Long, onDeleted: () -> Unit)
suspend fun getCardAdventureMissions(cardId: Long): List<CardDtos.CardAdventureWithSprites>
suspend fun getCardProgress(cardId: Long): Int
} }
@@ -3,6 +3,7 @@ package com.github.nacabaro.vbhelper.screens.cardScreen
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.CardDtos
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class CardScreenControllerImpl( class CardScreenControllerImpl(
@@ -11,7 +12,6 @@ class CardScreenControllerImpl(
private val application = componentActivity.applicationContext as VBHelper private val application = componentActivity.applicationContext as VBHelper
private val database = application.container.db private val database = application.container.db
override fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit) { override fun renameCard(cardId: Long, newName: String, onRenamed: (String) -> Unit) {
componentActivity.lifecycleScope.launch { componentActivity.lifecycleScope.launch {
database database
@@ -31,4 +31,16 @@ class CardScreenControllerImpl(
onDeleted() onDeleted()
} }
} }
override suspend fun getCardAdventureMissions(cardId: Long): List<CardDtos.CardAdventureWithSprites> {
return database
.cardAdventureDao()
.getAdventureForCard(cardId)
}
override suspend fun getCardProgress(cardId: Long): Int {
return database
.cardProgressDao()
.getCardProgress(cardId)
}
} }
@@ -16,6 +16,7 @@ import com.github.nacabaro.vbhelper.components.CharacterEntry
import com.github.nacabaro.vbhelper.components.TopBanner import com.github.nacabaro.vbhelper.components.TopBanner
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.CharacterDtos import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.DexCharaDetailsDialog import com.github.nacabaro.vbhelper.screens.cardScreen.dialogs.DexCharaDetailsDialog
import com.github.nacabaro.vbhelper.source.DexRepository import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -23,7 +24,7 @@ import kotlinx.coroutines.launch
@Composable @Composable
fun CardViewScreen( fun CardViewScreen(
navController: NavController, navController: NavController,
dimId: Long cardId: Long
) { ) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val application = LocalContext.current.applicationContext as VBHelper val application = LocalContext.current.applicationContext as VBHelper
@@ -36,10 +37,10 @@ fun CardViewScreen(
LaunchedEffect(dexRepository) { LaunchedEffect(dexRepository) {
coroutineScope.launch { coroutineScope.launch {
val newCharacterList = dexRepository.getCharactersByCardId(dimId) val newCharacterList = dexRepository.getCharactersByCardId(cardId)
characterList.value = newCharacterList characterList.value = newCharacterList
val newCardPossibleTransformations = dexRepository.getCardPossibleTransformations(dimId) val newCardPossibleTransformations = dexRepository.getCardPossibleTransformations(cardId)
cardPossibleTransformations.value = newCardPossibleTransformations cardPossibleTransformations.value = newCardPossibleTransformations
} }
} }
@@ -50,6 +51,17 @@ fun CardViewScreen(
text = "Discovered characters", text = "Discovered characters",
onBackClick = { onBackClick = {
navController.popBackStack() navController.popBackStack()
},
onAdventureClick = {
navController
.navigate(route = NavigationItems
.CardAdventure
.route
.replace(
"{cardId}",
cardId.toString()
)
)
} }
) )
} }
@@ -5,6 +5,7 @@ import androidx.lifecycle.lifecycleScope
import com.github.cfogrady.vbnfc.vb.SpecialMission import com.github.cfogrady.vbnfc.vb.SpecialMission
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.time.Instant import java.time.Instant
import kotlin.math.roundToInt import kotlin.math.roundToInt
@@ -41,6 +42,7 @@ class HomeScreenControllerImpl(
val randomItem = database val randomItem = database
.itemDao() .itemDao()
.getAllItems() .getAllItems()
.first()
.random() .random()
val randomItemAmount = (Random.nextFloat() * 5).roundToInt() val randomItemAmount = (Random.nextFloat() * 5).roundToInt()
@@ -0,0 +1,164 @@
package com.github.nacabaro.vbhelper.screens.itemsScreen
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.github.nacabaro.vbhelper.R
import com.github.nacabaro.vbhelper.domain.items.ItemType
import com.github.nacabaro.vbhelper.dtos.ItemDtos
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
@Composable
fun ItemDialog(
item: ItemDtos.ItemsWithQuantities,
onClickCancel: () -> Unit,
onClickUse: (() -> Unit)? = null,
onClickPurchase: (() -> Unit)? = null,
) {
Dialog(
onDismissRequest = onClickCancel,
properties = DialogProperties(
dismissOnBackPress = true,
dismissOnClickOutside = true
)
) {
Card (
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Column (
modifier = Modifier
.padding(16.dp)
) {
Row {
Box(modifier = Modifier) {
// Background image (full size)
Icon(
painter = painterResource(id = getIconResource(item.itemIcon)),
contentDescription = null,
modifier = Modifier
.size(96.dp)
.align(Alignment.Center)
)
Icon(
painter = painterResource(id = getLengthResource(item.itemLength)),
contentDescription = null,
tint = MaterialTheme.colorScheme.outline,
modifier = Modifier
.size(64.dp) // Set the size of the overlay image
.align(Alignment.BottomEnd) // Align to the top end (top-right corner)
)
}
Column (
modifier = Modifier
.padding(16.dp)
) {
Text(
fontSize = MaterialTheme.typography.titleLarge.fontSize,
text = item.name,
modifier = Modifier
.fillMaxWidth()
)
}
}
Text(
textAlign = TextAlign.Center,
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
fontFamily = MaterialTheme.typography.bodyMedium.fontFamily,
text = item.description,
modifier = Modifier
.fillMaxWidth()
.padding(4.dp)
)
if (onClickPurchase != null) {
Text(
textAlign = TextAlign.Center,
fontSize = MaterialTheme.typography.bodySmall.fontSize,
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
text = "Costs ${item.price} credits",
modifier = Modifier
.fillMaxWidth()
)
}
Text(
textAlign = TextAlign.Center,
fontSize = MaterialTheme.typography.bodySmall.fontSize,
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
text = "You have ${item.quantity} of this item",
modifier = Modifier
.fillMaxWidth()
)
Row (
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
) {
if (onClickUse != null) {
Button(
onClick = onClickUse
) {
Text("Use item")
}
}
if (onClickPurchase != null) {
Button(
onClick = onClickPurchase
) {
Text("Purchase")
}
}
Spacer(modifier = Modifier.size(8.dp))
Button(
onClick = onClickCancel
) {
Text("Cancel")
}
}
}
}
}
}
@Composable
@Preview(showBackground = true)
fun PreviewItemDialog() {
VBHelperTheme {
ItemDialog(
item = ItemDtos.ItemsWithQuantities(
name = "AP Training x3 (60 min)",
description = "Boosts AP during training (for 60 minutes)",
itemIcon = R.drawable.baseline_attack_24,
itemLength = R.drawable.baseline_60_min_timer,
quantity = 19,
id = 1,
price = 500,
itemType = ItemType.BEITEM
),
onClickUse = { },
onClickCancel = { }
)
}
}
@@ -1,36 +1,23 @@
package com.github.nacabaro.vbhelper.screens.itemsScreen package com.github.nacabaro.vbhelper.screens.itemsScreen
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import com.github.nacabaro.vbhelper.dtos.ItemDtos
import androidx.compose.ui.window.DialogProperties
import com.github.nacabaro.vbhelper.R
import com.github.nacabaro.vbhelper.ui.theme.VBHelperTheme
@Composable @Composable
fun ItemElement( fun ItemElement(
itemIcon: Int, item: ItemDtos.ItemsWithQuantities,
lengthIcon: Int,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onClick: (() -> Unit) = { } onClick: (() -> Unit) = { }
) { ) {
@@ -41,7 +28,7 @@ fun ItemElement(
) { ) {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
Icon( Icon(
painter = painterResource(id = itemIcon), painter = painterResource(id = getIconResource(item.itemIcon)),
contentDescription = null, contentDescription = null,
modifier = Modifier modifier = Modifier
.size(96.dp) .size(96.dp)
@@ -49,164 +36,14 @@ fun ItemElement(
.padding(16.dp) .padding(16.dp)
) )
Icon( Icon(
painter = painterResource(id = lengthIcon), painter = painterResource(id = getLengthResource(item.itemLength)),
contentDescription = null, contentDescription = null,
tint = MaterialTheme.colorScheme.surfaceTint, tint = MaterialTheme.colorScheme.surfaceTint,
modifier = Modifier modifier = Modifier
.size(48.dp) // Set the size of the overlay image .size(48.dp)
.align(Alignment.TopStart) // Align to the top end (top-right corner) .align(Alignment.TopStart)
.padding(8.dp) .padding(8.dp)
) )
} }
} }
} }
@Composable
fun ItemDialog(
name: String,
description: String,
itemIcon: Int,
lengthIcon: Int,
amount: Int,
onClickUse: () -> Unit,
onClickCancel: () -> Unit
) {
Dialog(
onDismissRequest = onClickCancel,
properties = DialogProperties(
dismissOnBackPress = true,
dismissOnClickOutside = true
)
) {
Card (
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Column (
modifier = Modifier
.padding(16.dp)
) {
Row {
Box(modifier = Modifier) {
// Background image (full size)
Icon(
painter = painterResource(id = itemIcon),
contentDescription = null,
modifier = Modifier
.size(96.dp)
.align(Alignment.Center)
)
Icon(
painter = painterResource(id = lengthIcon),
contentDescription = null,
tint = MaterialTheme.colorScheme.outline,
modifier = Modifier
.size(64.dp) // Set the size of the overlay image
.align(Alignment.BottomEnd) // Align to the top end (top-right corner)
)
}
Column (
modifier = Modifier
.padding(16.dp)
) {
Text(
fontSize = MaterialTheme.typography.titleLarge.fontSize,
text = name,
modifier = Modifier
.fillMaxWidth()
)
}
}
Text(
textAlign = TextAlign.Center,
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
fontFamily = MaterialTheme.typography.bodyMedium.fontFamily,
text = description,
modifier = Modifier
.fillMaxWidth()
.padding(4.dp)
)
Text(
textAlign = TextAlign.Center,
fontSize = MaterialTheme.typography.bodySmall.fontSize,
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
text = "You have $amount of this item",
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
)
Row (
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
) {
Button(
onClick = onClickUse
) {
Text("Use item")
}
Spacer(modifier = Modifier.size(8.dp))
Button(
onClick = onClickCancel
) {
Text("Cancel")
}
}
}
}
}
}
fun getIconResource(index: Int): Int {
return when (index) {
ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> R.drawable.baseline_agility_24
ItemsScreenControllerImpl.ItemTypes.APTraining.id -> R.drawable.baseline_attack_24
ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> R.drawable.baseline_shield_24
ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> R.drawable.baseline_trophy_24
ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> R.drawable.baseline_arrow_up_24
6 -> R.drawable.baseline_timer_24
7 -> R.drawable.baseline_rank_24
8 -> R.drawable.baseline_vitals_24
else -> R.drawable.baseline_question_mark_24
}
}
fun getLengthResource(index: Int): Int {
return when (index) {
15 -> R.drawable.baseline_15_min_timer
30 -> R.drawable.baseline_30_min_timer
60 -> R.drawable.baseline_60_min_timer
-60 -> R.drawable.baseline_60_min_timer
300 -> R.drawable.baseline_5_hour_timer
600 -> R.drawable.baseline_10_hour_timer
-720 -> R.drawable.baseline_12_hour_timer
-1440 -> R.drawable.baseline_24_hour_timer
6000 -> R.drawable.baseline_reset_24
1000 -> R.drawable.baseline_single_arrow_up
2500 -> R.drawable.baseline_double_arrow_up
5000 -> R.drawable.baseline_triple_arrow_up
9999 -> R.drawable.baseline_health_24
-500 -> R.drawable.baseline_single_arrow_down
-1000 -> R.drawable.baseline_double_arrow_down
-2500 -> R.drawable.baseline_triple_arrow_down
-9999 -> R.drawable.baseline_reset_24
else -> R.drawable.baseline_question_mark_24
}
}
@Composable
@Preview(showBackground = true)
fun PreviewItemDialog() {
VBHelperTheme {
ItemDialog(
name = "AP Training x3 (60 min)",
description = "Boosts AP during training (for 60 minutes)",
itemIcon = R.drawable.baseline_attack_24,
lengthIcon = R.drawable.baseline_60_min_timer,
onClickUse = { },
onClickCancel = { },
amount = 19
)
}
}
@@ -0,0 +1,40 @@
package com.github.nacabaro.vbhelper.screens.itemsScreen
import com.github.nacabaro.vbhelper.R
fun getIconResource(index: Int): Int {
return when (index) {
ItemsScreenControllerImpl.ItemTypes.PPTraining.id -> R.drawable.baseline_agility_24
ItemsScreenControllerImpl.ItemTypes.APTraining.id -> R.drawable.baseline_attack_24
ItemsScreenControllerImpl.ItemTypes.HPTraining.id -> R.drawable.baseline_shield_24
ItemsScreenControllerImpl.ItemTypes.BPTraining.id -> R.drawable.baseline_trophy_24
ItemsScreenControllerImpl.ItemTypes.AllTraining.id -> R.drawable.baseline_arrow_up_24
6 -> R.drawable.baseline_timer_24
7 -> R.drawable.baseline_rank_24
8 -> R.drawable.baseline_vitals_24
else -> R.drawable.baseline_question_mark_24
}
}
fun getLengthResource(index: Int): Int {
return when (index) {
15 -> R.drawable.baseline_15_min_timer
30 -> R.drawable.baseline_30_min_timer
60 -> R.drawable.baseline_60_min_timer
-60 -> R.drawable.baseline_60_min_timer
300 -> R.drawable.baseline_5_hour_timer
600 -> R.drawable.baseline_10_hour_timer
-720 -> R.drawable.baseline_12_hour_timer
-1440 -> R.drawable.baseline_24_hour_timer
6000 -> R.drawable.baseline_reset_24
1000 -> R.drawable.baseline_single_arrow_up
2500 -> R.drawable.baseline_double_arrow_up
5000 -> R.drawable.baseline_triple_arrow_up
9999 -> R.drawable.baseline_health_24
-500 -> R.drawable.baseline_single_arrow_down
-1000 -> R.drawable.baseline_double_arrow_down
-2500 -> R.drawable.baseline_triple_arrow_down
-9999 -> R.drawable.baseline_reset_24
else -> R.drawable.baseline_question_mark_24
}
}
@@ -1,25 +1,35 @@
package com.github.nacabaro.vbhelper.screens.itemsScreen package com.github.nacabaro.vbhelper.screens.itemsScreen
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.Card
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavController import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos
import com.github.nacabaro.vbhelper.source.CurrencyRepository
import com.github.nacabaro.vbhelper.source.ItemsRepository import com.github.nacabaro.vbhelper.source.ItemsRepository
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext import kotlinx.coroutines.launch
@Composable @Composable
fun ItemsStore( fun ItemsStore(
@@ -27,45 +37,98 @@ fun ItemsStore(
) { ) {
val application = LocalContext.current.applicationContext as VBHelper val application = LocalContext.current.applicationContext as VBHelper
val itemsRepository = ItemsRepository(application.container.db) val itemsRepository = ItemsRepository(application.container.db)
val myItems = remember { mutableStateOf(emptyList<ItemDtos.ItemsWithQuantities>()) } val myItems by itemsRepository.getAllItems().collectAsState(emptyList())
var selectedElementIndex by remember { mutableStateOf<Int?>(null) } var selectedElementIndex by remember { mutableStateOf<Int?>(null) }
LaunchedEffect(itemsRepository) { val currencyRepository = application.container.currencyRepository
withContext(Dispatchers.IO) { val currentCurrency = currencyRepository.currencyValue.collectAsState(0)
myItems.value = itemsRepository.getAllItems()
val scope = rememberCoroutineScope()
if (myItems.isEmpty()) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
Text("No items")
} }
} else {
Column() {
Card(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
) {
Text(
text = "${currentCurrency.value} credits",
modifier = Modifier
.padding(8.dp)
)
} }
if (myItems.value.isEmpty()) {
Text("No items")
} else {
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(3), columns = GridCells.Fixed(3),
modifier = Modifier modifier = Modifier
) { ) {
items(myItems.value) { index -> items(myItems) { index ->
ItemElement( ItemElement(
itemIcon = getIconResource(index.itemIcon), item = index,
lengthIcon = getLengthResource(index.itemLength),
modifier = Modifier modifier = Modifier
.padding(8.dp), .padding(8.dp),
onClick = { onClick = {
selectedElementIndex = myItems.value.indexOf(index) selectedElementIndex = myItems.indexOf(index)
} }
) )
} }
} }
}
}
if (selectedElementIndex != null) { if (selectedElementIndex != null) {
ItemDialog( ItemDialog(
name = myItems.value[selectedElementIndex!!].name, item = myItems[selectedElementIndex!!],
description = myItems.value[selectedElementIndex!!].description, onClickPurchase = {
itemIcon = getIconResource(myItems.value[selectedElementIndex!!].itemIcon), scope.launch {
lengthIcon = getLengthResource(myItems.value[selectedElementIndex!!].itemLength), Toast.makeText(
amount = myItems.value[selectedElementIndex!!].quantity, application.applicationContext,
onClickUse = { }, purchaseItem(
application.container.db,
myItems[selectedElementIndex!!],
currencyRepository
),
Toast.LENGTH_SHORT
).show(
)
}
},
onClickCancel = { selectedElementIndex = null } onClickCancel = { selectedElementIndex = null }
) )
} }
}
suspend fun purchaseItem(
db: AppDatabase,
item: ItemDtos.ItemsWithQuantities,
currencyRepository: CurrencyRepository
): String {
if (currencyRepository.currencyValue.first() < item.price) {
return "Not enough credits"
} else {
db
.itemDao()
.purchaseItem(
item.id,
1
)
currencyRepository
.setCurrencyValue(
currencyRepository.currencyValue.first() - item.price
)
return "Purchase successful!"
} }
} }
@@ -9,7 +9,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -20,11 +20,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavController import androidx.navigation.NavController
import com.github.nacabaro.vbhelper.di.VBHelper import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.dtos.ItemDtos
import com.github.nacabaro.vbhelper.navigation.NavigationItems import com.github.nacabaro.vbhelper.navigation.NavigationItems
import com.github.nacabaro.vbhelper.source.ItemsRepository import com.github.nacabaro.vbhelper.source.ItemsRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable @Composable
fun MyItems( fun MyItems(
@@ -32,16 +29,11 @@ fun MyItems(
) { ) {
val application = LocalContext.current.applicationContext as VBHelper val application = LocalContext.current.applicationContext as VBHelper
val itemsRepository = ItemsRepository(application.container.db) val itemsRepository = ItemsRepository(application.container.db)
val myItems = remember { mutableStateOf(emptyList<ItemDtos.ItemsWithQuantities>()) } val myItems by itemsRepository.getUserItems().collectAsState(emptyList())
var selectedElementIndex by remember { mutableStateOf<Int?>(null) } var selectedElementIndex by remember { mutableStateOf<Int?>(null) }
LaunchedEffect(itemsRepository) { if (myItems.isEmpty()) {
withContext(Dispatchers.IO) {
myItems.value = itemsRepository.getUserItems()
}
}
if (myItems.value.isEmpty()) {
Column( Column(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
@@ -54,14 +46,13 @@ fun MyItems(
columns = GridCells.Fixed(3), columns = GridCells.Fixed(3),
modifier = Modifier modifier = Modifier
) { ) {
items(myItems.value) { index -> items(myItems) { index ->
ItemElement( ItemElement(
itemIcon = getIconResource(index.itemIcon), item = index,
lengthIcon = getLengthResource(index.itemLength),
modifier = Modifier modifier = Modifier
.padding(8.dp), .padding(8.dp),
onClick = { onClick = {
selectedElementIndex = myItems.value.indexOf(index) selectedElementIndex = myItems.indexOf(index)
} }
) )
} }
@@ -69,11 +60,7 @@ fun MyItems(
if (selectedElementIndex != null) { if (selectedElementIndex != null) {
ItemDialog( ItemDialog(
name = myItems.value[selectedElementIndex!!].name, item = myItems[selectedElementIndex!!],
description = myItems.value[selectedElementIndex!!].description,
itemIcon = getIconResource(myItems.value[selectedElementIndex!!].itemIcon),
lengthIcon = getLengthResource(myItems.value[selectedElementIndex!!].itemLength),
amount = myItems.value[selectedElementIndex!!].quantity,
onClickUse = { onClickUse = {
navController navController
.navigate( .navigate(
@@ -81,7 +68,7 @@ fun MyItems(
.ApplyItem.route .ApplyItem.route
.replace( .replace(
"{itemId}", "{itemId}",
myItems.value[selectedElementIndex!!].id.toString() myItems[selectedElementIndex!!].id.toString()
) )
) )
selectedElementIndex = null selectedElementIndex = null
@@ -152,15 +152,13 @@ class FromNfcConverter (
nfcCharacter: NfcCharacter, nfcCharacter: NfcCharacter,
cardData: Card cardData: Card
) { ) {
val currentCardProgress = CardProgress(
cardId = cardData.id,
currentStage = nfcCharacter.nextAdventureMissionStage.toInt(),
unlocked = nfcCharacter.nextAdventureMissionStage.toInt() > cardData.stageCount
)
database database
.cardProgressDao() .cardProgressDao()
.updateDimProgress(currentCardProgress) .updateCardProgress(
currentStage = nfcCharacter.nextAdventureMissionStage.toInt(),
cardId = cardData.id,
unlocked = nfcCharacter.nextAdventureMissionStage.toInt() > cardData.stageCount,
)
} }
@@ -38,14 +38,10 @@ class ToNfcConverter(
.characterDao() .characterDao()
.getCharacterInfo(userCharacter.charId) .getCharacterInfo(userCharacter.charId)
val currentCardStage = database
.cardProgressDao()
.getCardProgress(characterInfo.cardId)
return if (userCharacter.characterType == DeviceType.BEDevice) return if (userCharacter.characterType == DeviceType.BEDevice)
nfcToBENfc(characterId, characterInfo, currentCardStage, userCharacter) nfcToBENfc(characterId, characterInfo, userCharacter)
else else
nfcToVBNfc(characterId, characterInfo, currentCardStage, userCharacter) nfcToVBNfc(characterId, characterInfo, userCharacter)
} }
@@ -53,7 +49,6 @@ class ToNfcConverter(
private suspend fun nfcToVBNfc( private suspend fun nfcToVBNfc(
characterId: Long, characterId: Long,
characterInfo: CharacterDtos.CardCharacterInfo, characterInfo: CharacterDtos.CardCharacterInfo,
currentCardStage: Int,
userCharacter: UserCharacter userCharacter: UserCharacter
): VBNfcCharacter { ): VBNfcCharacter {
val vbData = database val vbData = database
@@ -70,7 +65,7 @@ class ToNfcConverter(
stage = characterInfo.stage.toByte(), stage = characterInfo.stage.toByte(),
attribute = characterInfo.attribute, attribute = characterInfo.attribute,
ageInDays = userCharacter.ageInDays.toByte(), ageInDays = userCharacter.ageInDays.toByte(),
nextAdventureMissionStage = currentCardStage.toByte(), nextAdventureMissionStage = characterInfo.currentStage.toByte(),
mood = userCharacter.mood.toByte(), mood = userCharacter.mood.toByte(),
vitalPoints = userCharacter.vitalPoints.toUShort(), vitalPoints = userCharacter.vitalPoints.toUShort(),
transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(), transformationCountdownInMinutes = userCharacter.transformationCountdown.toUShort(),
@@ -173,7 +168,6 @@ class ToNfcConverter(
private suspend fun nfcToBENfc( private suspend fun nfcToBENfc(
characterId: Long, characterId: Long,
characterInfo: CharacterDtos.CardCharacterInfo, characterInfo: CharacterDtos.CardCharacterInfo,
currentCardStage: Int,
userCharacter: UserCharacter userCharacter: UserCharacter
): BENfcCharacter { ): BENfcCharacter {
val beData = database val beData = database
@@ -188,7 +182,7 @@ class ToNfcConverter(
stage = characterInfo.stage.toByte(), stage = characterInfo.stage.toByte(),
attribute = characterInfo.attribute, attribute = characterInfo.attribute,
ageInDays = userCharacter.ageInDays.toByte(), ageInDays = userCharacter.ageInDays.toByte(),
nextAdventureMissionStage = currentCardStage.toByte(), nextAdventureMissionStage = characterInfo.currentStage.toByte(),
mood = userCharacter.mood.toByte(), mood = userCharacter.mood.toByte(),
vitalPoints = userCharacter.vitalPoints.toUShort(), vitalPoints = userCharacter.vitalPoints.toUShort(),
itemEffectMentalStateValue = beData.itemEffectMentalStateValue.toByte(), itemEffectMentalStateValue = beData.itemEffectMentalStateValue.toByte(),
@@ -18,7 +18,7 @@ import com.github.nacabaro.vbhelper.di.VBHelper
import com.github.nacabaro.vbhelper.domain.characters.Sprite import com.github.nacabaro.vbhelper.domain.characters.Sprite
import com.github.nacabaro.vbhelper.domain.card.Card import com.github.nacabaro.vbhelper.domain.card.Card
import com.github.nacabaro.vbhelper.domain.card.CardProgress import com.github.nacabaro.vbhelper.domain.card.CardProgress
import com.github.nacabaro.vbhelper.domain.card.CharacterData import com.github.nacabaro.vbhelper.domain.card.CardCharacter
import com.github.nacabaro.vbhelper.source.ApkSecretsImporter import com.github.nacabaro.vbhelper.source.ApkSecretsImporter
import com.github.nacabaro.vbhelper.source.SecretsImporter import com.github.nacabaro.vbhelper.source.SecretsImporter
import com.github.nacabaro.vbhelper.source.SecretsRepository import com.github.nacabaro.vbhelper.source.SecretsRepository
@@ -192,7 +192,7 @@ class SettingsScreenControllerImpl(
false -> 10 false -> 10
} }
val domainCharacters = mutableListOf<CharacterData>() val domainCharacters = mutableListOf<CardCharacter>()
val characters = card val characters = card
.characterStats .characterStats
@@ -242,7 +242,7 @@ class SettingsScreenControllerImpl(
domainCharacters.add( domainCharacters.add(
CharacterData( CardCharacter(
cardId = cardId, cardId = cardId,
spriteId = spriteId, spriteId = spriteId,
charaIndex = index, charaIndex = index,
@@ -273,18 +273,78 @@ class SettingsScreenControllerImpl(
.insertCharacter(*domainCharacters.toTypedArray()) .insertCharacter(*domainCharacters.toTypedArray())
} }
private suspend fun importAdventureMissions(
cardId: Long,
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
) {
Log.d("importAdventureMissions", "Importing adventure missions")
if (card is BemCard) {
card.adventureLevels.levels.forEach {
database
.cardAdventureDao()
.insertNewAdventure(
cardId = cardId,
characterId = it.bossCharacterIndex,
steps = it.steps,
bossAp = it.bossAp,
bossHp = it.bossHp,
bossDp = it.bossDp,
bossBp = it.bossBp
)
}
} else if (card is DimCard) {
card.adventureLevels.levels.map {
database
.cardAdventureDao()
.insertNewAdventure(
cardId = cardId,
characterId = it.bossCharacterIndex,
steps = it.steps,
bossAp = it.bossAp,
bossHp = it.bossHp,
bossDp = it.bossDp,
bossBp = null
)
}
}
}
private suspend fun importCardFusions(
cardId: Long,
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
) {
Log.d("importCardFusions", "Importing card fusions")
if (card is DimCard) {
card
.attributeFusions
.entries
.forEach {
database
.cardFusionsDao()
.insertNewFusion(
cardId = cardId,
fromCharaId = it.characterIndex,
toCharaIdAttr1 = it.attribute1Fusion,
toCharaIdAttr2 = it.attribute2Fusion,
toCharaIdAttr3 = it.attribute3Fusion,
toCharaIdAttr4 = it.attribute4Fusion
)
}
}
}
private fun updateCardProgress( private fun updateCardProgress(
cardId: Long, cardId: Long,
) { ) {
val cardProgress = CardProgress(
cardId = cardId,
currentStage = 0,
unlocked = false
)
database database
.cardProgressDao() .cardProgressDao()
.updateDimProgress(cardProgress) .insertCardProgress(
CardProgress(
cardId = cardId,
currentStage = 1,
unlocked = false
)
)
} }
private fun importCard(uri: Uri) { private fun importCard(uri: Uri) {
@@ -315,6 +375,10 @@ class SettingsScreenControllerImpl(
importCharacterData(cardId, card) importCharacterData(cardId, card)
importEvoData(cardId, card) importEvoData(cardId, card)
importAdventureMissions(cardId, card)
importCardFusions(cardId, card)
} }
inputStream?.close() inputStream?.close()
@@ -0,0 +1,28 @@
package com.github.nacabaro.vbhelper.source
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class CurrencyRepository (
private val dataStore: DataStore<Preferences>
) {
private companion object {
val CURRENCY_VALUE = intPreferencesKey("currency_value")
}
val currencyValue: Flow<Int> = dataStore.data
.map { preferences ->
preferences[CURRENCY_VALUE] ?: 10000
}
suspend fun setCurrencyValue(newValue: Int) {
dataStore.edit { preferences ->
preferences[CURRENCY_VALUE] = newValue
}
}
}
@@ -1,17 +1,17 @@
package com.github.nacabaro.vbhelper.source package com.github.nacabaro.vbhelper.source
import com.github.nacabaro.vbhelper.database.AppDatabase import com.github.nacabaro.vbhelper.database.AppDatabase
import com.github.nacabaro.vbhelper.domain.items.Items
import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos
import kotlinx.coroutines.flow.Flow
class ItemsRepository( class ItemsRepository(
private val db: AppDatabase private val db: AppDatabase
) { ) {
suspend fun getAllItems(): List<ItemDtos.ItemsWithQuantities> { fun getAllItems(): Flow<List<ItemDtos.ItemsWithQuantities>> {
return db.itemDao().getAllItems() return db.itemDao().getAllItems()
} }
suspend fun getUserItems(): List<ItemDtos.ItemsWithQuantities> { fun getUserItems(): Flow<List<ItemDtos.ItemsWithQuantities>> {
return db.itemDao().getAllUserItems() return db.itemDao().getAllUserItems()
} }
} }
+1 -1
View File
@@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.12.2" agp = "8.13.1"
datastore = "1.1.2" datastore = "1.1.2"
kotlin = "2.0.0" kotlin = "2.0.0"
coreKtx = "1.15.0" coreKtx = "1.15.0"