DEADSOFTWARE

Add mobs damage and initial spawn
[cavedroid.git] / core / src / ru / deadsoftware / cavedroid / game / mobs / Mob.java
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.world.GameWorld;
13 import javax.annotation.CheckForNull;
14 import java.io.Serializable;
16 /**
17 * Mob class.
18 */
19 public abstract class Mob extends Rectangle implements Serializable {
21 private static final float DAMAGE_TINT_TIMEOUT_S = 0.5f;
22 private static final Color DAMAGE_TINT_COLOR = new Color(0xff8080ff);
24 private static final float HIT_RANGE = 8f;
26 protected static int ANIMATION_SPEED = 360;
28 public enum Type {
29 MOB,
30 SAND,
31 GRAVEL
32 }
34 public enum Direction {
36 LEFT(0, -1),
37 RIGHT(1, 1);
39 private final int index;
40 private final int basis;
42 /**
43 * Index for this direction (left = 0, right = 1)
44 */
45 public final int getIndex() {
46 return index;
47 }
49 /**
50 * Basis for this direction (left = -1, right = 1)
51 */
52 public final int getBasis() {
53 return basis;
54 }
56 Direction(int index, int basis) {
57 this.index = index;
58 this.basis = basis;
59 }
60 }
62 private class ResetTakeDamageTask extends Timer.Task {
64 @Override
65 public void run() {
66 mTakingDamage = false;
67 }
68 }
70 protected Vector2 mVelocity;
71 protected Type mType;
72 protected int mAnimDelta = ANIMATION_SPEED;
73 protected float mAnim;
75 private Direction mDirection;
76 protected boolean mDead;
77 private boolean mCanJump;
78 private boolean mFlyMode;
80 private final int mMaxHealth;
81 private int mHealth;
83 private transient boolean mTakingDamage = false;
84 @CheckForNull private transient ResetTakeDamageTask mResetTakeDamageTask = null;
86 /**
87 * @param x in pixels
88 * @param y in pixels
89 * @param width in pixels
90 * @param height in pixels
91 * @param mDirection Direction in which mob is looking
92 */
93 protected Mob(float x, float y, float width, float height, Direction mDirection, Type type, int maxHealth) {
94 super(x, y, width, height);
95 mVelocity = new Vector2(0, 0);
96 mCanJump = false;
97 mDead = false;
98 this.mDirection = mDirection;
99 this.mType = type;
100 this.mMaxHealth = maxHealth;
101 this.mHealth = mMaxHealth;
104 protected static Direction randomDir() {
105 return MathUtils.randomBoolean(.5f) ? Direction.LEFT : Direction.RIGHT;
108 private boolean isAnimationIncreasing() {
109 return mAnim > 0 && mAnimDelta > 0 || mAnim < 0 && mAnimDelta < 0;
112 private void checkHealth() {
113 mHealth = MathUtils.clamp(mHealth, 0, mMaxHealth);
115 if (mHealth <= 0) {
116 kill();
120 protected final void updateAnimation(float delta) {
121 final float velocityMultiplier = (Math.abs(getVelocity().x) / getSpeed());
122 final float animMultiplier = (velocityMultiplier == 0f ? 1f : velocityMultiplier) * delta;
123 final float maxAnim = 60f * (velocityMultiplier == 0f ? 1f : velocityMultiplier);
125 if (mVelocity.x != 0f || Math.abs(mAnim) > mAnimDelta * animMultiplier) {
126 mAnim += mAnimDelta * animMultiplier;
127 } else {
128 mAnim = 0;
131 if (mAnim > maxAnim) {
132 mAnim = maxAnim;
133 mAnimDelta = -ANIMATION_SPEED;
134 } else if (mAnim < -maxAnim) {
135 mAnim = -maxAnim;
136 mAnimDelta = ANIMATION_SPEED;
139 if (mVelocity.x == 0f && isAnimationIncreasing()) {
140 mAnimDelta = -mAnimDelta;
144 /**
145 * @return The X coordinate of a mob in blocks
146 */
147 public final int getMapX() {
148 return (int) (x + (getWidth() / 2)) / 16;
151 /**
152 * @return The Y coordinate of mob's upper edge in blocks
153 */
154 public final int getUpperMapY() {
155 return (int) (y / 16);
158 /**
159 * @return The Y coordinate if mob's vertical center in blocks
160 */
161 public final int getMiddleMapY() {
162 return (int) (y + (getHeight() / 2)) / 16;
165 /**
166 * @return The Y coordinate of mob's legs in blocks
167 */
168 public final int getLowerMapY() {
169 return (int) (y + getHeight()) / 16;
172 public final float getWidth() {
173 return width;
176 public final float getHeight() {
177 return height;
180 /**
181 * @return Integer representing a direction in which mob is looking, where 0 is left and 1 is right
182 */
183 public final Direction getDirection() {
184 return mDirection;
187 public final boolean looksLeft() {
188 return mDirection == Direction.LEFT;
191 public final boolean looksRight() {
192 return mDirection == Direction.RIGHT;
195 /**
196 * Switches direction in which mob is looking
197 */
198 protected final void switchDir() {
199 mDirection = looksLeft() ? Direction.RIGHT : Direction.LEFT;
202 public final boolean isDead() {
203 return mDead;
206 public final float getAnim() {
207 return mAnim;
210 /**
211 * Set's mob's dead variable to true and nothing else. It doesn't delete the
212 */
213 public final void kill() {
214 mDead = true;
217 public final void move(float delta) {
218 x += mVelocity.x * delta;
219 y += mVelocity.y * delta;
222 public final Vector2 getVelocity() {
223 return mVelocity;
226 protected final void setVelocity(Vector2 velocity) {
227 mVelocity = velocity;
230 public final boolean canJump() {
231 return mCanJump;
234 public final void setCanJump(boolean canJump) {
235 this.mCanJump = canJump;
238 public final boolean isFlyMode() {
239 return mFlyMode;
242 public final void setFlyMode(boolean flyMode) {
243 this.mFlyMode = flyMode;
246 public final Type getType() {
247 return mType;
250 public final void checkWorldBounds(GameWorld gameWorld) {
251 if (x + width / 2 < 0) {
252 x += gameWorld.getWidthPx();
254 if (x + width / 2 > gameWorld.getWidthPx()) {
255 x -= gameWorld.getWidthPx();
259 public final int getHealth() {
260 return mHealth;
263 public final void attachToController(MobsController controller) {
264 controller.addMob(this);
267 public void damage(int damage) {
268 if (damage == 0) {
269 return;
272 if (damage < 0) {
273 Gdx.app.error(this.getClass().getSimpleName(), "Damage cant be negative!");
274 return;
277 if (mHealth <= Integer.MIN_VALUE + damage) {
278 mHealth = Integer.MIN_VALUE + damage;
281 mHealth -= damage;
282 checkHealth();
284 setTakingDamage(true);
287 public void heal(int heal) {
288 if (heal < 0) {
289 Gdx.app.error(this.getClass().getSimpleName(), "Heal cant be negative!");
290 return;
293 if (mHealth >= Integer.MAX_VALUE - heal) {
294 mHealth = Integer.MAX_VALUE - heal;
297 mHealth += heal;
298 checkHealth();
301 public Rectangle getHitBox() {
302 return new Rectangle(x - HIT_RANGE, y - HIT_RANGE, width + HIT_RANGE, height + HIT_RANGE);
305 public boolean isTakingDamage() {
306 return mTakingDamage;
309 public void setTakingDamage(boolean takingDamage) {
310 mTakingDamage = takingDamage;
312 if (takingDamage) {
313 if (mResetTakeDamageTask != null && mResetTakeDamageTask.isScheduled()) {
314 mResetTakeDamageTask.cancel();
315 } else if (mResetTakeDamageTask == null) {
316 mResetTakeDamageTask = new ResetTakeDamageTask();
319 Timer.schedule(mResetTakeDamageTask, DAMAGE_TINT_TIMEOUT_S);
323 protected Color getTintColor() {
324 return isTakingDamage() ? DAMAGE_TINT_COLOR : Color.WHITE;
327 public abstract void draw(SpriteBatch spriteBatch, float x, float y, float delta);
329 public abstract void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, MobsController mobsController, float delta);
331 public abstract void changeDir();
333 public abstract float getSpeed();
335 public abstract void jump();