Compare commits

...
14 Commits
Author SHA1 Message Date
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
Nacho cb9fe8e716 Merge remote-tracking branch 'origin/main' 2025-09-05 02:22:09 +02:00
Nacho 540766eefb VERSION UP 2025-09-05 02:21:47 +02:00
nacabaroandGitHub af2c852bac Merge pull request #38 from nacabaro/vb/special_missions
Small fix related to special missions
2025-09-05 02:19:36 +02:00
Nacho 8dc5bbbdde Small fix related to special missions
- Now you can clear failed special missions. When a special mission fails, you will not get an item after clearing it.

- Huge note: I have not tested this fix, but it should work... At least that's what my intuition is telling me!

Jokes aside, this should work, but the home screen will not update. I should update this to make use of StateFlows and Flows to keep the home screen updated at all times.
2025-09-05 02:19:09 +02:00
nacabaroandGitHub aac05a3b1c Merge pull request #37 from nacabaro/card/import_more
Agarrate, que vienen curvas
2025-09-05 02:10:05 +02:00
Nacho b827fdccbe Agarrate, que vienen curvas
- Importing more data from the card, this time it's to enhance the functionality of the dex. Things that I'm importing:
  - Tropies
  - Vitals
  - WinRate
  - Battles needed
  - Adventure level cleared
- This commit also includes displaying the data, modifying the relational model and modifying the underlying import functions.

- I've also changed the name from Character to CharacterData, since there is another class that is also called Character. I wanted to move it into the card package, since it's more related to Cards, while character is more designed for user raised characters.

- Another small addition was the ability to construct ImageBitmaps from the BitmapData.kt class. reduces code and makes it more simple, will refactor the old code to use this later on.

- Things I might do
  - Move out of the SettingsScreenControllerImpl.kt file all the card import stuff, since it's slowly growing a lot, and it could benefit from some independence
