DEADSOFTWARE

Add chest
[cavedroid.git] / core / src / ru / deadsoftware / cavedroid / game / world / GameWorldFluidsLogicControllerTask.java
1 package ru.deadsoftware.cavedroid.game.world;
3 import com.badlogic.gdx.utils.Timer;
4 import ru.deadsoftware.cavedroid.game.GameItemsHolder;
5 import ru.deadsoftware.cavedroid.game.GameScope;
6 import ru.deadsoftware.cavedroid.game.mobs.MobsController;
7 import ru.deadsoftware.cavedroid.game.model.block.Block;
9 import javax.annotation.CheckForNull;
10 import javax.inject.Inject;
11 import java.util.*;
13 @GameScope
14 public class GameWorldFluidsLogicControllerTask extends Timer.Task {
16 public static final float FLUID_UPDATE_INTERVAL_SEC = 0.25f;
18 private short mUpdateTick = 0;
20 private final GameWorld mGameWorld;
21 private final MobsController mMobsController;
22 private final GameItemsHolder mGameItemsHolder;
24 private final Map<Class<? extends Block.Fluid>, List<? extends Block.Fluid>> mFluidStatesMap;
26 private final class UpdateCommand {
27 final Runnable command;
28 final int priority;
30 private UpdateCommand(int priority, Runnable command) {
31 this.priority = priority;
32 this.command = command;
33 }
35 private UpdateCommand(Block block, int x, int y, int priority) {
36 this(priority, () -> mGameWorld.setForeMap(x, y, block));
37 }
39 private UpdateCommand(Block.Fluid fluid, int x, int y) {
40 this(fluid, x, y, ((5 -fluid.getState() )+ 1) * (fluid.isLava() ? 2 : 1));
41 }
43 private int getPriority() {
44 return priority;
45 }
47 private void exec() {
48 command.run();
49 }
50 }
52 private final PriorityQueue<UpdateCommand> mUpdateQueue
53 = new PriorityQueue<>(Comparator.comparingInt(UpdateCommand::getPriority));
55 @Inject
56 GameWorldFluidsLogicControllerTask(GameWorld gameWorld,
57 MobsController mobsController,
58 GameItemsHolder gameItemsHolder) {
59 mGameWorld = gameWorld;
60 mMobsController = mobsController;
61 mGameItemsHolder = gameItemsHolder;
63 final List<Block.Water> waters = mGameItemsHolder.getBlocksByType(Block.Water.class);
64 waters.sort(Comparator.comparingInt(Block.Water::getState));
66 final List<Block.Lava> lavas = mGameItemsHolder.getBlocksByType(Block.Lava.class);
67 lavas.sort(Comparator.comparingInt(Block.Lava::getState));
69 mFluidStatesMap = new HashMap<>();
70 mFluidStatesMap.put(Block.Water.class, waters);
71 mFluidStatesMap.put(Block.Lava.class, lavas);
72 }
74 @CheckForNull
75 private List<? extends Block.Fluid> getFluidStateList(Block.Fluid fluid) {
76 return mFluidStatesMap.get(fluid.getClass());
77 }
79 private int getCurrentStateIndex(Block.Fluid fluid) {
80 @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
82 if (stateList == null) {
83 return -1;
84 }
86 return stateList.indexOf(fluid);
87 }
89 @CheckForNull
90 private Block.Fluid getNextStateBlock(Block.Fluid fluid) {
91 @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
93 if (stateList == null) {
94 return null;
95 }
97 int currentState = stateList.indexOf(fluid);
99 if (currentState < 0) {
100 return null;
103 int nextState = currentState + 1;
105 if (nextState == 1) {
106 nextState++;
109 if (nextState < stateList.size()) {
110 return stateList.get(nextState);
113 return null;
116 private boolean noFluidNearby(int x, int y) {
117 return !mGameWorld.getForeMap(x, y - 1).isFluid() &&
118 (!mGameWorld.getForeMap(x - 1, y).isFluid() || ((Block.Fluid)mGameWorld.getForeMap(x - 1, y)).getState() >= ((Block.Fluid)mGameWorld.getForeMap(x, y)).getState()) &&
119 (!mGameWorld.getForeMap(x + 1, y).isFluid() || ((Block.Fluid)mGameWorld.getForeMap(x + 1, y)).getState() >= ((Block.Fluid)mGameWorld.getForeMap(x, y)).getState());
122 private boolean drainFluid(int x, int y) {
123 final Block block = mGameWorld.getForeMap(x, y);
125 if (!(block instanceof Block.Fluid fluid)) {
126 return true;
129 if (fluid.getState() > 0) {
130 if (noFluidNearby(x, y)) {
131 @CheckForNull final Block.Fluid nextState = getNextStateBlock(fluid);
132 if (nextState == null) {
133 mUpdateQueue.offer(new UpdateCommand(-1, () -> mGameWorld.resetForeMap(x, y)));
134 return true;
137 mUpdateQueue.offer(new UpdateCommand(nextState, x, y));
140 return false;
143 private boolean fluidCanFlowThere(Block.Fluid fluid, Block targetBlock) {
144 return targetBlock == mGameItemsHolder.getFallbackBlock() ||
145 (!targetBlock.getParams().getHasCollision() && !targetBlock.isFluid()) ||
146 (fluid.getClass() == targetBlock.getClass() && fluid.getState() < ((Block.Fluid)targetBlock).getState());
149 private void flowFluidTo(Block.Fluid currentFluid, int x, int y, Block.Fluid nextStateFluid) {
150 final Block targetBlock = mGameWorld.getForeMap(x, y);
152 if (fluidCanFlowThere(currentFluid, targetBlock)) {
153 mUpdateQueue.offer(new UpdateCommand(nextStateFluid, x, y));
154 } else if (currentFluid.isWater() && targetBlock.isLava()) {
155 if (((Block.Lava)targetBlock).getState() > 0) {
156 mUpdateQueue.offer(new UpdateCommand(100, () -> mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("cobblestone"))));
157 } else {
158 mUpdateQueue.offer(new UpdateCommand(300, () -> mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("obsidian"))));
160 } else if (currentFluid.isLava() && targetBlock.isWater()) {
161 mUpdateQueue.offer(new UpdateCommand(200, () -> mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("stone"))));
165 private void flowFluid(int x, int y) {
166 Block.Fluid fluid = (Block.Fluid) mGameWorld.getForeMap(x, y);
167 @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
169 if (stateList == null) {
170 return;
173 if (fluid.getState() < stateList.size() - 1 && mGameWorld.getForeMap(x, y + 1).hasCollision()) {
174 @CheckForNull Block.Fluid nextState = getNextStateBlock(fluid);
176 if (nextState == null) {
177 return;
180 flowFluidTo(fluid, x - 1, y, nextState);
181 flowFluidTo(fluid, x + 1, y, nextState);
182 } else {
183 flowFluidTo(fluid, x, y + 1, stateList.get(1));
188 private void updateFluids(int x, int y) {
189 final Block block = mGameWorld.getForeMap(x, y);
190 if (!block.isFluid() || (block.isLava() && mUpdateTick % 2 == 0)) {
191 return;
193 if (drainFluid(x, y)) {
194 return;
196 flowFluid(x, y);
199 private void fluidUpdater() {
200 int midScreen = (int) mMobsController.getPlayer().x / 16;
201 for (int y = mGameWorld.getHeight() - 1; y >= 0; y--) {
202 for (int x = 0; x <= Math.min(mGameWorld.getWidth() / 2, 32); x++) {
203 updateFluids(midScreen + x, y);
204 updateFluids(midScreen - x, y);
208 while (!mUpdateQueue.isEmpty()) {
209 final UpdateCommand command = mUpdateQueue.poll();
210 command.exec();
214 @Override
215 public void run() {
216 if (mUpdateTick < 0xFF) {
217 mUpdateTick ++;
218 } else {
219 mUpdateTick = 0;
221 fluidUpdater();