DEADSOFTWARE

04fa1bbe91f8b88ca6879094dbf343f5dbbef068
[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 FALLING_BLOCK
31 }
33 public enum Direction {
35 LEFT(0, -1),
36 RIGHT(1, 1);
38 private final int index;
39 private final int basis;
41 /**
42 * Index for this direction (left = 0, right = 1)
43 */
44 public final int getIndex() {
45 return index;
46 }
48 /**
49 * Basis for this direction (left = -1, right = 1)
50 */
51 public final int getBasis() {
52 return basis;
53 }
55 Direction(int index, int basis) {
56 this.index = index;
57 this.basis = basis;
58 }
59 }
61 private class ResetTakeDamageTask extends Timer.Task {
63 @Override
64 public void run() {
65 mTakingDamage = false;
66 }
67 }
69 protected Vector2 mVelocity;
70 protected Type mType;
71 protected int mAnimDelta = ANIMATION_SPEED;
72 protected float mAnim;
74 private Direction mDirection;
75 protected boolean mDead;
76 private boolean mCanJump;
77 private boolean mFlyMode;
79 private final int mMaxHealth;
80 private int mHealth;
82 private transient boolean mTakingDamage = false;
83 @CheckForNull private transient ResetTakeDamageTask mResetTakeDamageTask = null;
85 /**
86 * @param x in pixels
87 * @param y in pixels
88 * @param width in pixels
89 * @param height in pixels
90 * @param mDirection Direction in which mob is looking
91 */
92 protected Mob(float x, float y, float width, float height, Direction mDirection, Type type, int maxHealth) {
93 super(x, y, width, height);
94 mVelocity = new Vector2(0, 0);
95 mCanJump = false;
96 mDead = false;
97 this.mDirection = mDirection;
98 this.mType = type;
99 this.mMaxHealth = maxHealth;
100 this.mHealth = mMaxHealth;
103 protected static Direction randomDir() {
104 return MathUtils.randomBoolean(.5f) ? Direction.LEFT : Direction.RIGHT;
107 private boolean isAnimationIncreasing() {
108 return mAnim > 0 && mAnimDelta > 0 || mAnim < 0 && mAnimDelta < 0;
111 private void checkHealth() {
112 mHealth = MathUtils.clamp(mHealth, 0, mMaxHealth);
114 if (mHealth <= 0) {
115 kill();
119 protected final void updateAnimation(float delta) {
120 final float velocityMultiplier = (Math.abs(getVelocity().x) / getSpeed());
121 final float animMultiplier = (velocityMultiplier == 0f ? 1f : velocityMultiplier) * delta;
122 final float maxAnim = 60f * (velocityMultiplier == 0f ? 1f : velocityMultiplier);
124 if (mVelocity.x != 0f || Math.abs(mAnim) > mAnimDelta * animMultiplier) {
125 mAnim += mAnimDelta * animMultiplier;
126 } else {
127 mAnim = 0;
130 if (mAnim > maxAnim) {
131 mAnim = maxAnim;
132 mAnimDelta = -ANIMATION_SPEED;
133 } else if (mAnim < -maxAnim) {
134 mAnim = -maxAnim;
135 mAnimDelta = ANIMATION_SPEED;
138 if (mVelocity.x == 0f && isAnimationIncreasing()) {
139 mAnimDelta = -mAnimDelta;
143 /**
144 * @return The X coordinate of a mob in blocks
145 */
146 public final int getMapX() {
147 return (int) (x + (getWidth() / 2)) / 16;
150 /**
151 * @return The Y coordinate of mob's upper edge in blocks
152 */
153 public final int getUpperMapY() {
154 return (int) (y / 16);
157 /**
158 * @return The Y coordinate if mob's vertical center in blocks
159 */
160 public final int getMiddleMapY() {
161 return (int) (y + (getHeight() / 2)) / 16;
164 /**
165 * @return The Y coordinate of mob's legs in blocks
166 */
167 public final int getLowerMapY() {
168 return (int) (y + getHeight()) / 16;
171 public final float getWidth() {
172 return width;
175 public final float getHeight() {
176 return height;
179 /**
180 * @return Integer representing a direction in which mob is looking, where 0 is left and 1 is right
181 */
182 public final Direction getDirection() {
183 return mDirection;
186 public final boolean looksLeft() {
187 return mDirection == Direction.LEFT;
190 public final boolean looksRight() {
191 return mDirection == Direction.RIGHT;
194 /**
195 * Switches direction in which mob is looking
196 */
197 protected final void switchDir() {
198 mDirection = looksLeft() ? Direction.RIGHT : Direction.LEFT;
201 public final boolean isDead() {
202 return mDead;
205 public final float getAnim() {
206 return mAnim;
209 /**
210 * Set's mob's dead variable to true and nothing else. It doesn't delete the
211 */
212 public final void kill() {
213 mDead = true;
216 public final void move(float delta) {
217 x += mVelocity.x * delta;
218 y += mVelocity.y * delta;
221 public final Vector2 getVelocity() {
222 return mVelocity;
225 protected final void setVelocity(Vector2 velocity) {
226 mVelocity = velocity;
229 public final boolean canJump() {
230 return mCanJump;
233 public final void setCanJump(boolean canJump) {
234 this.mCanJump = canJump;
237 public final boolean isFlyMode() {
238 return mFlyMode;
241 public final void setFlyMode(boolean flyMode) {
242 this.mFlyMode = flyMode;
245 public final Type getType() {
246 return mType;
249 public final void checkWorldBounds(GameWorld gameWorld) {
250 if (x + width / 2 < 0) {
251 x += gameWorld.getWidthPx();
253 if (x + width / 2 > gameWorld.getWidthPx()) {
254 x -= gameWorld.getWidthPx();
258 public final int getHealth() {
259 return mHealth;
262 public final void attachToController(MobsController controller) {
263 controller.addMob(this);
266 public void damage(int damage) {
267 if (damage == 0) {
268 return;
271 if (damage < 0) {
272 Gdx.app.error(this.getClass().getSimpleName(), "Damage cant be negative!");
273 return;
276 if (mHealth <= Integer.MIN_VALUE + damage) {
277 mHealth = Integer.MIN_VALUE + damage;
280 mHealth -= damage;
281 checkHealth();
283 setTakingDamage(true);
286 public void heal(int heal) {
287 if (heal < 0) {
288 Gdx.app.error(this.getClass().getSimpleName(), "Heal cant be negative!");
289 return;
292 if (mHealth >= Integer.MAX_VALUE - heal) {
293 mHealth = Integer.MAX_VALUE - heal;
296 mHealth += heal;
297 checkHealth();
300 public Rectangle getHitBox() {
301 return new Rectangle(x - HIT_RANGE, y - HIT_RANGE, width + HIT_RANGE, height + HIT_RANGE);
304 public boolean isTakingDamage() {
305 return mTakingDamage;
308 public void setTakingDamage(boolean takingDamage) {
309 mTakingDamage = takingDamage;
311 if (takingDamage) {
312 if (mResetTakeDamageTask != null && mResetTakeDamageTask.isScheduled()) {
313 mResetTakeDamageTask.cancel();
314 } else if (mResetTakeDamageTask == null) {
315 mResetTakeDamageTask = new ResetTakeDamageTask();
318 Timer.schedule(mResetTakeDamageTask, DAMAGE_TINT_TIMEOUT_S);
322 protected Color getTintColor() {
323 return isTakingDamage() ? DAMAGE_TINT_COLOR : Color.WHITE;
326 public abstract void draw(SpriteBatch spriteBatch, float x, float y, float delta);
328 public abstract void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, MobsController mobsController, float delta);
330 public abstract void changeDir();
332 public abstract float getSpeed();
334 public abstract void jump();