1 package ru
.deadsoftware
.cavedroid
.game
;
3 import com
.badlogic
.gdx
.Gdx
;
4 import com
.badlogic
.gdx
.Input
;
5 import com
.badlogic
.gdx
.graphics
.g2d
.TextureRegion
;
6 import com
.badlogic
.gdx
.math
.Intersector
;
7 import com
.badlogic
.gdx
.math
.MathUtils
;
8 import com
.badlogic
.gdx
.utils
.TimeUtils
;
9 import com
.google
.common
.collect
.Range
;
10 import ru
.deadsoftware
.cavedroid
.MainConfig
;
11 import ru
.deadsoftware
.cavedroid
.game
.actions
.CommonBlockActionUtilsKt
;
12 import ru
.deadsoftware
.cavedroid
.game
.actions
.placeblock
.IPlaceBlockAction
;
13 import ru
.deadsoftware
.cavedroid
.game
.actions
.useitem
.IUseItemAction
;
14 import ru
.deadsoftware
.cavedroid
.game
.mobs
.Mob
;
15 import ru
.deadsoftware
.cavedroid
.game
.mobs
.MobsController
;
16 import ru
.deadsoftware
.cavedroid
.game
.mobs
.Pig
;
17 import ru
.deadsoftware
.cavedroid
.game
.mobs
.Player
;
18 import ru
.deadsoftware
.cavedroid
.game
.model
.item
.Item
;
19 import ru
.deadsoftware
.cavedroid
.game
.objects
.DropController
;
20 import ru
.deadsoftware
.cavedroid
.game
.world
.GameWorld
;
21 import ru
.deadsoftware
.cavedroid
.misc
.Assets
;
22 import ru
.deadsoftware
.cavedroid
.misc
.ControlMode
;
24 import javax
.annotation
.CheckForNull
;
25 import javax
.inject
.Inject
;
30 public class GameInput
{
32 private static final String TAG
= "GameInput";
34 private final MainConfig mMainConfig
;
35 private final GameWorld mGameWorld
;
36 private final DropController mDropController
;
37 private final MobsController mMobsController
;
38 private final GameItemsHolder mGameItemsHolder
;
39 private final Map
<String
, IUseItemAction
> mUseItemActionMap
;
40 private final Map
<String
, IPlaceBlockAction
> mPlaceBlockActionMap
;
42 private final Player mPlayer
;
44 private ControlMode mControlMode
;
46 private boolean mKeyDown
;
47 private boolean mTouchedDown
;
48 private boolean mDragging
;
50 private int mKeyDownCode
;
51 private int mTouchDownBtn
;
52 private float mTouchDownX
;
53 private float mTouchDownY
;
54 private long mTouchDownTime
;
56 private int mCreativeScroll
;
59 public GameInput(MainConfig mainConfig
,
61 DropController dropController
,
62 MobsController mobsController
,
63 GameItemsHolder gameItemsHolder
,
64 Map
<String
, IUseItemAction
> useItemActionMap
,
65 Map
<String
, IPlaceBlockAction
> placeBlockActionMap
) {
66 mMainConfig
= mainConfig
;
67 mGameWorld
= gameWorld
;
68 mDropController
= dropController
;
69 mMobsController
= mobsController
;
70 mGameItemsHolder
= gameItemsHolder
;
71 mUseItemActionMap
= useItemActionMap
;
72 mPlaceBlockActionMap
= placeBlockActionMap
;
74 mPlayer
= mMobsController
.getPlayer();
76 mControlMode
= mMainConfig
.isTouch() ? ControlMode
.WALK
: ControlMode
.CURSOR
;
79 private boolean checkSwim() {
80 return mGameWorld
.getForeMap(mPlayer
.getMapX(), mPlayer
.getLowerMapY()).isFluid();
83 private void goUpwards() {
86 } else if (mPlayer
.canJump()) {
88 } else if (!mPlayer
.isFlyMode() && mPlayer
.gameMode
== 1) {
89 mPlayer
.setFlyMode(true);
90 mPlayer
.getVelocity().y
= 0;
91 } else if (mPlayer
.isFlyMode()) {
92 mPlayer
.getVelocity().y
= -mPlayer
.getSpeed();
96 @SuppressWarnings("IntegerDivisionInFloatingPointContext")
97 private boolean insideCreativeInv(float screenX
, float screenY
) {
98 TextureRegion creative
= Assets
.textureRegions
.get("creative");
99 return (screenX
> mMainConfig
.getWidth() / 2 - creative
.getRegionWidth() / 2 &&
100 screenX
< mMainConfig
.getWidth() / 2 + creative
.getRegionWidth() / 2 &&
101 screenY
> mMainConfig
.getHeight() / 2 - creative
.getRegionHeight() / 2 &&
102 screenY
< mMainConfig
.getHeight() / 2 + creative
.getRegionHeight() / 2);
105 private void wasdPressed(int keycode
) {
106 if (mControlMode
== ControlMode
.WALK
|| !mMainConfig
.isTouch()) {
109 mPlayer
.getVelocity().x
= -mPlayer
.getSpeed();
110 mPlayer
.setDir(Mob
.Direction
.LEFT
);
111 if (mMainConfig
.isTouch() && checkSwim()) {
116 mPlayer
.getVelocity().x
= mPlayer
.getSpeed();
117 mPlayer
.setDir(Mob
.Direction
.RIGHT
);
118 if (mMainConfig
.isTouch() && checkSwim()) {
123 case Input
.Keys
.SPACE
:
127 case Input
.Keys
.CONTROL_LEFT
:
128 mPlayer
.getVelocity().y
= mPlayer
.getSpeed();
146 mPlayer
.blockDamage
= 0;
150 private boolean isNotAutoselectable(int x
, int y
) {
151 return (!mGameWorld
.hasForeAt(x
, y
) || !mGameWorld
.getForeMap(x
, y
).hasCollision());
154 private void checkCursorBounds() {
155 if (mPlayer
.cursorY
< 0) {
157 } else if (mPlayer
.cursorY
>= mGameWorld
.getHeight()) {
158 mPlayer
.cursorY
= mGameWorld
.getHeight() - 1;
161 if (mControlMode
== ControlMode
.CURSOR
) {
162 if (mPlayer
.cursorX
* 16 + 8 < mPlayer
.getX() + mPlayer
.getWidth() / 2) {
163 mPlayer
.setDir(Mob
.Direction
.LEFT
);
165 mPlayer
.setDir(Mob
.Direction
.RIGHT
);
170 public void moveCursor(GameRenderer gameRenderer
) {
171 int pastX
= mPlayer
.cursorX
;
172 int pastY
= mPlayer
.cursorY
;
174 if (mControlMode
== ControlMode
.WALK
&& mMainConfig
.isTouch()) {
175 mPlayer
.cursorX
= mPlayer
.getMapX() + (mPlayer
.looksLeft() ?
-1 : 1);
176 mPlayer
.cursorY
= mPlayer
.getUpperMapY();
177 for (int i
= 0; i
< 2 && isNotAutoselectable(mPlayer
.cursorX
, mPlayer
.cursorY
); i
++) {
180 if (isNotAutoselectable(mPlayer
.cursorX
, mPlayer
.cursorY
)) {
181 mPlayer
.cursorX
+= mPlayer
.looksLeft() ?
1 : -1;
183 } else if (!mMainConfig
.isTouch()) {
184 final int tmpX
= (int) (Gdx
.input
.getX() * (mMainConfig
.getWidth() /
185 Gdx
.graphics
.getWidth()) + gameRenderer
.getCamX());
186 mPlayer
.cursorX
= tmpX
/ 16;
188 final int tmpY
= (int) (Gdx
.input
.getY() * (mMainConfig
.getHeight() /
189 Gdx
.graphics
.getHeight()) + gameRenderer
.getCamY());
190 mPlayer
.cursorY
= tmpY
/ 16;
196 final double a
= tmpX
- mPlayer
.x
;
197 final double b
= tmpY
- mPlayer
.y
;
199 mPlayer
.headRotation
= (float) Math
.atan(b
/ a
) * MathUtils
.radDeg
;
202 if (pastX
!= mPlayer
.cursorX
|| pastY
!= mPlayer
.cursorY
) {
203 mPlayer
.blockDamage
= 0;
209 private void useItem(int x
, int y
, @CheckForNull Item item
, boolean bg
) {
210 mPlayer
.startHitting();
216 if (item
instanceof Item
.Placeable
) {
218 CommonBlockActionUtilsKt
.placeToForegroundAction(mPlaceBlockActionMap
, (Item
.Placeable
) item
, x
, y
);
220 CommonBlockActionUtilsKt
.placeToBackgroundAction(mPlaceBlockActionMap
, (Item
.Placeable
) item
, x
, y
);
222 } else if (item
instanceof Item
.Usable
) {
223 final String actionKey
= ((Item
.Usable
) item
).getUseActionKey();
224 final IUseItemAction useItemAction
= mUseItemActionMap
.get(actionKey
);
226 if (useItemAction
!= null) {
227 useItemAction
.perform((Item
.Usable
) item
, x
, y
);
229 Gdx
.app
.error(TAG
, "use item action " + actionKey
+ " not found");
234 private void hitMobs() {
235 final Player player
= mMobsController
.getPlayer();
236 mMobsController
.getMobs().forEach((mob
) -> {
237 if (Intersector
.overlaps(mob
, player
)) {
244 private void pressLMB() {
245 if (mMainConfig
.checkGameUiWindow(GameUiWindow
.NONE
)) {
246 mPlayer
.startHitting();
248 if ((mGameWorld
.hasForeAt(mPlayer
.cursorX
, mPlayer
.cursorY
) && mGameWorld
.getForeMap(mPlayer
.cursorX
, mPlayer
.cursorY
).getHp() >= 0) ||
249 (!mGameWorld
.hasForeAt(mPlayer
.cursorX
, mPlayer
.cursorY
) && mGameWorld
.hasBackAt(mPlayer
.cursorX
, mPlayer
.cursorY
) &&
250 mGameWorld
.getBackMap(mPlayer
.cursorX
, mPlayer
.cursorY
).getHp() >= 0)) {
251 if (mPlayer
.gameMode
== 0) {
252 if (mGameWorld
.hasForeAt(mPlayer
.cursorX
, mPlayer
.cursorY
)) {
253 if (mPlayer
.blockDamage
>= mGameWorld
.getForeMap(mPlayer
.cursorX
, mPlayer
.cursorY
).getHp()) {
254 mGameWorld
.destroyForeMap(mPlayer
.cursorX
, mPlayer
.cursorY
);
255 mPlayer
.blockDamage
= 0;
257 } else if (mGameWorld
.hasBackAt(mPlayer
.cursorX
, mPlayer
.cursorY
)) {
258 if (mPlayer
.blockDamage
>= mGameWorld
.getBackMap(mPlayer
.cursorX
, mPlayer
.cursorY
).getHp()) {
259 mGameWorld
.destroyBackMap(mPlayer
.cursorX
, mPlayer
.cursorY
);
260 mPlayer
.blockDamage
= 0;
264 if (mGameWorld
.hasForeAt(mPlayer
.cursorX
, mPlayer
.cursorY
)) {
265 mGameWorld
.placeToForeground(mPlayer
.cursorX
, mPlayer
.cursorY
, mGameItemsHolder
.getFallbackBlock());
266 } else if (mGameWorld
.hasBackAt(mPlayer
.cursorX
, mPlayer
.cursorY
)) {
267 mGameWorld
.placeToBackground(mPlayer
.cursorX
, mPlayer
.cursorY
, mGameItemsHolder
.getFallbackBlock());
269 mTouchedDown
= false;
273 mTouchedDown
= false;
278 private boolean insideHotbar(float x
, float y
) {
279 TextureRegion hotbar
= Assets
.textureRegions
.get("hotbar");
280 return y
< hotbar
.getRegionHeight() &&
281 Range
.open(mMainConfig
.getWidth() / 2 - (float) hotbar
.getRegionWidth() / 2,
282 mMainConfig
.getWidth() / 2 + (float) hotbar
.getRegionWidth() / 2).contains(x
);
285 private void holdMB() {
286 if (mTouchDownBtn
== Input
.Buttons
.RIGHT
) {
287 useItem(mPlayer
.cursorX
, mPlayer
.cursorY
, mPlayer
.inventory
[mPlayer
.slot
].getItem(), true);
288 mTouchedDown
= false;
290 if (insideHotbar(mTouchDownX
, mTouchDownY
)) {
291 mMainConfig
.setGameUiWindow(GameUiWindow
.CREATIVE_INVENTORY
);
292 mTouchedDown
= false;
297 public void keyDown(int keycode
) {
299 mKeyDownCode
= keycode
;
305 case Input
.Keys
.SPACE
:
306 case Input
.Keys
.CONTROL_LEFT
:
307 wasdPressed(keycode
);
310 case Input
.Keys
.ALT_LEFT
:
311 if (mMainConfig
.isTouch()) {
312 mControlMode
= mControlMode
== ControlMode
.WALK ? ControlMode
.CURSOR
: ControlMode
.WALK
;
317 if (mMainConfig
.checkGameUiWindow(GameUiWindow
.NONE
)) {
318 switch (mPlayer
.gameMode
) {
323 mMainConfig
.setGameUiWindow(GameUiWindow
.CREATIVE_INVENTORY
);
327 mMainConfig
.setGameUiWindow(GameUiWindow
.NONE
);
332 final Mob pig
= new Pig(mPlayer
.cursorX
* 16, mPlayer
.cursorY
* 16);
333 pig
.attachToController(mMobsController
);
336 case Input
.Keys
.GRAVE
:
337 mMobsController
.getPlayer().gameMode
= (mMobsController
.getPlayer().gameMode
+ 1) % 2;
340 case Input
.Keys
.ESCAPE
:
341 case Input
.Keys
.BACK
:
342 GameSaver
.save(mMainConfig
, mDropController
, mMobsController
, mGameWorld
);
343 mMainConfig
.getCaveGame().quitGame();
347 mMainConfig
.setShowInfo(!mMainConfig
.isShowInfo());
351 mMainConfig
.setShowMap(!mMainConfig
.isShowMap());
356 public void keyUp(int keycode
) {
360 mPlayer
.getVelocity().x
= 0;
361 if (mMainConfig
.isTouch() && mPlayer
.swim
) {
362 mPlayer
.swim
= false;
368 case Input
.Keys
.SPACE
:
369 case Input
.Keys
.CONTROL_LEFT
:
370 if (mPlayer
.isFlyMode()) {
371 mPlayer
.getVelocity().y
= 0;
374 mPlayer
.swim
= false;
380 public void touchDown(float touchX
, float touchY
, int button
) {
381 mTouchDownTime
= TimeUtils
.millis();
383 mTouchDownBtn
= button
;
384 mTouchDownX
= touchX
;
385 mTouchDownY
= touchY
;
388 public void touchUp(float screenX
, float screenY
, int button
) {
394 if (mMainConfig
.isTouch() && mKeyDown
) {
398 TextureRegion hotbar
= Assets
.textureRegions
.get("hotbar");
399 TextureRegion creative
= Assets
.textureRegions
.get("creative");
401 if (mMainConfig
.checkGameUiWindow(GameUiWindow
.CREATIVE_INVENTORY
) && insideCreativeInv(screenX
, screenY
)) {
402 int ix
= (int) (screenX
- (mMainConfig
.getWidth() / 2 - creative
.getRegionWidth() / 2 + 8)) / 18;
403 int iy
= (int) (screenY
- (mMainConfig
.getHeight() / 2 - creative
.getRegionHeight() / 2 + 18)) / 18;
404 int itemPos
= mCreativeScroll
* 8 + (ix
+ iy
* 8);
405 if (ix
>= 8 || ix
< 0 || iy
< 0 || iy
>= 5) {
409 System
.arraycopy(mPlayer
.inventory
, 0, mPlayer
.inventory
, 1, 8);
410 mPlayer
.inventory
[0] = mGameItemsHolder
.getItemFromCreativeInventory(itemPos
).toInventoryItem();
411 } else if (mMainConfig
.checkGameUiWindow(GameUiWindow
.CREATIVE_INVENTORY
)) {
412 mMainConfig
.setGameUiWindow(GameUiWindow
.NONE
);
413 } else if (screenY
< hotbar
.getRegionHeight() &&
414 screenX
> mMainConfig
.getWidth() / 2 - (float) hotbar
.getRegionWidth() / 2 &&
415 screenX
< mMainConfig
.getWidth() / 2 + (float) hotbar
.getRegionWidth() / 2) {
416 mPlayer
.slot
= (int) ((screenX
- (mMainConfig
.getWidth() / 2 - hotbar
.getRegionWidth() / 2)) / 20);
417 } else if (button
== Input
.Buttons
.RIGHT
) {
418 useItem(mPlayer
.cursorX
, mPlayer
.cursorY
,
419 mPlayer
.inventory
[mPlayer
.slot
].getItem(), false);
420 } else if (button
== Input
.Buttons
.LEFT
) {
421 mPlayer
.stopHitting();
422 mPlayer
.blockDamage
= 0;
425 mTouchedDown
= false;
428 public void touchDragged(float screenX
, float screenY
) {
429 if (Math
.abs(screenX
- mTouchDownX
) < 16 && Math
.abs(screenY
- mTouchDownY
) < 16) {
434 if (mMainConfig
.checkGameUiWindow(GameUiWindow
.CREATIVE_INVENTORY
) && Math
.abs(screenY
- mTouchDownY
) > 16) {
435 if (insideCreativeInv(screenX
, screenY
)) {
436 mCreativeScroll
-= (screenY
- mTouchDownY
) / 16;
437 mTouchDownX
= screenX
;
438 mTouchDownY
= screenY
;
439 if (mCreativeScroll
< 0) {
443 final int maxScroll
= mGameItemsHolder
.getCreativeScrollAmount();
444 if (mCreativeScroll
> maxScroll
) {
445 mCreativeScroll
= maxScroll
;
451 public void scrolled(float amountX
, float amountY
) {
452 switch (mMainConfig
.getGameUiWindow()) {
454 mPlayer
.slot
+= (int) amountY
;
455 if (mPlayer
.slot
< 0) {
458 if (mPlayer
.slot
> 8) {
462 case CREATIVE_INVENTORY
:
463 mCreativeScroll
+= (int) amountY
;
464 if (mCreativeScroll
< 0) {
468 final int maxScroll
= mGameItemsHolder
.getCreativeScrollAmount();
469 if (mCreativeScroll
> maxScroll
) {
470 mCreativeScroll
= maxScroll
;
476 public int getKeyDownCode() {
480 public boolean isKeyDown() {
484 public int getCreativeScroll() {
485 return mCreativeScroll
;
488 public ControlMode
getControlMode() {
492 public void setControlMode(ControlMode controlMode
) {
493 mControlMode
= controlMode
;
498 mPlayer
.stopHitting();
502 if (mTouchDownBtn
== Input
.Buttons
.LEFT
) {
506 if (mTouchedDown
&& TimeUtils
.timeSinceMillis(mTouchDownTime
) > 500) {