1 package ru
.deadsoftware
.cavedroid
.game
;
3 import com
.badlogic
.gdx
.Gdx
;
4 import com
.badlogic
.gdx
.graphics
.Color
;
5 import com
.badlogic
.gdx
.graphics
.GL20
;
6 import com
.badlogic
.gdx
.graphics
.g2d
.Sprite
;
7 import com
.badlogic
.gdx
.graphics
.g2d
.TextureRegion
;
8 import com
.badlogic
.gdx
.graphics
.glutils
.ShapeRenderer
;
9 import com
.badlogic
.gdx
.math
.Intersector
;
10 import com
.badlogic
.gdx
.math
.MathUtils
;
11 import com
.badlogic
.gdx
.math
.Rectangle
;
12 import kotlin
.collections
.CollectionsKt
;
13 import ru
.deadsoftware
.cavedroid
.MainConfig
;
14 import ru
.deadsoftware
.cavedroid
.game
.mobs
.Mob
;
15 import ru
.deadsoftware
.cavedroid
.game
.mobs
.MobsController
;
16 import ru
.deadsoftware
.cavedroid
.game
.mobs
.Player
;
17 import ru
.deadsoftware
.cavedroid
.game
.model
.block
.Block
;
18 import ru
.deadsoftware
.cavedroid
.game
.model
.item
.Item
;
19 import ru
.deadsoftware
.cavedroid
.game
.objects
.Drop
;
20 import ru
.deadsoftware
.cavedroid
.game
.objects
.DropController
;
21 import ru
.deadsoftware
.cavedroid
.game
.world
.GameWorld
;
22 import ru
.deadsoftware
.cavedroid
.misc
.ControlMode
;
23 import ru
.deadsoftware
.cavedroid
.misc
.Renderer
;
25 import javax
.annotation
.CheckForNull
;
26 import javax
.annotation
.Nullable
;
27 import javax
.inject
.Inject
;
29 import java
.util
.Collection
;
31 import static ru
.deadsoftware
.cavedroid
.misc
.Assets
.*;
34 public class GameRenderer
extends Renderer
{
36 private static final String TAG
= "GameRenderer";
38 private final MainConfig mMainConfig
;
39 private final GameInput mGameInput
;
40 private final GameWorld mGameWorld
;
41 private final MobsController mMobsController
;
42 private final DropController mDropController
;
43 private final GameItemsHolder mGameItemsHolder
;
46 GameRenderer(MainConfig mainConfig
,
49 MobsController mobsController
,
50 DropController dropController
,
51 GameItemsHolder gameItemsHolder
) {
52 super(mainConfig
.getWidth(), mainConfig
.getHeight());
54 mMainConfig
= mainConfig
;
55 mGameInput
= gameInput
;
56 mGameWorld
= gameWorld
;
57 mMobsController
= mobsController
;
58 mDropController
= dropController
;
59 mGameItemsHolder
= gameItemsHolder
;
61 Gdx
.gl
.glClearColor(0f, .6f, .6f, 1f);
64 private float drawX(int x
) {
65 return x
* 16 - getCamX();
68 private float drawY(int y
) {
69 return y
* 16 - getCamY();
72 private void drawWreck(Block bl
) {
73 if (mGameInput
.getBlockDamage() > 0) {
74 int index
= 10 * mGameInput
.getBlockDamage() / bl
.getHp();
75 String key
= "break_" + index
;
77 if (index
> 10 || index
< 0) {
81 spriter
.draw(textureRegions
.get(key
), mGameInput
.getCurX() * 16 - getCamX(),
82 mGameInput
.getCurY() * 16 - getCamY());
86 private void drawBlock(int x
, int y
, boolean drawBG
) {
88 if ((!mGameWorld
.hasForeAt(x
, y
) || mGameWorld
.getForeMap(x
, y
).isTransparent())
89 && mGameWorld
.hasBackAt(x
, y
)) {
90 mGameWorld
.getBackMap(x
, y
).draw(spriter
, drawX(x
), drawY(y
));
91 if (!mGameWorld
.hasForeAt(x
, y
) && x
== mGameInput
.getCurX() && y
== mGameInput
.getCurY()) {
92 drawWreck(mGameWorld
.getBackMap(mGameInput
.getCurX(), mGameInput
.getCurY()));
96 if (mGameWorld
.hasForeAt(x
, y
) && mGameWorld
.getForeMap(x
, y
).isBackground() == drawBG
) {
97 mGameWorld
.getForeMap(x
, y
).draw(spriter
, drawX(x
), drawY(y
));
98 if (x
== mGameInput
.getCurX() && y
== mGameInput
.getCurY()) {
99 drawWreck(mGameWorld
.getForeMap(mGameInput
.getCurX(), mGameInput
.getCurY()));
104 private void drawWorld(boolean bg
) {
105 int minX
= (int) (getCamX() / 16) - 1;
106 int minY
= (int) (getCamY() / 16) - 1;
107 int maxX
= (int) ((getCamX() + getWidth()) / 16) + 1;
108 int maxY
= (int) ((getCamY() + getHeight()) / 16) + 1;
112 if (maxY
> mGameWorld
.getHeight()) {
113 maxY
= mGameWorld
.getHeight();
115 for (int y
= minY
; y
< maxY
; y
++) {
116 for (int x
= minX
; x
< maxX
; x
++) {
122 Gdx
.gl
.glEnable(GL20
.GL_BLEND
);
123 Gdx
.gl
.glBlendFunc(GL20
.GL_SRC_ALPHA
, GL20
.GL_ONE_MINUS_SRC_ALPHA
);
124 shaper
.begin(ShapeRenderer
.ShapeType
.Filled
);
125 shaper
.setColor(0f, 0f, 0f, .5f);
126 for (int y
= minY
; y
< maxY
; y
++) {
127 for (int x
= minX
; x
< maxX
; x
++) {
128 if ((!mGameWorld
.hasForeAt(x
, y
) || mGameWorld
.getForeMap(x
, y
).isTransparent())
129 && mGameWorld
.hasBackAt(x
, y
)) {
130 shaper
.rect(drawX(x
), drawY(y
), 16, 16);
135 Gdx
.gl
.glDisable(GL20
.GL_BLEND
);
140 private Rectangle
getShiftedRectRespectfulToViewPort(final Rectangle rect
, final float shift
) {
141 return new Rectangle(rect
.x
+ shift
- getCamX(), rect
.y
- getCamY(), rect
.width
, rect
.height
);
145 private Rectangle
getDrawingRectIfInsideViewport(final Rectangle rectangle
) {
146 final Rectangle viewportRect
= new Rectangle(0, 0, getWidth(), getHeight());
148 final Rectangle notShifted
= getShiftedRectRespectfulToViewPort(rectangle
, 0);
149 if (Intersector
.overlaps(viewportRect
, notShifted
)) {
153 final Rectangle shiftedLeft
= getShiftedRectRespectfulToViewPort(rectangle
, -mGameWorld
.getWidthPx());
154 if (Intersector
.overlaps(viewportRect
, shiftedLeft
)) {
158 final Rectangle shiftedRight
= getShiftedRectRespectfulToViewPort(rectangle
, mGameWorld
.getWidthPx());
159 if (Intersector
.overlaps(viewportRect
, shiftedRight
)) {
166 private void drawMob(Mob mob
, float delta
) {
167 float mobDrawX
= mob
.getX() - getCamX();
168 float mobDrawY
= mob
.getY() - getCamY();
170 if (mobDrawX
+ mob
.getWidth() < 0 && mobDrawX
+ mGameWorld
.getWidthPx() > 0) {
171 mobDrawX
+= mGameWorld
.getWidthPx();
172 } else if (mobDrawX
> getWidth() && mobDrawX
+ mob
.getWidth() - mGameWorld
.getWidthPx() > 0) {
173 mobDrawX
-= mGameWorld
.getWidthPx();
174 } else if (mobDrawX
+ mob
.getWidth() < 0 && mobDrawX
> getWidth()) {
178 mob
.draw(spriter
, mobDrawX
, mobDrawY
, delta
);
181 private void drawDrop(Drop drop
) {
182 @CheckForNull final Rectangle drawingRect
= getDrawingRectIfInsideViewport(drop
);
184 if (drawingRect
== null) {
188 final Sprite sprite
= drop
.getItem().getSprite();
190 sprite
.setPosition(drawingRect
.x
, drawingRect
.y
);
191 sprite
.setSize(drawingRect
.width
, drawingRect
.height
);
192 sprite
.draw(spriter
);
195 @SuppressWarnings("IntegerDivisionInFloatingPointContext")
196 private void drawCreative() {
197 TextureRegion creative
= textureRegions
.get("creative");
198 float x
= getWidth() / 2 - (float) creative
.getRegionWidth() / 2;
199 float y
= getHeight() / 2 - (float) creative
.getRegionHeight() / 2;
200 spriter
.draw(creative
, x
, y
);
201 spriter
.draw(textureRegions
.get("handle"), x
+ 156,
202 y
+ 18 + (mGameInput
.getCreativeScroll() * (72f / mGameItemsHolder
.getCreativeScrollAmount())));
203 final Collection
<Item
> items
= mGameItemsHolder
.getAllItems();
204 for (int i
= mGameInput
.getCreativeScroll() * 8; i
< mGameInput
.getCreativeScroll() * 8 + 40; i
++) {
205 if (i
> 0 && i
< items
.size()) {
206 Item item
= CollectionsKt
.elementAt(items
, i
);
207 if (item
!= mGameItemsHolder
.getFallbackItem()) {
208 spriter
.draw(CollectionsKt
.elementAt(items
, i
).getSprite(),
209 x
+ 8 + ((i
- mGameInput
.getCreativeScroll() * 8) % 8) * 18,
210 y
+ 18 + ((i
- mGameInput
.getCreativeScroll() * 8) / 8) * 18);
214 for (int i
= 0; i
< 9; i
++) {
215 if (mMobsController
.getPlayer().inventory
[i
] != null && mMobsController
.getPlayer().inventory
[i
] != mGameItemsHolder
.getFallbackItem()) {
216 spriter
.draw(mMobsController
.getPlayer().inventory
[i
].getSprite(),
217 x
+ 8 + i
* 18, y
+ creative
.getRegionHeight() - 24);
223 private void drawHealth(float x
, float y
) {
224 Player player
= mMobsController
.getPlayer();
226 if (player
.gameMode
== 1) {
230 TextureRegion wholeHeart
= textureRegions
.get("heart_whole");
231 TextureRegion halfHeart
= textureRegions
.get("heart_half");
233 int wholeHearts
= player
.getHealth() / 2;
235 for (int i
= 0; i
< wholeHearts
; i
++) {
236 spriter
.draw(wholeHeart
, x
+ i
* wholeHeart
.getRegionWidth(), y
);
239 if (player
.getHealth() % 2 == 1) {
240 spriter
.draw(halfHeart
, x
+ wholeHearts
* wholeHeart
.getRegionWidth(), y
);
244 private void drawGUI() {
245 TextureRegion cursor
= textureRegions
.get("cursor");
246 TextureRegion hotbar
= textureRegions
.get("hotbar");
247 TextureRegion hotbarSelector
= textureRegions
.get("hotbar_selector");
249 if (mGameWorld
.hasForeAt(mGameInput
.getCurX(), mGameInput
.getCurY()) ||
250 mGameWorld
.hasBackAt(mGameInput
.getCurX(), mGameInput
.getCurY()) ||
251 mGameInput
.getControlMode() == ControlMode
.CURSOR
|| mMainConfig
.isTouch()) {
252 spriter
.draw(cursor
, mGameInput
.getCurX() * 16 - getCamX(), mGameInput
.getCurY() * 16 - getCamY());
255 float hotbarX
= getWidth() / 2 - (float) hotbar
.getRegionWidth() / 2;
256 spriter
.draw(hotbar
, hotbarX
, 0);
257 drawHealth(hotbarX
, hotbar
.getRegionHeight());
259 for (int i
= 0; i
< 9; i
++) {
260 if (mMobsController
.getPlayer().inventory
[i
] != null && mMobsController
.getPlayer().inventory
[i
] != mGameItemsHolder
.getFallbackItem()) {
261 spriter
.draw(mMobsController
.getPlayer().inventory
[i
].getSprite(),
262 getWidth() / 2 - (float) hotbar
.getRegionWidth() / 2 + 3 + i
* 20,
266 spriter
.draw(hotbarSelector
,
267 getWidth() / 2 - (float) hotbar
.getRegionWidth() / 2 - 1 + 20 * mMobsController
.getPlayer().slot
,
271 private void drawTouchGui() {
272 for (int i
= 0; i
< guiMap
.size
; i
++) {
273 Rectangle touchKey
= guiMap
.getValueAt(i
).getRect();
274 spriter
.draw(textureRegions
.get(guiMap
.getKeyAt(i
)),
275 touchKey
.x
, touchKey
.y
, touchKey
.width
, touchKey
.height
);
277 if (mGameInput
.getControlMode() == ControlMode
.CURSOR
) {
278 spriter
.draw(textureRegions
.get("shade"), 83, getHeight() - 21);
282 private void drawGamePlay(float delta
) {
283 Player player
= mMobsController
.getPlayer();
286 player
.draw(spriter
, player
.getX() - getCamX() - player
.getWidth() / 2, player
.getY() - getCamY(), delta
);
287 mMobsController
.getMobs().forEach((mob
) -> {
290 mDropController
.forEach(this::drawDrop
);
295 private void updateCameraPosition() {
296 Player player
= mMobsController
.getPlayer();
297 setCamPos(player
.getX() + player
.getWidth() / 2 - getWidth() / 2,
298 player
.getY() + player
.getHeight() / 2 - getHeight() / 2);
302 private Color
getMinimapColor(int x
, int y
) {
303 @Nullable Color result
= null;
305 final boolean hasForeMap
= mGameWorld
.hasForeAt(x
, y
);
306 final boolean hasBackMap
= mGameWorld
.hasBackAt(x
, y
);
309 final Block block
= mGameWorld
.getForeMap(x
, y
);
311 if (block
.isWater()) {
313 } else if (block
.isLava()) {
316 result
= Color
.BLACK
;
318 } else if (hasBackMap
) {
319 result
= Color
.DARK_GRAY
;
325 private void drawMiniMap(float miniMapX
, float miniMapY
, float size
) {
326 shaper
.begin(ShapeRenderer
.ShapeType
.Filled
);
328 shaper
.setColor(Color
.LIGHT_GRAY
);
329 shaper
.rect(miniMapX
, miniMapY
, size
, size
);
331 for (int x
= 0; x
< size
; x
++) {
332 for (int y
= 0; y
< size
; y
++) {
334 final int worldX
= (int) (mMobsController
.getPlayer().getMapX() - size
/ 2 + x
);
335 final int worldY
= (int) (mMobsController
.getPlayer().getUpperMapY() - size
/ 2 + y
);
337 @Nullable final Color color
= getMinimapColor(worldX
, worldY
);
340 shaper
.setColor(color
);
341 shaper
.rect(miniMapX
+ x
, miniMapY
+ y
, 1, 1);
346 shaper
.setColor(Color
.OLIVE
);
347 shaper
.rect(miniMapX
+ size
/ 2, miniMapY
+ size
/ 2, 1, 2);
352 public void render(float delta
) {
353 int fps
= MathUtils
.ceil(1 / delta
);
354 updateCameraPosition();
355 mGameInput
.moveCursor(this);
357 Gdx
.gl
.glClear(GL20
.GL_COLOR_BUFFER_BIT
);
363 switch (mMainConfig
.getGameUiWindow()) {
364 case CREATIVE_INVENTORY
:
367 //TODO draw other ui windows
371 if (mMainConfig
.isTouch()) {
377 if (mMainConfig
.isShowMap()) {
378 drawMiniMap(getWidth() - 64f - 24f, 24f, 64f);
381 if (mMainConfig
.isShowInfo()) {
383 Player player
= mMobsController
.getPlayer();
384 drawString("FPS: " + fps
, 0, 0);
385 drawString("X: " + player
.getMapX(), 0, 10);
386 drawString("Y: " + player
.getUpperMapY(), 0, 20);
387 drawString("CurX: " + mGameInput
.getCurX(), 0, 30);
388 drawString("CurY: " + mGameInput
.getCurY(), 0, 40);
389 drawString("Velocity: " + player
.getVelocity(), 0, 50);
390 drawString("Swim: " + player
.swim
, 0, 60);
391 drawString("Mobs: " + mMobsController
.getMobs().size(), 0, 70);
392 drawString("Drops: " + mDropController
.getSize(), 0, 80);
393 drawString("Block: " + mGameWorld
.getForeMap(mGameInput
.getCurX(), mGameInput
.getCurY()).getParams().getKey(), 0, 90);
394 drawString("Hand: " + mMobsController
.getPlayer().inventory
[mMobsController
.getPlayer().slot
].getParams().getKey(), 0, 100);
395 drawString("Game mode: " + player
.gameMode
, 0, 110);