1 package ru
.deadsoftware
.cavedroid
.game
.mobs
.player
;
3 import com
.badlogic
.gdx
.graphics
.g2d
.Sprite
;
4 import com
.badlogic
.gdx
.graphics
.g2d
.SpriteBatch
;
5 import com
.badlogic
.gdx
.math
.MathUtils
;
6 import com
.badlogic
.gdx
.math
.Vector2
;
7 import org
.jetbrains
.annotations
.NotNull
;
8 import org
.jetbrains
.annotations
.Nullable
;
9 import ru
.deadsoftware
.cavedroid
.game
.GameItemsHolder
;
10 import ru
.deadsoftware
.cavedroid
.game
.mobs
.Mob
;
11 import ru
.deadsoftware
.cavedroid
.game
.mobs
.MobsController
;
12 import ru
.deadsoftware
.cavedroid
.game
.model
.block
.Block
;
13 import ru
.deadsoftware
.cavedroid
.game
.model
.dto
.SaveDataDto
;
14 import ru
.deadsoftware
.cavedroid
.game
.model
.item
.InventoryItem
;
15 import ru
.deadsoftware
.cavedroid
.game
.model
.item
.Item
;
16 import ru
.deadsoftware
.cavedroid
.game
.objects
.drop
.Drop
;
17 import ru
.deadsoftware
.cavedroid
.game
.objects
.drop
.DropController
;
18 import ru
.deadsoftware
.cavedroid
.game
.ui
.TooltipManager
;
19 import ru
.deadsoftware
.cavedroid
.game
.world
.GameWorld
;
20 import ru
.deadsoftware
.cavedroid
.misc
.Assets
;
21 import ru
.deadsoftware
.cavedroid
.misc
.utils
.SpriteOrigin
;
22 import ru
.deadsoftware
.cavedroid
.misc
.utils
.SpriteUtilsKt
;
24 public class Player
extends Mob
{
26 private static final int SAVE_DATA_VERSION
= 1;
28 private static final float SPEED
= 69.072f;
29 private static final float JUMP_VELOCITY
= -133.332f;
30 private static final int SURVIVAL_CURSOR_RANGE
= 4;
32 public static final int MAX_HEALTH
= 20;
33 public static final int INVENTORY_SIZE
= 36;
34 public static final int HOTBAR_SIZE
= 9;
36 private boolean hitting
= false, hittingWithDamage
= false;
37 private float hitAnim
= 0f;
38 private float hitAnimDelta
= ANIMATION_SPEED
;
40 public Inventory inventory
;
44 public float headRotation
= 0f;
46 public float blockDamage
= 0f;
47 public int cursorX
= 0;
48 public int cursorY
= 0;
51 private Vector2 spawnPoint
= null;
53 public ControlMode controlMode
= ControlMode
.WALK
;
55 public enum ControlMode
{
60 public Player(GameItemsHolder gameItemsHolder
, TooltipManager tooltipManager
) {
61 super(0, 0, 4, 30, randomDir(), Type
.MOB
, MAX_HEALTH
);
62 inventory
= new Inventory(INVENTORY_SIZE
, HOTBAR_SIZE
, gameItemsHolder
, tooltipManager
);
66 public void initInventory(GameItemsHolder gameItemsHolder
, TooltipManager tooltipManager
) {
67 inventory
.initItems(gameItemsHolder
, tooltipManager
);
70 public void respawn(GameWorld gameWorld
, GameItemsHolder itemsHolder
) {
71 Vector2 pos
= getSpawnPoint(gameWorld
, itemsHolder
);
79 public void decreaseCurrentItemCount(GameItemsHolder gameItemsHolder
) {
84 final InventoryItem item
= inventory
.getActiveItem();
86 if (item
.getAmount() <= 0) {
87 setCurrentInventorySlotItem(gameItemsHolder
.getFallbackItem());
91 public void dropCurrentItem(DropController dropController
) {
92 final InventoryItem activeItem
= inventory
.getActiveItem();
96 private Vector2
getSpawnPoint(GameWorld gameWorld
, GameItemsHolder itemsHolder
) {
97 if (spawnPoint
!= null) {
101 int y
, x
= gameWorld
.getWidth() / 2;
102 for (y
= 0; y
<= gameWorld
.getWorldConfig().getSeaLevel(); y
++) {
103 if (y
== gameWorld
.getWorldConfig().getSeaLevel()) {
104 for (x
= 0; x
< gameWorld
.getWidth(); x
++) {
105 if (gameWorld
.getForeMap(x
, y
).getParams().getHasCollision()) {
108 if (x
== gameWorld
.getWidth() - 1) {
109 gameWorld
.setForeMap(x
, y
, itemsHolder
.getBlock("grass"));
115 if (gameWorld
.hasForeAt(x
, y
) && gameWorld
.getForeMap(x
, y
).hasCollision()) {
119 spawnPoint
= new Vector2(x
* 16 + 8 - getWidth() / 2, (float) y
* 16 - getHeight());
123 public void setDir(Direction dir
) {
124 if (dir
!= getDirection()) {
129 public void setCurrentInventorySlotItem(Item item
) {
130 inventory
.getItems().set(inventory
.getActiveSlot(), item
.toInventoryItem());
134 public float getSpeed() {
145 getVelocity().y
= 0f;
151 mVelocity
.y
= JUMP_VELOCITY
;
154 private boolean checkBlockCanBeHit(Block block
) {
155 return !block
.isNone() && block
.getParams().getHitPoints() >= 0;
159 * @return true if any mob fas hit
161 private boolean hitMobs(GameItemsHolder gameItemsHolder
, MobsController mobsController
) {
162 if (!hitting
|| !hittingWithDamage
) {
166 boolean result
= false;
167 for (Mob mob
: mobsController
.getMobs()) {
168 if (overlaps(mob
.getHitBox())) {
169 final Item activeItem
= inventory
.getActiveItem().getItem();
170 final Item
.Tool tool
= activeItem
.isTool() ?
(Item
.Tool
) activeItem
: null;
172 decreaseCurrentItemCount(gameItemsHolder
);
175 mob
.damage(MathUtils
.floor(tool
!= null ? tool
.getMobDamageMultiplier() : 1));
181 private void hitBlock(GameWorld gameWorld
, GameItemsHolder gameItemsHolder
) {
182 if (!hitting
|| !hittingWithDamage
) {
186 final Block foregroundBlock
= gameWorld
.getForeMap(cursorX
, cursorY
);
187 final Block backgroundBlock
= gameWorld
.getBackMap(cursorX
, cursorY
);
190 if ((checkBlockCanBeHit(foregroundBlock
)) ||
191 (foregroundBlock
.isNone() && checkBlockCanBeHit(backgroundBlock
))) {
193 if (!foregroundBlock
.isNone()) {
194 if (blockDamage
>= foregroundBlock
.getParams().getHitPoints()) {
195 gameWorld
.destroyForeMap(cursorX
, cursorY
);
198 } else if (!backgroundBlock
.isNone()) {
199 if (blockDamage
>= backgroundBlock
.getParams().getHitPoints()) {
200 gameWorld
.destroyBackMap(cursorX
, cursorY
);
205 if (!foregroundBlock
.isNone()) {
206 gameWorld
.placeToForeground(cursorX
, cursorY
, gameItemsHolder
.getFallbackBlock());
207 } else if (!backgroundBlock
.isNone()) {
208 gameWorld
.placeToBackground(cursorX
, cursorY
, gameItemsHolder
.getFallbackBlock());
218 public void ai(GameWorld gameWorld
, GameItemsHolder gameItemsHolder
, MobsController mobsController
, float delta
) {
219 updateAnimation(delta
);
221 if (!hitMobs(gameItemsHolder
, mobsController
)) {
222 hitBlock(gameWorld
, gameItemsHolder
);
231 final Block foregroundBlock
= gameWorld
.getForeMap(cursorX
, cursorY
);
232 final Block backgroundBlock
= gameWorld
.getBackMap(cursorX
, cursorY
);
233 @Nullable final Block target
;
235 if (checkBlockCanBeHit(foregroundBlock
)) {
236 target
= foregroundBlock
;
237 } else if (checkBlockCanBeHit(backgroundBlock
)) {
238 target
= backgroundBlock
;
243 final boolean canHitBlock
= target
!= null;
245 float multiplier
= 1f;
246 final Item currentItem
= inventory
.getActiveItem().getItem();
247 if (currentItem
instanceof Item
.Tool
&& canHitBlock
) {
248 if (target
.getParams().getToolType() == currentItem
.getClass()
249 && ((Item
.Tool
)currentItem
).getLevel() >= target
.getParams().getToolLevel()) {
250 multiplier
= 2f * ((Item
.Tool
)currentItem
).getLevel();
252 multiplier
*= ((Item
.Tool
)currentItem
).getBlockDamageMultiplier();
255 if (hitting
&& hittingWithDamage
&& canHitBlock
) {
256 blockDamage
+= 60f * delta
* multiplier
;
263 public void changeDir() {
267 public void damage(int damage
) {
273 getVelocity().y
+= JUMP_VELOCITY
/ 3f;
276 super.damage(damage
);
280 public void heal(int heal
) {
287 public void checkCursorBounds(GameWorld gameWorld
) {
289 int minCursorX
= getMapX() - SURVIVAL_CURSOR_RANGE
;
290 int maxCursorX
= getMapX() + SURVIVAL_CURSOR_RANGE
;
291 int minCursorY
= getMiddleMapY() - SURVIVAL_CURSOR_RANGE
;
292 int maxCursorY
= getMiddleMapY() + SURVIVAL_CURSOR_RANGE
;
294 cursorX
= MathUtils
.clamp(cursorX
, minCursorX
, maxCursorX
);
295 cursorY
= MathUtils
.clamp(cursorY
, minCursorY
, maxCursorY
);
298 cursorY
= MathUtils
.clamp(cursorY
, 0, gameWorld
.getHeight() - 1);
301 private void drawItem(SpriteBatch spriteBatch
, float x
, float y
, float anim
) {
302 final Item item
= inventory
.getActiveItem().getItem();
304 if (item
== null || item
.isNone()) {
308 final Sprite sprite
= item
.getSprite();
309 final boolean smallSprite
= !item
.isTool() || item
.isShears();
311 final float originalWidth
= sprite
.getWidth();
312 final float originalHeight
= sprite
.getHeight();
315 sprite
.setSize(Drop
.DROP_SIZE
, Drop
.DROP_SIZE
);
318 final float handLength
= Assets
.playerSprite
[0][2].getHeight();
320 final SpriteOrigin spriteOrigin
= item
.getParams().getInHandSpriteOrigin();
321 final int handMultiplier
= -getDirection().getBasis();
322 final float xOffset
= (-1 + getDirection().getIndex()) * sprite
.getWidth() + 4 + handMultiplier
* (sprite
.getWidth() * spriteOrigin
.getX());
323 final float yOffset
= !smallSprite ?
-sprite
.getHeight() / 2 : 0;
325 float rotate
= anim
+ 30;
328 sprite
.rotate90(looksLeft());
331 final float itemX
= x
+ handLength
* MathUtils
.sin(handMultiplier
* anim
* MathUtils
.degRad
) + xOffset
;
332 final float itemY
= y
+ handLength
* MathUtils
.cos(handMultiplier
* anim
* MathUtils
.degRad
) + yOffset
;
335 sprite
.setFlip(!item
.isTool(), sprite
.isFlipY());
336 SpriteUtilsKt
.applyOrigin(sprite
, spriteOrigin
.getFlipped(true, false));
338 sprite
.setFlip(item
.isTool(), sprite
.isFlipY());
339 SpriteUtilsKt
.applyOrigin(sprite
, spriteOrigin
);
342 sprite
.setRotation(-handMultiplier
* rotate
);
343 sprite
.setPosition(itemX
, itemY
);
344 sprite
.draw(spriteBatch
);
346 // dont forget to reset
347 sprite
.setFlip(false, sprite
.isFlipY());
348 sprite
.setRotation(0);
349 sprite
.setOriginCenter();
350 sprite
.setSize(originalWidth
, originalHeight
);
352 sprite
.rotate90(looksRight());
356 public void startHitting(boolean withDamage
) {
362 hittingWithDamage
= withDamage
;
364 hitAnimDelta
= ANIMATION_SPEED
;
367 public void startHitting() {
371 public void stopHitting() {
376 private float getRightHandAnim(float delta
) {
377 hitAnim
-= hitAnimDelta
* delta
;
379 if (hitAnim
< 30f || hitAnim
> 90f) {
381 hitAnim
= MathUtils
.clamp(hitAnim
, 30f, 90f);
382 hitAnimDelta
= -hitAnimDelta
;
384 hitAnimDelta
= ANIMATION_SPEED
;
389 if (hitAnim
< hitAnimDelta
* delta
) {
400 public void draw(SpriteBatch spriteBatch
, float x
, float y
, float delta
) {
401 final Sprite backHand
= Assets
.playerSprite
[1][2];
402 final Sprite backLeg
= Assets
.playerSprite
[1][3];
403 final Sprite frontLeg
= Assets
.playerSprite
[0][3];
404 final Sprite head
= Assets
.playerSprite
[getDirection().getIndex()][0];
405 final Sprite body
= Assets
.playerSprite
[getDirection().getIndex()][1];
406 final Sprite frontHand
= Assets
.playerSprite
[0][2];
408 float backHandAnim
, frontHandAnim
;
410 final float rightHandAnim
= getRightHandAnim(delta
);
413 backHandAnim
= rightHandAnim
;
414 frontHandAnim
= mAnim
;
416 backHandAnim
= -mAnim
;
417 frontHandAnim
= -rightHandAnim
;
420 backHand
.setColor(getTintColor());
421 backLeg
.setColor(getTintColor());
422 frontLeg
.setColor(getTintColor());
423 head
.setColor(getTintColor());
424 body
.setColor(getTintColor());
425 frontHand
.setColor(getTintColor());
427 SpriteUtilsKt
.drawSprite(spriteBatch
, backHand
, x
+ 2, y
+ 8, backHandAnim
);
430 drawItem(spriteBatch
, x
, y
, -backHandAnim
);
433 SpriteUtilsKt
.drawSprite(spriteBatch
, backLeg
, x
+ 2, y
+ 20, mAnim
);
434 SpriteUtilsKt
.drawSprite(spriteBatch
, frontLeg
, x
+ 2, y
+ 20, -mAnim
);
435 SpriteUtilsKt
.drawSprite(spriteBatch
, head
, x
, y
, headRotation
);
436 SpriteUtilsKt
.drawSprite(spriteBatch
, body
, x
+ 2, y
+ 8);
439 drawItem(spriteBatch
, x
, y
, frontHandAnim
);
442 SpriteUtilsKt
.drawSprite(spriteBatch
, frontHand
, x
+ 2, y
+ 8, frontHandAnim
);
447 public SaveDataDto
.PlayerSaveData
getSaveData() {
448 return new SaveDataDto
.PlayerSaveData(
469 inventory
.getSaveData(),
476 spawnPoint
!= null ? spawnPoint
.x
: 0f,
477 spawnPoint
!= null ? spawnPoint
.y
: 0f,
482 public static Player
fromSaveData(
483 SaveDataDto
.PlayerSaveData saveData
,
484 GameItemsHolder gameItemsHolder
,
485 TooltipManager tooltipManager
487 saveData
.verifyVersion(SAVE_DATA_VERSION
);
489 Player player
= new Player(gameItemsHolder
, tooltipManager
);
491 player
.mType
= saveData
.getType();
492 player
.mAnimDelta
= saveData
.getAnimDelta();
493 player
.mAnim
= saveData
.getAnim();
494 player
.mDirection
= saveData
.getDirection();
495 player
.mDead
= saveData
.getDead();
496 player
.mCanJump
= saveData
.getCanJump();
497 player
.mFlyMode
= saveData
.getFlyMode();
498 player
.mMaxHealth
= saveData
.getMaxHealth();
499 player
.mHealth
= saveData
.getHealth();
500 player
.x
= saveData
.getX();
501 player
.y
= saveData
.getY();
502 player
.width
= saveData
.getWidth();
503 player
.height
= saveData
.getHeight();
504 player
.hitting
= saveData
.getHitting();
505 player
.hittingWithDamage
= saveData
.getHittingWithDamage();
506 player
.hitAnim
= saveData
.getHitAnim();
507 player
.hitAnimDelta
= saveData
.getHitAnimDelta();
508 player
.inventory
= Inventory
.Companion
.fromSaveData(saveData
.getInventory(), gameItemsHolder
, tooltipManager
);
509 player
.gameMode
= saveData
.getGameMode();
510 player
.swim
= saveData
.getSwim();
511 player
.headRotation
= saveData
.getHeadRotation();
512 player
.blockDamage
= saveData
.getBlockDamage();
513 player
.cursorX
= saveData
.getCursorX();
514 player
.cursorY
= saveData
.getCursorY();
515 player
.spawnPoint
= new Vector2(saveData
.getSpawnPointX(), saveData
.getSpawnPointY());
516 player
.controlMode
= saveData
.getControlMode();