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
;
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
;
30 private UpdateCommand(int priority
, Runnable command
) {
31 this.priority
= priority
;
32 this.command
= command
;
35 private UpdateCommand(Block block
, int x
, int y
, int priority
) {
36 this(priority
, () -> mGameWorld
.setForeMap(x
, y
, block
));
39 private UpdateCommand(Block
.Fluid fluid
, int x
, int y
) {
40 this(fluid
, x
, y
, ((5 -fluid
.getState() )+ 1) * (fluid
.isLava() ?
2 : 1));
43 private int getPriority() {
52 private final PriorityQueue
<UpdateCommand
> mUpdateQueue
53 = new PriorityQueue
<>(Comparator
.comparingInt(UpdateCommand
::getPriority
));
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
);
75 private List
<?
extends Block
.Fluid
> getFluidStateList(Block
.Fluid fluid
) {
76 return mFluidStatesMap
.get(fluid
.getClass());
79 private int getCurrentStateIndex(Block
.Fluid fluid
) {
80 @CheckForNull final List
<?
extends Block
.Fluid
> stateList
= getFluidStateList(fluid
);
82 if (stateList
== null) {
86 return stateList
.indexOf(fluid
);
90 private Block
.Fluid
getNextStateBlock(Block
.Fluid fluid
) {
91 @CheckForNull final List
<?
extends Block
.Fluid
> stateList
= getFluidStateList(fluid
);
93 if (stateList
== null) {
97 int currentState
= stateList
.indexOf(fluid
);
99 if (currentState
< 0) {
103 int nextState
= currentState
+ 1;
105 if (nextState
== 1) {
109 if (nextState
< stateList
.size()) {
110 return stateList
.get(nextState
);
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
)) {
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
)));
137 mUpdateQueue
.offer(new UpdateCommand(nextState
, x
, y
));
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"))));
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) {
173 if (fluid
.getState() < stateList
.size() - 1 && mGameWorld
.getForeMap(x
, y
+ 1).hasCollision()) {
174 @CheckForNull Block
.Fluid nextState
= getNextStateBlock(fluid
);
176 if (nextState
== null) {
180 flowFluidTo(fluid
, x
- 1, y
, nextState
);
181 flowFluidTo(fluid
, x
+ 1, y
, nextState
);
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)) {
193 if (drainFluid(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();
216 if (mUpdateTick
< 0xFF) {