[cavedroid.git] / core / src / ru / deadsoftware / cavedroid / game / world / GameWorldFluidsLogicControllerTask.java
diff --git a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.java b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.java
index d996cf42989c41a1025b54fb1c1fd2b77e053cf0..416721f7c25b6c946b6e9768ef41afad42c12da7 100644 (file)
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.Arrays;
-
-import static ru.deadsoftware.cavedroid.game.GameItems.*;
+import java.util.*;
@GameScope
public class GameWorldFluidsLogicControllerTask extends Timer.Task {
- public static final float FLUID_UPDATE_INTERVAL_SEC = 0.1f;
- private static final int FLUID_STATES = 5;
+ public static final float FLUID_UPDATE_INTERVAL_SEC = 0.25f;
- private static final int[] WATER_IDS = {8, 60, 61, 62, 63};
- private static final int[] LAVA_IDS = {9, 64, 65, 66, 67};
+ 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) {
+ 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);
}
- private int getBlockState(int id) {
- return isWater(id) ? Arrays.binarySearch(WATER_IDS, id) : Arrays.binarySearch(LAVA_IDS, id);
+ @CheckForNull
+ private List<? extends Block.Fluid> getFluidStateList(Block.Fluid fluid) {
+ return mFluidStatesMap.get(fluid.getClass());
}
- private int getNextBlockState(int id) {
- if (!isFluid(id)) {
+ private int getCurrentStateIndex(Block.Fluid fluid) {
+ @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
+
+ if (stateList == null) {
return -1;
}
- int state = getBlockState(id);
- if (state < FLUID_STATES - 1) {
- return state + 1;
- }
- return -1;
+
+ return stateList.indexOf(fluid);
}
- private int getNextBlockStateId(int id) {
- int nextState = getNextBlockState(id);
- if (nextState == -1) {
- return 0;
+ @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;
}
- if (isWater(id)) {
- return WATER_IDS[nextState];
+
+ int nextState = currentState + 1;
+
+ if (nextState == 1) {
+ nextState++;
}
- return LAVA_IDS[nextState];
- }
- private int id(int x, int y) {
- return mGameWorld.getForeMap(x, y);
- }
+ if (nextState < stateList.size()) {
+ return stateList.get(nextState);
+ }
- private boolean sameFluid(int thisId, int thatId) {
- return isFluid(thatId) && isWater(thatId) == isWater(thisId);
+ return null;
}
private boolean noFluidNearby(int x, int y) {
- return !isFluid(id(x, y - 1)) &&
- (!isFluid(id(x - 1, y)) || id(x - 1, y) >= id(x, y)) &&
- (!isFluid(id(x + 1, y)) || id(x + 1, y) >= id(x, 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) {
- if (getBlockState(id(x, y)) > 0) {
+ final Block block = mGameWorld.getForeMap(x, y);
+
+ if (!(block instanceof Block.Fluid fluid)) {
+ return true;
+ }
+
+ if (fluid.getState() > 0) {
if (noFluidNearby(x, y)) {
- mGameWorld.setForeMap(x, y, getNextBlockStateId(id(x, y)));
- }
- if (!isFluid(id(x, y))) {
- mGameWorld.setForeMap(x, y, 0);
- return true;
+ @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 void flowFluidTo(int thisId, int x, int y, int nextStateId) {
- int thatId = id(x, y);
- if (fluidCanFlowThere(thisId, thatId)) {
- mGameWorld.setForeMap(x, y, nextStateId);
- } else if (isWater(thisId) && isLava(thatId)) {
- if (getBlockState(thatId) > 0) {
- mGameWorld.setForeMap(x, y, 4); //cobblestone
+ 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 {
- mGameWorld.setForeMap(x, y, 68); //obsidian
+ mUpdateQueue.offer(new UpdateCommand(300, () -> mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("obsidian"))));
}
- } else if (isLava(thisId) && isWater(thatId)) {
- mGameWorld.setForeMap(x, y, 1); //stone
+ } 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) {
- int id = id(x, y);
- if (getBlockState(id) < FLUID_STATES - 1 && getBlock(id(x, y + 1)).hasCollision()) {
- int nextState = getNextBlockState(id);
- int nextStateId = getNextBlockStateId(id);
- if (nextState == 1) {
- nextStateId++;
+ 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(id, x - 1, y, nextStateId);
- flowFluidTo(id, x + 1, y, nextStateId);
+
+ flowFluidTo(fluid, x - 1, y, nextState);
+ flowFluidTo(fluid, x + 1, y, nextState);
} else {
- flowFluidTo(id, x, y + 1, isWater(id) ? WATER_IDS[1] : LAVA_IDS[1]);
+ flowFluidTo(fluid, x, y + 1, stateList.get(1));
}
}
private void updateFluids(int x, int y) {
- if (!isFluid(id(x, y))) {
+ final Block block = mGameWorld.getForeMap(x, y);
+ if (!block.isFluid() || (block.isLava() && mUpdateTick % 2 == 0)) {
return;
}
if (drainFluid(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 <= mGameWorld.getWidth() / 2; x++) {
+ 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();
}
}