1 package ru
.deadsoftware
.cavedroid
.game
.mobs
;
3 import com
.badlogic
.gdx
.Gdx
;
4 import com
.badlogic
.gdx
.graphics
.Color
;
5 import com
.badlogic
.gdx
.graphics
.g2d
.SpriteBatch
;
6 import com
.badlogic
.gdx
.math
.MathUtils
;
7 import com
.badlogic
.gdx
.math
.Rectangle
;
8 import com
.badlogic
.gdx
.math
.Vector2
;
9 import com
.badlogic
.gdx
.utils
.Timer
;
10 import ru
.deadsoftware
.cavedroid
.game
.GameItemsHolder
;
11 import ru
.deadsoftware
.cavedroid
.game
.model
.dto
.SaveDataDto
;
12 import ru
.deadsoftware
.cavedroid
.game
.model
.item
.InventoryItem
;
13 import ru
.deadsoftware
.cavedroid
.game
.model
.item
.Item
;
14 import ru
.deadsoftware
.cavedroid
.game
.world
.GameWorld
;
15 import ru
.deadsoftware
.cavedroid
.misc
.Saveable
;
17 import javax
.annotation
.CheckForNull
;
18 import java
.io
.Serializable
;
19 import java
.util
.Collections
;
20 import java
.util
.List
;
25 public abstract class Mob
extends Rectangle
implements Serializable
, Saveable
{
27 private static final float DAMAGE_TINT_TIMEOUT_S
= 0.5f;
28 private static final Color DAMAGE_TINT_COLOR
= new Color(0xff8080ff);
30 private static final float HIT_RANGE
= 8f;
32 protected static int ANIMATION_SPEED
= 360;
39 public enum Direction
{
44 private final int index
;
45 private final int basis
;
48 * Index for this direction (left = 0, right = 1)
50 public final int getIndex() {
55 * Basis for this direction (left = -1, right = 1)
57 public final int getBasis() {
61 Direction(int index
, int basis
) {
67 private class ResetTakeDamageTask
extends Timer
.Task
{
71 mTakingDamage
= false;
75 protected Vector2 mVelocity
;
77 protected int mAnimDelta
= ANIMATION_SPEED
;
78 protected float mAnim
;
80 protected Direction mDirection
;
81 protected boolean mDead
;
82 protected boolean mCanJump
;
83 protected boolean mFlyMode
;
85 protected int mMaxHealth
;
86 protected int mHealth
;
88 private transient boolean mTakingDamage
= false;
89 @CheckForNull private transient ResetTakeDamageTask mResetTakeDamageTask
= null;
94 * @param width in pixels
95 * @param height in pixels
96 * @param mDirection Direction in which mob is looking
98 protected Mob(float x
, float y
, float width
, float height
, Direction mDirection
, Type type
, int maxHealth
) {
99 super(x
, y
, width
, height
);
100 mVelocity
= new Vector2(0, 0);
103 this.mDirection
= mDirection
;
105 this.mMaxHealth
= maxHealth
;
106 this.mHealth
= mMaxHealth
;
109 protected static Direction
randomDir() {
110 return MathUtils
.randomBoolean(.5f) ? Direction
.LEFT
: Direction
.RIGHT
;
113 private boolean isAnimationIncreasing() {
114 return mAnim
> 0 && mAnimDelta
> 0 || mAnim
< 0 && mAnimDelta
< 0;
117 private void checkHealth() {
118 mHealth
= MathUtils
.clamp(mHealth
, 0, mMaxHealth
);
125 protected final void updateAnimation(float delta
) {
126 final float velocityMultiplier
= (Math
.abs(getVelocity().x
) / getSpeed());
127 final float animMultiplier
= (velocityMultiplier
== 0f ?
1f : velocityMultiplier
) * delta
;
128 final float maxAnim
= 60f * (velocityMultiplier
== 0f ?
1f : velocityMultiplier
);
130 if (mVelocity
.x
!= 0f || Math
.abs(mAnim
) > mAnimDelta
* animMultiplier
) {
131 mAnim
+= mAnimDelta
* animMultiplier
;
136 if (mAnim
> maxAnim
) {
138 mAnimDelta
= -ANIMATION_SPEED
;
139 } else if (mAnim
< -maxAnim
) {
141 mAnimDelta
= ANIMATION_SPEED
;
144 if (mVelocity
.x
== 0f && isAnimationIncreasing()) {
145 mAnimDelta
= -mAnimDelta
;
150 * @return The X coordinate of a mob in blocks
152 public final int getMapX() {
153 return (int) (x
+ (getWidth() / 2)) / 16;
157 * @return The Y coordinate of mob's upper edge in blocks
159 public final int getUpperMapY() {
160 return (int) (y
/ 16);
164 * @return The Y coordinate if mob's vertical center in blocks
166 public final int getMiddleMapY() {
167 return (int) (y
+ (getHeight() / 2)) / 16;
171 * @return The Y coordinate of mob's legs in blocks
173 public final int getLowerMapY() {
174 return (int) (y
+ getHeight()) / 16;
177 public final float getWidth() {
181 public final float getHeight() {
186 * @return Integer representing a direction in which mob is looking, where 0 is left and 1 is right
188 public final Direction
getDirection() {
192 public final boolean looksLeft() {
193 return mDirection
== Direction
.LEFT
;
196 public final boolean looksRight() {
197 return mDirection
== Direction
.RIGHT
;
201 * Switches direction in which mob is looking
203 protected final void switchDir() {
204 mDirection
= looksLeft() ? Direction
.RIGHT
: Direction
.LEFT
;
207 public final boolean isDead() {
211 public final float getAnim() {
216 * Set's mob's dead variable to true and nothing else. It doesn't delete the
222 public final void move(float delta
) {
223 x
+= mVelocity
.x
* delta
;
224 y
+= mVelocity
.y
* delta
;
227 public final Vector2
getVelocity() {
231 protected final void setVelocity(Vector2 velocity
) {
232 mVelocity
= velocity
;
235 public final boolean canJump() {
239 public final void setCanJump(boolean canJump
) {
240 this.mCanJump
= canJump
;
243 public final boolean isFlyMode() {
247 public final void setFlyMode(boolean flyMode
) {
248 this.mFlyMode
= flyMode
;
251 public final Type
getType() {
255 public final void checkWorldBounds(GameWorld gameWorld
) {
256 if (x
+ width
/ 2 < 0) {
257 x
+= gameWorld
.getWidthPx();
259 if (x
+ width
/ 2 > gameWorld
.getWidthPx()) {
260 x
-= gameWorld
.getWidthPx();
264 public final int getHealth() {
268 public final int getMaxHealth() {
272 public final void attachToController(MobsController controller
) {
273 controller
.addMob(this);
276 public void damage(int damage
) {
282 Gdx
.app
.error(this.getClass().getSimpleName(), "Damage cant be negative!");
286 if (mHealth
<= Integer
.MIN_VALUE
+ damage
) {
287 mHealth
= Integer
.MIN_VALUE
+ damage
;
293 setTakingDamage(true);
296 public void heal(int heal
) {
298 Gdx
.app
.error(this.getClass().getSimpleName(), "Heal cant be negative!");
302 if (mHealth
>= Integer
.MAX_VALUE
- heal
) {
303 mHealth
= Integer
.MAX_VALUE
- heal
;
310 public Rectangle
getHitBox() {
311 return new Rectangle(x
- HIT_RANGE
, y
- HIT_RANGE
, width
+ HIT_RANGE
, height
+ HIT_RANGE
);
314 public boolean isTakingDamage() {
315 return mTakingDamage
;
318 public void setTakingDamage(boolean takingDamage
) {
319 mTakingDamage
= takingDamage
;
322 if (mResetTakeDamageTask
!= null && mResetTakeDamageTask
.isScheduled()) {
323 mResetTakeDamageTask
.cancel();
324 } else if (mResetTakeDamageTask
== null) {
325 mResetTakeDamageTask
= new ResetTakeDamageTask();
328 Timer
.schedule(mResetTakeDamageTask
, DAMAGE_TINT_TIMEOUT_S
);
332 protected Color
getTintColor() {
333 return isTakingDamage() ? DAMAGE_TINT_COLOR
: Color
.WHITE
;
336 public List
<InventoryItem
> getDrop(GameItemsHolder gameItemsHolder
) {
337 return Collections
.emptyList();
340 public abstract void draw(SpriteBatch spriteBatch
, float x
, float y
, float delta
);
342 public abstract void ai(GameWorld gameWorld
, GameItemsHolder gameItemsHolder
, MobsController mobsController
, float delta
);
344 public abstract void changeDir();
346 public abstract float getSpeed();
348 public abstract void jump();
351 public abstract SaveDataDto
.MobSaveDataDto
getSaveData();
353 public static Mob
fromSaveData(SaveDataDto
.MobSaveDataDto saveData
) {
354 return MobSaveDataMapperKt
.fromSaveData(saveData
);