From 2a954daf1cee3726b7634429e1754c6f7337fa84 Mon Sep 17 00:00:00 2001 From: Yome <695312637@qq.com> Date: Sat, 17 Aug 2024 21:41:51 -0700 Subject: [PATCH] DamageValue --- .../battle/functions/function_executor.dart | 12 +- lib/app/battle/functions/gain_hp.dart | 97 ++++++++++++--- .../interactions/damage_value_adjustor.dart | 115 ++++++++++++++++++ 3 files changed, 204 insertions(+), 20 deletions(-) create mode 100644 lib/app/battle/interactions/damage_value_adjustor.dart diff --git a/lib/app/battle/functions/function_executor.dart b/lib/app/battle/functions/function_executor.dart index 91abb7ff7..c02ca2c86 100644 --- a/lib/app/battle/functions/function_executor.dart +++ b/lib/app/battle/functions/function_executor.dart @@ -367,11 +367,17 @@ class FunctionExecutor { break; case FuncType.gainHp: case FuncType.gainHpPer: + await GainHP.gainHP(battleData, dataVals, activator, targets, function.funcType); + break; case FuncType.lossHp: case FuncType.lossHpSafe: case FuncType.lossHpPer: case FuncType.lossHpPerSafe: - await GainHP.gainHP(battleData, dataVals, activator, targets, function.funcType); + await GainHP.lossHP(battleData, dataVals, activator, targets, function.funcType); + break; + case FuncType.damageValue: + case FuncType.damageValueSafe: + await GainHP.damageValue(battleData, dataVals, activator, targets, function.funcType); break; case FuncType.gainHpFromTargets: await GainHpFromTargets.gainHpFromTargets(battleData, dataVals, activator!, targetedAlly, targetedEnemy); @@ -430,10 +436,8 @@ class FunctionExecutor { AddBattlePoint.addBattlePoint(battleData, dataVals, targets, overchargeState, ignoreBattlePoints); break; case FuncType.updateEnemyEntryMaxCountEachTurn: - case FuncType.damageValue: - case FuncType.damageValueSafe: - case FuncType.damageValueSafeOnce: // ↑↑↑ should be implemented ↑↑↑ + case FuncType.damageValueSafeOnce: case FuncType.subFieldBuff: case FuncType.damageNpAndCheckIndividuality: case FuncType.damageNpStateIndividual: diff --git a/lib/app/battle/functions/gain_hp.dart b/lib/app/battle/functions/gain_hp.dart index 370d94c57..d36fd67c0 100644 --- a/lib/app/battle/functions/gain_hp.dart +++ b/lib/app/battle/functions/gain_hp.dart @@ -1,3 +1,4 @@ +import 'package:chaldea/app/battle/interactions/damage_value_adjustor.dart'; import 'package:chaldea/app/battle/models/battle.dart'; import 'package:chaldea/app/battle/utils/battle_utils.dart'; import 'package:chaldea/models/gamedata/gamedata.dart'; @@ -5,8 +6,7 @@ import 'package:chaldea/models/gamedata/gamedata.dart'; class GainHP { GainHP._(); - static final lossFuncTypes = {FuncType.lossHp, FuncType.lossHpSafe, FuncType.lossHpPer, FuncType.lossHpPerSafe}; - static final lethalFuncTypes = {FuncType.lossHp, FuncType.lossHpPer}; + static final lethalFuncTypes = {FuncType.lossHp, FuncType.lossHpPer, FuncType.damageValue}; static final percentFuncTypes = {FuncType.gainHpPer, FuncType.lossHpPer, FuncType.lossHpPerSafe}; static Future gainHP( @@ -20,8 +20,7 @@ class GainHP { if (functionRate < battleData.options.threshold) { return; } - final isLoss = lossFuncTypes.contains(funcType); - final isLethal = lethalFuncTypes.contains(funcType); + final isPercent = percentFuncTypes.contains(funcType); for (final target in targets) { @@ -31,19 +30,85 @@ class GainHP { final previousHp = target.hp; final baseValue = isPercent ? target.maxHp * toModifier(dataVals.Value!) : dataVals.Value!; - if (isLoss) { - target.lossHp(baseValue.toInt(), lethal: isLethal); - target.actionHistory.add(BattleServantActionHistory( - actType: BattleServantActionHistoryType.hploss, - targetUniqueId: activator?.uniqueId ?? -1, - isOpponent: false, - )); - } else { - final healGrantEff = await activator?.getBuffValue(battleData, BuffAction.giveGainHp, other: target) ?? 1000; - final healReceiveEff = await target.getBuffValue(battleData, BuffAction.gainHp); - final finalHeal = (baseValue * toModifier(healReceiveEff) * toModifier(healGrantEff)).toInt(); - target.heal(finalHeal); + final healGrantEff = await activator?.getBuffValue(battleData, BuffAction.giveGainHp, other: target) ?? 1000; + final healReceiveEff = await target.getBuffValue(battleData, BuffAction.gainHp); + final finalHeal = (baseValue * toModifier(healReceiveEff) * toModifier(healGrantEff)).toInt(); + target.heal(finalHeal); + target.procAccumulationDamage(previousHp); + battleData.setFuncResult(target.uniqueId, true); + } + } + + static Future lossHP( + final BattleData battleData, + final DataVals dataVals, + final BattleServantData? activator, + final Iterable targets, + final FuncType funcType, + ) async { + final functionRate = dataVals.Rate ?? 1000; + if (functionRate < battleData.options.threshold) { + return; + } + final isLethal = lethalFuncTypes.contains(funcType); + final isPercent = percentFuncTypes.contains(funcType); + + for (final target in targets) { + if (target.hp <= 0) { + continue; + } + + final previousHp = target.hp; + final baseValue = isPercent ? (target.maxHp * toModifier(dataVals.Value!)).toInt() : dataVals.Value!; + int finalValue = baseValue; + if (dataVals.Value2 != null) { + final upperBound = isPercent ? (target.maxHp * toModifier(dataVals.Value2!).toInt()) : dataVals.Value2!; + finalValue = await DamageValueAdjustor.show(battleData, activator, target, funcType, baseValue, upperBound); } + + target.lossHp(finalValue, lethal: isLethal); + target.actionHistory.add(BattleServantActionHistory( + actType: BattleServantActionHistoryType.hploss, + targetUniqueId: activator?.uniqueId ?? -1, + isOpponent: false, + )); + target.procAccumulationDamage(previousHp); + battleData.setFuncResult(target.uniqueId, true); + } + } + + // basically the same as lossHp + static Future damageValue( + final BattleData battleData, + final DataVals dataVals, + final BattleServantData? activator, + final Iterable targets, + final FuncType funcType, + ) async { + final functionRate = dataVals.Rate ?? 1000; + if (functionRate < battleData.options.threshold) { + return; + } + + final isLethal = funcType == FuncType.damageValueSafe; + for (final target in targets) { + if (target.hp <= 0) { + continue; + } + + final previousHp = target.hp; + final baseVal = dataVals.Value!; + int finalValue = baseVal; + if (dataVals.Value2 != null) { + finalValue = await DamageValueAdjustor.show(battleData, activator, target, funcType, baseVal, dataVals.Value2!); + } + + target.lossHp(finalValue, lethal: isLethal); + target.actionHistory.add(BattleServantActionHistory( + actType: BattleServantActionHistoryType.damageValue, + targetUniqueId: activator?.uniqueId ?? -1, + isOpponent: false, + )); target.procAccumulationDamage(previousHp); battleData.setFuncResult(target.uniqueId, true); } diff --git a/lib/app/battle/interactions/damage_value_adjustor.dart b/lib/app/battle/interactions/damage_value_adjustor.dart new file mode 100644 index 000000000..7cdfd274a --- /dev/null +++ b/lib/app/battle/interactions/damage_value_adjustor.dart @@ -0,0 +1,115 @@ +import 'package:chaldea/generated/l10n.dart'; +import 'package:chaldea/widgets/widgets.dart'; +import '../../../models/gamedata/func.dart'; +import '../../../models/gamedata/mappings.dart'; +import '../models/battle.dart'; +import '_dialog.dart'; + +class DamageValueAdjustor extends StatefulWidget { + final BattleServantData? activator; + final BattleServantData target; + final FuncType funcType; + final int minDamage; + final int maxDamage; + + const DamageValueAdjustor({ + super.key, + required this.activator, + required this.target, + required this.funcType, + required this.minDamage, + required this.maxDamage, + }); + + @override + State createState() => _DamageValueAdjustorState(); + + static Future show( + final BattleData battleData, + final BattleServantData? activator, + final BattleServantData target, + final FuncType funcType, + final int minDamage, + final int maxDamage, + ) async { + int damage = minDamage; + + if (battleData.options.tailoredExecution) { + if (battleData.delegate?.damageRandom != null) { + return await battleData.delegate!.damageRandom!.call(damage); + } else if (battleData.mounted) { + final damage = await showUserConfirm( + context: battleData.context!, + barrierDismissible: false, + builder: (context, _) { + return DamageValueAdjustor( + activator: activator, + target: target, + funcType: funcType, + minDamage: minDamage, + maxDamage: maxDamage, + ); + }, + ); + battleData.replayDataRecord.damageSelections.add(damage); + return damage; + } + } + + return damage; + } +} + +class _DamageValueAdjustorState extends State { + bool exceptionThrown = false; + int damage = 0; + + @override + void initState() { + super.initState(); + damage = damage.clamp(widget.minDamage, widget.maxDamage); + } + + @override + Widget build(BuildContext context) { + return SimpleCancelOkDialog( + title: Text(S.current.battle_select_effect), + scrollable: true, + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${widget.activator?.lBattleName ?? S.current.battle_no_source} - ${Transl.funcType(widget.funcType).l}' + '\nvs ${widget.target.lBattleName} (HP: ${widget.target.hp})', + style: Theme.of(context).textTheme.bodyMedium, + textScaler: const TextScaler.linear(0.9), + ), + const SizedBox(height: 8), + Text('${widget.minDamage} - ${widget.maxDamage}', style: Theme.of(context).textTheme.bodySmall), + SliderWithPrefix( + titled: true, + label: S.current.damage, + min: widget.minDamage, + max: widget.maxDamage, + value: damage, + onChange: (v) { + damage = v.toInt(); + if (mounted) setState(() {}); + }, + ), + ], + ), + hideOk: true, + hideCancel: true, + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(damage); + }, + child: Text(S.current.confirm), + ) + ], + ); + } +}