package ru.vagrant_ai.questionmarkgame.obj.mob;
import java.util.Random;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.geom.Point;
import org.newdawn.slick.geom.Rectangle;
import ru.vagrant_ai.questionmarkgame.main.Game;
import ru.vagrant_ai.questionmarkgame.main.GameplayState;
import ru.vagrant_ai.questionmarkgame.obj.Elements;
import ru.vagrant_ai.questionmarkgame.util.Particle;
import ru.vagrant_ai.questionmarkgame.util.list.ITEM;
import ru.vagrant_ai.questionmarkgame.util.list.ML;
import ru.vagrant_ai.questionmarkgame.util.list.MS;
import ru.vagrant_ai.questionmarkgame.util.list.PT;
public class Monster {
/**
* Monster special options:
* Knockback
* onDamage(int power)
* onSpecialDamage(int power)
*/
/* CONSTRUCTOR ZONE */
protected short ground_level = GameplayState.ground_level+45;
public Rectangle hitbox;
public Rectangle hitbox_crit;
public Point center;
protected ML id;
protected short hp;
protected short hp_default;
protected short power;
protected float y;
protected float x;
protected short height;
protected short width;
protected float speed;
public MS state;
protected boolean state_damaged;
protected boolean gravity_rule = true; //true, if monster affected by impulse methods
protected boolean crit_death = false;
public boolean opt_knockback = true; //true if monster have knockback upon damage
protected short poison_sec;
private short poison_frame;
private byte poison_damage;
protected final void performDefault()
{
hp = hp_default;
x = Game.getAppX()+30+(new Random().nextInt(20));
hitbox = new Rectangle(x-width, y-height, width, height); //setting hitbox
hitbox_crit = new Rectangle(x-width+7, y-height+2, 9, 9);
center = new Point(x-(float)width/2, y+(float)height/2);
updateFacing();
}
/* UPDATE ZONE */
boolean direction;
float speed_scale = 1;
public void update()
{
perform();
checkUtil();
checkPlayerDamaged();
checkDamaged();
if (gravity_rule) checkFalling();
}
protected void perform()
{
}
protected void checkUtil()
{
center.setLocation(x-(float)width/2, y-(float)height/2); //set center point
if (poison_sec > 0) //set poison dmg
{
poison_frame--;
if (poison_frame < 0)
{
poison_sec--;
poison_frame = (short) Game.app.getFPS();
onDamage(poison_damage, false, false);
}
}
}
protected void checkPlayerDamaged()
{
if (hitbox.intersects(GameplayState.player.hitbox) && state != MS.DEAD && state != MS.NULL)
{
GameplayState.player.addDamage(power);
onAttackSuccess();
}
}
private float util_special_impulse = 0;
private float util_special_rand = 0;
private int knockback_x = 0;
protected void checkDamaged()
{
if (util_special_impulse != 0 && state != MS.DEAD && state != MS.NULL)
{
x += ((id == ML.KHATUM?1.28f:1.7f)+(id == ML.EEL?0.6f:0)+util_special_rand) * (knockback_x>=center.getX()?-1:1);
y -= util_special_impulse;
util_special_impulse -= 0.3f+util_special_rand;
if (util_special_impulse == 0)
util_special_impulse -= 0.00001f;
if (y >= ground_level)
{
y = ground_level;
util_special_impulse = 0;
knockback_x = 0;
state = MS.WALK;
}
}
}
private void checkFalling()
{
if (state == MS.WALK && (util_velo_x > 0 || util_velo_y > 0)
&& id != ML.WALDO3 //WALDO3 exception
&& state != MS.NULL && state != MS.DEAD
)
state = MS.STOP;
if (util_velo_x > 0)
{
x += util_velo_x + util_impulse_direction;
util_velo_x -= 0.5f;
if (util_velo_x <= 0)
{
util_velo_x = 0;
if (util_velo_y <= 0 && state != MS.NULL && state != MS.DEAD)
state = MS.WALK;
}
}
if (util_velo_y > 0 || (y < ground_level
&& state != MS.JUMP //WALDO3 exception
&& util_special_impulse == 0 //Thorny and poison cloud bonus exception
&& state != MS.DEAD && state != MS.NULL
)) //if jumping
{
y -= util_velo_y;
util_velo_y -= 0.5f;
if (y >= ground_level) //checking, if monster reached ground level
{
y = ground_level;
util_velo_y = 0;
util_velo_x = 0;
state = MS.WALK;
}
}
}
/* RENDER ZONE */
public void render(Graphics g)
{
renderMain(g);
if (state != MS.DEAD && state != MS.NULL) renderHP(g);
g.setColor(Color.red);
g.setLineWidth(1);
}
protected void renderMain(Graphics g)
{
}
protected void renderHP(Graphics g)
{
g.setColor(new Color(185, 80, 80));
g.setLineWidth(1);
g.drawRect(x-width-(36-width)/2, y-height-15, 36, 4);
g.setColor(Color.red);
g.fillRect(x-width-(36-width)/2+1, y-height-14, (float)35/hp_default*hp, 3);
}
/* EXTERNAL-USE METHDOS */
public void onDamage(int power, boolean crit)
{
onDamage(power, true, crit);
}
public void onDamage(int power, boolean knockback, boolean crit)
{
if (hp < 1 || state == MS.DEAD) return;
if (id == ML.EEL && state == MS.JUMP)
state = MS.WALK;
hp -= power;
if (hp < 1)
{
crit_death = crit;
state = MS.DEAD;
if (Elements.kamikaze)
Elements.addExplosion((int)center.getX(), (int)center.getY());
if (poison_sec > 0)
{
if (GameplayState.player.util_poison_cloud && Elements.extractLevel(ITEM.P_GUN_POISON_BULLET)+Elements.extractLevel(ITEM.P_UPG_POISON_CLOUD) >= 2)
Particle.addNew(PT.POISON_CLOUD, (int) center.getX(), (int) center.getY());
for (int i = 0; i < 6; ++i)
Particle.addNew(PT.POISON_EFFECT, (short) (x-new Random().nextInt(width)), (short) (y-new Random().nextInt(height)));
}
for (int i = 0; i < id.getMoneyAmt(); ++i)
Elements.addMoney((int)center.getX(), (int)center.getY(), (byte) 1);
}
else if (knockback)
{
if (id == ML.KHATUM) addImpulse(1,1);
else if (id == ML.EEL) addImpulse(1.9f,1.9f);
else addImpulse(1.7f,1.7f);
}
}
public void onSpecialDamage(float power, int hp, int knockback_source)
{
if (state == MS.DEAD) return;
knockback_x = knockback_source;
util_velo_y = 0; //nullify current falling state
if (state != MS.DEAD && state != MS.NULL) state = MS.STOP; //prevent monster from moving
util_special_impulse = 3+power; //add special knockback
onDamage(hp, true, false); //add damage
float rand = (float)new Random().nextInt(100)+50;
util_special_rand = 10f/rand; //random factor
}
public void addPoison(byte sec, byte damage)
{
if (poison_sec > 0 && damage < poison_damage)
poison_sec += (sec*damage)/poison_damage;
else
{
poison_sec = sec;
poison_damage = damage;
poison_frame = (short) Game.app.getFPS();
onDamage(damage, false, false);
}
}
protected void onAttackSuccess()
{
}
/* UTIL */
private float util_velo_x, util_velo_y;
private float util_impulse_direction;
public void addImpulse(float impulse_x, float impulse_y)
{
if (state == MS.DEAD) return;
util_velo_x = impulse_x;
util_impulse_direction = (GameplayState.player.pl_center.getX()>=center.getX()?-1:1) * (1.2f/(float)Game.getAppX()*getDistance());
util_velo_y = impulse_y;
}
public float getDistance()
{
float distance = GameplayState.player.pl_center.getX()-center.getX();
if (distance < 0) distance *= -1;
return distance;
}
/**
* Sets direction to true if monster must go left or to false if monster must go right
*/
public void updateFacing()
{
if (GameplayState.player.pl_center.getX() >= center.getX())
direction = false;
else
direction = true;
}
/**
* Returns true if current facing is ok, returns false otherwise
*/
public boolean checkFacing()
{
if ((direction == false && GameplayState.player.pl_center.getX() >= center.getX()) || (direction == true && GameplayState.player.pl_center.getX() < center.getX())) return true;
else return false;
}
/**
* Monster instant silent suicide
* @param rule 0 - monster suicide w/ dying anim, rule 1 - monster suicide w/o dying anim
*/
public void Suicide(int rule)
{
hp = 0;
if (rule == 0) state = MS.NULL;
}
public boolean getDirection()
{
return direction;
}
public ML getId()
{
return id;
}
public float getX()
{
return x;
}
protected void drawImage(Image img, short x, short y)
{
if (poison_sec > 0)
img.setImageColor(0, 255, 0);
img.draw(x, y);
}
}