diff --git a/lib/app/battle/functions/add_state.dart b/lib/app/battle/functions/add_state.dart index 3ed6a7d35..28e34db93 100644 --- a/lib/app/battle/functions/add_state.dart +++ b/lib/app/battle/functions/add_state.dart @@ -187,39 +187,37 @@ class AddState { final tdTypeChangeIDs = baseTd.script?.tdTypeChangeIDs; if (tdTypeChangeIDs == null || tdTypeChangeIDs.isEmpty) return null; - final validCardTypes = [CardType.arts, CardType.buster, CardType.quick]; - final cardIdTypeMap = {for (final c in validCardTypes) c.value: c}; + final validCardIndex = [CardType.arts.value, CardType.buster.value, CardType.quick.value]; if (excludeTypes != null && excludeTypes.isNotEmpty) { - validCardTypes.removeWhere((e) => excludeTypes.contains(e.value)); + validCardIndex.removeWhere((e) => excludeTypes.contains(e)); } - final tdExistTypes = - cardIdTypeMap.entries.where((e) => e.key <= tdTypeChangeIDs.length).map((e) => e.value).toList(); - validCardTypes.removeWhere((e) => !tdExistTypes.contains(e)); - if (validCardTypes.isEmpty) return null; + validCardIndex.retainWhere((e) => e <= tdTypeChangeIDs.length); + if (validCardIndex.isEmpty) return null; // in UI, Q/A/B order - CardType? targetType; + int? targetType; if (buff.type == BuffType.tdTypeChangeArts) { - targetType = CardType.arts; + targetType = CardType.arts.value; } else if (buff.type == BuffType.tdTypeChangeBuster) { - targetType = CardType.buster; + targetType = CardType.buster.value; } else if (buff.type == BuffType.tdTypeChangeQuick) { - targetType = CardType.quick; + targetType = CardType.quick.value; } else if (buff.type == BuffType.tdTypeChange) { if (battleData.delegate?.tdTypeChange != null) { - targetType = await battleData.delegate!.tdTypeChange!(svt, validCardTypes); + targetType = await battleData.delegate!.tdTypeChange!(svt, validCardIndex); } else if (battleData.mounted) { - targetType = await TdTypeChangeSelector.show(battleData, validCardTypes); + targetType = await TdTypeChangeSelector.show(battleData, tdTypeChangeIDs, validCardIndex, + svt.getBaseTD()?.script?.selectTreasureDeviceInfo?.getOrNull(svt.tdLv - 1)); if (targetType != null) { battleData.replayDataRecord.tdTypeChanges.add(targetType); } } } NiceTd? targetTd; - if (targetType != null && validCardTypes.contains(targetType)) { + if (targetType != null && validCardIndex.contains(targetType)) { // start from Q/A/B=1/2/3 -> index 0/1/2 - final tdId = tdTypeChangeIDs.getOrNull(targetType.value - 1); + final tdId = tdTypeChangeIDs.getOrNull(targetType - 1); if (tdId == null) return null; final List tds = svt.isPlayer diff --git a/lib/app/battle/interactions/_delegate.dart b/lib/app/battle/interactions/_delegate.dart index b06661987..7153d8fb6 100644 --- a/lib/app/battle/interactions/_delegate.dart +++ b/lib/app/battle/interactions/_delegate.dart @@ -7,7 +7,7 @@ import '../models/battle.dart'; class BattleDelegate { Future Function(BattleServantData? actor)? actWeight; Future Function(BattleServantData? actor)? skillActSelect; - Future Function(BattleServantData? actor, List tdTypes)? tdTypeChange; + Future Function(BattleServantData? actor, List tdIndex)? tdTypeChange; Future Function(List targets)? ptRandom; Future Function(bool curResult)? canActivate; Future Function(int curRandom)? damageRandom; diff --git a/lib/app/battle/interactions/td_type_change_selector.dart b/lib/app/battle/interactions/td_type_change_selector.dart index 0846beaba..151ee3bab 100644 --- a/lib/app/battle/interactions/td_type_change_selector.dart +++ b/lib/app/battle/interactions/td_type_change_selector.dart @@ -11,39 +11,86 @@ import '_dialog.dart'; class TdTypeChangeSelector extends StatelessWidget { final BattleData battleData; - final List tdTypes; - final Completer completer; - const TdTypeChangeSelector({super.key, required this.battleData, required this.tdTypes, required this.completer}); + final List tdTypeChangeIds; + final List tdIndexes; + final SelectTreasureDeviceInfo? selectTdInfo; + final Completer completer; - static Future show(BattleData battleData, List tdTypes) { + const TdTypeChangeSelector({ + super.key, + required this.battleData, + required this.tdTypeChangeIds, + required this.tdIndexes, + required this.selectTdInfo, + required this.completer, + }); + + static Future show( + BattleData battleData, List tdTypeChangeIds, List tdIndexes, SelectTreasureDeviceInfo? selectTdInfo) { if (!battleData.mounted) return Future.value(); - return showUserConfirm( + return showUserConfirm( context: battleData.context!, - builder: (context, completer) => - TdTypeChangeSelector(battleData: battleData, tdTypes: tdTypes, completer: completer), + builder: (context, completer) => TdTypeChangeSelector( + battleData: battleData, + tdTypeChangeIds: tdTypeChangeIds, + tdIndexes: tdIndexes, + selectTdInfo: selectTdInfo, + completer: completer), ); } @override Widget build(BuildContext context) { - final tdTypes = this.tdTypes.toList(); - tdTypes.sort2((e) => [CardType.quick, CardType.arts, CardType.buster].indexOf(e)); + final tdIndexes = this.tdIndexes.toList(); + if (selectTdInfo == null) { + tdIndexes.sort2((e) => [CardType.quick.value, CardType.arts.value, CardType.buster.value].indexOf(e)); + } + List children = []; + final transl = Transl.miscScope('selectTreasureDeviceInfo'); + for (final tdIndex in tdIndexes) { + final tdId = tdTypeChangeIds.getOrNull(tdIndex - 1); + SelectTdInfoTdChangeParam? tdInfo; + if (selectTdInfo != null) { + tdInfo = selectTdInfo!.treasureDevices.firstWhereOrNull((e) => e.id == tdId); + } + CardType cardType = tdInfo?.type ?? CardType.values.firstWhere((e) => e.value == tdIndex); + Widget child = CommandCardWidget(card: cardType, width: 80); + if (tdInfo != null && tdInfo.message.isNotEmpty) { + child = Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + child, + Text( + transl(tdInfo.message).l, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 12), + ), + ], + ); + } + children.add(Flexible( + child: InkWell( + onTap: () { + Navigator.of(context).pop(tdIndex); + battleData.battleLogger.action('${S.current.battle_select_effect}: $tdIndex/${cardType.name.toTitle()}' + ' ${S.current.battle_np_card}'); + }, + child: child, + ), + )); + } + + // String? title; + // if (selectTdInfo != null) { + // title = transl(selectTdInfo!.title).l; + // title = title.replaceAll('\n', ''); + // } return SimpleCancelOkDialog( title: Text(S.current.battle_select_effect), content: Row( - children: [ - for (final tdType in tdTypes) - Flexible( - child: InkWell( - onTap: () { - Navigator.of(context).pop(tdType); - battleData.battleLogger.action('${S.current.battle_select_effect}: ${tdType.name.toUpperCase()}' - ' ${S.current.battle_np_card}'); - }, - child: CommandCardWidget(card: tdType, width: 80), - ), - ) - ], + mainAxisAlignment: MainAxisAlignment.center, + children: children, ), hideOk: true, hideCancel: true, diff --git a/lib/generated/models/gamedata/skill.g.dart b/lib/generated/models/gamedata/skill.g.dart index 2399ddae9..9d9f714b5 100644 --- a/lib/generated/models/gamedata/skill.g.dart +++ b/lib/generated/models/gamedata/skill.g.dart @@ -379,6 +379,9 @@ SkillScript _$SkillScriptFromJson(Map json) => SkillScript( SelectAddInfo: (json['SelectAddInfo'] as List?) ?.map((e) => SkillSelectAddInfo.fromJson(Map.from(e as Map))) .toList(), + selectTreasureDeviceInfo: (json['selectTreasureDeviceInfo'] as List?) + ?.map((e) => SelectTreasureDeviceInfo.fromJson(Map.from(e as Map))) + .toList(), IgnoreValueUp: json['IgnoreValueUp'], IgnoreBattlePointUp: json['IgnoreBattlePointUp'] as List?, tdChangeByBattlePoint: (json['tdChangeByBattlePoint'] as List?) @@ -397,6 +400,7 @@ Map _$SkillScriptToJson(SkillScript instance) { writeNotNull('actRarity', instance.actRarity); writeNotNull('SelectAddInfo', instance.SelectAddInfo?.map((e) => e.toJson()).toList()); + writeNotNull('selectTreasureDeviceInfo', instance.selectTreasureDeviceInfo?.map((e) => e.toJson()).toList()); writeNotNull('IgnoreValueUp', instance.IgnoreValueUp); writeNotNull('IgnoreBattlePointUp', instance.IgnoreBattlePointUp); writeNotNull('tdChangeByBattlePoint', instance.tdChangeByBattlePoint?.map((e) => e.toJson()).toList()); @@ -451,6 +455,35 @@ const _$SkillScriptCondEnumMap = { SkillScriptCond.hpPerLower: 'HP_PER_LOWER', }; +SelectTreasureDeviceInfo _$SelectTreasureDeviceInfoFromJson(Map json) => SelectTreasureDeviceInfo( + dialogType: (json['dialogType'] as num?)?.toInt() ?? 0, + title: json['title'] as String? ?? "", + messageOnSelected: json['messageOnSelected'] as String? ?? "", + treasureDevices: (json['treasureDevices'] as List?) + ?.map((e) => SelectTdInfoTdChangeParam.fromJson(Map.from(e as Map))) + .toList() ?? + const [], + ); + +Map _$SelectTreasureDeviceInfoToJson(SelectTreasureDeviceInfo instance) => { + 'dialogType': instance.dialogType, + 'title': instance.title, + 'messageOnSelected': instance.messageOnSelected, + 'treasureDevices': instance.treasureDevices.map((e) => e.toJson()).toList(), + }; + +SelectTdInfoTdChangeParam _$SelectTdInfoTdChangeParamFromJson(Map json) => SelectTdInfoTdChangeParam( + id: (json['id'] as num?)?.toInt() ?? 0, + type: $enumDecodeNullable(_$CardTypeEnumMap, json['type']) ?? CardType.none, + message: json['message'] as String? ?? "", + ); + +Map _$SelectTdInfoTdChangeParamToJson(SelectTdInfoTdChangeParam instance) => { + 'id': instance.id, + 'type': _$CardTypeEnumMap[instance.type]!, + 'message': instance.message, + }; + TdChangeByBattlePoint _$TdChangeByBattlePointFromJson(Map json) => TdChangeByBattlePoint( battlePointId: (json['battlePointId'] as num).toInt(), phase: (json['phase'] as num).toInt(), diff --git a/lib/generated/models/userdata/battle.g.dart b/lib/generated/models/userdata/battle.g.dart index c7dc2def0..f2fc37e6e 100644 --- a/lib/generated/models/userdata/battle.g.dart +++ b/lib/generated/models/userdata/battle.g.dart @@ -585,10 +585,10 @@ BattleReplayDelegateData _$BattleReplayDelegateDataFromJson(Map json) => $checke skillActSelectSelections: $checkedConvert( 'skillActSelectSelections', (v) => (v as List?)?.map((e) => (e as num?)?.toInt()).toList()), tdTypeChanges: $checkedConvert( - 'tdTypeChanges', - (v) => (v as List?) - ?.map((e) => $enumDecode(_$CardTypeEnumMap, e, unknownValue: CardType.none)) - .toList()), + 'tdTypeChanges', + (v) => (v as List?)?.map((e) => (e as num).toInt()).toList(), + readValue: BattleReplayDelegateData._readTdTypeChanges, + ), ptRandomIndexes: $checkedConvert( 'ptRandomIndexes', (v) => (v as List?)?.map((e) => (e as num?)?.toInt()).toList()), canActivateDecisions: @@ -608,24 +608,13 @@ BattleReplayDelegateData _$BattleReplayDelegateDataFromJson(Map json) => $checke Map _$BattleReplayDelegateDataToJson(BattleReplayDelegateData instance) => { 'actWeightSelections': instance.actWeightSelections, 'skillActSelectSelections': instance.skillActSelectSelections, - 'tdTypeChanges': instance.tdTypeChanges.map((e) => _$CardTypeEnumMap[e]!).toList(), + 'tdTypeChanges': instance.tdTypeChanges, 'ptRandomIndexes': instance.ptRandomIndexes, 'canActivateDecisions': instance.canActivateDecisions, 'damageSelections': instance.damageSelections, 'replaceMemberIndexes': instance.replaceMemberIndexes, }; -const _$CardTypeEnumMap = { - CardType.none: 'none', - CardType.arts: 'arts', - CardType.buster: 'buster', - CardType.quick: 'quick', - CardType.extra: 'extra', - CardType.blank: 'blank', - CardType.weak: 'weak', - CardType.strength: 'strength', -}; - BattleActionOptions _$BattleActionOptionsFromJson(Map json) => $checkedCreate( 'BattleActionOptions', json, @@ -713,3 +702,14 @@ Map _$BattleAttackRecordDataToJson(BattleAttackRecordData insta 'critical': instance.critical, 'cardType': _$CardTypeEnumMap[instance.cardType]!, }; + +const _$CardTypeEnumMap = { + CardType.none: 'none', + CardType.arts: 'arts', + CardType.buster: 'buster', + CardType.quick: 'quick', + CardType.extra: 'extra', + CardType.blank: 'blank', + CardType.weak: 'weak', + CardType.strength: 'strength', +}; diff --git a/lib/models/gamedata/buff.dart b/lib/models/gamedata/buff.dart index 329c8e501..435c0943e 100644 --- a/lib/models/gamedata/buff.dart +++ b/lib/models/gamedata/buff.dart @@ -129,6 +129,18 @@ class RelationOverwriteDetail { Map toJson() => _$RelationOverwriteDetailToJson(this); } +enum BuffCheckIndivType { + orType(0), + andType(1), + bothOrOnlyInGroup(2), // bothOrType + bothAndAllType(3), // bothAndType + bothOrAll(4), + ; + + const BuffCheckIndivType(this.value); + final int value; +} + @JsonSerializable(includeIfNull: false) class BuffScript with DataScriptBase { int? checkIndvType; // 1-AND, default-OR diff --git a/lib/models/gamedata/skill.dart b/lib/models/gamedata/skill.dart index 96c96d420..912645e1e 100644 --- a/lib/models/gamedata/skill.dart +++ b/lib/models/gamedata/skill.dart @@ -847,6 +847,7 @@ class SkillScript with DataScriptBase { // TD script List? get tdTypeChangeIDs => toList('tdTypeChangeIDs'); List? get excludeTdChangeTypes => toList('excludeTdChangeTypes'); + final List? selectTreasureDeviceInfo; // skill.script, not in skillLv.script final bool? IgnoreValueUp; @@ -870,6 +871,7 @@ class SkillScript with DataScriptBase { additionalSkillLv?.isNotEmpty == true || additionalSkillActorType?.isNotEmpty == true || SelectAddInfo?.isNotEmpty == true || + selectTreasureDeviceInfo?.isNotEmpty == true || tdTypeChangeIDs?.isNotEmpty == true || excludeTdChangeTypes?.isNotEmpty == true || IgnoreValueUp != null || @@ -893,6 +895,7 @@ class SkillScript with DataScriptBase { this.SelectAddInfo, // this.tdTypeChangeIDs, // this.excludeTdChangeTypes, + this.selectTreasureDeviceInfo, dynamic IgnoreValueUp, List? IgnoreBattlePointUp, this.tdChangeByBattlePoint, @@ -968,6 +971,42 @@ class SkillSelectAddInfoBtnCond { Map toJson() => _$SkillSelectAddInfoBtnCondToJson(this); } +@JsonSerializable() +class SelectTreasureDeviceInfo { + final int dialogType; + final String title; + final String messageOnSelected; + final List treasureDevices; + + SelectTreasureDeviceInfo({ + this.dialogType = 0, + this.title = "", + this.messageOnSelected = "", + this.treasureDevices = const [], + }); + + factory SelectTreasureDeviceInfo.fromJson(Map json) => _$SelectTreasureDeviceInfoFromJson(json); + + Map toJson() => _$SelectTreasureDeviceInfoToJson(this); +} + +@JsonSerializable() +class SelectTdInfoTdChangeParam { + final int id; + final CardType type; + final String message; + + SelectTdInfoTdChangeParam({ + this.id = 0, + this.type = CardType.none, + this.message = "", + }); + + factory SelectTdInfoTdChangeParam.fromJson(Map json) => _$SelectTdInfoTdChangeParamFromJson(json); + + Map toJson() => _$SelectTdInfoTdChangeParamToJson(this); +} + @JsonSerializable() class TdChangeByBattlePoint { int battlePointId; diff --git a/lib/models/userdata/battle.dart b/lib/models/userdata/battle.dart index c1fa9753b..54931ccd5 100644 --- a/lib/models/userdata/battle.dart +++ b/lib/models/userdata/battle.dart @@ -978,10 +978,19 @@ enum SupportSvtType { @JsonSerializable() class BattleReplayDelegateData { + static List? _readTdTypeChanges(Map data, String key) { + List? values = data[key]; + if (values == null) return null; + return values.map((e) { + if (e is int) return e; + return CardType.values.firstWhere((card) => card.name == e).value; + }).toList(); + } + List actWeightSelections; List skillActSelectSelections; - @JsonKey(unknownEnumValue: CardType.none) - List tdTypeChanges; + @JsonKey(readValue: BattleReplayDelegateData._readTdTypeChanges) + List tdTypeChanges; List ptRandomIndexes; List canActivateDecisions; List damageSelections; @@ -990,7 +999,7 @@ class BattleReplayDelegateData { BattleReplayDelegateData({ List? actWeightSelections, List? skillActSelectSelections, - List? tdTypeChanges, + List? tdTypeChanges, List? ptRandomIndexes, List? canActivateDecisions, List? damageSelections, diff --git a/test/app/battle/models/battle_test.dart b/test/app/battle/models/battle_test.dart index 148ccafce..285bb8979 100644 --- a/test/app/battle/models/battle_test.dart +++ b/test/app/battle/models/battle_test.dart @@ -1903,7 +1903,7 @@ void main() async { int count = 0; battle.delegate = BattleDelegate(); - battle.delegate?.tdTypeChange = (_actor, _list) async => [CardType.arts, CardType.buster][count++]; + battle.delegate?.tdTypeChange = (_actor, _list) async => [CardType.arts.value, CardType.buster.value][count++]; expect(emiya.getNPCard()!.cardType, CardType.buster);