DEADSOFTWARE

Add crafting
[cavedroid.git] / core / src / ru / deadsoftware / cavedroid / game / mobs / player / Player.java
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;
42 public int gameMode;
43 public boolean swim;
44 public float headRotation = 0f;
46 public float blockDamage = 0f;
47 public int cursorX = 0;
48 public int cursorY = 0;
50 @Nullable
51 private Vector2 spawnPoint = null;
53 public ControlMode controlMode = ControlMode.WALK;
55 public enum ControlMode {
56 WALK,
57 CURSOR
58 }
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);
63 swim = false;
64 }
66 public void initInventory(GameItemsHolder gameItemsHolder, TooltipManager tooltipManager) {
67 inventory.initItems(gameItemsHolder, tooltipManager);
68 }
70 public void respawn(GameWorld gameWorld, GameItemsHolder itemsHolder) {
71 Vector2 pos = getSpawnPoint(gameWorld, itemsHolder);
72 this.x = pos.x;
73 this.y = pos.y;
74 mVelocity.setZero();
75 mDead = false;
76 heal(MAX_HEALTH);
77 }
79 public void decreaseCurrentItemCount(GameItemsHolder gameItemsHolder) {
80 if (gameMode == 1) {
81 return;
82 }
84 final InventoryItem item = inventory.getActiveItem();
85 item.subtract();
86 if (item.getAmount() <= 0) {
87 setCurrentInventorySlotItem(gameItemsHolder.getFallbackItem());
88 }
89 }
91 public void dropCurrentItem(DropController dropController) {
92 final InventoryItem activeItem = inventory.getActiveItem();
94 }
96 private Vector2 getSpawnPoint(GameWorld gameWorld, GameItemsHolder itemsHolder) {
97 if (spawnPoint != null) {
98 return spawnPoint;
99 }
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()) {
106 break;
108 if (x == gameWorld.getWidth() - 1) {
109 gameWorld.setForeMap(x, y, itemsHolder.getBlock("grass"));
110 break;
113 break;
115 if (gameWorld.hasForeAt(x, y) && gameWorld.getForeMap(x, y).hasCollision()) {
116 break;
119 spawnPoint = new Vector2(x * 16 + 8 - getWidth() / 2, (float) y * 16 - getHeight());
120 return spawnPoint;
123 public void setDir(Direction dir) {
124 if (dir != getDirection()) {
125 switchDir();
129 public void setCurrentInventorySlotItem(Item item) {
130 inventory.getItems().set(inventory.getActiveSlot(), item.toInventoryItem());
133 @Override
134 public float getSpeed() {
135 return SPEED;
138 @Override
139 public void jump() {
140 if (!canJump()) {
141 if (gameMode == 1) {
142 if (isFlyMode()) {
143 setFlyMode(false);
144 } else {
145 getVelocity().y = 0f;
146 setFlyMode(true);
149 return;
151 mVelocity.y = JUMP_VELOCITY;
154 private boolean checkBlockCanBeHit(Block block) {
155 return !block.isNone() && block.getParams().getHitPoints() >= 0;
158 /**
159 * @return true if any mob fas hit
160 */
161 private boolean hitMobs(GameItemsHolder gameItemsHolder, MobsController mobsController) {
162 if (!hitting || !hittingWithDamage) {
163 return false;
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;
171 if (tool != null) {
172 decreaseCurrentItemCount(gameItemsHolder);
174 result = true;
175 mob.damage(MathUtils.floor(tool != null ? tool.getMobDamageMultiplier() : 1));
178 return result;
181 private void hitBlock(GameWorld gameWorld, GameItemsHolder gameItemsHolder) {
182 if (!hitting || !hittingWithDamage) {
183 return;
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))) {
192 if (gameMode == 0) {
193 if (!foregroundBlock.isNone()) {
194 if (blockDamage >= foregroundBlock.getParams().getHitPoints()) {
195 gameWorld.destroyForeMap(cursorX, cursorY);
196 blockDamage = 0;
198 } else if (!backgroundBlock.isNone()) {
199 if (blockDamage >= backgroundBlock.getParams().getHitPoints()) {
200 gameWorld.destroyBackMap(cursorX, cursorY);
201 blockDamage = 0;
204 } else {
205 if (!foregroundBlock.isNone()) {
206 gameWorld.placeToForeground(cursorX, cursorY, gameItemsHolder.getFallbackBlock());
207 } else if (!backgroundBlock.isNone()) {
208 gameWorld.placeToBackground(cursorX, cursorY, gameItemsHolder.getFallbackBlock());
210 stopHitting();
212 } else {
213 stopHitting();
217 @Override
218 public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, MobsController mobsController, float delta) {
219 updateAnimation(delta);
221 if (!hitMobs(gameItemsHolder, mobsController)) {
222 hitBlock(gameWorld, gameItemsHolder);
223 } else {
224 stopHitting();
227 if (gameMode == 1) {
228 return;
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;
239 } else {
240 target = null;
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;
257 } else {
258 blockDamage = 0f;
262 @Override
263 public void changeDir() {
266 @Override
267 public void damage(int damage) {
268 if (gameMode == 1) {
269 return;
272 if (damage > 0) {
273 getVelocity().y += JUMP_VELOCITY / 3f;
276 super.damage(damage);
279 @Override
280 public void heal(int heal) {
281 if (gameMode == 1) {
282 return;
284 super.heal(heal);
287 public void checkCursorBounds(GameWorld gameWorld) {
288 if (gameMode == 0) {
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()) {
305 return;
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();
314 if (smallSprite) {
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;
327 if (item.isTool()) {
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;
334 if (looksLeft()) {
335 sprite.setFlip(!item.isTool(), sprite.isFlipY());
336 SpriteUtilsKt.applyOrigin(sprite, spriteOrigin.getFlipped(true, false));
337 } else {
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);
351 if (item.isTool()) {
352 sprite.rotate90(looksRight());
356 public void startHitting(boolean withDamage) {
357 if (hitting) {
358 return;
361 hitting = true;
362 hittingWithDamage = withDamage;
363 hitAnim = 90f;
364 hitAnimDelta = ANIMATION_SPEED;
367 public void startHitting() {
368 startHitting(true);
371 public void stopHitting() {
372 blockDamage = 0f;
373 hitting = false;
376 private float getRightHandAnim(float delta) {
377 hitAnim -= hitAnimDelta * delta;
379 if (hitAnim < 30f || hitAnim > 90f) {
380 if (hitting) {
381 hitAnim = MathUtils.clamp(hitAnim, 30f, 90f);
382 hitAnimDelta = -hitAnimDelta;
383 } else {
384 hitAnimDelta = ANIMATION_SPEED;
388 if (!hitting) {
389 if (hitAnim < hitAnimDelta * delta) {
390 hitAnim = 0;
391 hitAnimDelta = 0;
392 return -mAnim;
396 return hitAnim;
399 @Override
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);
412 if (looksLeft()) {
413 backHandAnim = rightHandAnim;
414 frontHandAnim = mAnim;
415 } else {
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);
429 if (looksLeft()) {
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);
438 if (looksRight()) {
439 drawItem(spriteBatch, x, y, frontHandAnim);
442 SpriteUtilsKt.drawSprite(spriteBatch, frontHand, x + 2, y + 8, frontHandAnim);
445 @NotNull
446 @Override
447 public SaveDataDto.PlayerSaveData getSaveData() {
448 return new SaveDataDto.PlayerSaveData(
449 SAVE_DATA_VERSION,
450 mType,
451 mAnimDelta,
452 mAnim,
453 mDirection,
454 mDead,
455 mCanJump,
456 mFlyMode,
457 mMaxHealth,
458 mHealth,
459 x,
460 y,
461 width,
462 height,
463 getVelocity().x,
464 getVelocity().y,
465 hitting,
466 hittingWithDamage,
467 hitAnim,
468 hitAnimDelta,
469 inventory.getSaveData(),
470 gameMode,
471 swim,
472 headRotation,
473 blockDamage,
474 cursorX,
475 cursorY,
476 spawnPoint != null ? spawnPoint.x : 0f,
477 spawnPoint != null ? spawnPoint.y : 0f,
478 controlMode
479 );
482 public static Player fromSaveData(
483 SaveDataDto.PlayerSaveData saveData,
484 GameItemsHolder gameItemsHolder,
485 TooltipManager tooltipManager
486 ) {
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();
518 return player;