From 70cec76abbc013ee9161f32bfcc6f1ca5f1d5ba6 Mon Sep 17 00:00:00 2001 From: narumi Date: Mon, 7 Oct 2024 06:27:32 +0800 Subject: [PATCH] Show bond progress in user formation decks --- .../battle/formation/formation_card.dart | 46 +++++++++++-- lib/app/modules/faker/faker.dart | 64 +++++++++---------- .../sniff_details/formation_decks.dart | 2 + lib/models/gamedata/servant.dart | 8 +++ 4 files changed, 81 insertions(+), 39 deletions(-) diff --git a/lib/app/modules/battle/formation/formation_card.dart b/lib/app/modules/battle/formation/formation_card.dart index 95f1b2f2d..f9e4ce6ab 100644 --- a/lib/app/modules/battle/formation/formation_card.dart +++ b/lib/app/modules/battle/formation/formation_card.dart @@ -11,6 +11,7 @@ class FormationCard extends StatelessWidget { final bool showAllMysticCodeIcon; final bool fadeOutMysticCode; final Map? userSvtCollections; + final bool showBond; const FormationCard({ super.key, @@ -18,6 +19,7 @@ class FormationCard extends StatelessWidget { this.showAllMysticCodeIcon = false, this.fadeOutMysticCode = false, this.userSvtCollections, + this.showBond = false, }); @override @@ -34,13 +36,15 @@ class FormationCard extends StatelessWidget { Widget _buildServantIcons(BuildContext context, final SvtSaveData? storedData) { String svtInfo = '', ceInfo = ""; + final svtCollection = userSvtCollections?[storedData?.svtId]; if (storedData != null) { if (storedData.svtId != null && storedData.svtId != 0) { svtInfo = [ - ' Lv.${storedData.lv} NP${storedData.tdLv}', + if (showBond && svtCollection != null) ' ◈ ${svtCollection.friendshipRank}', + ' Lv.${storedData.lv} NP${storedData.tdLv} ', if (storedData.atkFou != 1000 || storedData.hpFou != 1000) ' ${storedData.atkFou}/${storedData.hpFou}', ' ${storedData.skillLvs.join("/")}', - ' ${storedData.appendLvs.map((e) => e == 0 ? "-" : e).join("/")}', + ' ${storedData.appendLvs.map((e) => e == 0 ? "-" : e).join("/")} ', ].join('\n'); } if (storedData.ceId != null && storedData.ceId != 0) { @@ -52,10 +56,11 @@ class FormationCard extends StatelessWidget { } } + final svt = db.gameData.servantsById[storedData?.svtId]; + Widget svtIcon = GameCardMixin.cardIconBuilder( context: context, - icon: - db.gameData.servantsById[storedData?.svtId]?.ascendIcon(storedData!.limitCount) ?? Atlas.common.emptySvtIcon, + icon: svt?.ascendIcon(storedData!.limitCount) ?? Atlas.common.emptySvtIcon, // width: 80, aspectRatio: 132 / 144, text: svtInfo, @@ -134,10 +139,20 @@ class FormationCard extends StatelessWidget { )); }, ); + final bonds = + svtCollection == null ? null : svt?.getPastNextBonds(svtCollection.friendshipRank, svtCollection.friendship); + Widget child = Column( children: [ svtIcon, ceIcon, + if (showBond && bonds != null) + BondProgress( + value: bonds.$1, + total: bonds.$1 + bonds.$2, + padding: EdgeInsets.only(top: 1.5), + minHeight: 3, + ), ], ); child = Container( @@ -197,3 +212,26 @@ class FormationCard extends StatelessWidget { ); } } + +class BondProgress extends StatelessWidget { + final int value; + final int total; + final double? minHeight; + final EdgeInsetsGeometry? padding; + const BondProgress({super.key, required this.value, required this.total, this.padding, this.minHeight}); + + @override + Widget build(BuildContext context) { + final highlight = value == 0 || value == total; + Widget child = LinearProgressIndicator( + minHeight: minHeight, + value: value / total, + color: highlight ? Colors.green : Colors.blue, + backgroundColor: highlight ? Colors.amber.shade800 : Colors.red, + ); + if (padding != null) { + child = Padding(padding: padding!, child: child); + } + return child; + } +} diff --git a/lib/app/modules/faker/faker.dart b/lib/app/modules/faker/faker.dart index 44d8be6fd..0d1ffe971 100644 --- a/lib/app/modules/faker/faker.dart +++ b/lib/app/modules/faker/faker.dart @@ -263,13 +263,11 @@ class _FakeGrandOrderState extends State { ), )); if (curLv != null && nextLv != null && userGame.exp >= curLv.requiredExp && userGame.exp <= nextLv.requiredExp) { - children.add(Row( - children: [ - const SizedBox(width: 16), - Expanded(flex: userGame.exp - curLv.requiredExp, child: Container(height: 4, color: Colors.blue)), - Expanded(flex: nextLv.requiredExp - userGame.exp, child: Container(height: 4, color: Colors.red)), - const SizedBox(width: 16), - ], + children.add(BondProgress( + value: userGame.exp - curLv.requiredExp, + total: nextLv.requiredExp - curLv.requiredExp, + padding: EdgeInsets.symmetric(horizontal: 16), + minHeight: 4, )); } @@ -796,12 +794,8 @@ class _FakeGrandOrderState extends State { svt.bondGrowth.length < collection.friendshipRank + 1) { return const Expanded(flex: 10, child: SizedBox.shrink()); } - final prevBondTotal = - collection.friendshipRank == 0 ? 0 : svt.bondGrowth[collection.friendshipRank - 1], - curBondTotal = svt.bondGrowth[collection.friendshipRank]; - final bool reachBondLimit = collection.friendship >= curBondTotal; - final int bondA = collection.friendship - prevBondTotal, - bondB = curBondTotal - collection.friendship; + final (bondA, bondB) = svt.getPastNextBonds(collection.friendshipRank, collection.friendship); + final bool reachBondLimit = bondB == 0; String bondText = 'Lv.${collection.friendshipRank}/${10 + collection.friendshipExceedCount}' // '\n${collection.friendship}' @@ -825,13 +819,11 @@ class _FakeGrandOrderState extends State { maxLines: bondText.count('\n') + 1, style: reachBondLimit ? TextStyle(color: Theme.of(context).colorScheme.error) : null, ), - Row( - children: [ - const SizedBox(width: 4), - Expanded(flex: bondA, child: Container(height: 4, color: Colors.blue)), - Expanded(flex: bondB, child: Container(height: 4, color: Colors.red)), - const SizedBox(width: 4), - ], + BondProgress( + value: bondA, + total: bondA + bondB, + padding: EdgeInsets.symmetric(horizontal: 4), + minHeight: 4, ), ], ), @@ -939,19 +931,6 @@ class _FakeGrandOrderState extends State { icon: const Icon(Icons.add_circle), ), ), - SwitchListTile.adaptive( - dense: true, - value: battleOption.supportCeMaxLimitBreak, - title: Text('${S.current.support_servant} - ${S.current.craft_essence_short} ${S.current.max_limit_break}'), - onChanged: (v) { - runtime.lockTask(() { - setState(() { - battleOption.supportCeMaxLimitBreak = v; - }); - }); - }, - controlAffinity: ListTileControlAffinity.trailing, - ), const Divider(), SwitchListTile.adaptive( dense: true, @@ -980,8 +959,10 @@ class _FakeGrandOrderState extends State { value: battleOption.isApHalf, title: const Text("During AP Half Event"), onChanged: (v) { - setState(() { - battleOption.isApHalf = v; + runtime.lockTask(() { + setState(() { + battleOption.isApHalf = v; + }); }); }, ), @@ -1107,6 +1088,19 @@ class _FakeGrandOrderState extends State { }, controlAffinity: ListTileControlAffinity.trailing, ), + SwitchListTile.adaptive( + dense: true, + value: battleOption.supportCeMaxLimitBreak, + title: Text('${S.current.support_servant} - ${S.current.craft_essence_short} ${S.current.max_limit_break}'), + onChanged: (v) { + runtime.lockTask(() { + setState(() { + battleOption.supportCeMaxLimitBreak = v; + }); + }); + }, + controlAffinity: ListTileControlAffinity.trailing, + ), CheckboxListTile.adaptive( dense: true, value: battleOption.useEventDeck, diff --git a/lib/app/modules/import_data/sniff_details/formation_decks.dart b/lib/app/modules/import_data/sniff_details/formation_decks.dart index cd77713bf..067aecf7a 100644 --- a/lib/app/modules/import_data/sniff_details/formation_decks.dart +++ b/lib/app/modules/import_data/sniff_details/formation_decks.dart @@ -43,6 +43,8 @@ class UserFormationDecksPageState extends State { DividerWithTitle(title: '[${deck.id}] No.${deck.deckNo} ${deck.name}'), FormationCard( formation: deck.toFormation(mstData: mstData, userSvts: userSvts), + userSvtCollections: mstData.userSvtCollection.dict, + showBond: true, ), if (widget.onSelected != null) Padding( diff --git a/lib/models/gamedata/servant.dart b/lib/models/gamedata/servant.dart index 51e9ef6fc..585b8b439 100644 --- a/lib/models/gamedata/servant.dart +++ b/lib/models/gamedata/servant.dart @@ -634,6 +634,14 @@ class Servant extends BasicServant { return materials; } + (int past, int next) getPastNextBonds(int friendshipRank, int friendship) { + if (bondGrowth.isEmpty || bondGrowth.length < friendshipRank) return (1, 0); + if (friendshipRank == bondGrowth.length) { + return (bondGrowth[friendshipRank - 1] - bondGrowth[friendshipRank - 2], 0); + } + return (friendship - bondGrowth[friendshipRank - 1], bondGrowth[friendshipRank] - friendship); + } + void updateStat() { db.itemCenter.updateSvts(svts: [this]); }