Initial commit
This commit is contained in:
235
Java/Microcraft/src/com/jdh/microcraft/entity/Entity.java
Normal file
235
Java/Microcraft/src/com/jdh/microcraft/entity/Entity.java
Normal file
@@ -0,0 +1,235 @@
|
||||
package com.jdh.microcraft.entity;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.ai.AI;
|
||||
import com.jdh.microcraft.gfx.Light;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
import com.jdh.microcraft.level.LevelEntityMetadata;
|
||||
import com.jdh.microcraft.level.tile.Tile;
|
||||
import com.jdh.microcraft.level.tile.TileLiquid;
|
||||
import com.jdh.microcraft.level.tile.TileStair;
|
||||
import com.jdh.microcraft.util.Direction;
|
||||
import com.jdh.microcraft.util.FMath;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Entity {
|
||||
// current location
|
||||
public int x, y;
|
||||
|
||||
// size
|
||||
public int width, height;
|
||||
|
||||
// level this entity belongs to
|
||||
public Level level;
|
||||
|
||||
// set to true on ticks where entity moves
|
||||
public boolean moving;
|
||||
|
||||
// set to true if the entity is in a liquid
|
||||
public boolean swimming;
|
||||
|
||||
// true if the entity is currently on stairs (onStairs) or has entered stairs this tick (onStairsTick)
|
||||
public boolean onStairs;
|
||||
|
||||
// metadata about this entity, tracked by the current level
|
||||
public final LevelEntityMetadata metadata;
|
||||
|
||||
// current tile locations, managed by Level
|
||||
// location of CENTER of entity
|
||||
public int tileX = -1, tileY = -1;
|
||||
|
||||
// unique entity id
|
||||
public final int id;
|
||||
|
||||
// entity AI, optional
|
||||
public AI ai;
|
||||
|
||||
// current direction and direction last tick
|
||||
protected Direction direction = Direction.DOWN;
|
||||
private Direction lastDirection;
|
||||
|
||||
// positions at the LAST tick
|
||||
private int lastX, lastY;
|
||||
|
||||
// knockback velocity
|
||||
private double kx, ky;
|
||||
|
||||
public Entity(Level level) {
|
||||
this.level = level;
|
||||
this.id = Global.game.getNextEntityId();
|
||||
this.width = 6;
|
||||
this.height = 6;
|
||||
this.metadata = new LevelEntityMetadata(this);
|
||||
}
|
||||
|
||||
public Direction getDirection() {
|
||||
return this.direction;
|
||||
}
|
||||
|
||||
public Light getLight() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
this.metadata.remove = true;
|
||||
}
|
||||
|
||||
// schedules this entity to be moved to another level on the next tick
|
||||
public void moveLevel(int depth, int x, int y) {
|
||||
this.metadata.moveLevel(depth, x, y);
|
||||
}
|
||||
|
||||
// called when this entity moves between levels
|
||||
public void onDepthChange(int prevDepth) {
|
||||
|
||||
}
|
||||
|
||||
public void knockback(double strength, Direction d) {
|
||||
this.knockback(strength, d.x, d.y);
|
||||
}
|
||||
|
||||
public void knockback(double strength, int x, int y) {
|
||||
this.kx = Math.signum(x) * strength;
|
||||
this.ky = Math.signum(y) * strength;
|
||||
}
|
||||
|
||||
public int getRenderOffsetX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getRenderOffsetY() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected void onDirectionChange() {
|
||||
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
this.moving = this.lastX != this.x || this.lastY != this.y;
|
||||
|
||||
int dx = this.x - this.lastX, dy = this.y - this.lastY;
|
||||
if (dx != 0 || dy != 0) {
|
||||
this.direction = Direction.get(dx, dy);
|
||||
}
|
||||
|
||||
if (this.direction != this.lastDirection) {
|
||||
this.onDirectionChange();
|
||||
}
|
||||
|
||||
this.lastDirection = this.direction;
|
||||
|
||||
this.lastX = this.x;
|
||||
this.lastY = this.y;
|
||||
this.swimming = Tile.TILES[this.level.getTile(this.tileX, this.tileY)] instanceof TileLiquid;
|
||||
this.onStairs = Tile.TILES[this.level.getTile(this.tileX, this.tileY)] instanceof TileStair;
|
||||
|
||||
// apply knockback
|
||||
this.move(
|
||||
FMath.tickedDoubleToInt(Global.ticks, this.kx),
|
||||
FMath.tickedDoubleToInt(Global.ticks, this.ky)
|
||||
);
|
||||
this.kx *= 0.85;
|
||||
this.ky *= 0.85;
|
||||
|
||||
if (this.ai != null) {
|
||||
this.ai.tick();
|
||||
}
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if (this.ai != null) {
|
||||
this.ai.update();
|
||||
}
|
||||
}
|
||||
|
||||
public void render() {
|
||||
|
||||
}
|
||||
|
||||
public boolean collides(Entity e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void collide(Entity e) {
|
||||
|
||||
}
|
||||
|
||||
// called if THIS entity hits E
|
||||
public boolean hit(Entity e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// called when E hits THIS entity
|
||||
public void onHit(Entity e) {
|
||||
|
||||
}
|
||||
|
||||
public boolean interact(Entity e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void move(int dx, int dy) {
|
||||
this.moveAxis(dx, 0);
|
||||
this.moveAxis(0, dy);
|
||||
}
|
||||
|
||||
// moves along a single axis
|
||||
protected boolean moveAxis(int dx, int dy) {
|
||||
assert(dx == 0 || dy == 0);
|
||||
int ox = this.x, oy = this.y;
|
||||
this.x += dx;
|
||||
this.y += dy;
|
||||
|
||||
// deny any movement that would move this entity off of the map
|
||||
if (this.x < 0 || this.y < 0 ||
|
||||
(this.x + this.width) >= Level.toPixel(this.level.width) ||
|
||||
(this.y + this.height >= Level.toPixel(this.level.height))) {
|
||||
this.x = ox;
|
||||
this.y = oy;
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Entity> entities = level.getEntityCollisions(this);
|
||||
for (Entity e : entities) {
|
||||
if (e != this && this.collides(e) && e.collides(this)) {
|
||||
this.collide(e);
|
||||
e.collide(this);
|
||||
|
||||
// deny movement if it is TOWARDS the colliding entity
|
||||
if ((dx != 0 && FMath.sameSign(dx, e.x - this.x)) ||
|
||||
(dy != 0 && FMath.sameSign(dy, e.y - this.y))) {
|
||||
this.x = ox;
|
||||
this.y = oy;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int[] p : level.getTileCollisions(this)) {
|
||||
Tile t = Tile.TILES[level.getTile(p[0], p[1])];
|
||||
t.bump(level, p[0], p[1], this);
|
||||
|
||||
if (t.collides(level, p[0], p[1], this)) {
|
||||
this.x = ox;
|
||||
this.y = oy;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean canSwimIn(Tile tile, int x, int y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getCenterX() {
|
||||
return this.x + (this.width / 2);
|
||||
}
|
||||
|
||||
public int getCenterY() {
|
||||
return this.y + (this.height / 2);
|
||||
}
|
||||
}
|
||||
115
Java/Microcraft/src/com/jdh/microcraft/entity/EntityItem.java
Normal file
115
Java/Microcraft/src/com/jdh/microcraft/entity/EntityItem.java
Normal file
@@ -0,0 +1,115 @@
|
||||
package com.jdh.microcraft.entity;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.mob.EntityMob;
|
||||
import com.jdh.microcraft.entity.projectile.EntityProjectile;
|
||||
import com.jdh.microcraft.item.ItemStack;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
import com.jdh.microcraft.util.Direction;
|
||||
import com.jdh.microcraft.util.Time;
|
||||
|
||||
public class EntityItem extends Entity {
|
||||
public final ItemStack stack;
|
||||
public int timeToLive, timeToPickup;
|
||||
|
||||
private int z;
|
||||
private double vx, vy, vz, dx, dy;
|
||||
|
||||
public EntityItem(Level level, ItemStack stack, int x, int y, double vx, double vy, double vz) {
|
||||
super(level);
|
||||
this.stack = stack;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = 4;
|
||||
this.vx = vx;
|
||||
this.vy = vy;
|
||||
this.vz = vz;
|
||||
this.dx = 0;
|
||||
this.dy = 0;
|
||||
this.timeToLive = Time.TPS * (180 + Global.random.nextInt(60));
|
||||
this.timeToPickup = 20;
|
||||
this.width = 8;
|
||||
this.height = 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collides(Entity e) {
|
||||
return e instanceof EntityMob ?
|
||||
((EntityMob) e).canPickup(this) && this.timeToPickup == 0 :
|
||||
!(e instanceof EntityItem);
|
||||
}
|
||||
|
||||
public static EntityItem spawn(Level level, ItemStack stack, int x, int y, Direction d) {
|
||||
Global.random.setSeed(stack.hashCode() + (x * 31) ^ (y * 17));
|
||||
EntityItem result = new EntityItem(
|
||||
level, stack, x, y,
|
||||
d.x * (0.5 + Global.random.nextDouble() * 0.1),
|
||||
d.y * (0.5 + Global.random.nextDouble() * 0.1),
|
||||
1.0 + (Global.random.nextDouble() * 0.2)
|
||||
);
|
||||
level.addEntity(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static EntityItem spawn(Level level, ItemStack stack, int x, int y) {
|
||||
Global.random.setSeed(stack.hashCode() + (x * 31) ^ (y * 17));
|
||||
EntityItem result = new EntityItem(
|
||||
level, stack, x, y,
|
||||
(Global.random.nextBoolean() ? -1.0 : 1.0) * (0.3 + Global.random.nextDouble() * 0.1),
|
||||
(Global.random.nextBoolean() ? -1.0 : 1.0) * (0.3 + Global.random.nextDouble() * 0.1),
|
||||
1.0 + (Global.random.nextDouble() * 0.2)
|
||||
);
|
||||
level.addEntity(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean canPickup(Entity e) {
|
||||
return this.timeToPickup == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if (--this.timeToLive == 0) {
|
||||
this.remove();
|
||||
}
|
||||
|
||||
this.timeToPickup = Math.max(this.timeToPickup - 1, 0);
|
||||
|
||||
this.dx += this.vx;
|
||||
this.dy += this.vy;
|
||||
|
||||
while (Math.abs(this.dx) >= 1.0) {
|
||||
if (!this.moveAxis((int) Math.signum(this.dx), 0)) {
|
||||
this.vx *= -0.8;
|
||||
}
|
||||
this.dx += -Math.signum(this.dx);
|
||||
}
|
||||
|
||||
while (Math.abs(this.dy) >= 1.0) {
|
||||
if (!this.moveAxis(0, (int) Math.signum(this.dy))) {
|
||||
this.vy *= -0.8;
|
||||
}
|
||||
this.dy += -Math.signum(this.dy);
|
||||
}
|
||||
|
||||
this.z += this.vz;
|
||||
|
||||
// bounce
|
||||
if (this.z < 0) {
|
||||
this.z = 0;
|
||||
this.vx *= 0.4;
|
||||
this.vy *= 0.4;
|
||||
this.vz *= -0.6;
|
||||
}
|
||||
|
||||
// gravity
|
||||
this.vz -= 0.2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
this.stack.instance.item.render(this.stack.instance, this.level, this.x, this.y);
|
||||
}
|
||||
}
|
||||
289
Java/Microcraft/src/com/jdh/microcraft/entity/EntityPlayer.java
Normal file
289
Java/Microcraft/src/com/jdh/microcraft/entity/EntityPlayer.java
Normal file
@@ -0,0 +1,289 @@
|
||||
package com.jdh.microcraft.entity;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.mob.EntityHumanoid;
|
||||
import com.jdh.microcraft.entity.particle.EntitySmashParticle;
|
||||
import com.jdh.microcraft.gfx.Color;
|
||||
import com.jdh.microcraft.gfx.Light;
|
||||
import com.jdh.microcraft.gfx.Renderer;
|
||||
import com.jdh.microcraft.gui.PlayerInventoryMenu;
|
||||
import com.jdh.microcraft.gui.TransitionMenu;
|
||||
import com.jdh.microcraft.gui.crafting.InventoryCraftingMenu;
|
||||
import com.jdh.microcraft.item.Inventory;
|
||||
import com.jdh.microcraft.item.Item;
|
||||
import com.jdh.microcraft.item.ItemInstance;
|
||||
import com.jdh.microcraft.item.ItemStack;
|
||||
import com.jdh.microcraft.item.consumable.ItemConsumable;
|
||||
import com.jdh.microcraft.item.tool.ItemTool;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
import com.jdh.microcraft.level.tile.Tile;
|
||||
import com.jdh.microcraft.sound.Sound;
|
||||
import com.jdh.microcraft.util.ControlHandler;
|
||||
import com.jdh.microcraft.util.Direction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class EntityPlayer extends EntityHumanoid {
|
||||
// maintains a list of the previous N equipped items, used so when
|
||||
// this.equipped is null we can go back to the last existing item from this
|
||||
// list in the player's current inventory. fx.:
|
||||
// - player has sword equipped
|
||||
// - player picks up lantern, lantern is equipped
|
||||
// - player puts down lantern
|
||||
// - sword should be re-equipped
|
||||
private final List<Integer> lastEquippedItems;
|
||||
|
||||
private final int color;
|
||||
|
||||
public EntityPlayer(Level level, int color) {
|
||||
super(level, new Inventory(256));
|
||||
this.lastEquippedItems = new ArrayList<>();
|
||||
this.color = color;
|
||||
|
||||
ItemStack glove = new ItemStack(new ItemInstance(Item.GLOVE, 0), 1);
|
||||
this.inventory.add(glove);
|
||||
this.equipped = glove;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hurt(int amount, boolean ignoreArmor) {
|
||||
if (super.hurt(amount, ignoreArmor)) {
|
||||
Sound.PLAYER_HURT.play();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Light getLight() {
|
||||
Light parent = super.getLight();
|
||||
return parent != null ? parent : new Light(
|
||||
this.x + (this.width / 2),
|
||||
this.y + (this.height / 2),
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die() {
|
||||
super.die();
|
||||
EntitySmashParticle.spawn(
|
||||
this.level,
|
||||
this.getCenterX(), this.getCenterY(),
|
||||
this.getColor(),
|
||||
10, 30
|
||||
);
|
||||
Global.game.playerDiedTicks = Global.ticks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPickup(EntityItem e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pickup(ItemStack stack) {
|
||||
super.pickup(stack);
|
||||
Sound.PICKUP.play();
|
||||
Global.game.score++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDepthChange(int prevDepth) {
|
||||
super.onDepthChange(prevDepth);
|
||||
Global.game.setLevel(this.level.depth);
|
||||
Global.game.setMenu(new TransitionMenu());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
// track currently equipped item
|
||||
if (this.equipped != null &&
|
||||
(this.lastEquippedItems.size() == 0 ||
|
||||
this.lastEquippedItems.get(0) != this.equipped.instance.id)) {
|
||||
this.lastEquippedItems.add(0, this.equipped.instance.id);
|
||||
}
|
||||
|
||||
// if nothing is equipped, try to equip previously equipped items
|
||||
// if there are no previous items to equip, equip the POW GLOVE
|
||||
if (this.equipped == null) {
|
||||
while (this.lastEquippedItems.size() != 0 && this.equipped == null) {
|
||||
this.equipped = this.inventory.findByInstanceId(
|
||||
this.lastEquippedItems.remove(0));
|
||||
}
|
||||
|
||||
if (this.equipped == null) {
|
||||
this.equipped = this.inventory.find(Item.GLOVE);
|
||||
}
|
||||
}
|
||||
|
||||
// crafting menu
|
||||
if (ControlHandler.TOGGLE_CRAFTING.pressedTick()) {
|
||||
Global.game.setMenu(new InventoryCraftingMenu(this));
|
||||
return;
|
||||
}
|
||||
|
||||
// item dropping
|
||||
if (ControlHandler.DROP.pressedTick() &&
|
||||
this.equipped != null &&
|
||||
this.equipped.instance.item.isDroppable()) {
|
||||
this.drop(this.equipped);
|
||||
}
|
||||
|
||||
// get tile player is currently facing
|
||||
int fx = this.getFacingTileX(), fy = this.getFacingTileY();
|
||||
Tile ft = Tile.TILES[level.getTile(fx, fy)];
|
||||
|
||||
// entities in this tile and the tile being faced, excluding this one
|
||||
List<Entity> facingEntities = Stream.concat(
|
||||
level.getEntities(this.tileX, this.tileY).stream(),
|
||||
level.getEntities(fx, fy).stream()
|
||||
).filter(e -> e != this).collect(Collectors.toList());
|
||||
|
||||
if (ControlHandler.INTERACT.pressedTick()) {
|
||||
boolean interactEntity = false, interactTile = false;
|
||||
|
||||
if (!this.swimming) {
|
||||
// try to interact with facing entities
|
||||
for (Entity e : facingEntities) {
|
||||
interactEntity |= e.interact(this);
|
||||
}
|
||||
|
||||
// interact with facing tile if no entities were interacted with
|
||||
if (!interactEntity) {
|
||||
interactTile = ft.interact(this.level, fx, fy, this);
|
||||
}
|
||||
}
|
||||
|
||||
// toggle inventory
|
||||
if (!interactEntity && !interactTile) {
|
||||
Global.game.setMenu(new PlayerInventoryMenu(this, true));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.swimming && ControlHandler.HIT.pressedTick()) {
|
||||
this.updateAnimationFrame(true);
|
||||
|
||||
boolean hitEntity = false;
|
||||
for (Entity e : facingEntities) {
|
||||
if (this.hit(e)) {
|
||||
e.onHit(this);
|
||||
hitEntity = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hitEntity) {
|
||||
this.animateAction();
|
||||
} else if (this.equipped.instance.item instanceof ItemConsumable) {
|
||||
this.equipped.instance.item.use(this.equipped.instance, level, this.tileX, this.tileY, this);
|
||||
} else if (this.equipped.instance.item instanceof ItemTool) {
|
||||
// try to use the active tool on the facing tile
|
||||
ItemTool tool = (ItemTool) this.equipped.instance.item;
|
||||
boolean ideal = (ft.getIdealTools() & tool.type) != 0,
|
||||
usable = (ft.getUsableTools() & tool.type) != 0;
|
||||
|
||||
if (ideal || usable) {
|
||||
int staminaCost = (int) (tool.getStaminaCostMultiplier() *
|
||||
(ideal ?
|
||||
(1 + Global.random.nextInt(2)) :
|
||||
(3 + Global.random.nextInt(3))));
|
||||
if (this.takeStamina(staminaCost)) {
|
||||
Item equippedItem = this.equipped.instance.item;
|
||||
if (ft.hit(this.level, fx, fy, this)) {
|
||||
this.animateHit(equippedItem, fx, fy);
|
||||
} else {
|
||||
// give stamina back if hit failed
|
||||
this.stamina += staminaCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// try to use the active item on the facing tile
|
||||
Item equippedItem = this.equipped.instance.item;
|
||||
if (equippedItem.use(this.equipped.instance, level, fx, fy, this)) {
|
||||
this.animateHit(equippedItem, fx, fy);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hitTicks == 0 && !hitEntity) {
|
||||
// no hit? animate an action
|
||||
this.animateAction();
|
||||
Sound.MISS.play();
|
||||
} else {
|
||||
Sound.HIT.play();
|
||||
}
|
||||
}
|
||||
|
||||
int dx = 0, dy = 0;
|
||||
|
||||
if (ControlHandler.UP.down()) {
|
||||
dy--;
|
||||
}
|
||||
|
||||
if (ControlHandler.DOWN.down()) {
|
||||
dy++;
|
||||
}
|
||||
|
||||
if (ControlHandler.LEFT.down()) {
|
||||
dx--;
|
||||
}
|
||||
|
||||
if (ControlHandler.RIGHT.down()) {
|
||||
dx++;
|
||||
}
|
||||
|
||||
if (!this.swimming || Global.ticks % 2 == 0) {
|
||||
this.move(dx, dy);
|
||||
}
|
||||
|
||||
// override entity direction, the player should be able to change their
|
||||
// direction even when stationary
|
||||
if (dx != 0 || dy != 0) {
|
||||
this.direction = Direction.get(dx, dy);
|
||||
}
|
||||
|
||||
// center camera on the player
|
||||
this.updateCamera();
|
||||
}
|
||||
|
||||
// updates current renderer camera to track the player
|
||||
public void updateCamera() {
|
||||
Renderer.camera.centerOn(
|
||||
this.x + (this.width / 2), this.y + (this.height / 2),
|
||||
0, 0,
|
||||
Level.toPixel(this.level.width), Level.toPixel(this.level.height)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getStaminaRechargeRate() {
|
||||
return 0.11;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStamina() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHealth() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColor() {
|
||||
return this.color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHurtColor() {
|
||||
return Color.addAll(this.color, 222);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.jdh.microcraft.entity;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.mob.EntitySkeleton;
|
||||
import com.jdh.microcraft.entity.mob.EntitySlime;
|
||||
import com.jdh.microcraft.entity.mob.EntityZombie;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class SpawnProperties {
|
||||
public final Class<? extends Entity> cls;
|
||||
public final Function<Level, Entity> spawnFunction;
|
||||
public final int chance, cap;
|
||||
|
||||
// chance is spawn rate/minute
|
||||
// cap and chance are per 128x128 area
|
||||
public SpawnProperties(Level level, Class<? extends Entity> cls, Function<Level, Entity> spawnFunction, int chance, int cap) {
|
||||
this.cls = cls;
|
||||
this.spawnFunction = spawnFunction;
|
||||
|
||||
double scale = ((level.width * level.height) / (128.0 * 128.0)) * Global.game.difficulty;
|
||||
this.chance = (int) (chance * scale);
|
||||
this.cap = (int) (cap * scale);
|
||||
}
|
||||
|
||||
public static List<SpawnProperties> getSpawnProperties(Level level) {
|
||||
return switch (level.depth) {
|
||||
case 1 -> List.of(
|
||||
new SpawnProperties(level, EntitySlime.class, EntitySlime::createBlueSlime, 50, 4),
|
||||
new SpawnProperties(level, EntitySkeleton.class, EntitySkeleton::createSkeleton, 100, 4)
|
||||
);
|
||||
case 0 -> List.of(
|
||||
new SpawnProperties(level, EntitySlime.class, EntitySlime::createGreenSlime, 300, 24),
|
||||
new SpawnProperties(level, EntityZombie.class, EntityZombie::createEasyZombie, 80, 3)
|
||||
);
|
||||
case -1 -> List.of(
|
||||
new SpawnProperties(level, EntitySlime.class, EntitySlime::createGreenSlime, 200, 24),
|
||||
new SpawnProperties(level, EntitySlime.class, EntitySlime::createBlueSlime, 100, 4),
|
||||
new SpawnProperties(level, EntityZombie.class, EntityZombie::createEasyZombie, 200, 20),
|
||||
new SpawnProperties(level, EntitySkeleton.class, EntitySkeleton::createSkeleton, 200, 24)
|
||||
);
|
||||
case -2 -> List.of(
|
||||
new SpawnProperties(level, EntitySlime.class, EntitySlime::createGreenSlime, 200, 32),
|
||||
new SpawnProperties(level, EntitySlime.class, EntitySlime::createBlueSlime, 220, 32),
|
||||
new SpawnProperties(level, EntitySlime.class, EntitySlime::createRedSlime, 80, 4),
|
||||
new SpawnProperties(level, EntityZombie.class, EntityZombie::createMediumZombie, 200, 24),
|
||||
new SpawnProperties(level, EntitySkeleton.class, EntitySkeleton::createSkeleton, 250, 24)
|
||||
);
|
||||
case -3 -> List.of(
|
||||
new SpawnProperties(level, EntitySlime.class, EntitySlime::createGreenSlime, 100, 32),
|
||||
new SpawnProperties(level, EntitySlime.class, EntitySlime::createBlueSlime, 100, 32),
|
||||
new SpawnProperties(level, EntitySlime.class, EntitySlime::createRedSlime, 400, 32),
|
||||
new SpawnProperties(level, EntityZombie.class, EntityZombie::createHardZombie, 250, 32),
|
||||
new SpawnProperties(level, EntitySkeleton.class, EntitySkeleton::createSkeleton, 250, 32)
|
||||
);
|
||||
default -> List.of();
|
||||
};
|
||||
}
|
||||
}
|
||||
19
Java/Microcraft/src/com/jdh/microcraft/entity/ai/AI.java
Normal file
19
Java/Microcraft/src/com/jdh/microcraft/entity/ai/AI.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.jdh.microcraft.entity.ai;
|
||||
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
|
||||
public class AI {
|
||||
protected final Entity entity;
|
||||
|
||||
public AI(Entity entity) {
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
|
||||
}
|
||||
|
||||
public void update() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.jdh.microcraft.entity.ai;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.attack.WizardAttackBeam;
|
||||
import com.jdh.microcraft.entity.attack.WizardAttackBurst;
|
||||
import com.jdh.microcraft.entity.attack.WizardAttackSpiral;
|
||||
import com.jdh.microcraft.entity.mob.EntityAirWizard;
|
||||
import com.jdh.microcraft.gfx.Renderer;
|
||||
import com.jdh.microcraft.sound.Sound;
|
||||
import com.jdh.microcraft.util.Direction;
|
||||
|
||||
public class AIAirWizard extends AIHostileHumanoid {
|
||||
private long lastAttack;
|
||||
private final EntityAirWizard wizard;
|
||||
|
||||
private Direction randomDirection;
|
||||
|
||||
public AIAirWizard(EntityAirWizard wizard) {
|
||||
super(wizard, 0.75, 3.0 * wizard.strength);
|
||||
this.wizard = wizard;
|
||||
this.lastAttack = Global.ticks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (this.wizard.attack == null) {
|
||||
super.tick();
|
||||
} else {
|
||||
// move randomly
|
||||
if (this.randomDirection == null || Global.random.nextInt(80) == 0) {
|
||||
this.randomDirection = Direction.ALL.get(Global.random.nextInt(4));
|
||||
}
|
||||
|
||||
this.moveTowards(
|
||||
this.wizard.x + this.randomDirection.x * 10,
|
||||
this.wizard.y + this.randomDirection.y * 10
|
||||
);
|
||||
}
|
||||
|
||||
if ((Global.ticks - this.lastAttack) > 120 + Global.random.nextInt(480)) {
|
||||
this.wizard.attack = switch (Global.random.nextInt(3)) {
|
||||
case 0 -> new WizardAttackBurst(this.wizard);
|
||||
case 1 -> new WizardAttackSpiral(this.wizard);
|
||||
default -> new WizardAttackBeam(this.wizard);
|
||||
};
|
||||
|
||||
if (Renderer.inBounds(this.wizard.getCenterX(), this.wizard.getCenterY())) {
|
||||
Sound.WIZARD_ATTACK.play();
|
||||
}
|
||||
|
||||
this.lastAttack = Global.ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.jdh.microcraft.entity.ai;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.mob.EntityHumanoid;
|
||||
import com.jdh.microcraft.util.Direction;
|
||||
import com.jdh.microcraft.util.FMath;
|
||||
|
||||
public class AIHostileHumanoid extends AIHostileMob {
|
||||
private final EntityHumanoid humanoid;
|
||||
private final double speed, strength;
|
||||
|
||||
private Direction randomMoveDirection;
|
||||
|
||||
public AIHostileHumanoid(EntityHumanoid humanoid, double speed, double strength) {
|
||||
super(humanoid, strength);
|
||||
this.humanoid = humanoid;
|
||||
this.speed = speed;
|
||||
this.strength = strength;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveTowards(int x, int y) {
|
||||
double dx = FMath.sign(x - this.humanoid.x),
|
||||
dy = FMath.sign(y - this.humanoid.y),
|
||||
l = FMath.norm(dx, dy);
|
||||
|
||||
dx /= l;
|
||||
dy /= l;
|
||||
dx = Double.isNaN(dx) ? 0 : dx;
|
||||
dy = Double.isNaN(dy) ? 0 : dy;
|
||||
|
||||
dx *= this.speed;
|
||||
dy *= this.speed;
|
||||
|
||||
if (!this.humanoid.swimming || Global.ticks % 2 == 0) {
|
||||
int mx = FMath.tickedDoubleToInt(Global.ticks, dx),
|
||||
my = FMath.tickedDoubleToInt(Global.ticks, dy);
|
||||
|
||||
if (this.shouldMove(mx, 0)) {
|
||||
this.humanoid.move(mx, 0);
|
||||
}
|
||||
|
||||
if (this.shouldMove(0, my)) {
|
||||
this.humanoid.move(0, my);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if (this.target == null) {
|
||||
if (this.randomMoveDirection == null && Global.random.nextInt(240) == 0) {
|
||||
this.randomMoveDirection = Direction.ALL.get(Global.random.nextInt(4));
|
||||
} else if (this.randomMoveDirection != null) {
|
||||
this.moveTowards(
|
||||
this.humanoid.x + this.randomMoveDirection.x * 10,
|
||||
this.humanoid.y + this.randomMoveDirection.y * 10
|
||||
);
|
||||
|
||||
if (Global.random.nextInt(120) == 0) {
|
||||
this.randomMoveDirection = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.jdh.microcraft.entity.ai;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
import com.jdh.microcraft.level.tile.Tile;
|
||||
import com.jdh.microcraft.level.tile.TileLava;
|
||||
import com.jdh.microcraft.util.Direction;
|
||||
import com.jdh.microcraft.util.FMath;
|
||||
import com.jdh.microcraft.util.Time;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class AIHostileMob extends AI {
|
||||
protected final double sightDistance;
|
||||
protected final int targetPermanenceTicks;
|
||||
|
||||
protected final double strength;
|
||||
protected int ticksSinceTargetSeen;
|
||||
public Entity target;
|
||||
|
||||
public AIHostileMob(Entity entity, double strength) {
|
||||
super(entity);
|
||||
this.strength = strength;
|
||||
this.sightDistance = (16 * 10) * this.strength;
|
||||
this.targetPermanenceTicks = (int) (180 * this.strength);
|
||||
}
|
||||
|
||||
// avoid really dangerous stuff
|
||||
protected boolean shouldMove(int dx, int dy) {
|
||||
int cx = this.entity.getCenterX() + dx,
|
||||
cy = this.entity.getCenterY() + dy,
|
||||
tx = Level.toTile(cx),
|
||||
ty = Level.toTile(cy);
|
||||
|
||||
return !(Tile.TILES[this.entity.level.getTile(tx, ty)] instanceof TileLava);
|
||||
}
|
||||
|
||||
protected abstract void moveTowards(int x, int y);
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if (this.entity.level.player != null &&
|
||||
FMath.norm(this.entity.x - this.entity.level.player.x,
|
||||
this.entity.y - this.entity.level.player.y) <= this.sightDistance) {
|
||||
this.target = this.entity.level.player;
|
||||
this.ticksSinceTargetSeen = 0;
|
||||
} else {
|
||||
this.ticksSinceTargetSeen++;
|
||||
|
||||
// lose target if it has been too long
|
||||
if (this.ticksSinceTargetSeen >= this.targetPermanenceTicks) {
|
||||
this.target = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.target != null) {
|
||||
int tcx = this.target.x + (this.target.width / 2),
|
||||
tcy = this.target.y + (this.target.height / 2),
|
||||
tdx = tcx - this.entity.x,
|
||||
tdy = tcy - this.entity.y;
|
||||
|
||||
double pd = this.getPreferredTargetDistance(),
|
||||
pdt = this.getPreferredTargetDistanceThreshold();
|
||||
|
||||
if (Math.abs(tdx) - pd > pdt ||
|
||||
Math.abs(tdy) - pd > pdt) {
|
||||
this.moveTowards(this.target.x, this.target.y);
|
||||
} else if (pd != 0.0 && pdt != 0.0) {
|
||||
// ranged mobs: strafe in directions not towards the target
|
||||
Global.random.setSeed(Global.ticks / Time.TPS);
|
||||
if (Global.random.nextInt(3) == 0) {
|
||||
Direction td = Direction.get(tdx, tdy);
|
||||
Direction strafeDir = new ArrayList<>(Direction.ALL)
|
||||
.stream()
|
||||
.filter(d -> d != td && d != td.opposite())
|
||||
.collect(Collectors.toList())
|
||||
.get(Global.random.nextInt(2));
|
||||
this.moveTowards(this.entity.x + strafeDir.x, this.entity.y + strafeDir.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected double getPreferredTargetDistance() {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
protected double getPreferredTargetDistanceThreshold() {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.jdh.microcraft.entity.ai;
|
||||
|
||||
import com.jdh.microcraft.entity.mob.EntitySkeleton;
|
||||
|
||||
public class AISkeleton extends AIHostileHumanoid {
|
||||
private final EntitySkeleton skeleton;
|
||||
|
||||
public AISkeleton(EntitySkeleton skeleton) {
|
||||
super(skeleton, 0.5 * skeleton.strength, skeleton.strength);
|
||||
this.skeleton = skeleton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if (this.target != null) {
|
||||
this.skeleton.shoot(this.target.x, this.target.y);
|
||||
}
|
||||
}
|
||||
|
||||
protected double getPreferredTargetDistance() {
|
||||
return 16 * 3;
|
||||
}
|
||||
|
||||
protected double getPreferredTargetDistanceThreshold() {
|
||||
return 24;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.jdh.microcraft.entity.ai;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.mob.EntitySlime;
|
||||
import com.jdh.microcraft.util.Direction;
|
||||
|
||||
public class AISlime extends AIHostileMob {
|
||||
private final EntitySlime slime;
|
||||
|
||||
private long lastJump;
|
||||
private final int jumpDelay;
|
||||
|
||||
public AISlime(EntitySlime slime) {
|
||||
super(slime, slime.strength);
|
||||
this.slime = slime;
|
||||
this.jumpDelay = (int) Math.max(140 - (20 * slime.strength), 60);
|
||||
}
|
||||
|
||||
private boolean canJump() {
|
||||
return Global.ticks - this.lastJump >= this.jumpDelay;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveTowards(int x, int y) {
|
||||
if (!this.slime.jumping && this.canJump()) {
|
||||
this.lastJump = Global.ticks;
|
||||
this.slime.jump(x - this.slime.x, y - this.slime.y, 0.5 * slime.strength);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
Global.random.setSeed(Global.ticks * this.slime.id);
|
||||
if (this.target == null && Global.random.nextInt(180) == 0) {
|
||||
this.lastJump = Global.ticks;
|
||||
this.slime.jump(Direction.ALL.get(Global.random.nextInt(4)), 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.jdh.microcraft.entity.ai;
|
||||
|
||||
import com.jdh.microcraft.entity.mob.EntityZombie;
|
||||
|
||||
public class AIZombie extends AIHostileHumanoid {
|
||||
public AIZombie(EntityZombie zombie) {
|
||||
super(zombie, 0.45 * zombie.strength, zombie.strength);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.jdh.microcraft.entity.attack;
|
||||
|
||||
import com.jdh.microcraft.entity.mob.EntityAirWizard;
|
||||
|
||||
public abstract class WizardAttack {
|
||||
protected final EntityAirWizard wizard;
|
||||
public int time;
|
||||
|
||||
public WizardAttack(EntityAirWizard wizard, int time) {
|
||||
this.wizard = wizard;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public boolean done() {
|
||||
return this.time <= 0;
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
this.time--;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.jdh.microcraft.entity.attack;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.ai.AIHostileMob;
|
||||
import com.jdh.microcraft.entity.mob.EntityAirWizard;
|
||||
import com.jdh.microcraft.entity.projectile.EntityAirBlast;
|
||||
|
||||
public class WizardAttackBeam extends WizardAttack {
|
||||
public WizardAttackBeam(EntityAirWizard wizard) {
|
||||
super(wizard, 60);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if ((Global.ticks % 2) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
AIHostileMob ai = (AIHostileMob) this.wizard.ai;
|
||||
if (ai.target != null) {
|
||||
this.wizard.level.addEntity(
|
||||
new EntityAirBlast(
|
||||
this.wizard.level, this.wizard,
|
||||
this.wizard.getCenterX(), this.wizard.getCenterY(),
|
||||
ai.target.getCenterX(), ai.target.getCenterY(),
|
||||
1.4,
|
||||
(int) (3 * this.wizard.strength))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.jdh.microcraft.entity.attack;
|
||||
|
||||
import com.jdh.microcraft.entity.mob.EntityAirWizard;
|
||||
import com.jdh.microcraft.entity.projectile.EntityAirBlast;
|
||||
|
||||
public class WizardAttackBurst extends WizardAttack {
|
||||
private static final int SIZE = 32;
|
||||
|
||||
private boolean attacked = false;
|
||||
|
||||
public WizardAttackBurst(EntityAirWizard wizard) {
|
||||
super(wizard, 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if (this.attacked) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.attacked = true;
|
||||
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
int dx = (int) (Math.cos((i / (double) SIZE) * (2 * Math.PI)) * 16),
|
||||
dy = (int) (Math.sin((i / (double) SIZE) * (2 * Math.PI)) * 16);
|
||||
|
||||
this.wizard.level.addEntity(
|
||||
new EntityAirBlast(
|
||||
this.wizard.level, this.wizard,
|
||||
this.wizard.getCenterX() + dx,
|
||||
this.wizard.getCenterY() + dy,
|
||||
this.wizard.getCenterX() + dx * 10,
|
||||
this.wizard.getCenterY() + dy * 10,
|
||||
1.2,
|
||||
(int) (3 * this.wizard.strength)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.jdh.microcraft.entity.attack;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.mob.EntityAirWizard;
|
||||
import com.jdh.microcraft.entity.projectile.EntityAirBlast;
|
||||
|
||||
public class WizardAttackSpiral extends WizardAttack {
|
||||
public WizardAttackSpiral(EntityAirWizard wizard) {
|
||||
super(wizard, 240);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if ((Global.ticks % 2) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int dx = (int) (Math.cos(((Global.ticks % 60) / 60.0) * (2 * Math.PI)) * 16),
|
||||
dy = (int) (Math.sin(((Global.ticks % 60) / 60.0) * (2 * Math.PI)) * 16);
|
||||
|
||||
this.wizard.level.addEntity(
|
||||
new EntityAirBlast(
|
||||
this.wizard.level, this.wizard,
|
||||
this.wizard.getCenterX() + dx,
|
||||
this.wizard.getCenterY() + dy,
|
||||
this.wizard.getCenterX() + dx * 10,
|
||||
this.wizard.getCenterY() + dy * 10,
|
||||
1.4,
|
||||
(int) (3 * this.wizard.strength)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.jdh.microcraft.entity.furniture;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityPlayer;
|
||||
import com.jdh.microcraft.gui.crafting.AnvilCraftingMenu;
|
||||
import com.jdh.microcraft.item.Item;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
public class EntityAnvil extends EntityFurniture {
|
||||
public EntityAnvil(Level level) {
|
||||
super(level, Item.ANVIL, 15, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean interact(Entity e) {
|
||||
if (!(e instanceof EntityPlayer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Global.game.setMenu(new AnvilCraftingMenu((EntityPlayer) e, this));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.jdh.microcraft.entity.furniture;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityItem;
|
||||
import com.jdh.microcraft.entity.EntityPlayer;
|
||||
import com.jdh.microcraft.gui.ChestMenu;
|
||||
import com.jdh.microcraft.item.Inventory;
|
||||
import com.jdh.microcraft.item.Item;
|
||||
import com.jdh.microcraft.item.ItemStack;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
public class EntityChest extends EntityFurniture {
|
||||
public Inventory inventory = new Inventory(512);
|
||||
|
||||
public EntityChest(Level level) {
|
||||
super(level, Item.CHEST, 15, 13);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hit(Entity e) {
|
||||
if (super.hit(e)) {
|
||||
for (ItemStack s : this.inventory.stacks) {
|
||||
EntityItem.spawn(this.level, s, Level.toCenter(e.tileX), Level.toCenter(e.tileY));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean interact(Entity e) {
|
||||
if (!(e instanceof EntityPlayer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Global.game.setMenu(new ChestMenu((EntityPlayer) e, this));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.jdh.microcraft.entity.furniture;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityPlayer;
|
||||
import com.jdh.microcraft.gui.crafting.BenchCraftingMenu;
|
||||
import com.jdh.microcraft.item.Item;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
public class EntityCraftingBench extends EntityFurniture {
|
||||
public EntityCraftingBench(Level level) {
|
||||
super(level, Item.CRAFTING_BENCH, 15, 12);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean interact(Entity e) {
|
||||
if (!(e instanceof EntityPlayer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Global.game.setMenu(new BenchCraftingMenu((EntityPlayer) e, this));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.jdh.microcraft.entity.furniture;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityPlayer;
|
||||
import com.jdh.microcraft.gui.crafting.FurnaceMenu;
|
||||
import com.jdh.microcraft.item.Item;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
public class EntityFurnace extends EntityFurniture {
|
||||
public EntityFurnace(Level level) {
|
||||
super(level, Item.FURNACE, 15, 14);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean interact(Entity e) {
|
||||
if (!(e instanceof EntityPlayer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Global.game.setMenu(new FurnaceMenu((EntityPlayer) e, this));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.jdh.microcraft.entity.furniture;
|
||||
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityItem;
|
||||
import com.jdh.microcraft.entity.mob.EntityMob;
|
||||
import com.jdh.microcraft.entity.projectile.EntityProjectile;
|
||||
import com.jdh.microcraft.gfx.Renderer;
|
||||
import com.jdh.microcraft.item.ItemInstance;
|
||||
import com.jdh.microcraft.item.ItemStack;
|
||||
import com.jdh.microcraft.item.furniture.ItemFurniture;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
import com.jdh.microcraft.level.tile.Tile;
|
||||
import com.jdh.microcraft.util.Direction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class EntityFurniture extends Entity {
|
||||
private final ItemFurniture item;
|
||||
private int pushTime;
|
||||
|
||||
public EntityFurniture(Level level, ItemFurniture item, int width, int height) {
|
||||
super(level);
|
||||
this.item = item;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
super.render();
|
||||
|
||||
for (int i = 0; i <= 1; i++) {
|
||||
for (int j = 0; j <= 1; j++) {
|
||||
Renderer.render(
|
||||
this.getSpriteOffsetX() + i, this.getSpriteOffsetY() + j,
|
||||
this.x + this.getRenderOffsetX() + (i * 8),
|
||||
this.y + this.getRenderOffsetY() + (j * 8),
|
||||
this.getColor(), Renderer.FLIP_NONE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collides(Entity e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collide(Entity e) {
|
||||
super.collide(e);
|
||||
|
||||
if (e instanceof EntityMob) {
|
||||
this.pushTime++;
|
||||
|
||||
if (this.pushTime == 3) {
|
||||
// push in the direction the mob is moving
|
||||
EntityMob mob = ((EntityMob) e);
|
||||
Direction d = mob.getDirection();
|
||||
this.move(d.x, d.y);
|
||||
this.pushTime = 0;
|
||||
}
|
||||
} else if (e instanceof EntityProjectile) {
|
||||
for (ItemStack s : this.getDrops()) {
|
||||
EntityItem.spawn(level, s, Level.toCenter(this.tileX), Level.toCenter(this.tileY));
|
||||
}
|
||||
this.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHit(Entity e) {
|
||||
if (!(e instanceof EntityMob)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ItemStack s : this.getDrops()) {
|
||||
EntityItem.spawn(level, s, Level.toCenter(this.tileX), Level.toCenter(this.tileY));
|
||||
}
|
||||
this.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSwimIn(Tile tile, int x, int y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected List<ItemStack> getDrops() {
|
||||
return List.of(new ItemStack(new ItemInstance(this.item)));
|
||||
}
|
||||
|
||||
public int getSpriteOffsetX() {
|
||||
return this.item.getTileSpriteX();
|
||||
}
|
||||
|
||||
public int getSpriteOffsetY() {
|
||||
return this.item.getTileSpriteY();
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return this.item.getColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean interact(Entity e);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.jdh.microcraft.entity.furniture;
|
||||
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.mob.EntityMob;
|
||||
import com.jdh.microcraft.gfx.Light;
|
||||
import com.jdh.microcraft.item.Item;
|
||||
import com.jdh.microcraft.item.ItemInstance;
|
||||
import com.jdh.microcraft.item.ItemStack;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
public class EntityLantern extends EntityFurniture {
|
||||
public static final int LIGHT_POWER = 6;
|
||||
|
||||
public EntityLantern(Level level) {
|
||||
super(level, Item.LANTERN, 13, 13);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderOffsetX() {
|
||||
return -2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderOffsetY() {
|
||||
return -3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Light getLight() {
|
||||
return new Light(
|
||||
this.getCenterX(),
|
||||
this.getCenterY(),
|
||||
LIGHT_POWER
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean interact(Entity e) {
|
||||
ItemStack s = new ItemStack(new ItemInstance(Item.LANTERN, 0), 1);
|
||||
|
||||
if (e instanceof EntityMob && ((EntityMob) e).inventory.add(s)) {
|
||||
EntityMob mob = (EntityMob) e;
|
||||
mob.equipped = s;
|
||||
this.remove();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.jdh.microcraft.entity.furniture;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityPlayer;
|
||||
import com.jdh.microcraft.gui.crafting.OvenMenu;
|
||||
import com.jdh.microcraft.item.Item;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
public class EntityOven extends EntityFurniture {
|
||||
public EntityOven(Level level) {
|
||||
super(level, Item.OVEN, 15, 13);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean interact(Entity e) {
|
||||
if (!(e instanceof EntityPlayer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Global.game.setMenu(new OvenMenu((EntityPlayer) e, this));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.jdh.microcraft.entity.mob;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityPlayer;
|
||||
import com.jdh.microcraft.entity.ai.AIAirWizard;
|
||||
import com.jdh.microcraft.entity.attack.WizardAttack;
|
||||
import com.jdh.microcraft.entity.particle.EntitySmokeParticle;
|
||||
import com.jdh.microcraft.entity.projectile.EntityProjectile;
|
||||
import com.jdh.microcraft.gfx.Color;
|
||||
import com.jdh.microcraft.gui.WinMenu;
|
||||
import com.jdh.microcraft.item.Inventory;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
import com.jdh.microcraft.level.tile.Tile;
|
||||
import com.jdh.microcraft.sound.Sound;
|
||||
|
||||
public class EntityAirWizard extends EntityHumanoid {
|
||||
public final double strength = 1.0 * Global.game.difficulty;
|
||||
|
||||
public WizardAttack attack;
|
||||
|
||||
public EntityAirWizard(Level level) {
|
||||
super(level, Inventory.NONE);
|
||||
this.health = this.getMaxHealth();
|
||||
this.stamina = this.getMaxStamina();
|
||||
this.ai = new AIAirWizard(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die() {
|
||||
super.die();
|
||||
Sound.WIZARD_DEATH.play();
|
||||
Global.game.score += 5000;
|
||||
Global.game.setMenu(new WinMenu());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hurt(int amount) {
|
||||
return super.hurt(amount == 1 ? 0 : amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collides(Entity e) {
|
||||
return !(e instanceof EntityProjectile) && super.collides(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collide(Entity e) {
|
||||
if (e instanceof EntityPlayer) {
|
||||
this.hit(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if (Global.random.nextInt(10) == 0) {
|
||||
EntitySmokeParticle.spawn(this.level, this.getCenterX(), this.getCenterY(), Tile.CLOUD.getColor(), 1, 4);
|
||||
}
|
||||
|
||||
if (this.attack != null) {
|
||||
if (this.attack.done()) {
|
||||
this.attack = null;
|
||||
} else {
|
||||
this.attack.tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHitDamage(Entity e) {
|
||||
return (int) ((2 + Global.random.nextInt(5)) * this.strength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseSpriteX() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseSpriteY() {
|
||||
return 9;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCarrySpriteX() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCarrySpriteY() {
|
||||
return 9;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColor() {
|
||||
return Color.get(111, 224, 441, 555);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHurtColor() {
|
||||
return Color.get(444, 335, 552, 555);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getStaminaRechargeRate() {
|
||||
return 0.3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStamina() {
|
||||
return (int) (this.strength * 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHealth() {
|
||||
return (int) (this.strength * 80);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
package com.jdh.microcraft.entity.mob;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityItem;
|
||||
import com.jdh.microcraft.gfx.Color;
|
||||
import com.jdh.microcraft.gfx.Light;
|
||||
import com.jdh.microcraft.gfx.Renderer;
|
||||
import com.jdh.microcraft.item.Inventory;
|
||||
import com.jdh.microcraft.item.Item;
|
||||
import com.jdh.microcraft.item.ItemInstance;
|
||||
import com.jdh.microcraft.item.ItemStack;
|
||||
import com.jdh.microcraft.item.armor.*;
|
||||
import com.jdh.microcraft.item.tool.ItemTool;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
import com.jdh.microcraft.level.tile.Tile;
|
||||
import com.jdh.microcraft.level.tile.TileLiquid;
|
||||
import com.jdh.microcraft.util.Direction;
|
||||
|
||||
public abstract class EntityHumanoid extends EntityMob {
|
||||
public static final int BASE_SPRITE_X = 0, BASE_SPRITE_Y = 11;
|
||||
public static final int CARRY_SPRITE_X = 0, CARRY_SPRITE_Y = 9;
|
||||
private static final int SWIM_SPRITE_X = 9, SWIM_SPRITE_Y = 11;
|
||||
|
||||
private static final int SWIM_COLOR = Color.get(334, 445, 555, 555);
|
||||
|
||||
// current sprite location
|
||||
private int spriteX, spriteY;
|
||||
|
||||
// current animation frame ticks and flipped/frame state
|
||||
private int animationTicks;
|
||||
private boolean animationFlipped, animationXFrame;
|
||||
|
||||
// equipped armor
|
||||
public ItemStack[] armor = new ItemStack[ItemArmor.NUM_TYPES];
|
||||
|
||||
public EntityHumanoid(Level level, Inventory inventory) {
|
||||
super(level, inventory);
|
||||
this.width = 11;
|
||||
this.height = 11;
|
||||
this.animationTicks = this.getAnimationFrameTicks();
|
||||
this.updateAnimationFrame(false);
|
||||
}
|
||||
|
||||
// level must be in [1..3]
|
||||
public static void giveRandomEquipment(EntityHumanoid entity, int level, boolean armor, boolean weapon) {
|
||||
assert (level >= 1 && level <= 3);
|
||||
Item[][] armorTable = new Item[][]{
|
||||
{Item.IRON_HELMET, Item.IRON_CHESTPLATE, Item.IRON_LEGGINGS, Item.IRON_BOOTS},
|
||||
{Item.GOLD_HELMET, Item.GOLD_CHESTPLATE, Item.GOLD_LEGGINGS, Item.GOLD_BOOTS},
|
||||
{Item.GOLD_HELMET, Item.GOLD_CHESTPLATE, Item.GOLD_LEGGINGS, Item.GOLD_BOOTS}
|
||||
};
|
||||
|
||||
Item[][] weaponTable = new Item[][]{
|
||||
{Item.ROCK_SWORD, Item.ROCK_AXE},
|
||||
{Item.IRON_SWORD, Item.IRON_AXE},
|
||||
{Item.GOLD_SWORD, Item.GEM_SWORD, Item.MITHRIL_SWORD}
|
||||
};
|
||||
|
||||
if (armor) {
|
||||
for (int i = 0; i < ItemArmor.NUM_TYPES; i++) {
|
||||
if (Global.random.nextInt((4 - level) * 7) == 0) {
|
||||
ItemStack item = new ItemStack(new ItemInstance(armorTable[level - 1][i], 0), 1);
|
||||
entity.armor[i] = item;
|
||||
entity.inventory.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (weapon && Global.random.nextInt((4 - level) * 8) == 0) {
|
||||
ItemStack item = new ItemStack(
|
||||
new ItemInstance(
|
||||
weaponTable[level - 1][Global.random.nextInt(weaponTable[level - 1].length)],
|
||||
0),
|
||||
1
|
||||
);
|
||||
entity.equipped = item;
|
||||
entity.inventory.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(Item item, int count) {
|
||||
ItemStack removed = super.removeItem(item, count);
|
||||
|
||||
for (int i = 0; i < this.armor.length; i++) {
|
||||
if (this.armor[i] == removed) {
|
||||
this.armor[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die() {
|
||||
super.die();
|
||||
|
||||
for (ItemStack s : this.inventory.stacks) {
|
||||
EntityItem.spawn(this.level, s, this.getCenterX(), this.getCenterY());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pickup(ItemStack stack) {
|
||||
super.pickup(stack);
|
||||
|
||||
if (stack.instance.item instanceof ItemArmor) {
|
||||
ItemArmor armor = (ItemArmor) stack.instance.item;
|
||||
if (this.armor[armor.slot] == null) {
|
||||
this.armor[armor.slot] = stack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int getAnimationFrameTicks() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
public boolean hurt(int amount, boolean ignoreArmor) {
|
||||
if (ignoreArmor) {
|
||||
return super.hurt(amount);
|
||||
}
|
||||
|
||||
double reduction = 0;
|
||||
for (ItemStack s : this.armor) {
|
||||
if (s != null) {
|
||||
reduction += ((ItemArmor) s.instance.item).getDamageReduction(s.instance);
|
||||
}
|
||||
}
|
||||
|
||||
reduction = reduction > 0 ? Global.random.nextInt(Math.max((int) (reduction / 2.5), 2)) : 0;
|
||||
return super.hurt((int) Math.max(amount - reduction, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hurt(int amount) {
|
||||
return this.hurt(amount, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Light getLight() {
|
||||
if (this.equipped != null) {
|
||||
int power = this.equipped.instance.item.getLightPower();
|
||||
if (power > 0) {
|
||||
return new Light(
|
||||
this.getCenterX(),
|
||||
this.getCenterY(),
|
||||
power
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHitDamage(Entity e) {
|
||||
return (this.equipped != null && (this.equipped.instance.item instanceof ItemTool)) ?
|
||||
((ItemTool) this.equipped.instance.item).getDamage(this.equipped.instance) : 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderOffsetX() {
|
||||
return -3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderOffsetY() {
|
||||
return -2;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDirectionChange() {
|
||||
super.onDirectionChange();
|
||||
this.updateAnimationFrame(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
// drowning
|
||||
if (this.swimming && (Global.ticks % 60) == 0) {
|
||||
if (this.stamina == 0) {
|
||||
this.hurt(1, true);
|
||||
} else {
|
||||
this.takeStamina(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.moving) {
|
||||
this.animationTicks--;
|
||||
|
||||
if (this.animationTicks == 0) {
|
||||
this.updateAnimationFrame(true);
|
||||
}
|
||||
}
|
||||
|
||||
// pick the correct current animation frame but don't switch
|
||||
this.updateAnimationFrame(false);
|
||||
}
|
||||
|
||||
protected boolean carrying() {
|
||||
return this.equipped != null && this.equipped.instance.item.carry(this);
|
||||
}
|
||||
|
||||
public int getBaseSpriteX() {
|
||||
return BASE_SPRITE_X;
|
||||
}
|
||||
|
||||
public int getBaseSpriteY() {
|
||||
return BASE_SPRITE_Y;
|
||||
}
|
||||
|
||||
public int getCarrySpriteX() {
|
||||
return CARRY_SPRITE_X;
|
||||
}
|
||||
|
||||
public int getCarrySpriteY() {
|
||||
return CARRY_SPRITE_Y;
|
||||
}
|
||||
|
||||
protected int getSpritesheetOffsetX() {
|
||||
return this.carrying() ? this.getCarrySpriteX() : this.getBaseSpriteX();
|
||||
}
|
||||
|
||||
protected int getSpritesheetOffsetY() {
|
||||
return this.carrying() ? this.getCarrySpriteY() : this.getBaseSpriteY();
|
||||
}
|
||||
|
||||
protected void updateAnimationFrame(boolean switchFrame) {
|
||||
if (switchFrame) {
|
||||
this.animationTicks = this.getAnimationFrameTicks();
|
||||
}
|
||||
|
||||
this.spriteY = this.getSpritesheetOffsetY();
|
||||
|
||||
switch (this.direction) {
|
||||
case NORTH, SOUTH -> {
|
||||
if (switchFrame) {
|
||||
this.animationFlipped = !this.animationFlipped;
|
||||
}
|
||||
|
||||
this.spriteX = this.getSpritesheetOffsetX() +
|
||||
(this.direction == Direction.NORTH ? 2 : 0);
|
||||
}
|
||||
case EAST, WEST -> {
|
||||
if (switchFrame) {
|
||||
this.animationXFrame = !this.animationXFrame;
|
||||
}
|
||||
|
||||
this.animationFlipped = this.direction == Direction.LEFT;
|
||||
this.spriteX = this.getSpritesheetOffsetX() +
|
||||
(this.animationXFrame ? 4 : 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderArmor(ItemArmor item) {
|
||||
if (this.swimming && item.type != ItemArmor.TYPE_HELMET) {
|
||||
return;
|
||||
}
|
||||
|
||||
int sx = this.spriteX - this.getCarrySpriteX(),
|
||||
sy = this.spriteY - this.getCarrySpriteY();
|
||||
for (int i = 0; i <= 1; i++) {
|
||||
for (int j = 0; j <= 1; j++) {
|
||||
Renderer.render(
|
||||
sx + item.getSpriteBaseX() + (this.animationFlipped ? (1 - i) : i),
|
||||
sy + item.getSpriteBaseY() + j,
|
||||
this.x + (i * 8) + this.getRenderOffsetX(),
|
||||
this.y + (j * 8) + this.getRenderOffsetY(),
|
||||
item.getColor(),
|
||||
this.animationFlipped ? Renderer.FLIP_X : Renderer.FLIP_NONE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
super.render();
|
||||
|
||||
// flash with hurt color on hurt
|
||||
int color = this.invulnerableTicks > 0 && ((Global.ticks / 4) % 2) == 0 ?
|
||||
this.getHurtColor() :
|
||||
this.getColor();
|
||||
|
||||
for (int i = 0; i <= 1; i++) {
|
||||
for (int j = 0; j <= (this.swimming ? 0 : 1); j++) {
|
||||
Renderer.render(
|
||||
this.spriteX + (this.animationFlipped ? (1 - i) : i), this.spriteY + j,
|
||||
this.x + (i * 8) + this.getRenderOffsetX(),
|
||||
this.y + (j * 8) + this.getRenderOffsetY(),
|
||||
color,
|
||||
this.animationFlipped ? Renderer.FLIP_X : Renderer.FLIP_NONE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// render armor
|
||||
for (ItemStack s : this.armor) {
|
||||
if (s != null) {
|
||||
this.renderArmor((ItemArmor) s.instance.item);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.swimming) {
|
||||
for (int i = 0; i <= 1; i++) {
|
||||
Renderer.render(
|
||||
SWIM_SPRITE_X, SWIM_SPRITE_Y,
|
||||
this.x + (i * 8) + this.getRenderOffsetX(),
|
||||
this.y + 2 + this.getRenderOffsetY(),
|
||||
SWIM_COLOR,
|
||||
i == 0 ? Renderer.FLIP_NONE : Renderer.FLIP_X
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// render carrying item
|
||||
if (this.equipped != null && this.equipped.instance.item.carry(this)) {
|
||||
this.equipped.instance.item.renderCarry(this.equipped.instance, this.level, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSwimIn(Tile tile, int x, int y) {
|
||||
return tile instanceof TileLiquid;
|
||||
}
|
||||
|
||||
public abstract int getColor();
|
||||
|
||||
public abstract int getHurtColor();
|
||||
}
|
||||
327
Java/Microcraft/src/com/jdh/microcraft/entity/mob/EntityMob.java
Normal file
327
Java/Microcraft/src/com/jdh/microcraft/entity/mob/EntityMob.java
Normal file
@@ -0,0 +1,327 @@
|
||||
package com.jdh.microcraft.entity.mob;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityItem;
|
||||
import com.jdh.microcraft.entity.furniture.EntityFurniture;
|
||||
import com.jdh.microcraft.entity.particle.EntityTextParticle;
|
||||
import com.jdh.microcraft.gfx.Color;
|
||||
import com.jdh.microcraft.gfx.Renderer;
|
||||
import com.jdh.microcraft.item.Inventory;
|
||||
import com.jdh.microcraft.item.Item;
|
||||
import com.jdh.microcraft.item.ItemInstance;
|
||||
import com.jdh.microcraft.item.ItemStack;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
import com.jdh.microcraft.util.Direction;
|
||||
import com.jdh.microcraft.util.FMath;
|
||||
|
||||
public abstract class EntityMob extends Entity {
|
||||
private static final int HIT_SPRITE_X = 9, HIT_SPRITE_Y = 12;
|
||||
private static final int HIT_COLOR = Color.get(222, 533, 544, 555);
|
||||
private static final int ACTION_SPRITE_X = 8, ACTION_SPRITE_Y = 11;
|
||||
private static final int ACTION_COLOR = Color.get(555, 555, 555, 555);
|
||||
private static final int ACTION_TICKS = 6;
|
||||
private static final int HIT_TICKS = 4;
|
||||
private static final int USE_ITEM_TICKS = 4;
|
||||
|
||||
private static final int STAMINA_RECHARGE_DELAY_TICKS = 40;
|
||||
private static final int INVULNERABLE_TICKS = 30;
|
||||
|
||||
protected int actionTicks;
|
||||
protected int hitTicks, hitX, hitY;
|
||||
|
||||
protected Item useItem;
|
||||
protected int useItemTicks;
|
||||
|
||||
public int health, stamina;
|
||||
public int invulnerableTicks;
|
||||
public int staminaRechargeDelayTicks;
|
||||
|
||||
public final Inventory inventory;
|
||||
public ItemStack equipped;
|
||||
|
||||
public EntityMob(Level level, Inventory inventory) {
|
||||
super(level);
|
||||
this.health = this.getMaxHealth();
|
||||
this.stamina = this.getMaxStamina();
|
||||
this.inventory = inventory;
|
||||
}
|
||||
|
||||
public void drop(ItemStack stack) {
|
||||
assert(this.inventory.contains(stack));
|
||||
|
||||
assert(stack.instance.item.isDroppable());
|
||||
EntityItem item = EntityItem.spawn(
|
||||
this.level,
|
||||
this.removeItem(this.equipped.instance.item, this.equipped.size),
|
||||
this.getCenterX(), this.getCenterY(),
|
||||
this.getDirection()
|
||||
);
|
||||
item.timeToPickup = 80;
|
||||
}
|
||||
|
||||
public int getFacingTileX() {
|
||||
return switch (this.direction) {
|
||||
case EAST -> tileX + 1;
|
||||
case WEST -> tileX - 1;
|
||||
default -> tileX;
|
||||
};
|
||||
}
|
||||
|
||||
public int getFacingTileY() {
|
||||
return switch (this.direction) {
|
||||
case NORTH -> tileY - 1;
|
||||
case SOUTH -> tileY + 1;
|
||||
default -> tileY;
|
||||
};
|
||||
}
|
||||
|
||||
public ItemStack removeItem(Item item, int count) {
|
||||
ItemStack removed = this.inventory.remove(item, count);
|
||||
|
||||
// remove equipped item if equipped stack was removed
|
||||
if (this.equipped == removed && count == this.equipped.size) {
|
||||
this.equipped = null;
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDirectionChange() {
|
||||
this.actionTicks = 0;
|
||||
this.useItemTicks = 0;
|
||||
this.hitTicks = 0;
|
||||
}
|
||||
|
||||
public int getHitDamage(Entity e) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public boolean canPickup(EntityItem e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void pickup(ItemStack stack) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hit(Entity e) {
|
||||
if (!this.swimming && e instanceof EntityMob && this.takeApproxStamina(2)) {
|
||||
EntityMob mob = (EntityMob) e;
|
||||
mob.hurt(Math.max(this.getHitDamage(e), 1), this);
|
||||
return true;
|
||||
}
|
||||
|
||||
return e instanceof EntityFurniture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collide(Entity e) {
|
||||
super.collide(e);
|
||||
|
||||
if (!e.metadata.remove && e instanceof EntityItem &&
|
||||
((EntityItem) e).canPickup(this) &&
|
||||
this.canPickup(((EntityItem) e))) {
|
||||
EntityItem item = ((EntityItem) e);
|
||||
if (this.inventory.add(item.stack)) {
|
||||
this.pickup(item.stack);
|
||||
item.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collides(Entity e) {
|
||||
return !(e instanceof EntityMob && this.swimming);
|
||||
}
|
||||
|
||||
protected void animateHit(Item used, int x, int y) {
|
||||
this.useItem = used;
|
||||
this.useItemTicks = USE_ITEM_TICKS;
|
||||
this.hitTicks = HIT_TICKS;
|
||||
this.hitX = Level.toPixel(x);
|
||||
this.hitY = Level.toPixel(y);
|
||||
}
|
||||
|
||||
protected void animateAction() {
|
||||
this.actionTicks = ACTION_TICKS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
super.render();
|
||||
|
||||
// used item
|
||||
if (this.useItemTicks > 0) {
|
||||
ItemInstance instance = new ItemInstance(this.useItem, 0);
|
||||
switch (this.direction) {
|
||||
case NORTH -> this.useItem.render(
|
||||
instance, this.level,
|
||||
this.x + 2, this.y - 6);
|
||||
case SOUTH -> this.useItem.render(
|
||||
instance, this.level,
|
||||
this.x + 2, this.y + this.height);
|
||||
case EAST -> this.useItem.render(
|
||||
instance, this.level,
|
||||
this.x + this.width, this.y + 2);
|
||||
case WEST -> this.useItem.render(
|
||||
instance, this.level,
|
||||
this.x - 6, this.y + 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hitTicks > 0) {
|
||||
Renderer.render(HIT_SPRITE_X, HIT_SPRITE_Y,
|
||||
this.hitX + 0, this.hitY + 0, HIT_COLOR, Renderer.FLIP_Y);
|
||||
Renderer.render(HIT_SPRITE_X, HIT_SPRITE_Y,
|
||||
this.hitX + 8, this.hitY + 0, HIT_COLOR, Renderer.FLIP_XY);
|
||||
Renderer.render(HIT_SPRITE_X, HIT_SPRITE_Y,
|
||||
this.hitX + 0, this.hitY + 8, HIT_COLOR, Renderer.FLIP_NONE);
|
||||
Renderer.render(HIT_SPRITE_X, HIT_SPRITE_Y,
|
||||
this.hitX + 8, this.hitY + 8, HIT_COLOR, Renderer.FLIP_X);
|
||||
} else if (this.actionTicks > 0) {
|
||||
int px = this.x + this.getRenderOffsetX(), py = this.y + this.getRenderOffsetY();
|
||||
|
||||
switch (this.direction) {
|
||||
case NORTH -> {
|
||||
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y,
|
||||
px, py - 4, ACTION_COLOR, Renderer.FLIP_X);
|
||||
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y,
|
||||
px + 8, py - 4, ACTION_COLOR, Renderer.FLIP_NONE);
|
||||
}
|
||||
case SOUTH -> {
|
||||
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y,
|
||||
px, py + this.height + 1, ACTION_COLOR, Renderer.FLIP_XY);
|
||||
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y,
|
||||
px + 8, py + this.height + 1, ACTION_COLOR, Renderer.FLIP_Y);
|
||||
}
|
||||
case EAST -> {
|
||||
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y + 1,
|
||||
px + this.width, py + 0, ACTION_COLOR, Renderer.FLIP_NONE);
|
||||
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y + 1,
|
||||
px + this.width, py + 8, ACTION_COLOR, Renderer.FLIP_Y);
|
||||
}
|
||||
case WEST -> {
|
||||
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y + 1,
|
||||
px - 4, py + 0, ACTION_COLOR, Renderer.FLIP_X);
|
||||
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y + 1,
|
||||
px - 4, py + 8, ACTION_COLOR, Renderer.FLIP_XY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void heal(int amount) {
|
||||
assert (amount >= 0);
|
||||
this.health = Math.min(this.health + amount, this.getMaxHealth());
|
||||
}
|
||||
|
||||
// hurt from entity
|
||||
public boolean hurt(int amount, Entity e) {
|
||||
return this.hurt(
|
||||
amount,
|
||||
this.moving ?
|
||||
this.getDirection().opposite() :
|
||||
Direction.get(this.x - e.x, this.y - e.y)
|
||||
);
|
||||
}
|
||||
|
||||
// hurt with knockback
|
||||
public boolean hurt(int amount, Direction direction) {
|
||||
if (this.hurt(amount)) {
|
||||
this.knockback(4.0, direction);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hurt(int amount) {
|
||||
assert (amount >= 0);
|
||||
if (this.invulnerableTicks > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EntityTextParticle.spawn(
|
||||
this.level, this.x, this.y,
|
||||
Integer.toString(amount),
|
||||
amount == 0 ? 114 : 500);
|
||||
|
||||
this.invulnerableTicks = INVULNERABLE_TICKS;
|
||||
this.health -= amount;
|
||||
|
||||
if (this.health <= 0) {
|
||||
this.die();
|
||||
this.remove();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// amount is not exact, it's a multiplier. the true amount is random.
|
||||
public boolean takeApproxStamina(int amount) {
|
||||
return this.takeStamina(amount - 1 + Global.random.nextInt(1 + amount + (amount / 2)));
|
||||
}
|
||||
|
||||
// returns true if stamina was taken
|
||||
public boolean takeStamina(int amount) {
|
||||
if (this.stamina - amount >= 0) {
|
||||
this.stamina -= amount;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void rechargeStamina() {
|
||||
if (this.swimming) {
|
||||
return;
|
||||
}
|
||||
|
||||
double rr = this.getStaminaRechargeRate();
|
||||
if (rr >= 1.0) {
|
||||
this.stamina += (int) rr;
|
||||
} else if (Global.ticks % ((int) (1.0 / this.getStaminaRechargeRate())) == 0) {
|
||||
this.stamina++;
|
||||
}
|
||||
|
||||
this.stamina = FMath.clamp(this.stamina, 1, this.getMaxStamina());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
this.invulnerableTicks = Math.max(this.invulnerableTicks - 1, 0);
|
||||
this.hitTicks = Math.max(this.hitTicks - 1, 0);
|
||||
this.actionTicks = Math.max(this.actionTicks - 1, 0);
|
||||
this.useItemTicks = Math.max(this.useItemTicks - 1, 0);
|
||||
|
||||
this.staminaRechargeDelayTicks = Math.max(this.staminaRechargeDelayTicks - 1, 0);
|
||||
|
||||
if (this.staminaRechargeDelayTicks > 0) {
|
||||
this.staminaRechargeDelayTicks--;
|
||||
|
||||
if (this.staminaRechargeDelayTicks == 0) {
|
||||
this.rechargeStamina();
|
||||
}
|
||||
} else if (this.stamina <= 0) {
|
||||
this.staminaRechargeDelayTicks = STAMINA_RECHARGE_DELAY_TICKS;
|
||||
this.stamina = 0;
|
||||
} else if (this.stamina != this.getMaxStamina()) {
|
||||
this.rechargeStamina();
|
||||
}
|
||||
}
|
||||
|
||||
public void die() {
|
||||
|
||||
}
|
||||
|
||||
// per tick
|
||||
public abstract double getStaminaRechargeRate();
|
||||
|
||||
public abstract int getMaxStamina();
|
||||
|
||||
public abstract int getMaxHealth();
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.jdh.microcraft.entity.mob;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityItem;
|
||||
import com.jdh.microcraft.entity.EntityPlayer;
|
||||
import com.jdh.microcraft.entity.ai.AISkeleton;
|
||||
import com.jdh.microcraft.entity.particle.EntitySmashParticle;
|
||||
import com.jdh.microcraft.entity.projectile.EntityArrow;
|
||||
import com.jdh.microcraft.gfx.Color;
|
||||
import com.jdh.microcraft.item.Inventory;
|
||||
import com.jdh.microcraft.item.Item;
|
||||
import com.jdh.microcraft.item.ItemInstance;
|
||||
import com.jdh.microcraft.item.ItemStack;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
import com.jdh.microcraft.level.tile.Tile;
|
||||
|
||||
public class EntitySkeleton extends EntityHumanoid {
|
||||
private static final Item[] LOOT_TABLE = {
|
||||
Item.GEM, Item.GOLD_INGOT,
|
||||
Item.COAL, Item.COAL, Item.IRON_INGOT, Item.IRON_INGOT,
|
||||
Item.COAL, Item.COAL, Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE,
|
||||
Item.GLASS, Item.GLASS, Item.GLASS, Item.PIE, Item.IRON_ORE,
|
||||
Item.ROCK, Item.ROCK, Item.ROCK, Item.ROCK, Item.SAND, Item.SAND, Item.SAND
|
||||
};
|
||||
|
||||
private long lastShotTicks;
|
||||
public final double strength;
|
||||
|
||||
public EntitySkeleton(Level level, int x, int y, double strength) {
|
||||
super(level, new Inventory(4));
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.strength = strength * Global.game.difficulty;
|
||||
this.ai = new AISkeleton(this);
|
||||
this.health = this.getMaxHealth();
|
||||
this.stamina = this.getMaxStamina();
|
||||
}
|
||||
|
||||
public static EntitySkeleton createSkeleton(Level level) {
|
||||
EntitySkeleton skeleton = new EntitySkeleton(level, 0, 0, 1.0);
|
||||
EntityHumanoid.giveRandomEquipment(skeleton, 1, true, false);
|
||||
return skeleton;
|
||||
}
|
||||
|
||||
private boolean canShoot() {
|
||||
return (Global.ticks - this.lastShotTicks) >= Math.max(180 - (30 * this.strength), 80);
|
||||
}
|
||||
|
||||
public void shoot(int dx, int dy) {
|
||||
if (Tile.TILES[this.level.getTile(this.getFacingTileX(), this.getFacingTileY())].isSolid()) {
|
||||
// only shoot if facing tile is empty
|
||||
return;
|
||||
} else if (!this.canShoot()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateAnimationFrame(true);
|
||||
this.lastShotTicks = Global.ticks;
|
||||
this.level.addEntity(new EntityArrow(
|
||||
this.level, this,
|
||||
this.getCenterX(), this.getCenterY(),
|
||||
dx, dy,
|
||||
2.0,
|
||||
(int) (1 * this.strength + Global.random.nextInt((int) (2 * this.strength)))
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getAnimationFrameTicks() {
|
||||
return Math.max(12 - ((int) (2.0 / this.strength)), 6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die() {
|
||||
super.die();
|
||||
EntitySmashParticle.spawn(this.level, this.x, this.y, this.getColor(), 4, 8);
|
||||
|
||||
for (int i = Global.random.nextInt(3); i > 0; i--) {
|
||||
EntityItem.spawn(
|
||||
this.level,
|
||||
new ItemStack(new ItemInstance(Item.BONE)),
|
||||
this.x, this.y
|
||||
);
|
||||
}
|
||||
|
||||
if (Global.random.nextBoolean()) {
|
||||
EntityItem.spawn(
|
||||
this.level,
|
||||
new ItemStack(new ItemInstance(LOOT_TABLE[Global.random.nextInt(LOOT_TABLE.length)], 0), 1),
|
||||
this.x, this.y
|
||||
);
|
||||
}
|
||||
|
||||
Global.game.score += 25 * this.strength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHitDamage(Entity e) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collide(Entity e) {
|
||||
super.collide(e);
|
||||
|
||||
if (e instanceof EntityPlayer) {
|
||||
this.hit(e);
|
||||
e.onHit(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColor() {
|
||||
return Color.get(331, 555, 555, 554);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHurtColor() {
|
||||
return Color.get(444, 555, 555, 554);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getStaminaRechargeRate() {
|
||||
return 0.2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStamina() {
|
||||
return (int) (this.strength * 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHealth() {
|
||||
return (int) (this.strength * 10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package com.jdh.microcraft.entity.mob;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityItem;
|
||||
import com.jdh.microcraft.entity.EntityPlayer;
|
||||
import com.jdh.microcraft.entity.ai.AISlime;
|
||||
import com.jdh.microcraft.entity.particle.EntitySmashParticle;
|
||||
import com.jdh.microcraft.gfx.Color;
|
||||
import com.jdh.microcraft.gfx.Renderer;
|
||||
import com.jdh.microcraft.item.Inventory;
|
||||
import com.jdh.microcraft.item.Item;
|
||||
import com.jdh.microcraft.item.ItemInstance;
|
||||
import com.jdh.microcraft.item.ItemStack;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
import com.jdh.microcraft.level.tile.Tile;
|
||||
import com.jdh.microcraft.util.Direction;
|
||||
import com.jdh.microcraft.util.FMath;
|
||||
|
||||
public class EntitySlime extends EntityMob {
|
||||
private static final int BASE_SPRITE_X = 16, BASE_SPRITE_Y = 3;
|
||||
|
||||
public boolean jumping;
|
||||
private double vx, vy, vz;
|
||||
|
||||
private final boolean jumpTrail;
|
||||
private final int color, hitColor, trailColor;
|
||||
public final double strength;
|
||||
|
||||
public EntitySlime(Level level, int x, int y, int color, int hitColor,
|
||||
boolean jumpTrail, int trailColor, double strength) {
|
||||
super(level, Inventory.NONE);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.color = color;
|
||||
this.hitColor = hitColor;
|
||||
this.jumpTrail = jumpTrail;
|
||||
this.trailColor = trailColor;
|
||||
this.strength = strength * Global.game.difficulty;
|
||||
this.width = 12;
|
||||
this.height = 8;
|
||||
this.health = this.getMaxHealth();
|
||||
this.stamina = this.getMaxStamina();
|
||||
this.ai = new AISlime(this);
|
||||
}
|
||||
|
||||
public static EntitySlime createGreenSlime(Level level) {
|
||||
return new EntitySlime(
|
||||
level,
|
||||
0, 0,
|
||||
Color.get(010, 020, 151, 353),
|
||||
Color.get(010, 020, 353, 454),
|
||||
false, 0,
|
||||
0.6
|
||||
);
|
||||
}
|
||||
|
||||
public static EntitySlime createBlueSlime(Level level) {
|
||||
return new EntitySlime(
|
||||
level,
|
||||
0, 0,
|
||||
Color.get(001, 002, 114, 225),
|
||||
Color.get(001, 002, 335, 445),
|
||||
false, 0,
|
||||
1.0
|
||||
);
|
||||
}
|
||||
|
||||
public static EntitySlime createRedSlime(Level level) {
|
||||
return new EntitySlime(
|
||||
level,
|
||||
0, 0,
|
||||
Color.get(100, 200, 411, 522),
|
||||
Color.get(100, 200, 533, 544),
|
||||
true, Color.get(330, 440, 550, 551),
|
||||
1.15
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHitDamage(Entity e) {
|
||||
return (int) (this.strength + Global.random.nextInt((int) (4 * this.strength)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collide(Entity e) {
|
||||
super.collide(e);
|
||||
|
||||
if (e instanceof EntityPlayer) {
|
||||
this.hit(e);
|
||||
e.onHit(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die() {
|
||||
super.die();
|
||||
EntitySmashParticle.spawn(this.level, this.x, this.y, this.color, 4, 10);
|
||||
|
||||
for (int i = 1 + Global.random.nextInt(1 + (2 * (int) this.strength)); i > 0; i--) {
|
||||
EntityItem.spawn(
|
||||
this.level,
|
||||
new ItemStack(new ItemInstance(Item.SLIME, 0), 1),
|
||||
this.x, this.y
|
||||
);
|
||||
}
|
||||
|
||||
Global.game.score += 20 * this.strength;
|
||||
}
|
||||
|
||||
public void jump(double dx, double dy, double v) {
|
||||
double l = FMath.norm(dx, dy);
|
||||
this.jumping = true;
|
||||
this.vx = 5.0 * (dx / l) * v;
|
||||
this.vy = 5.0 * (dy / l) * v;
|
||||
this.vz = 5.0 * v;
|
||||
}
|
||||
|
||||
public void jump(Direction d, double v) {
|
||||
this.jump(
|
||||
d.x + (d == Direction.NORTH || d == Direction.SOUTH ? 0.1 : 0.0),
|
||||
d.y + (d == Direction.EAST || d == Direction.WEST ? 0.3 : 0.0),
|
||||
v
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if (this.jumping && this.jumpTrail && Global.random.nextInt(6) == 0) {
|
||||
EntitySmashParticle.spawn(this.level, this.x, this.y, this.trailColor, 2, 4);
|
||||
}
|
||||
|
||||
if (this.vz > 0.0) {
|
||||
this.vx *= 0.97;
|
||||
this.vy *= 0.97;
|
||||
this.vz -= 0.11;
|
||||
} else if (this.vz <= 0.0) {
|
||||
this.jumping = false;
|
||||
this.vz = 0.0;
|
||||
this.vx *= 0.6;
|
||||
this.vy *= 0.6;
|
||||
}
|
||||
|
||||
if (Math.abs(this.vx) < 0.001) {
|
||||
this.vx = 0.0;
|
||||
}
|
||||
|
||||
if (Math.abs(this.vy) < 0.001) {
|
||||
this.vy = 0.0;
|
||||
}
|
||||
|
||||
this.move(
|
||||
FMath.tickedDoubleToInt(Global.ticks, this.vx),
|
||||
FMath.tickedDoubleToInt(Global.ticks, this.vy)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderOffsetX() {
|
||||
return -2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderOffsetY() {
|
||||
return -8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
super.render();
|
||||
|
||||
Global.random.setSeed(this.id);
|
||||
int color = this.invulnerableTicks > 0 && ((Global.ticks / 4) % 2) == 0 ? this.hitColor : this.color;
|
||||
boolean sprite = this.jumping || ((Global.ticks + this.id) / 30) % 2 == 0;
|
||||
int sx = sprite ? BASE_SPRITE_X + 2 : BASE_SPRITE_X, sy = BASE_SPRITE_Y;
|
||||
for (int i = 0; i <= 1; i++) {
|
||||
for (int j = 0; j <= 1; j++) {
|
||||
Renderer.render(
|
||||
sx + i, sy + j,
|
||||
this.x + this.getRenderOffsetX() + (i * 8),
|
||||
this.y + this.getRenderOffsetY() + (j * 8),
|
||||
color, Renderer.FLIP_NONE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getStaminaRechargeRate() {
|
||||
return 0.2 * this.strength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStamina() {
|
||||
return (int) (10 * this.strength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHealth() {
|
||||
return (int) (10 * this.strength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSwimIn(Tile tile, int x, int y) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.jdh.microcraft.entity.mob;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityItem;
|
||||
import com.jdh.microcraft.entity.EntityPlayer;
|
||||
import com.jdh.microcraft.entity.ai.AIZombie;
|
||||
import com.jdh.microcraft.entity.particle.EntitySmashParticle;
|
||||
import com.jdh.microcraft.gfx.Color;
|
||||
import com.jdh.microcraft.item.Inventory;
|
||||
import com.jdh.microcraft.item.Item;
|
||||
import com.jdh.microcraft.item.ItemInstance;
|
||||
import com.jdh.microcraft.item.ItemStack;
|
||||
import com.jdh.microcraft.item.armor.ItemArmor;
|
||||
import com.jdh.microcraft.item.tool.ItemTool;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
public class EntityZombie extends EntityHumanoid {
|
||||
private static final Item[] LOOT_TABLE = {
|
||||
Item.GEM, Item.GOLD_INGOT, Item.MITHRIL_INGOT,
|
||||
Item.IRON_INGOT, Item.IRON_INGOT, Item.IRON_INGOT, Item.IRON_INGOT,
|
||||
Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE,
|
||||
Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE,
|
||||
Item.ROCK, Item.ROCK, Item.ROCK, Item.ROCK, Item.ROCK, Item.ROCK, Item.ROCK
|
||||
};
|
||||
|
||||
private final int pantsColor, shirtColor;
|
||||
public final double strength;
|
||||
|
||||
public EntityZombie(Level level, int x, int y, double strength) {
|
||||
super(level, new Inventory(5));
|
||||
this.strength = strength * Global.game.difficulty;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.pantsColor = Color.randomRGB(1, 5);
|
||||
this.shirtColor = Color.randomRGB(1, 5);
|
||||
this.ai = new AIZombie(this);
|
||||
this.health = this.getMaxHealth();
|
||||
this.stamina = this.getMaxStamina();
|
||||
}
|
||||
|
||||
public static EntityZombie createEasyZombie(Level level) {
|
||||
EntityZombie zombie = new EntityZombie(
|
||||
level, 0, 0, 0.9
|
||||
);
|
||||
EntityHumanoid.giveRandomEquipment(zombie, 1, true, true);
|
||||
return zombie;
|
||||
}
|
||||
|
||||
public static EntityZombie createMediumZombie(Level level) {
|
||||
EntityZombie zombie = new EntityZombie(
|
||||
level, 0, 0, 1.0
|
||||
);
|
||||
EntityHumanoid.giveRandomEquipment(zombie, 2, true, true);
|
||||
return zombie;
|
||||
}
|
||||
|
||||
|
||||
public static EntityZombie createHardZombie(Level level) {
|
||||
EntityZombie zombie = new EntityZombie(
|
||||
level, 0, 0, 1.15
|
||||
);
|
||||
EntityHumanoid.giveRandomEquipment(zombie, 3, true, true);
|
||||
return zombie;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected int getAnimationFrameTicks() {
|
||||
return Math.max(10 - ((int) (2.0 / this.strength)), 6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die() {
|
||||
super.die();
|
||||
EntitySmashParticle.spawn(this.level, this.x, this.y, this.getColor(), 4, 8);
|
||||
|
||||
if (Global.random.nextBoolean()) {
|
||||
EntityItem.spawn(
|
||||
this.level,
|
||||
new ItemStack(new ItemInstance(LOOT_TABLE[Global.random.nextInt(LOOT_TABLE.length)], 0), 1),
|
||||
this.x, this.y
|
||||
);
|
||||
}
|
||||
|
||||
Global.game.score += 30 * this.strength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHitDamage(Entity e) {
|
||||
return (int) (super.getHitDamage(e) * this.strength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collide(Entity e) {
|
||||
super.collide(e);
|
||||
|
||||
if (e instanceof EntityPlayer) {
|
||||
this.hit(e);
|
||||
e.onHit(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPickup(EntityItem e) {
|
||||
return (e.stack.instance.item instanceof ItemTool && this.equipped == null) ||
|
||||
(e.stack.instance.item instanceof ItemArmor &&
|
||||
this.armor[((ItemArmor) e.stack.instance.item).slot] == null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pickup(ItemStack stack) {
|
||||
super.pickup(stack);
|
||||
if (stack.instance.item instanceof ItemTool && this.equipped == null) {
|
||||
this.equipped = stack;
|
||||
} else if (stack.instance.item instanceof ItemArmor) {
|
||||
ItemArmor armor = (ItemArmor) stack.instance.item;
|
||||
if (this.armor[armor.slot] == null) {
|
||||
this.armor[armor.slot] = stack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColor() {
|
||||
return Color.get(000, this.pantsColor, this.shirtColor, 141);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHurtColor() {
|
||||
return Color.get(544, Color.add(this.pantsColor, 222), Color.add(this.shirtColor, 222), 555);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getStaminaRechargeRate() {
|
||||
return 0.15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStamina() {
|
||||
return (int) (10 * this.strength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHealth() {
|
||||
return (int) (10 * this.strength);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.jdh.microcraft.entity.particle;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
import com.jdh.microcraft.level.tile.Tile;
|
||||
|
||||
public class EntityParticle extends Entity {
|
||||
protected int timeToLive;
|
||||
protected double vx, vy, dx, dy;
|
||||
protected boolean gravity;
|
||||
|
||||
public EntityParticle(Level level, int x, int y) {
|
||||
super(level);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = 2;
|
||||
this.height = 2;
|
||||
this.timeToLive = 45 + Global.random.nextInt(60);
|
||||
this.vx = (Global.random.nextBoolean() ? -1.0 : 1.0) * (0.2 + (Global.random.nextDouble() * 0.4));
|
||||
this.vy = -(0.8 + (Global.random.nextDouble() * 0.5));
|
||||
this.gravity = true;
|
||||
}
|
||||
|
||||
protected int getRenderX() {
|
||||
return (int) (this.x + this.dx);
|
||||
}
|
||||
|
||||
protected int getRenderY() {
|
||||
return (int) (this.y + this.dy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (--this.timeToLive == 0) {
|
||||
this.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
this.dx += this.vx;
|
||||
this.dy += this.vy;
|
||||
|
||||
if (this.gravity && this.dy >= 8.0) {
|
||||
// bounce at bottom
|
||||
this.vx *= 0.3;
|
||||
this.vy *= (this.vy > 0 ? -1.0 : 1.0) * 0.4;
|
||||
} else if (Math.abs(this.vy) < 0.01) {
|
||||
// clamp small values
|
||||
this.vx = 0.0;
|
||||
this.vy = 0.0;
|
||||
} else if (this.gravity) {
|
||||
// gravity
|
||||
this.vy += 0.1;
|
||||
}
|
||||
|
||||
if (Math.abs(this.vx) < 0.01) {
|
||||
this.vx = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collides(Entity e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSwimIn(Tile tile, int x, int y) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.jdh.microcraft.entity.particle;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.gfx.Renderer;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
public class EntitySmashParticle extends EntityParticle {
|
||||
private static final int SPRITE_X = 12, SPRITE_Y = 2;
|
||||
|
||||
private final int color;
|
||||
|
||||
public EntitySmashParticle(Level level, int x, int y, int color) {
|
||||
super(level, x, y);
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public static void spawn(Level level, int x, int y, int color, int min, int max) {
|
||||
int n = min + Global.random.nextInt(max - min + 1);
|
||||
for (int i = 0; i < n; i++) {
|
||||
level.addEntity(new EntitySmashParticle(level, x, y, color));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Global.random.setSeed(this.id);
|
||||
Renderer.render(
|
||||
SPRITE_X, SPRITE_Y, this.getRenderX(), this.getRenderY(), color,
|
||||
(Global.random.nextBoolean() ? Renderer.FLIP_X : Renderer.FLIP_NONE) |
|
||||
(Global.random.nextBoolean() ? Renderer.FLIP_Y : Renderer.FLIP_NONE)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.jdh.microcraft.entity.particle;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.gfx.Renderer;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
public class EntitySmokeParticle extends EntityParticle {
|
||||
private static final int SPRITE_X = 12, SPRITE_Y = 2;
|
||||
|
||||
private final int color;
|
||||
|
||||
public EntitySmokeParticle(Level level, int x, int y, int color) {
|
||||
super(level, x, y);
|
||||
this.color = color;
|
||||
this.gravity = false;
|
||||
this.vx = (Global.random.nextBoolean() ? -1.0 : 1.0) * (0.1 + (Global.random.nextDouble() * 0.1));
|
||||
this.vy = -(0.4 + (Global.random.nextDouble() * 0.3));
|
||||
}
|
||||
|
||||
public static void spawn(Level level, int x, int y, int color, int min, int max) {
|
||||
int n = min + Global.random.nextInt(max - min + 1);
|
||||
for (int i = 0; i < n; i++) {
|
||||
level.addEntity(new EntitySmokeParticle(level, x, y, color));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Global.random.setSeed(this.id);
|
||||
Renderer.render(
|
||||
SPRITE_X, SPRITE_Y, this.getRenderX(), this.getRenderY(), color,
|
||||
(Global.random.nextBoolean() ? Renderer.FLIP_X : Renderer.FLIP_NONE) |
|
||||
(Global.random.nextBoolean() ? Renderer.FLIP_Y : Renderer.FLIP_NONE)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.jdh.microcraft.entity.particle;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.gfx.Color;
|
||||
import com.jdh.microcraft.gfx.Font;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
public class EntityTextParticle extends EntityParticle {
|
||||
private final String text;
|
||||
private final int color;
|
||||
|
||||
public EntityTextParticle(Level level, int x, int y, String text, int color) {
|
||||
super(level, x, y);
|
||||
this.text = text;
|
||||
this.color = color;
|
||||
this.timeToLive = 30 + Global.random.nextInt(30);
|
||||
}
|
||||
|
||||
public static void spawn(Level level, int x, int y, String text, int color) {
|
||||
level.addEntity(new EntityTextParticle(level, x, y, text, color));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Global.random.setSeed(this.id);
|
||||
Font.render(this.text, this.getRenderX() + 1, this.getRenderY() + 1, Color.add(this.color, -222));
|
||||
Font.render(this.text, this.getRenderX(), this.getRenderY(), this.color);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.jdh.microcraft.entity.projectile;
|
||||
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityItem;
|
||||
import com.jdh.microcraft.entity.mob.EntityMob;
|
||||
import com.jdh.microcraft.gfx.Color;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
public class EntityAirBlast extends EntityProjectile {
|
||||
public EntityAirBlast(Level level, Entity shooter, int x, int y, int dx, int dy, double s, int damage) {
|
||||
super(level, shooter, x, y, dx, dy, s, damage, 180);
|
||||
this.width = 4;
|
||||
this.height = 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hit(Entity e) {
|
||||
if (e instanceof EntityItem) {
|
||||
e.remove();
|
||||
return true;
|
||||
} else if (!(e instanceof EntityMob)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
((EntityMob) e).hurt(this.damage, this);
|
||||
this.remove();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean moveAxis(int dx, int dy) {
|
||||
if (!super.moveAxis(dx, dy)) {
|
||||
this.remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColor() {
|
||||
return Color.get(222, 333, 445, 555);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHorizontalSpriteX() {
|
||||
return 14;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHorizontalSpriteY() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVerticalSpriteX() {
|
||||
return 14;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVerticalSpriteY() {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.jdh.microcraft.entity.projectile;
|
||||
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityItem;
|
||||
import com.jdh.microcraft.entity.mob.EntityMob;
|
||||
import com.jdh.microcraft.gfx.Color;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
|
||||
public class EntityArrow extends EntityProjectile {
|
||||
public EntityArrow(Level level, Entity shooter, int x, int y, int dx, int dy, double s, int damage) {
|
||||
super(level, shooter, x, y, dx, dy, s, damage, 180);
|
||||
this.width = 4;
|
||||
this.height = 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hit(Entity e) {
|
||||
if (e instanceof EntityItem) {
|
||||
e.remove();
|
||||
return true;
|
||||
} else if (!(e instanceof EntityMob)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
((EntityMob) e).hurt(this.damage, this);
|
||||
this.remove();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColor() {
|
||||
return Color.get(220, 330, 333, 444);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHorizontalSpriteX() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHorizontalSpriteY() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVerticalSpriteX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVerticalSpriteY() {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.jdh.microcraft.entity.projectile;
|
||||
|
||||
import com.jdh.microcraft.Global;
|
||||
import com.jdh.microcraft.entity.Entity;
|
||||
import com.jdh.microcraft.entity.EntityItem;
|
||||
import com.jdh.microcraft.entity.furniture.EntityFurnace;
|
||||
import com.jdh.microcraft.entity.furniture.EntityFurniture;
|
||||
import com.jdh.microcraft.entity.mob.EntityMob;
|
||||
import com.jdh.microcraft.gfx.Renderer;
|
||||
import com.jdh.microcraft.level.Level;
|
||||
import com.jdh.microcraft.level.tile.Tile;
|
||||
import com.jdh.microcraft.util.Direction;
|
||||
import com.jdh.microcraft.util.FMath;
|
||||
|
||||
public abstract class EntityProjectile extends Entity {
|
||||
protected final int damage;
|
||||
protected final Entity shooter;
|
||||
private int timeToLive;
|
||||
private double vx, vy;
|
||||
|
||||
public EntityProjectile(Level level, Entity shooter, int x, int y, int dx, int dy, double s, int damage, int ttl) {
|
||||
super(level);
|
||||
this.shooter = shooter;
|
||||
this.damage = damage;
|
||||
this.timeToLive = ttl;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
// compute velocities in direction of dx, dy
|
||||
double xx = FMath.sign(dx - this.x),
|
||||
yy = FMath.sign(dy - this.y),
|
||||
l = FMath.norm(xx, yy);
|
||||
|
||||
xx = (xx / l) * s;
|
||||
yy = (yy / l) * s;
|
||||
this.vx = Double.isNaN(xx) ? 0 : xx;
|
||||
this.vy = Double.isNaN(yy) ? 0 : yy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
this.move(
|
||||
FMath.tickedDoubleToInt(Global.ticks, this.vx),
|
||||
FMath.tickedDoubleToInt(Global.ticks, this.vy)
|
||||
);
|
||||
|
||||
if (--this.timeToLive == 0) {
|
||||
this.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
int sx = 0, sy = 0, flip = 0;
|
||||
|
||||
switch (this.direction) {
|
||||
case NORTH, SOUTH -> {
|
||||
sx = this.getVerticalSpriteX();
|
||||
sy = this.getVerticalSpriteY();
|
||||
flip = this.direction == Direction.SOUTH ? Renderer.FLIP_Y : 0;
|
||||
}
|
||||
case EAST, WEST -> {
|
||||
sx = this.getHorizontalSpriteX();
|
||||
sy = this.getHorizontalSpriteY();
|
||||
flip = this.direction == Direction.WEST ? Renderer.FLIP_X : 0;
|
||||
}
|
||||
}
|
||||
|
||||
Renderer.render(
|
||||
sx, sy,
|
||||
this.x + this.getRenderOffsetX(), this.y + this.getRenderOffsetY(),
|
||||
this.getColor(), flip
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collides(Entity e) {
|
||||
return e != this.shooter &&
|
||||
!(e instanceof EntityProjectile) &&
|
||||
(Math.abs(this.vx) > 0.0 || Math.abs(this.vy) > 0.0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collide(Entity e) {
|
||||
super.collide(e);
|
||||
|
||||
if (this.hit(e)) {
|
||||
e.onHit(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean moveAxis(int dx, int dy) {
|
||||
if (!super.moveAxis(dx, dy)) {
|
||||
this.vx = 0.0;
|
||||
this.vy = 0.0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSwimIn(Tile tile, int x, int y) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean hit(Entity e);
|
||||
|
||||
public abstract int getColor();
|
||||
|
||||
protected abstract int getHorizontalSpriteX();
|
||||
|
||||
protected abstract int getHorizontalSpriteY();
|
||||
|
||||
protected abstract int getVerticalSpriteX();
|
||||
|
||||
protected abstract int getVerticalSpriteY();
|
||||
}
|
||||
Reference in New Issue
Block a user