summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: e98430f)
raw | patch | inline | side by side (parent: e98430f)
author | fredboy <fredboy@protonmail.com> | |
Wed, 15 May 2024 16:57:25 +0000 (23:57 +0700) | ||
committer | fredboy <fredboy@protonmail.com> | |
Wed, 15 May 2024 16:57:25 +0000 (23:57 +0700) |
core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.java | [deleted file] | patch | blob | history |
core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.kt | [new file with mode: 0644] | patch | blob |
diff --git a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.java b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.java
+++ /dev/null
@@ -1,223 +0,0 @@
-package ru.deadsoftware.cavedroid.game.world;
-
-import com.badlogic.gdx.utils.Timer;
-import ru.deadsoftware.cavedroid.game.GameItemsHolder;
-import ru.deadsoftware.cavedroid.game.GameScope;
-import ru.deadsoftware.cavedroid.game.mobs.MobsController;
-import ru.deadsoftware.cavedroid.game.model.block.Block;
-
-import javax.annotation.CheckForNull;
-import javax.inject.Inject;
-import java.util.*;
-
-@GameScope
-public class GameWorldFluidsLogicControllerTask extends Timer.Task {
-
- public static final float FLUID_UPDATE_INTERVAL_SEC = 0.25f;
-
- private short mUpdateTick = 0;
-
- private final GameWorld mGameWorld;
- private final MobsController mMobsController;
- private final GameItemsHolder mGameItemsHolder;
-
- private final Map<Class<? extends Block.Fluid>, List<? extends Block.Fluid>> mFluidStatesMap;
-
- private final class UpdateCommand {
- final Runnable command;
- final int priority;
-
- private UpdateCommand(int priority, Runnable command) {
- this.priority = priority;
- this.command = command;
- }
-
- private UpdateCommand(Block block, int x, int y, int priority) {
- this(priority, () -> mGameWorld.setForeMap(x, y, block));
- }
-
- private UpdateCommand(Block.Fluid fluid, int x, int y) {
- this(fluid, x, y, ((5 -fluid.getState() )+ 1) * (fluid.isLava() ? 2 : 1));
- }
-
- private int getPriority() {
- return priority;
- }
-
- private void exec() {
- command.run();
- }
- }
-
- private final PriorityQueue<UpdateCommand> mUpdateQueue
- = new PriorityQueue<>(Comparator.comparingInt(UpdateCommand::getPriority));
-
- @Inject
- GameWorldFluidsLogicControllerTask(GameWorld gameWorld,
- MobsController mobsController,
- GameItemsHolder gameItemsHolder) {
- mGameWorld = gameWorld;
- mMobsController = mobsController;
- mGameItemsHolder = gameItemsHolder;
-
- final List<Block.Water> waters = mGameItemsHolder.getBlocksByType(Block.Water.class);
- waters.sort(Comparator.comparingInt(Block.Water::getState));
-
- final List<Block.Lava> lavas = mGameItemsHolder.getBlocksByType(Block.Lava.class);
- lavas.sort(Comparator.comparingInt(Block.Lava::getState));
-
- mFluidStatesMap = new HashMap<>();
- mFluidStatesMap.put(Block.Water.class, waters);
- mFluidStatesMap.put(Block.Lava.class, lavas);
- }
-
- @CheckForNull
- private List<? extends Block.Fluid> getFluidStateList(Block.Fluid fluid) {
- return mFluidStatesMap.get(fluid.getClass());
- }
-
- private int getCurrentStateIndex(Block.Fluid fluid) {
- @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
-
- if (stateList == null) {
- return -1;
- }
-
- return stateList.indexOf(fluid);
- }
-
- @CheckForNull
- private Block.Fluid getNextStateBlock(Block.Fluid fluid) {
- @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
-
- if (stateList == null) {
- return null;
- }
-
- int currentState = stateList.indexOf(fluid);
-
- if (currentState < 0) {
- return null;
- }
-
- int nextState = currentState + 1;
-
- if (nextState == 1) {
- nextState++;
- }
-
- if (nextState < stateList.size()) {
- return stateList.get(nextState);
- }
-
- return null;
- }
-
- private boolean noFluidNearby(int x, int y) {
- return !mGameWorld.getForeMap(x, y - 1).isFluid() &&
- (!mGameWorld.getForeMap(x - 1, y).isFluid() || ((Block.Fluid)mGameWorld.getForeMap(x - 1, y)).getState() >= ((Block.Fluid)mGameWorld.getForeMap(x, y)).getState()) &&
- (!mGameWorld.getForeMap(x + 1, y).isFluid() || ((Block.Fluid)mGameWorld.getForeMap(x + 1, y)).getState() >= ((Block.Fluid)mGameWorld.getForeMap(x, y)).getState());
- }
-
- private boolean drainFluid(int x, int y) {
- final Block block = mGameWorld.getForeMap(x, y);
-
- if (!(block instanceof Block.Fluid fluid)) {
- return true;
- }
-
- if (fluid.getState() > 0) {
- if (noFluidNearby(x, y)) {
- @CheckForNull final Block.Fluid nextState = getNextStateBlock(fluid);
- if (nextState == null) {
- mUpdateQueue.offer(new UpdateCommand(-1, () -> mGameWorld.resetForeMap(x, y)));
- return true;
- }
-
- mUpdateQueue.offer(new UpdateCommand(nextState, x, y));
- }
- }
- return false;
- }
-
- private boolean fluidCanFlowThere(Block.Fluid fluid, Block targetBlock) {
- return targetBlock == mGameItemsHolder.getFallbackBlock() ||
- (!targetBlock.getParams().getHasCollision() && !targetBlock.isFluid()) ||
- (fluid.getClass() == targetBlock.getClass() && fluid.getState() < ((Block.Fluid)targetBlock).getState());
- }
-
- private void flowFluidTo(Block.Fluid currentFluid, int x, int y, Block.Fluid nextStateFluid) {
- final Block targetBlock = mGameWorld.getForeMap(x, y);
-
- if (fluidCanFlowThere(currentFluid, targetBlock)) {
- mUpdateQueue.offer(new UpdateCommand(nextStateFluid, x, y));
- } else if (currentFluid.isWater() && targetBlock.isLava()) {
- if (((Block.Lava)targetBlock).getState() > 0) {
- mUpdateQueue.offer(new UpdateCommand(100, () -> mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("cobblestone"))));
- } else {
- mUpdateQueue.offer(new UpdateCommand(300, () -> mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("obsidian"))));
- }
- } else if (currentFluid.isLava() && targetBlock.isWater()) {
- mUpdateQueue.offer(new UpdateCommand(200, () -> mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("stone"))));
- }
- }
-
- private void flowFluid(int x, int y) {
- Block.Fluid fluid = (Block.Fluid) mGameWorld.getForeMap(x, y);
- @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
-
- if (stateList == null) {
- return;
- }
-
- if (fluid.getState() < stateList.size() - 1 && mGameWorld.getForeMap(x, y + 1).hasCollision()) {
- @CheckForNull Block.Fluid nextState = getNextStateBlock(fluid);
-
- if (nextState == null) {
- return;
- }
-
- flowFluidTo(fluid, x - 1, y, nextState);
- flowFluidTo(fluid, x + 1, y, nextState);
- } else {
- flowFluidTo(fluid, x, y + 1, stateList.get(1));
- }
-
- }
-
- private void updateFluids(int x, int y) {
- final Block block = mGameWorld.getForeMap(x, y);
- if (!block.isFluid() || (block.isLava() && mUpdateTick % 2 == 0)) {
- return;
- }
- if (drainFluid(x, y)) {
- return;
- }
- flowFluid(x, y);
- }
-
- private void fluidUpdater() {
- int midScreen = (int) mMobsController.getPlayer().x / 16;
- for (int y = mGameWorld.getHeight() - 1; y >= 0; y--) {
- for (int x = 0; x <= Math.min(mGameWorld.getWidth() / 2, 32); x++) {
- updateFluids(midScreen + x, y);
- updateFluids(midScreen - x, y);
- }
- }
-
- while (!mUpdateQueue.isEmpty()) {
- final UpdateCommand command = mUpdateQueue.poll();
- command.exec();
- }
- }
-
- @Override
- public void run() {
- if (mUpdateTick < 0xFF) {
- mUpdateTick ++;
- } else {
- mUpdateTick = 0;
- }
- fluidUpdater();
- }
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.kt b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.kt
--- /dev/null
@@ -0,0 +1,189 @@
+package ru.deadsoftware.cavedroid.game.world
+
+import com.badlogic.gdx.utils.Timer
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.misc.utils.bl
+import java.util.PriorityQueue
+import javax.inject.Inject
+import kotlin.math.min
+import kotlin.reflect.KClass
+
+@GameScope
+class GameWorldFluidsLogicControllerTask @Inject constructor(
+ private val gameWorld: GameWorld,
+ private val mobsController: MobsController,
+ private val gameItemsHolder: GameItemsHolder,
+) : Timer.Task() {
+
+ private var updateTick: Short = 0;
+
+ private val fluidStatesMap = mutableMapOf<KClass<out Block.Fluid>, List<Block.Fluid>>()
+
+ private val updateQueue = PriorityQueue<UpdateCommand>(16) { c1, c2 ->
+ c1.priority.compareTo(c2.priority)
+ }
+
+ init {
+ val waters = gameItemsHolder.getBlocksByType(Block.Water::class.java)
+ .sortedBy(Block.Water::state)
+ val lavas = gameItemsHolder.getBlocksByType(Block.Lava::class.java)
+ .sortedBy(Block.Lava::state)
+
+ fluidStatesMap[Block.Water::class] = waters
+ fluidStatesMap[Block.Lava::class] = lavas
+ }
+
+ private fun getNextStateBlock(fluid: Block.Fluid): Block.Fluid? {
+ val stateList = fluidStatesMap[fluid::class] ?: return null
+ val currentState = stateList.indexOf(fluid)
+ .takeIf { it >= 0 } ?: return null
+
+ var nextState = currentState + 1
+
+ if (nextState == 1) {
+ nextState++
+ }
+
+ if (nextState < stateList.size) {
+ return stateList[nextState]
+ }
+
+ return null
+ }
+
+ private fun noFluidNearby(x: Int, y: Int): Boolean {
+ val current = gameWorld.getForeMap(x, y)
+
+ if (current !is Block.Fluid) {
+ throw IllegalArgumentException("block at $x;$y is not a fluid")
+ }
+
+ val onTop = gameWorld.getForeMap(x, y - 1)
+ val onLeft = gameWorld.getForeMap(x - 1, y)
+ val onRight = gameWorld.getForeMap(x + 1, y)
+
+ return !onTop.isFluid() &&
+ (onLeft !is Block.Fluid || onLeft.state >= current.state) &&
+ (onRight !is Block.Fluid || onRight.state >= current.state)
+ }
+
+ private fun drainFluid(x: Int, y: Int): Boolean {
+ val fluid = (gameWorld.getForeMap(x, y) as? Block.Fluid)
+ ?: return true
+
+ if (fluid.state > 0) {
+ if (noFluidNearby(x, y)) {
+ val nexState = getNextStateBlock(fluid)
+ if (nexState == null) {
+ updateQueue.offer(UpdateCommand(-1) { gameWorld.resetForeMap(x, y) })
+ return true
+ }
+ updateQueue.offer(UpdateCommand(nexState, x, y))
+ }
+ }
+
+ return false
+ }
+
+ private fun fluidCanFlowThere(fluid: Block.Fluid, targetBlock: Block): Boolean {
+ return targetBlock.isNone() ||
+ (!targetBlock.params.hasCollision && !targetBlock.isFluid()) ||
+ (fluid::class == targetBlock::class && fluid.state < (targetBlock as Block.Fluid).state)
+ }
+
+ private fun flowFluidTo(currentFluid: Block.Fluid, x: Int, y: Int, nextStateFluid: Block.Fluid) {
+ val targetBlock = gameWorld.getForeMap(x, y)
+
+ val command = when {
+ fluidCanFlowThere(currentFluid, targetBlock) -> UpdateCommand(nextStateFluid, x, y)
+
+ currentFluid.isWater() && targetBlock is Block.Lava && targetBlock.state > 0 ->
+ UpdateCommand(100) { gameWorld.setForeMap(x, y, gameItemsHolder.getBlock("cobblestone")) }
+
+ currentFluid.isWater() && targetBlock.isLava() ->
+ UpdateCommand(100) { gameWorld.setForeMap(x, y, gameItemsHolder.getBlock("obsidian")) }
+
+ currentFluid.isLava() && targetBlock.isWater() ->
+ UpdateCommand(200) { gameWorld.setForeMap(x, y, gameItemsHolder.getBlock("stone")) }
+
+ else -> null
+ }
+
+ command?.let(updateQueue::offer)
+ }
+
+ private fun flowFluid(x: Int, y: Int) {
+ val fluid = gameWorld.getForeMap(x, y) as Block.Fluid
+ val stateList = fluidStatesMap[fluid::class] ?: return
+
+ if (fluid.state < stateList.lastIndex && gameWorld.getForeMap(x, y + 1).params.hasCollision) {
+ val nextState = getNextStateBlock(fluid) ?: return
+
+ flowFluidTo(fluid, x - 1, y, nextState)
+ flowFluidTo(fluid, x + 1, y, nextState)
+ } else {
+ flowFluidTo(fluid, x, y + 1, stateList[1])
+ }
+ }
+
+ fun updateFluids(x: Int, y: Int) {
+ val block = gameWorld.getForeMap(x, y)
+ if (!block.isFluid() || (block.isLava() && updateTick % 2 == 0)) {
+ return
+ }
+
+ if (drainFluid(x, y)) {
+ return
+ }
+
+ flowFluid(x, y)
+ }
+
+ private fun fluidUpdater() {
+ val midScreen = mobsController.player.x.bl
+
+ for (y in gameWorld.height - 1 downTo 0) {
+ for (x in 0 ..< min(gameWorld.width / 2, 32)) {
+ updateFluids(midScreen + x, y)
+ updateFluids(midScreen - x, y)
+ }
+ }
+
+ while (!updateQueue.isEmpty()) {
+ updateQueue.poll().exec()
+ }
+ }
+
+ override fun run() {
+ if (updateTick < 0xFF) {
+ updateTick++
+ } else {
+ updateTick = 1
+ }
+
+ fluidUpdater()
+ }
+
+ private inner class UpdateCommand(
+ val priority: Int,
+ val command: Runnable
+ ) {
+
+ constructor(block: Block, x: Int, y: Int, priority: Int) :
+ this(priority, Runnable { gameWorld.setForeMap(x, y, block) })
+
+ constructor(fluid: Block.Fluid, x: Int, y: Int) :
+ this(fluid, x, y, ((5 - fluid.state) + 1) * (if (fluid.isLava()) 2 else 1))
+
+ fun exec() = command.run()
+
+ }
+
+ companion object {
+ const val FLUID_UPDATE_INTERVAL_SEC = 0.25f
+ }
+
+}
\ No newline at end of file