2025-09-05 02:08:44 +02:00
Nacho 2586190e5c Update libraries 2025-09-02 18:01:11 +02:00
Nacho 1a6753d2aa Fixed date issues with TransformationHistory 2025-09-02 04:54:08 +02:00
Nacho f8ec899335 Version up and credits 2025-08-09 12:59:01 +02:00
Nacho b95edfa2a1 New icon 2025-08-09 12:55:38 +02:00
Nacho 1a570170ca Merge remote-tracking branch 'origin/main' 2025-08-06 20:18:43 +02:00
Nacho 1566876106 Version up 2025-08-06 20:18:32 +02:00
56 changed files with 1273 additions and 240 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ android {
minSdk = 28 minSdk = 28
targetSdk = 35 targetSdk = 35
versionCode = 1 versionCode = 1
versionName = "Alpha 0.4.1" versionName = "Alpha 0.6.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@@ -169,7 +169,7 @@ fun SpecialMissionsEntry(
Card( Card(
modifier = modifier, modifier = modifier,
shape = androidx.compose.material.MaterialTheme.shapes.small, shape = androidx.compose.material.MaterialTheme.shapes.small,
onClick = if (specialMission.status == SpecialMission.Status.COMPLETED) { onClick = if (specialMission.status == SpecialMission.Status.COMPLETED || specialMission.status == SpecialMission.Status.FAILED) {
onClickCard onClickCard
} else { } else {
{ } { }
@@ -18,7 +18,8 @@ interface AdventureDao {
""") """)
fun getAdventureCount(): Int fun getAdventureCount(): Int
@Query(""" @Query(
"""
SELECT SELECT
uc.*, uc.*,
c.stage, c.stage,
@@ -30,11 +31,12 @@ 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 Character 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.dimId = 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
""") """
)
suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites> suspend fun getAdventureCharacters(): List<CharacterDtos.AdventureCharacterWithSprites>
@Query(""" @Query("""
@@ -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>
}
@@ -17,13 +17,15 @@ interface CardDao {
@Query("SELECT * FROM Card WHERE id = :id") @Query("SELECT * FROM Card WHERE id = :id")
fun getCardById(id: Long): Card? fun getCardById(id: Long): Card?
@Query(""" @Query(
"""
SELECT ca.* SELECT ca.*
FROM Card ca FROM Card ca
JOIN Character ch ON ca.id = ch.dimId 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
""") """
)
suspend fun getCardByCharacterId(id: Long): Card suspend fun getCardByCharacterId(id: Long): Card
@Query("UPDATE Card SET name = :newName WHERE id = :id") @Query("UPDATE Card SET name = :newName WHERE 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
)
}
@@ -13,5 +13,5 @@ interface CardProgressDao {
@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
} }
@@ -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.characters.Character 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: Character) suspend fun insertCharacter(vararg characterData: CardCharacter)
@Query("SELECT * FROM Character WHERE monIndex = :monIndex AND dimId = :dimId LIMIT 1") @Query("SELECT * FROM CardCharacter WHERE charaIndex = :monIndex AND cardId = :dimId LIMIT 1")
fun getCharacterByMonIndex(monIndex: Int, dimId: Long): Character fun getCharacterByMonIndex(monIndex: Int, dimId: Long): CardCharacter
@Insert @Insert
suspend fun insertSprite(vararg sprite: Sprite) suspend fun insertSprite(vararg sprite: Sprite)
@@ -22,14 +22,66 @@ interface CharacterDao {
""" """
SELECT SELECT
d.cardId as cardId, d.cardId as cardId,
c.monIndex as charId, c.charaIndex as charId,
c.stage as stage, c.stage as stage,
c.attribute as attribute c.attribute as attribute
FROM Character c FROM CardCharacter c
JOIN UserCharacter uc ON c.id = uc.charId JOIN UserCharacter uc ON c.id = uc.charId
JOIN Card d ON c.dimId = d.id JOIN Card d ON c.cardId = d.id
WHERE c.id = :charId WHERE c.id = :charId
""" """
) )
suspend fun getCharacterInfo(charId: Long): CharacterDtos.CardCharacterInfo suspend fun getCharacterInfo(charId: Long): CharacterDtos.CardCharacterInfo
@Query(
"""
INSERT INTO PossibleTransformations (charaId, requiredVitals, requiredTrophies, requiredBattles, requiredWinRate, changeTimerHours, requiredAdventureLevelCompleted, toCharaId)
SELECT
(SELECT id FROM CardCharacter WHERE charaIndex = :fromChraraIndex AND cardId = :cardId),
:requiredVitals,
:requiredTrophies,
:requiredBattles,
:requiredWinRate,
:changeTimerHours,
:requiredAdventureLevelCompleted,
(SELECT id FROM CardCharacter WHERE charaIndex = :toChraraIndex AND cardId = :cardId)
"""
)
suspend fun insertPossibleTransformation(
fromChraraIndex: Int,
toChraraIndex: Int,
cardId: Long,
requiredVitals: Int,
requiredTrophies: Int,
requiredBattles: Int,
changeTimerHours: Int,
requiredWinRate: Int,
requiredAdventureLevelCompleted: Int
)
@Query(
"""
SELECT
pt.charaId as fromCharaId,
pt.toCharaId as charaId,
s.spriteIdle1 as spriteIdle,
s.width as spriteWidth,
s.height as spriteHeight,
d.discoveredOn as discoveredOn,
pt.requiredTrophies as requiredTrophies,
pt.requiredVitals as requiredVitals,
pt.requiredBattles as requiredBattles,
pt.requiredWinRate as requiredWinRate,
pt.changeTimerHours as changeTimerHours,
pt.requiredAdventureLevelCompleted as requiredAdventureLevelCompleted
FROM
PossibleTransformations pt
JOIN CardCharacter c on pt.toCharaId = c.id
JOIN Sprite s ON s.id = c.spriteId
LEFT JOIN Dex d ON d.id = pt.toCharaId
WHERE
c.cardId = :cardId
"""
)
suspend fun getEvolutionRequirementsForCard(cardId: Long): List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>
} }
@@ -7,39 +7,53 @@ import com.github.nacabaro.vbhelper.dtos.CharacterDtos
@Dao @Dao
interface DexDao { interface DexDao {
@Query(""" @Query(
"""
INSERT OR IGNORE INTO Dex(id, discoveredOn) INSERT OR IGNORE INTO Dex(id, discoveredOn)
VALUES ( VALUES (
(SELECT id FROM Character WHERE monIndex = :charIndex AND dimId = :cardId), (SELECT id FROM CardCharacter WHERE charaIndex = :charIndex AND cardId = :cardId),
:discoveredOn :discoveredOn
) )
""") """
)
fun insertCharacter(charIndex: Int, cardId: Long, discoveredOn: Long) fun insertCharacter(charIndex: Int, cardId: Long, discoveredOn: Long)
@Query(""" @Query(
"""
SELECT SELECT
c.id AS id, c.id AS id,
s.spriteIdle1 AS spriteIdle, s.spriteIdle1 AS spriteIdle,
s.width AS spriteWidth, s.width AS spriteWidth,
s.height AS spriteHeight, s.height AS spriteHeight,
d.discoveredOn AS discoveredOn c.nameSprite AS nameSprite,
FROM Character c c.nameWidth AS nameSpriteWidth,
c.nameHeight AS nameSpriteHeight,
d.discoveredOn AS discoveredOn,
c.baseHp as baseHp,
c.baseBp as baseBp,
c.baseAp as baseAp,
c.stage as stage,
c.attribute as attribute
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.dimId = :cardId WHERE c.cardId = :cardId
""") """
suspend fun getSingleCardProgress(cardId: Long): List<CharacterDtos.CardProgress> )
suspend fun getSingleCardProgress(cardId: Long): List<CharacterDtos.CardCharaProgress>
@Query(""" @Query(
"""
SELECT SELECT
c.id as cardId, c.id as cardId,
c.name as cardName, c.name as cardName,
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 Character cc WHERE cc.dimId = c.id) AS totalCharacters, (SELECT COUNT(*) FROM CardCharacter cc WHERE cc.cardId = c.id) AS totalCharacters,
(SELECT COUNT(*) FROM Dex d JOIN Character cc ON d.id = cc.id WHERE cc.dimId = 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
""") """
)
suspend fun getCardsWithProgress(): List<CardDtos.CardProgress> suspend fun getCardsWithProgress(): List<CardDtos.CardProgress>
} }
@@ -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.characters.Character 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
@@ -37,19 +37,21 @@ interface UserCharacterDao {
@Upsert @Upsert
fun insertSpecialMissions(vararg specialMissions: SpecialMissions) fun insertSpecialMissions(vararg specialMissions: SpecialMissions)
@Query(""" @Query(
"""
SELECT SELECT
c.id AS id, c.id AS id,
s.spriteIdle1 AS spriteIdle, s.spriteIdle1 AS spriteIdle,
s.width AS spriteWidth, s.width AS spriteWidth,
s.height AS spriteHeight, s.height AS spriteHeight,
c.monIndex AS monIndex, c.charaIndex AS monIndex,
t.transformationDate AS transformationDate t.transformationDate AS transformationDate
FROM TransformationHistory t FROM TransformationHistory t
JOIN Character 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
""") """
)
suspend fun getTransformationHistory(monId: Long): List<CharacterDtos.TransformationHistory>? suspend fun getTransformationHistory(monId: Long): List<CharacterDtos.TransformationHistory>?
@Query( @Query(
@@ -62,14 +64,14 @@ interface UserCharacterDao {
s.spriteIdle2 AS spriteIdle2, s.spriteIdle2 AS spriteIdle2,
s.width AS spriteWidth, s.width AS spriteWidth,
s.height AS spriteHeight, s.height AS spriteHeight,
c.name as nameSprite, c.nameSprite as nameSprite,
c.nameWidth as nameSpriteWidth, c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight, c.nameHeight as nameSpriteHeight,
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 Character c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON d.id = c.dimId 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
""" """
@@ -86,18 +88,19 @@ interface UserCharacterDao {
s.spriteIdle2 AS spriteIdle2, s.spriteIdle2 AS spriteIdle2,
s.width AS spriteWidth, s.width AS spriteWidth,
s.height AS spriteHeight, s.height AS spriteHeight,
c.name as nameSprite, c.nameSprite as nameSprite,
c.nameWidth as nameSpriteWidth, c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight, c.nameHeight as nameSpriteHeight,
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 Character c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON c.dimId = 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
WHERE uc.id = :id WHERE uc.id = :id
""") """
)
suspend fun getCharacterWithSprites(id: Long): CharacterDtos.CharacterWithSprites suspend fun getCharacterWithSprites(id: Long): CharacterDtos.CharacterWithSprites
@Query("SELECT * FROM UserCharacter WHERE id = :id") @Query("SELECT * FROM UserCharacter WHERE id = :id")
@@ -122,19 +125,20 @@ interface UserCharacterDao {
s.spriteIdle2 AS spriteIdle2, s.spriteIdle2 AS spriteIdle2,
s.width AS spriteWidth, s.width AS spriteWidth,
s.height AS spriteHeight, s.height AS spriteHeight,
c.name as nameSprite, c.nameSprite as nameSprite,
c.nameWidth as nameSpriteWidth, c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight, c.nameHeight as nameSpriteHeight,
d.isBEm as isBemCard, d.isBEm as isBemCard,
a.characterId as isInAdventure a.characterId as isInAdventure
FROM UserCharacter uc FROM UserCharacter uc
JOIN Character c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON c.dimId = 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
WHERE uc.isActive = 1 WHERE uc.isActive = 1
LIMIT 1 LIMIT 1
""") """
)
suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites? suspend fun getActiveCharacter(): CharacterDtos.CharacterWithSprites?
@Query("DELETE FROM UserCharacter WHERE id = :id") @Query("DELETE FROM UserCharacter WHERE id = :id")
@@ -149,22 +153,24 @@ interface UserCharacterDao {
@Query( @Query(
""" """
SELECT c.* SELECT c.*
FROM Character 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): Character suspend fun getCharacterInfo(charId: Long): CardCharacter
@Query(""" @Query(
"""
INSERT INTO TransformationHistory(monId, stageId, transformationDate) INSERT INTO TransformationHistory(monId, stageId, transformationDate)
VALUES VALUES
(:monId, (:monId,
(SELECT id FROM Character WHERE monIndex = :stage AND dimId = :dimId), (SELECT id FROM CardCharacter WHERE charaIndex = :stage AND cardId = :dimId),
:transformationDate) :transformationDate)
""") """
)
fun insertTransformation(monId: Long, stage: Int, dimId: Long, transformationDate: Long) fun insertTransformation(monId: Long, stage: Int, dimId: Long, transformationDate: Long)
@Upsert @Upsert
@@ -183,14 +189,14 @@ interface UserCharacterDao {
s.spriteIdle2 AS spriteIdle2, s.spriteIdle2 AS spriteIdle2,
s.width AS spriteWidth, s.width AS spriteWidth,
s.height AS spriteHeight, s.height AS spriteHeight,
c.name as nameSprite, c.nameSprite as nameSprite,
c.nameWidth as nameSpriteWidth, c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight, c.nameHeight as nameSpriteHeight,
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 Character c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON d.id = c.dimId 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
WHERE uc.characterType = "BEDevice" WHERE uc.characterType = "BEDevice"
@@ -208,14 +214,14 @@ interface UserCharacterDao {
s.spriteIdle2 AS spriteIdle2, s.spriteIdle2 AS spriteIdle2,
s.width AS spriteWidth, s.width AS spriteWidth,
s.height AS spriteHeight, s.height AS spriteHeight,
c.name as nameSprite, c.nameSprite as nameSprite,
c.nameWidth as nameSpriteWidth, c.nameWidth as nameSpriteWidth,
c.nameHeight as nameSpriteHeight, c.nameHeight as nameSpriteHeight,
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 Character c ON uc.charId = c.id JOIN CardCharacter c ON uc.charId = c.id
JOIN Card d ON d.id = c.dimId 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
WHERE uc.characterType = "VBDevice" WHERE uc.characterType = "VBDevice"
@@ -3,17 +3,23 @@ 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.characters.Character import com.github.nacabaro.vbhelper.domain.card.Background
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.characters.Sprite import com.github.nacabaro.vbhelper.domain.characters.Sprite
import com.github.nacabaro.vbhelper.domain.characters.Adventure import com.github.nacabaro.vbhelper.domain.characters.Adventure
import com.github.nacabaro.vbhelper.domain.characters.Dex import com.github.nacabaro.vbhelper.domain.characters.Dex
@@ -30,7 +36,9 @@ import com.github.nacabaro.vbhelper.domain.items.Items
entities = [ entities = [
Card::class, Card::class,
CardProgress::class, CardProgress::class,
Character::class, CardCharacter::class,
CardAdventure::class,
CardFusions::class,
Sprite::class, Sprite::class,
UserCharacter::class, UserCharacter::class,
BECharacterData::class, BECharacterData::class,
@@ -40,7 +48,9 @@ import com.github.nacabaro.vbhelper.domain.items.Items
VitalsHistory::class, VitalsHistory::class,
Dex::class, Dex::class,
Items::class, Items::class,
Adventure::class Adventure::class,
Background::class,
PossibleTransformations::class
] ]
) )
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
@@ -53,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
} }
@@ -3,7 +3,6 @@ package com.github.nacabaro.vbhelper.domain
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.characters.Character
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
@@ -3,7 +3,6 @@ package com.github.nacabaro.vbhelper.domain
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.characters.Character
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
@@ -0,0 +1,23 @@
package com.github.nacabaro.vbhelper.domain.card
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
foreignKeys = [
ForeignKey(
entity = Card::class,
parentColumns = ["id"],
childColumns = ["cardId"],
onDelete = ForeignKey.CASCADE
)
]
)
data class Background (
@PrimaryKey(autoGenerate = true) val id: Long,
val cardId: Long,
val background: ByteArray,
val backgroundWidth: Int,
val backgroundHeight: Int
)
@@ -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?
)
@@ -1,17 +1,17 @@
package com.github.nacabaro.vbhelper.domain.characters package com.github.nacabaro.vbhelper.domain.card
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.ForeignKey import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.github.cfogrady.vbnfc.data.NfcCharacter import com.github.cfogrady.vbnfc.data.NfcCharacter
import com.github.nacabaro.vbhelper.domain.card.Card import com.github.nacabaro.vbhelper.domain.characters.Sprite
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
ForeignKey( ForeignKey(
entity = Card::class, entity = Card::class,
parentColumns = ["id"], parentColumns = ["id"],
childColumns = ["dimId"], childColumns = ["cardId"],
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
), ),
ForeignKey( ForeignKey(
@@ -28,17 +28,17 @@ import com.github.nacabaro.vbhelper.domain.card.Card
* 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 Character ( data class CardCharacter (
@PrimaryKey(autoGenerate = true) val id: Long = 0, @PrimaryKey(autoGenerate = true) val id: Long = 0,
val dimId: Long, val cardId: Long,
val spriteId: Long, val spriteId: Long,
val monIndex: Int, val charaIndex: Int,
val name: ByteArray,
val stage: Int, // These should be replaced with enums val stage: Int, // These should be replaced with enums
val attribute: NfcCharacter.Attribute, // This one too val attribute: NfcCharacter.Attribute, // This one too
val baseHp: Int, val baseHp: Int,
val baseBp: Int, val baseBp: Int,
val baseAp: Int, val baseAp: Int,
val nameSprite: ByteArray,
val nameWidth: Int, val nameWidth: Int,
val nameHeight: Int val nameHeight: Int
) )
@@ -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?
)
@@ -0,0 +1,33 @@
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 = ["charaId"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CardCharacter::class,
parentColumns = ["id"],
childColumns = ["toCharaId"],
onDelete = ForeignKey.CASCADE
)
]
)
data class PossibleTransformations (
@PrimaryKey(autoGenerate = true) val id: Long = 0,
var charaId: Long,
val requiredVitals: Int,
val requiredTrophies: Int,
val requiredBattles: Int,
val requiredWinRate: Int,
val changeTimerHours: Int,
val requiredAdventureLevelCompleted: Int,
val toCharaId: Long?
)
@@ -3,11 +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.CardCharacter
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
ForeignKey( ForeignKey(
entity = Character::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.characters.Character import com.github.nacabaro.vbhelper.domain.card.CardCharacter
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
@@ -14,7 +14,7 @@ import com.github.nacabaro.vbhelper.domain.characters.Character
onDelete = ForeignKey.CASCADE onDelete = ForeignKey.CASCADE
), ),
ForeignKey( ForeignKey(
entity = Character::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.characters.Character import com.github.nacabaro.vbhelper.domain.card.CardCharacter
@Entity( @Entity(
foreignKeys = [ foreignKeys = [
ForeignKey( ForeignKey(
entity = Character::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,7 +35,7 @@ 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
@@ -50,12 +50,27 @@ object CharacterDtos {
val transformationDate: Long val transformationDate: Long
) )
data class CardCharaProgress(
val id: Long,
val spriteIdle: ByteArray,
val spriteWidth: Int,
val spriteHeight: Int,
val nameSprite: ByteArray,
val nameSpriteWidth: Int,
val nameSpriteHeight: Int,
val discoveredOn: Long?,
val baseHp: Int,
val baseBp: Int,
val baseAp: Int,
val stage: Int,
val attribute: NfcCharacter.Attribute,
)
data class CardProgress( data class CardProgress(
val id: Long, val id: Long,
val spriteIdle: ByteArray, val spriteIdle: ByteArray,
val spriteWidth: Int, val spriteWidth: Int,
val spriteHeight: Int, val spriteHeight: Int,
val discoveredOn: Long?
) )
data class AdventureCharacterWithSprites( data class AdventureCharacterWithSprites(
@@ -83,4 +98,19 @@ object CharacterDtos {
val finishesAdventure: Long, val finishesAdventure: Long,
val originalTimeInMinutes: Long val originalTimeInMinutes: Long
) )
data class EvolutionRequirementsWithSpritesAndObtained(
val charaId: Long,
val fromCharaId: Long,
val spriteIdle: ByteArray,
val spriteWidth: Int,
val spriteHeight: Int,
val discoveredOn: Long?,
val requiredTrophies: Int,
val requiredVitals: Int,
val requiredBattles: Int,
val requiredWinRate: Int,
val changeTimerHours: Int,
val requiredAdventureLevelCompleted: Int
)
} }
@@ -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")
@@ -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,63 @@
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.mutableStateOf
import androidx.compose.runtime.remember
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 = remember { 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,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,24 +16,32 @@ 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.source.DexRepository import com.github.nacabaro.vbhelper.source.DexRepository
import kotlinx.coroutines.launch 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
val dexRepository = DexRepository(application.container.db) val dexRepository = DexRepository(application.container.db)
val characterList = remember { mutableStateOf<List<CharacterDtos.CardProgress>>(emptyList()) } val characterList = remember { mutableStateOf<List<CharacterDtos.CardCharaProgress>>(emptyList()) }
val cardPossibleTransformations = remember { mutableStateOf<List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>>(emptyList()) }
val selectedCharacter = remember { mutableStateOf<CharacterDtos.CardCharaProgress?>(null) }
LaunchedEffect(dexRepository) { LaunchedEffect(dexRepository) {
coroutineScope.launch { coroutineScope.launch {
val newCharacterList = dexRepository.getCharactersByDimId(dimId) val newCharacterList = dexRepository.getCharactersByCardId(cardId)
characterList.value = newCharacterList characterList.value = newCharacterList
val newCardPossibleTransformations = dexRepository.getCardPossibleTransformations(cardId)
cardPossibleTransformations.value = newCardPossibleTransformations
} }
} }
@@ -43,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()
)
)
} }
) )
} }
@@ -53,7 +72,9 @@ fun CardViewScreen(
) { ) {
items(characterList.value) { character -> items(characterList.value) { character ->
CharacterEntry( CharacterEntry(
onClick = { }, onClick = {
selectedCharacter.value = character
},
obscure = character.discoveredOn == null, obscure = character.discoveredOn == null,
icon = BitmapData( icon = BitmapData(
bitmap = character.spriteIdle, bitmap = character.spriteIdle,
@@ -63,5 +84,16 @@ fun CardViewScreen(
) )
} }
} }
if (selectedCharacter.value != null) {
DexCharaDetailsDialog(
currentChara = selectedCharacter.value!!,
possibleTransformations = cardPossibleTransformations.value,
obscure = selectedCharacter.value!!.discoveredOn == null,
onClickClose = {
selectedCharacter.value = null
}
)
}
} }
} }
@@ -0,0 +1,215 @@
package com.github.nacabaro.vbhelper.screens.cardScreen.dialogs
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.Button
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.Alignment
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 androidx.compose.ui.window.Dialog
import com.github.nacabaro.vbhelper.dtos.CharacterDtos
import com.github.nacabaro.vbhelper.utils.BitmapData
import com.github.nacabaro.vbhelper.utils.getImageBitmap
@Composable
fun DexCharaDetailsDialog(
currentChara: CharacterDtos.CardCharaProgress,
possibleTransformations: List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained>,
obscure: Boolean,
onClickClose: () -> Unit
) {
val nameMultiplier = 3
val charaMultiplier = 4
val currentCharaPossibleTransformations = possibleTransformations.filter { it.fromCharaId == currentChara.id }
val romanNumeralsStage = when (currentChara.stage) {
1 -> "II"
2 -> "III"
3 -> "IV"
4 -> "V"
5 -> "VI"
6 -> "VII"
else -> "I"
}
val charaBitmapData = BitmapData(
bitmap = currentChara.spriteIdle,
width = currentChara.spriteWidth,
height = currentChara.spriteHeight
)
val charaImageBitmapData = charaBitmapData.getImageBitmap(
context = LocalContext.current,
multiplier = charaMultiplier,
obscure = obscure
)
val nameBitmapData = BitmapData(
bitmap = currentChara.nameSprite,
width = currentChara.nameSpriteWidth,
height = currentChara.nameSpriteHeight
)
val nameImageBitmapData = nameBitmapData.getImageBitmap(
context = LocalContext.current,
multiplier = nameMultiplier,
obscure = obscure
)
Dialog(
onDismissRequest = onClickClose
) {
Card (
modifier = Modifier
.fillMaxWidth()
) {
Column (
modifier = Modifier
.padding(16.dp)
) {
Row (
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
) {
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(16.dp)
)
if (!obscure) {
Column {
Image(
bitmap = nameImageBitmapData.imageBitmap,
contentDescription = "Icon",
modifier = Modifier
.width(nameImageBitmapData.dpWidth)
.height(nameImageBitmapData.dpHeight),
filterQuality = FilterQuality.None
)
Spacer(modifier = Modifier.padding(4.dp))
if (currentChara.baseHp != 65535) {
Text(
text = "HP: ${currentChara.baseHp}, BP: ${currentChara.baseBp}, AP: ${currentChara.baseAp}"
)
Text(text = "Stg: ${romanNumeralsStage}, Atr: ${currentChara.attribute.toString().substring(0, 2)}")
}
}
} else {
Column {
Text(text = "????????????????")
Spacer(modifier = Modifier.padding(4.dp))
Text(text = "Stg: -, Atr: -")
Text(text = "HP: -, BP: -, AP: -")
}
}
}
Spacer(modifier = Modifier.padding(16.dp))
Column {
currentCharaPossibleTransformations.map {
val selectedCharaBitmap = BitmapData(
bitmap = it.spriteIdle,
width = it.spriteWidth,
height = it.spriteHeight
)
val selectedCharaImageBitmap = selectedCharaBitmap.getImageBitmap(
context = LocalContext.current,
multiplier = 4,
obscure = it.discoveredOn == null
)
Card (
modifier = Modifier
.padding(vertical = 8.dp)
) {
Row (
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
) {
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 = selectedCharaImageBitmap.imageBitmap,
contentDescription = "Icon",
modifier = Modifier
.size(selectedCharaImageBitmap.dpWidth)
.padding(8.dp),
colorFilter = when (it.discoveredOn == null) {
true -> ColorFilter.tint(color = MaterialTheme.colorScheme.secondary)
false -> null
},
filterQuality = FilterQuality.None
)
}
Spacer(
modifier = Modifier
.padding(16.dp)
)
Column {
Text("Tr: ${it.requiredTrophies}; Bt: ${it.requiredBattles}; Vr: ${it.requiredVitals}; Wr: ${it.requiredWinRate}%; Ct: ${it.changeTimerHours}h")
Text("AdvLvl ${it.requiredAdventureLevelCompleted + 1}")
}
}
}
}
}
Button(
onClick = onClickClose
) {
Text("Close")
}
}
}
}
}
@@ -1,8 +1,9 @@
package com.github.nacabaro.vbhelper.screens.homeScreens package com.github.nacabaro.vbhelper.screens.homeScreens
import com.github.cfogrady.vbnfc.vb.SpecialMission
import com.github.nacabaro.vbhelper.dtos.ItemDtos import com.github.nacabaro.vbhelper.dtos.ItemDtos
interface HomeScreenController { interface HomeScreenController {
fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit) fun didAdventureMissionsFinish(onCompletion: (Boolean) -> Unit)
fun clearSpecialMission(missionId: Long, onCleared: (ItemDtos.PurchasedItem) -> Unit) fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit)
} }
@@ -2,6 +2,7 @@ package com.github.nacabaro.vbhelper.screens.homeScreens
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
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.launch import kotlinx.coroutines.launch
@@ -30,37 +31,42 @@ class HomeScreenControllerImpl(
} }
} }
override fun clearSpecialMission(missionId: Long, onCleared: (ItemDtos.PurchasedItem) -> Unit) { override fun clearSpecialMission(missionId: Long, missionCompletion: SpecialMission.Status, onCleared: (ItemDtos.PurchasedItem?) -> Unit) {
componentActivity.lifecycleScope.launch { componentActivity.lifecycleScope.launch {
database database
.specialMissionDao() .specialMissionDao()
.clearSpecialMission(missionId) .clearSpecialMission(missionId)
val randomItem = database if (missionCompletion == SpecialMission.Status.COMPLETED) {
.itemDao() val randomItem = database
.getAllItems() .itemDao()
.random() .getAllItems()
.random()
val randomItemAmount = (Random.nextFloat() * 5).roundToInt() val randomItemAmount = (Random.nextFloat() * 5).roundToInt()
database database
.itemDao() .itemDao()
.purchaseItem( .purchaseItem(
itemId = randomItem.id,
itemAmount = randomItemAmount
)
val purchasedItem = ItemDtos.PurchasedItem(
itemId = randomItem.id, itemId = randomItem.id,
itemAmount = randomItemAmount itemName = randomItem.name,
itemDescription = randomItem.description,
itemIcon = randomItem.itemIcon,
itemLength = randomItem.itemLength,
itemAmount = randomItemAmount,
itemType = randomItem.itemType
) )
val purchasedItem = ItemDtos.PurchasedItem( onCleared(purchasedItem)
itemId = randomItem.id, } else {
itemName = randomItem.name, onCleared(null)
itemDescription = randomItem.description, }
itemIcon = randomItem.itemIcon,
itemLength = randomItem.itemLength,
itemAmount = randomItemAmount,
itemType = randomItem.itemType
)
onCleared(purchasedItem)
} }
} }
} }
@@ -34,7 +34,7 @@ fun VBDiMHomeScreen(
homeScreenController: HomeScreenControllerImpl, homeScreenController: HomeScreenControllerImpl,
transformationHistory: List<CharacterDtos.TransformationHistory>, transformationHistory: List<CharacterDtos.TransformationHistory>,
contentPadding: PaddingValues, contentPadding: PaddingValues,
onClickCollect: (ItemDtos.PurchasedItem) -> Unit onClickCollect: (ItemDtos.PurchasedItem?) -> Unit
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@@ -181,7 +181,7 @@ fun VBDiMHomeScreen(
.padding(8.dp), .padding(8.dp),
) { ) {
homeScreenController homeScreenController
.clearSpecialMission(mission.id, onClickCollect) .clearSpecialMission(mission.id, mission.status, onClickCollect)
} }
} }
} }
@@ -1,6 +1,7 @@
package com.github.nacabaro.vbhelper.screens.scanScreen.converters package com.github.nacabaro.vbhelper.screens.scanScreen.converters
import android.icu.util.Calendar import android.icu.util.Calendar
import android.icu.util.TimeZone
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import com.github.cfogrady.vbnfc.be.BENfcCharacter import com.github.cfogrady.vbnfc.be.BENfcCharacter
@@ -243,16 +244,23 @@ class ToNfcConverter(
.getTransformationHistory(characterId)!! .getTransformationHistory(characterId)!!
.map { .map {
val date = Date(it.transformationDate) val date = Date(it.transformationDate)
val calendar = android.icu.util.GregorianCalendar() val calendar = android.icu.util.GregorianCalendar(TimeZone.getTimeZone("UTC"))
calendar.time = date calendar.time = date
Log.d(
"TransformationHistory",
"Year: ${calendar.get(Calendar.YEAR)}, " +
"Month: ${calendar.get(Calendar.MONTH) + 1}, " +
"Day: ${calendar.get(Calendar.DAY_OF_MONTH)}"
)
NfcCharacter.Transformation( NfcCharacter.Transformation(
toCharIndex = it.monIndex.toUByte(), toCharIndex = it.monIndex.toUByte(),
year = calendar year = calendar
.get(Calendar.YEAR) .get(Calendar.YEAR)
.toUShort(), .toUShort(),
month = calendar month = (calendar
.get(Calendar.MONTH) .get(Calendar.MONTH) + 1)
.toUByte(), .toUByte(),
day = calendar day = calendar
.get(Calendar.DAY_OF_MONTH) .get(Calendar.DAY_OF_MONTH)
@@ -35,6 +35,7 @@ fun CreditsScreen(
SettingsEntry(title = "cfogrady", description = "Developed vb-lib-nfc and part of this application.") { } SettingsEntry(title = "cfogrady", description = "Developed vb-lib-nfc and part of this application.") { }
SettingsEntry(title = "nacabaro", description = "Developed this application.") { } SettingsEntry(title = "nacabaro", description = "Developed this application.") { }
SettingsEntry(title = "lightheel", description = "Developing the battling part for this application, including server. Still in the works.") { } SettingsEntry(title = "lightheel", description = "Developing the battling part for this application, including server. Still in the works.") { }
SettingsEntry(title = "shvstrz", description = "Designing the app icon in SVG.") { }
} }
} }
} }
@@ -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.characters.Character 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
@@ -109,10 +109,249 @@ class SettingsScreenControllerImpl(
filePickerCard.launch(arrayOf("*/*")) filePickerCard.launch(arrayOf("*/*"))
} }
private suspend fun importEvoData(
cardId: Long,
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
) {
for (index in 0 until card.transformationRequirements.transformationEntries.size) {
val evo = card.transformationRequirements.transformationEntries[index]
var transformationTimerHours: Int
var unlockAdventureLevel: Int
if (card is BemCard) {
transformationTimerHours = card
.transformationRequirements
.transformationEntries[index]
.minutesUntilTransformation / 60
unlockAdventureLevel = if (
card
.transformationRequirements
.transformationEntries[index]
.requiredCompletedAdventureLevel == 65535
) {
0
} else {
card
.transformationRequirements
.transformationEntries[index]
.requiredCompletedAdventureLevel
}
} else {
transformationTimerHours = (card as DimCard)
.transformationRequirements
.transformationEntries[index]
.hoursUntilEvolution
unlockAdventureLevel = if (
card
.adventureLevels
.levels
.last()
.bossCharacterIndex == card.transformationRequirements.transformationEntries[index].toCharacterIndex
) {
14
/*
Magic number incoming!!
In the case of DiMCards, stage 15 is the one that unlocks the locked character.
We know it is a locked character if the last adventure level's boss character index
is the current index. If it is, we add stage 15 complete as a requirement for transformation.
*/
} else {
0
/*
Another magic number...
The rest of the characters are not locked.
*/
}
}
database
.characterDao()
.insertPossibleTransformation(
cardId = cardId,
fromChraraIndex = evo.fromCharacterIndex,
toChraraIndex = evo.toCharacterIndex,
requiredVitals = evo.requiredVitalValues,
requiredTrophies = evo.requiredTrophies,
requiredBattles = evo.requiredBattles,
requiredWinRate = evo.requiredWinRatio,
requiredAdventureLevelCompleted = unlockAdventureLevel,
changeTimerHours = transformationTimerHours
)
}
}
private suspend fun importCharacterData(
cardId: Long,
card: com.github.cfogrady.vb.dim.card.Card<*, *, *, *, *, *>
) {
var spriteCounter = when (card is BemCard) {
true -> 54
false -> 10
}
val domainCharacters = mutableListOf<CardCharacter>()
val characters = card
.characterStats
.characterEntries
for (index in 0 until characters.size) {
var domainSprite: Sprite?
if (index < 2 && card is DimCard) {
domainSprite = Sprite(
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
spriteWalk1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteWalk2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
spriteRun1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteRun2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
spriteTrain1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteTrain2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
spriteHappy = card.spriteData.sprites[spriteCounter + 4].pixelData,
spriteSleep = card.spriteData.sprites[spriteCounter + 5].pixelData,
spriteAttack = card.spriteData.sprites[spriteCounter + 2].pixelData,
spriteDodge = card.spriteData.sprites[spriteCounter + 3].pixelData
)
} else {
domainSprite = Sprite(
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
spriteWalk1 = card.spriteData.sprites[spriteCounter + 3].pixelData,
spriteWalk2 = card.spriteData.sprites[spriteCounter + 4].pixelData,
spriteRun1 = card.spriteData.sprites[spriteCounter + 5].pixelData,
spriteRun2 = card.spriteData.sprites[spriteCounter + 6].pixelData,
spriteTrain1 = card.spriteData.sprites[spriteCounter + 7].pixelData,
spriteTrain2 = card.spriteData.sprites[spriteCounter + 8].pixelData,
spriteHappy = card.spriteData.sprites[spriteCounter + 9].pixelData,
spriteSleep = card.spriteData.sprites[spriteCounter + 10].pixelData,
spriteAttack = card.spriteData.sprites[spriteCounter + 11].pixelData,
spriteDodge = card.spriteData.sprites[spriteCounter + 12].pixelData
)
}
val spriteId = database
.spriteDao()
.insertSprite(domainSprite)
domainCharacters.add(
CardCharacter(
cardId = cardId,
spriteId = spriteId,
charaIndex = index,
nameSprite = card.spriteData.sprites[spriteCounter].pixelData,
stage = characters[index].stage,
attribute = NfcCharacter.Attribute.entries[characters[index].attribute],
baseHp = characters[index].hp,
baseBp = characters[index].dp,
baseAp = characters[index].ap,
nameWidth = card.spriteData.sprites[spriteCounter].spriteDimensions.width,
nameHeight = card.spriteData.sprites[spriteCounter].spriteDimensions.height
)
)
spriteCounter += if (card is BemCard) {
14
} else {
when (index) {
0 -> 6
1 -> 7
else -> 14
}
}
}
database
.characterDao()
.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(
cardId: Long,
) {
val cardProgress = CardProgress(
cardId = cardId,
currentStage = 0,
unlocked = false
)
database
.cardProgressDao()
.updateDimProgress(cardProgress)
}
private fun importCard(uri: Uri) { private fun importCard(uri: Uri) {
context.lifecycleScope.launch(Dispatchers.IO) { context.lifecycleScope.launch(Dispatchers.IO) {
val contentResolver = context.contentResolver val contentResolver = context.contentResolver
val inputStream = contentResolver.openInputStream(uri) val inputStream = contentResolver.openInputStream(uri)
inputStream.use { fileReader -> inputStream.use { fileReader ->
val dimReader = DimReader() val dimReader = DimReader()
val card = dimReader.readCard(fileReader, false) val card = dimReader.readCard(fileReader, false)
@@ -120,112 +359,26 @@ class SettingsScreenControllerImpl(
val cardModel = Card( val cardModel = Card(
cardId = card.header.dimId, cardId = card.header.dimId,
logo = card.spriteData.sprites[0].pixelData, logo = card.spriteData.sprites[0].pixelData,
name = card.spriteData.text, // TODO Make user write card name name = card.spriteData.text,
stageCount = card.adventureLevels.levels.size, stageCount = card.adventureLevels.levels.size,
logoHeight = card.spriteData.sprites[0].height, logoHeight = card.spriteData.sprites[0].height,
logoWidth = card.spriteData.sprites[0].width, logoWidth = card.spriteData.sprites[0].width,
isBEm = card is BemCard isBEm = card is BemCard
) )
val dimId = database val cardId = database
.cardDao() .cardDao()
.insertNewCard(cardModel) .insertNewCard(cardModel)
val cardProgress = CardProgress( updateCardProgress(cardId = cardId)
cardId = dimId,
currentStage = 0,
unlocked = false
)
database importCharacterData(cardId, card)
.cardProgressDao()
.updateDimProgress(cardProgress)
val characters = card importEvoData(cardId, card)
.characterStats
.characterEntries
var spriteCounter = when (card is BemCard) { importAdventureMissions(cardId, card)
true -> 54
false -> 10
}
val domainCharacters = mutableListOf<Character>() importCardFusions(cardId, card)
for (index in 0 until characters.size) {
var domainSprite: Sprite?
if (index < 2 && card is DimCard) {
domainSprite = Sprite(
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
spriteWalk1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteWalk2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
spriteRun1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteRun2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
spriteTrain1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteTrain2 = card.spriteData.sprites[spriteCounter + 3].pixelData,
spriteHappy = card.spriteData.sprites[spriteCounter + 4].pixelData,
spriteSleep = card.spriteData.sprites[spriteCounter + 5].pixelData,
spriteAttack = card.spriteData.sprites[spriteCounter + 2].pixelData,
spriteDodge = card.spriteData.sprites[spriteCounter + 3].pixelData
)
} else {
domainSprite = Sprite(
width = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.width,
height = card.spriteData.sprites[spriteCounter + 1].spriteDimensions.height,
spriteIdle1 = card.spriteData.sprites[spriteCounter + 1].pixelData,
spriteIdle2 = card.spriteData.sprites[spriteCounter + 2].pixelData,
spriteWalk1 = card.spriteData.sprites[spriteCounter + 3].pixelData,
spriteWalk2 = card.spriteData.sprites[spriteCounter + 4].pixelData,
spriteRun1 = card.spriteData.sprites[spriteCounter + 5].pixelData,
spriteRun2 = card.spriteData.sprites[spriteCounter + 6].pixelData,
spriteTrain1 = card.spriteData.sprites[spriteCounter + 7].pixelData,
spriteTrain2 = card.spriteData.sprites[spriteCounter + 8].pixelData,
spriteHappy = card.spriteData.sprites[spriteCounter + 9].pixelData,
spriteSleep = card.spriteData.sprites[spriteCounter + 10].pixelData,
spriteAttack = card.spriteData.sprites[spriteCounter + 11].pixelData,
spriteDodge = card.spriteData.sprites[spriteCounter + 12].pixelData
)
}
val spriteId = database
.spriteDao()
.insertSprite(domainSprite)
domainCharacters.add(
Character(
dimId = dimId,
spriteId = spriteId,
monIndex = index,
name = card.spriteData.sprites[spriteCounter].pixelData,
stage = characters[index].stage,
attribute = NfcCharacter.Attribute.entries[characters[index].attribute],
baseHp = characters[index].hp,
baseBp = characters[index].dp,
baseAp = characters[index].ap,
nameWidth = card.spriteData.sprites[spriteCounter].spriteDimensions.width,
nameHeight = card.spriteData.sprites[spriteCounter].spriteDimensions.height,
)
)
spriteCounter += if (card is BemCard) {
14
} else {
when (index) {
0 -> 6
1 -> 7
else -> 14
}
}
}
database
.characterDao()
.insertCharacter(*domainCharacters.toTypedArray())
} }
inputStream?.close() inputStream?.close()
@@ -11,7 +11,11 @@ class DexRepository (
return db.dexDao().getCardsWithProgress() return db.dexDao().getCardsWithProgress()
} }
suspend fun getCharactersByDimId(cardId: Long): List<CharacterDtos.CardProgress> { suspend fun getCharactersByCardId(cardId: Long): List<CharacterDtos.CardCharaProgress> {
return db.dexDao().getSingleCardProgress(cardId) return db.dexDao().getSingleCardProgress(cardId)
} }
suspend fun getCardPossibleTransformations(cardId: Long): List<CharacterDtos.EvolutionRequirementsWithSpritesAndObtained> {
return db.characterDao().getEvolutionRequirementsForCard(cardId)
}
} }
@@ -1,6 +1,11 @@
package com.github.nacabaro.vbhelper.utils package com.github.nacabaro.vbhelper.utils
import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.github.cfogrady.vb.dim.sprite.SpriteData import com.github.cfogrady.vb.dim.sprite.SpriteData
// simple, but smooth // simple, but smooth
@@ -10,6 +15,32 @@ data class BitmapData (
val height: Int val height: Int
) )
data class ImageBitmapData(
val imageBitmap: ImageBitmap,
val dpWidth: Dp,
val dpHeight: Dp
)
fun BitmapData.getImageBitmap(
context: Context,
multiplier: Int,
obscure: Boolean
): ImageBitmapData {
val density: Float = context.resources.displayMetrics.density
val imageBitmap: ImageBitmap = if (obscure) {
this.getObscuredBitmap().asImageBitmap()
} else {
this.getBitmap().asImageBitmap()
}
return ImageBitmapData(
imageBitmap = imageBitmap,
dpWidth = (this.width * multiplier / density).dp,
dpHeight = (this.height * multiplier / density).dp
)
}
fun BitmapData.getBitmap(): Bitmap { fun BitmapData.getBitmap(): Bitmap {
return Bitmap.createBitmap(createARGBIntArray(), this.width, this.height, Bitmap.Config.HARDWARE) return Bitmap.createBitmap(createARGBIntArray(), this.width, this.height, Bitmap.Config.HARDWARE)
} }
@@ -1,30 +1,39 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp" android:width="108dp"
android:height="108dp" android:height="108dp"
android:viewportWidth="108" android:viewportWidth="512"
android:viewportHeight="108"> android:viewportHeight="512">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> <group android:scaleX="0.79"
<aapt:attr name="android:fillColor"> android:scaleY="0.79"
<gradient android:translateX="53.76"
android:endX="85.84757" android:translateY="53.76">
android:endY="92.4963" <group>
android:startX="42.9492" <clip-path
android:startY="49.59793" android:pathData="M0,0h512v512h-512z"/>
android:type="linear"> <path
<item android:pathData="M0,0h512v512h-512z"
android:color="#44000000" android:fillColor="#000000"/>
android:offset="0.0" /> <path
<item android:pathData="M164,107L186.19,107L186.19,129C186.19,132.86 189.32,136 193.19,136L219,136L235.95,163.3C236.53,164.24 237.9,165 239,165L267,165C268.1,165 269.47,164.24 270.05,163.3L287,136L312.8,136C316.66,136 319.8,132.86 319.8,129L319.8,107L313.1,107L313.1,128C313.1,129.1 312.2,130 311.1,130L283,130L267.04,156.29C266.46,157.23 265.1,158 264,158L242,158C240.9,158 239.54,157.23 238.96,156.29L223,130L194.94,130C193.83,130 192.94,129.1 192.94,128L192.94,107L217.75,107L229,92L277,92L288,107L342,107L342,405L319.81,405L319.81,383C319.81,379.14 316.68,376 312.81,376L287,376L270.05,348.7C269.47,347.76 268.1,347 267,347L239,347C237.9,347 236.53,347.76 235.95,348.7L219,376L193.2,376C189.34,376 186.2,379.14 186.2,383L186.2,405L192.94,405L192.94,384C192.94,382.9 193.83,382 194.94,382L223,382L238.96,355.71C239.54,354.77 240.9,354 242,354L264,354C265.1,354 266.46,354.77 267.04,355.71L283,382L311.06,382C312.17,382 313.06,382.9 313.06,384L313.06,405L288.25,405L277,420L229,420L218,405L164,405L164,107Z"
android:color="#00000000" android:fillColor="#EBEBEB"/>
android:offset="1.0" /> <path
</gradient> android:pathData="M212.61,181.5L299.39,181.5C306.15,181.5 311.63,186.98 311.63,193.74L311.63,318.26C311.63,325.02 306.15,330.5 299.39,330.5L212.61,330.5C205.85,330.5 200.37,325.02 200.37,318.26L200.37,193.74C200.37,186.98 205.85,181.5 212.61,181.5Z"
</aapt:attr> android:fillColor="#000000"
</path> android:strokeColor="#00000000"/>
<path <path
android:fillColor="#FFFFFF" android:pathData="M0,249L192.88,249L214,208L232.38,249L253.69,171L274.85,255L296,194L310.27,249L512,249L512,269L296,269L294,261L274.85,331L262.59,283.5L253.69,249L232.38,316L213.38,255L205.75,269L0,269L0,249Z"
android:fillType="nonZero" android:fillColor="#EBEBEB"/>
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" <group>
android:strokeWidth="1" <clip-path
android:strokeColor="#00000000" /> android:pathData="M-1024,-1024h2048v2048h-2048zM0,249L192.88,249L214,208L232.38,249L253.69,171L274.85,255L296,194L310.27,249L512,249L512,269L296,269L294,261L274.85,331L262.59,283.5L253.69,249L232.38,316L213.38,255L205.75,269L0,269L0,249Z"/>
<path
android:pathData="M0,249L192.88,249L214,208L232.38,249L253.69,171L274.85,255L296,194L310.27,249L512,249L512,269L296,269L294,261L274.85,331L262.59,283.5L253.69,249L232.38,316L213.38,255L205.75,269L0,269L0,249Z"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#EBEBEB"
android:strokeColor="#000000"
android:strokeLineCap="square"/>
</group>
</group>
</group>
</vector> </vector>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 938 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#000000</color>
</resources>
+1 -1
View File
@@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.7.3" agp = "8.12.2"
datastore = "1.1.2" datastore = "1.1.2"
kotlin = "2.0.0" kotlin = "2.0.0"
coreKtx = "1.15.0" coreKtx = "1.15.0"
+1 -1
View File
@@ -1,6 +1,6 @@
#Tue Dec 24 10:55:57 UTC 2024 #Tue Dec 24 10:55:57 UTC 2024
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists