diff --git a/src/main/resources/db/migration/V1.19.0__Create_break_suppression_preference_table.sql b/src/main/resources/db/migration/V1.19.0__Create_break_suppression_preference_table.sql new file mode 100644 index 0000000000..6154fc19c8 --- /dev/null +++ b/src/main/resources/db/migration/V1.19.0__Create_break_suppression_preference_table.sql @@ -0,0 +1,8 @@ +USE seichiassist; + +CREATE TABLE player_break_suppression_preference( + uuid CHAR(36) NOT NULL, + do_break_suppression_due_to_mana BOOL NOT NULL DEFAULT FALSE, + PRIMARY KEY (uuid), + CONSTRAINT fk_player_break_suppression_preference_uuid FOREIGN KEY (uuid) REFERENCES playerdata(uuid) +); diff --git a/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala b/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala index f7359a6b8b..f0bd4e3822 100644 --- a/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala +++ b/src/main/scala/com/github/unchama/seichiassist/SeichiAssist.scala @@ -63,6 +63,7 @@ import com.github.unchama.seichiassist.subsystems.autosave.application.SystemCon import com.github.unchama.seichiassist.subsystems.breakcount.{BreakCountAPI, BreakCountReadAPI} import com.github.unchama.seichiassist.subsystems.breakcountbar.BreakCountBarAPI import com.github.unchama.seichiassist.subsystems.breakskilltargetconfig.BreakSkillTargetConfigAPI +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.BreakSuppressionPreferenceAPI import com.github.unchama.seichiassist.subsystems.buildcount.BuildCountAPI import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI import com.github.unchama.seichiassist.subsystems.donate.DonatePremiumPointAPI @@ -495,6 +496,10 @@ class SeichiAssist extends JavaPlugin() { lazy val breakSkillTargetConfigSystem: subsystems.breakskilltargetconfig.System[IO, Player] = subsystems.breakskilltargetconfig.System.wired[IO, SyncIO].unsafeRunSync() + lazy val breakSuppressionPreferenceSystem + : subsystems.breaksuppressionpreference.System[IO, Player] = + subsystems.breaksuppressionpreference.System.wired[IO, SyncIO].unsafeRunSync() + /* TODO: mineStackSystemは本来privateであるべきだが、mineStackにアイテムを格納するAPIが現状の BreakUtilの実装から呼び出されている都合上やむを得ずpublicになっている。*/ lazy val mineStackSystem: subsystems.minestack.System[IO, Player, ItemStack] = @@ -562,6 +567,7 @@ class SeichiAssist extends JavaPlugin() { openirontrapdoor.System.wired, gridRegionSystem, breakSkillTargetConfigSystem, + breakSuppressionPreferenceSystem, joinAndQuitMessenger, elevatorSystem, blockLiquidStreamSystem, @@ -746,6 +752,8 @@ class SeichiAssist extends JavaPlugin() { implicit val gridRegionAPI: GridRegionAPI[IO, Player, Location] = gridRegionSystem.api implicit val breakSkillTargetConfigAPI: BreakSkillTargetConfigAPI[IO, Player] = breakSkillTargetConfigSystem.api + implicit val breakSuppressionPreferenceAPI: BreakSuppressionPreferenceAPI[IO, Player] = + breakSuppressionPreferenceSystem.api implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] = playerHeadSkinSystem.api val menuRouter = TopLevelRouter.apply diff --git a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala index ebc784fff2..18ed69bfbb 100644 --- a/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala +++ b/src/main/scala/com/github/unchama/seichiassist/listener/PlayerBlockBreakListener.scala @@ -7,6 +7,7 @@ import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.ManagedWorld._ import com.github.unchama.seichiassist.MaterialSets.{BlockBreakableBySkill, BreakTool} import com.github.unchama.seichiassist.concurrent.PluginExecutionContexts +import com.github.unchama.seichiassist.data.{AxisAlignedCuboid} import com.github.unchama.seichiassist.seichiskill.ActiveSkillRange.MultiArea import com.github.unchama.seichiassist.seichiskill.SeichiSkillUsageMode.Disabled import com.github.unchama.seichiassist.seichiskill.{BlockSearching, BreakArea} @@ -17,6 +18,7 @@ import com.github.unchama.seichiassist.subsystems.minestack.MineStackAPI import com.github.unchama.seichiassist.util.BreakUtil import com.github.unchama.seichiassist.{MaterialSets, SeichiAssist} import com.github.unchama.targetedeffect.player.FocusedSoundEffect +import com.github.unchama.targetedeffect.player.ActionBarMessageEffect import com.github.unchama.util.bukkit.ItemStackUtil import com.github.unchama.util.effect.BukkitResources import com.github.unchama.util.external.WorldGuardWrapper @@ -101,20 +103,66 @@ class PlayerBlockBreakListener( return } - // 追加マナ獲得 - manaApi - .manaAmount(player) - .restoreAbsolute(ManaAmount(BreakUtil.calcManaDrop(player))) - .unsafeRunSync() - + // 選択したスキル val selectedSkill = skillState .activeSkill .getOrElse( return ) - if (!selectedSkill.range.isInstanceOf[MultiArea] || skillState.usageMode == Disabled) return + // 消費するマナが不足しているか判定 + { + // プレイヤーのY座標 + val playerLocY = player.getLocation.getBlockY - 1 + // スキル破壊範囲 + val skillArea = BreakArea(selectedSkill, skillState.usageMode) + // 破壊エリアリスト + val breakAreaList = skillArea.makeBreakArea(player).unsafeRunSync() + // 複数種類ブロック同時破壊設定 + val isMultiTypeBreakingSkillEnabled = + BreakUtil.performsMultipleIDBlockBreakWhenUsingSkills(player).unsafeRunSync() + // 破壊範囲のブロック計算 + val totalBreakRangeVolume = { + val breakLength = skillArea.breakLength + breakLength.x * breakLength.y * breakLength.z * skillArea.breakNum + } + breakAreaList.foreach { breakArea => + import com.github.unchama.seichiassist.data.syntax._ + val BlockSearching.Result(breakBlocks, waterBlocks, lavaBlocks) = + BlockSearching + .searchForBlocksBreakableWithSkill(player, breakArea.gridPoints(), block) + .unsafeRunSync() + .filterSolids(targetBlock => + isMultiTypeBreakingSkillEnabled || BlockSearching + .multiTypeBreakingFilterPredicate(block)(targetBlock) + ) + .filterAll(targetBlock => + player.isSneaking || targetBlock + .getLocation + .getBlockY > playerLocY || targetBlock == block + ) + + // 破壊範囲で消費されるマナ計算 + val manaToConsumeOnBreakArea = ManaAmount { + (gravity + 1) * selectedSkill.manaCost * (breakBlocks.size + 1).toDouble / totalBreakRangeVolume + } + // 消費マナが不足している場合は処理を終了 + manaApi.manaAmount(player).canAcquire(manaToConsumeOnBreakArea).unsafeRunSync() match { + case false if isBreakBlockManaFullyConsumed(player).unsafeRunSync() => + event.setCancelled(true) + return + case _ => + } + } + } + + // 追加マナ獲得 + manaApi + .manaAmount(player) + .restoreAbsolute(ManaAmount(BreakUtil.calcManaDrop(player))) + .unsafeRunSync() + // 破壊不可能ブロックの時処理を終了 if (!BreakUtil.canBreakWithSkill(player, block)) { event.setCancelled(true) @@ -123,21 +171,22 @@ class PlayerBlockBreakListener( event.setCancelled(true) + // ブロック破壊時に行う処理 { - // プレイヤーの足のy座標を取得 + // プレイヤーのY座標 val playerLocY = player.getLocation.getBlockY - 1 - + // スキル破壊範囲 val skillArea = BreakArea(selectedSkill, skillState.usageMode) + // 破壊エリアリスト val breakAreaList = skillArea.makeBreakArea(player).unsafeRunSync() - + // 複数種類ブロック同時破壊設定 val isMultiTypeBreakingSkillEnabled = BreakUtil.performsMultipleIDBlockBreakWhenUsingSkills(player).unsafeRunSync() - + // 破壊範囲のブロック計算 val totalBreakRangeVolume = { val breakLength = skillArea.breakLength breakLength.x * breakLength.y * breakLength.z * skillArea.breakNum } - // エフェクト用に壊されるブロック全てのリストデータ val multiBreakList = new ArrayBuffer[Set[BlockBreakableBySkill]] // 壊される溶岩の全てのリストデータ @@ -169,7 +218,6 @@ class PlayerBlockBreakListener( .getLocation .getBlockY > playerLocY || targetBlock == block ) - // このチャンクで消費されるマナ val manaToConsumeOnThisChunk = ManaAmount { (gravity + 1) * selectedSkill.manaCost * (breakBlocks.size + 1).toDouble / totalBreakRangeVolume @@ -373,4 +421,22 @@ class PlayerBlockBreakListener( event.setCancelled(true) player.sendMessage(s"${RED}Y-59以下に敷かれたハーフブロックは破壊不可能です。") } + + /** + * ブロック破壊時、「マナ切れブロック破壊停止設定」を取得する。 + * マナ切れブロック破壊設定が `true` になっている場合、プレイヤーに破壊抑制メッセージを送信する。 + * @param player マナ切れブロック破壊停止設定を取得するプレイヤー + */ + def isBreakBlockManaFullyConsumed(player: Player): IO[Boolean] = { + for { + breakSuppressionPreference <- SeichiAssist + .instance + .breakSuppressionPreferenceSystem + .api + .isBreakSuppressionEnabled(player) + _ <- ActionBarMessageEffect(s"${RED}マナ切れでブロック破壊を止めるスキルは有効化されています") + .run(player) + .whenA(breakSuppressionPreference) + } yield breakSuppressionPreference + } } diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/TopLevelRouter.scala b/src/main/scala/com/github/unchama/seichiassist/menus/TopLevelRouter.scala index c7b44baa6c..d3180f98b2 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/TopLevelRouter.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/TopLevelRouter.scala @@ -31,6 +31,7 @@ import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountAPI import com.github.unchama.seichiassist.subsystems.breakcount.domain.SeichiAmountData import com.github.unchama.seichiassist.subsystems.breakcountbar.BreakCountBarAPI import com.github.unchama.seichiassist.subsystems.breakskilltargetconfig.BreakSkillTargetConfigAPI +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.BreakSuppressionPreferenceAPI import com.github.unchama.seichiassist.subsystems.buildcount.domain.playerdata.BuildAmountData import com.github.unchama.seichiassist.subsystems.discordnotification.DiscordNotificationAPI import com.github.unchama.seichiassist.subsystems.donate.DonatePremiumPointAPI @@ -104,6 +105,7 @@ object TopLevelRouter { fairySpeechAPI: FairySpeechAPI[IO, Player], gridRegionAPI: GridRegionAPI[IO, Player, Location], breakSkillTargetConfigAPI: BreakSkillTargetConfigAPI[IO, Player], + breakSuppressionPreferenceAPI: BreakSuppressionPreferenceAPI[IO, Player], playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ): TopLevelRouter[IO] = new TopLevelRouter[IO] { import assortedRankingApi._ diff --git a/src/main/scala/com/github/unchama/seichiassist/menus/skill/PassiveSkillMenu.scala b/src/main/scala/com/github/unchama/seichiassist/menus/skill/PassiveSkillMenu.scala index 9def38cd0f..94aedca3d5 100644 --- a/src/main/scala/com/github/unchama/seichiassist/menus/skill/PassiveSkillMenu.scala +++ b/src/main/scala/com/github/unchama/seichiassist/menus/skill/PassiveSkillMenu.scala @@ -13,6 +13,8 @@ import com.github.unchama.seichiassist.menus.stickmenu.FirstPage import com.github.unchama.seichiassist.subsystems.breakcount.BreakCountAPI import com.github.unchama.seichiassist.subsystems.breakskilltargetconfig.BreakSkillTargetConfigAPI import com.github.unchama.seichiassist.subsystems.breakskilltargetconfig.domain.BreakSkillTargetConfigKey +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.BreakSuppressionPreferenceAPI +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.domain.BreakSuppressionPreference import com.github.unchama.seichiassist.subsystems.playerheadskin.PlayerHeadSkinAPI import com.github.unchama.targetedeffect._ import com.github.unchama.targetedeffect.commandsender.MessageEffect @@ -34,6 +36,7 @@ object PassiveSkillMenu extends Menu { class Environment( implicit val breakCountApi: BreakCountAPI[IO, SyncIO, Player], implicit val breakSkillTargetConfigAPI: BreakSkillTargetConfigAPI[IO, Player], + implicit val breakSuppressionPreferenceAPI: BreakSuppressionPreferenceAPI[IO, Player], val ioCanOpenFirstPage: IO CanOpen FirstPage.type, implicit val playerHeadSkinAPI: PlayerHeadSkinAPI[IO, Player] ) @@ -62,6 +65,7 @@ object PassiveSkillMenu extends Menu { val dynamicPartComputation = List( ChestSlotRef(0, 0) -> computeToggleMultipleBlockTypeDestructionButton, ChestSlotRef(0, 1) -> computeToggleChestBreakButton, + ChestSlotRef(0, 2) -> computeToggleManaFullyConsumedBreakStopButton, ChestSlotRef(1, 0) -> computeGiganticBerserkButton, ChestSlotRef(1, 1) -> computeToggleNetherQuartzBlockButton ).traverse(_.sequence) @@ -231,6 +235,47 @@ object PassiveSkillMenu extends Menu { ) }) + val computeToggleManaFullyConsumedBreakStopButton: IO[Button] = RecomputedButton(for { + originalBreakStopConfig <- breakSuppressionPreferenceAPI.isBreakSuppressionEnabled(player) + + } yield { + val baseLore = List(s"${YELLOW}マナ切れでブロック破壊を止めるスキル") + val statusLore = if (originalBreakStopConfig) { + List(s"${GREEN}ON (マナが切れるとブロック破壊を止めます。)", s"${DARK_RED}クリックでOFF") + } else { + List(s"${RED}OFF (マナが切れてもブロック破壊を続けます。)", s"${DARK_GREEN}クリックでON") + } + + Button( + new IconItemStackBuilder(Material.LAPIS_LAZULI) + .tap { builder => + if (originalBreakStopConfig) + builder.enchanted() + } + .title(s"$WHITE$UNDERLINE${BOLD}マナ切れでブロック破壊を止めるスキル切り替え") + .lore(baseLore ++ statusLore) + .build(), + LeftClickButtonEffect { + SequentialEffect( + breakSuppressionPreferenceAPI.toggleBreakSuppression, + DeferredEffect(IO { + if (!originalBreakStopConfig) { + SequentialEffect( + MessageEffect(s"${GREEN}マナが切れたらブロック破壊を止めるスキルを有効化しました。"), + FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 1f) + ) + } else { + SequentialEffect( + MessageEffect(s"${RED}マナが切れたらブロック破壊を止めるスキルを無効化しました。"), + FocusedSoundEffect(Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1f, 0.5f) + ) + } + }) + ) + } + ) + }) + val computeGiganticBerserkButton: IO[Button] = RecomputedButton { environment .breakCountApi diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/BreakSuppressionPreferenceAPI.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/BreakSuppressionPreferenceAPI.scala new file mode 100644 index 0000000000..9e18059cf1 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/BreakSuppressionPreferenceAPI.scala @@ -0,0 +1,25 @@ +package com.github.unchama.seichiassist.subsystems.breaksuppressionpreference + +import cats.data.Kleisli + +trait BreakSuppressionPreferenceAPI[F[_], Player] { + + /** + * @return 破壊抑制の設定をトグルする作用 + */ + def toggleBreakSuppression: Kleisli[F, Player, Unit] + + /** + * @return 現在の破壊抑制の設定を取得する作用 + */ + def isBreakSuppressionEnabled(player: Player): F[Boolean] + +} + +object BreakSkillTriggerConfigAPI { + + def apply[F[_], Player]( + implicit ev: BreakSuppressionPreferenceAPI[F, Player] + ): BreakSuppressionPreferenceAPI[F, Player] = ev + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/System.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/System.scala new file mode 100644 index 0000000000..f7b3016909 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/System.scala @@ -0,0 +1,62 @@ +package com.github.unchama.seichiassist.subsystems.breaksuppressionpreference + +import cats.data.Kleisli +import cats.effect.SyncEffect +import com.github.unchama.datarepository.bukkit.player.BukkitRepositoryControls +import com.github.unchama.generic.ContextCoercion +import com.github.unchama.seichiassist.meta.subsystem.Subsystem +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.application.repository.BreakSuppressionPreferenceRepositoryDefinition +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.domain.{ + BreakSuppressionPreference, + BreakSuppressionPreferencePersistence +} +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.persistence.JdbcBreakSuppressionPreferencePersistence +import org.bukkit.entity.Player + +trait System[F[_], Player] extends Subsystem[F] { + val api: BreakSuppressionPreferenceAPI[F, Player] +} + +object System { + + import cats.implicits._ + + def wired[F[_], G[_]: SyncEffect: ContextCoercion[*[_], F]]: G[System[F, Player]] = { + implicit val breakSuppressionPreferencePersistence + : BreakSuppressionPreferencePersistence[G] = + new JdbcBreakSuppressionPreferencePersistence[G] + + for { + breakSuppressionPreferenceRepositoryControls <- BukkitRepositoryControls.createHandles( + BreakSuppressionPreferenceRepositoryDefinition.withContext[G, Player] + ) + } yield { + val breakSuppressionPreferenceRepository = + breakSuppressionPreferenceRepositoryControls.repository + + new System[F, Player] { + override val api: BreakSuppressionPreferenceAPI[F, Player] = + new BreakSuppressionPreferenceAPI[F, Player] { + override def toggleBreakSuppression: Kleisli[F, Player, Unit] = + Kleisli { player => + ContextCoercion( + breakSuppressionPreferenceRepository(player).update(pref => + pref.copy(doBreakSuppression = !pref.doBreakSuppression) + ) + ) + } + + override def isBreakSuppressionEnabled(player: Player): F[Boolean] = + ContextCoercion( + breakSuppressionPreferenceRepository(player).get.map(_.doBreakSuppression) + ) + } + + override val managedRepositoryControls: Seq[BukkitRepositoryControls[F, _]] = Seq( + breakSuppressionPreferenceRepositoryControls.coerceFinalizationContextTo[F] + ) + } + } + } + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/application/BreakSuppressionPreferenceRepositoryDefinition.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/application/BreakSuppressionPreferenceRepositoryDefinition.scala new file mode 100644 index 0000000000..5a5703c82a --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/application/BreakSuppressionPreferenceRepositoryDefinition.scala @@ -0,0 +1,23 @@ +package com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.application.repository + +import cats.effect.Sync +import cats.effect.concurrent.Ref +import com.github.unchama.datarepository.definitions.RefDictBackedRepositoryDefinition +import com.github.unchama.datarepository.template.RepositoryDefinition +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.domain.{ + BreakSuppressionPreference, + BreakSuppressionPreferencePersistence +} + +object BreakSuppressionPreferenceRepositoryDefinition { + + def withContext[F[_]: Sync, Player]( + implicit persistence: BreakSuppressionPreferencePersistence[F] + ): RepositoryDefinition[F, Player, Ref[F, BreakSuppressionPreference]] = + RefDictBackedRepositoryDefinition + .usingUuidRefDict[F, Player, BreakSuppressionPreference](persistence)( + BreakSuppressionPreference(doBreakSuppression = false) + ) + .toRefRepository + +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/domain/BreakSuppressionPreference.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/domain/BreakSuppressionPreference.scala new file mode 100644 index 0000000000..d0725622f4 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/domain/BreakSuppressionPreference.scala @@ -0,0 +1,24 @@ +package com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.domain + +case class BreakSuppressionPreference(doBreakSuppression: Boolean) { + + /** + * @return 破壊抑制の設定をトグルする + */ + def toggleBreakSuppression(): BreakSuppressionPreference = + this.copy(doBreakSuppression = !this.doBreakSuppression) + + /** + * @return 現在の破壊抑制の設定を取得する + */ + def isBreakSuppressionEnabled: Boolean = doBreakSuppression +} + +object BreakSuppressionPreference { + + /** + * [[BreakSuppressionPreference]]の初期値 + */ + val initial: BreakSuppressionPreference = + BreakSuppressionPreference(doBreakSuppression = false) +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/domain/BreakSuppressionPreferencePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/domain/BreakSuppressionPreferencePersistence.scala new file mode 100644 index 0000000000..1b2810b0b2 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/domain/BreakSuppressionPreferencePersistence.scala @@ -0,0 +1,8 @@ +package com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.domain + +import com.github.unchama.generic.RefDict + +import java.util.UUID + +trait BreakSuppressionPreferencePersistence[F[_]] + extends RefDict[F, UUID, BreakSuppressionPreference] diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/infrastructure/JdbcBreakSuppressionPreferencePersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/infrastructure/JdbcBreakSuppressionPreferencePersistence.scala new file mode 100644 index 0000000000..e0d2bc9a0f --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/breaksuppressionpreference/infrastructure/JdbcBreakSuppressionPreferencePersistence.scala @@ -0,0 +1,35 @@ +package com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.persistence + +import cats.effect.Sync +import com.github.unchama.seichiassist.subsystems.breaksuppressionpreference.domain.{ + BreakSuppressionPreference, + BreakSuppressionPreferencePersistence +} +import scalikejdbc.{DB, scalikejdbcSQLInterpolationImplicitDef} + +import java.util.UUID + +class JdbcBreakSuppressionPreferencePersistence[F[_]: Sync] + extends BreakSuppressionPreferencePersistence[F] { + + override def read(key: UUID): F[Option[BreakSuppressionPreference]] = Sync[F].delay { + val doBreakSuppression = DB.readOnly { implicit session => + sql"SELECT do_break_suppression_due_to_mana FROM player_break_suppression_preference WHERE uuid = ${key.toString}" + .map(rs => rs.boolean("do_break_suppression_due_to_mana")) + .single() + } + + doBreakSuppression.map(BreakSuppressionPreference) + } + + override def write(key: UUID, value: BreakSuppressionPreference): F[Unit] = Sync[F].delay { + DB.localTx { implicit session => + val uuid = key.toString + sql"""INSERT INTO player_break_suppression_preference (uuid, do_break_suppression_due_to_mana) + | VALUES ($uuid, ${value.doBreakSuppression}) + | ON DUPLICATE KEY UPDATE + | do_break_suppression_due_to_mana = VALUES(do_break_suppression_due_to_mana) + """.stripMargin.update() + } + } +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/LevelCappedManaAmount.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/LevelCappedManaAmount.scala index 7c75a89e4e..9e56b4d153 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/LevelCappedManaAmount.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/LevelCappedManaAmount.scala @@ -21,7 +21,6 @@ case class LevelCappedManaAmount private (manaAmount: ManaAmount, level: SeichiL )(manaMultiplier: ManaMultiplier): Option[LevelCappedManaAmount] = { manaAmount.tryUse(amount)(manaMultiplier).map(LevelCappedManaAmount(_, level)) } - def withHigherLevelOption(newLevel: SeichiLevel): Option[LevelCappedManaAmount] = Option.when(newLevel > level)(LevelCappedManaAmount(manaAmount, newLevel).fillToCap) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaManipulation.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaManipulation.scala index ded6a9a728..67e818eb29 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaManipulation.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/mana/domain/ManaManipulation.scala @@ -28,6 +28,11 @@ trait ManaManipulation[F[_]] { */ def tryAcquire(amount: ManaAmount): F[Option[ManaAmount]] + /** + * `amount` だけマナを消費することが可能ならば `true`、そうでないなら `false` を返す作用 + */ + def canAcquire(amount: ManaAmount): F[Boolean] + } object ManaManipulation { @@ -54,6 +59,12 @@ object ManaManipulation { } } } + override def canAcquire(amount: ManaAmount): F[Boolean] = { + for { + multiplier <- dragonNightTimeMultiplierRef.get + original <- ref.get + } yield original.tryUse(amount)(multiplier).isDefined + } } }