Compare commits

...

6 Commits

Author SHA1 Message Date
43212a033b slime runs away when it gets hit 2025-11-30 00:49:04 +01:00
2ed21add1c make hp bar hideable 2025-11-30 00:44:12 +01:00
8ec1805877 added health bar to monster 2025-11-30 00:38:00 +01:00
9aae9ad20f add death animation to dying monsters 2025-11-30 00:13:54 +01:00
c55bf8d043 re-arranged some variables 2025-11-29 23:40:47 +01:00
8a228685b1 fixed arrangement 2025-11-29 23:33:53 +01:00
6 changed files with 81 additions and 26 deletions

View File

@@ -17,10 +17,8 @@ public class GamePanel extends JPanel implements Runnable {
// SCREEN SETTINGS // SCREEN SETTINGS
final int originalTileSize = 16; //16x16 tile final int originalTileSize = 16; //16x16 tile
final int scale = 3; final int scale = 3;
public final int tileSize = originalTileSize * scale; //48x48 tile
// 4:3 ratio
public final int maxScreenCol = 16; public final int maxScreenCol = 16;
public final int tileSize = originalTileSize * scale; //48x48 tile
public final int maxScreenRow = 12; public final int maxScreenRow = 12;
public final int screenWidth = tileSize * maxScreenCol; // 768 pixels public final int screenWidth = tileSize * maxScreenCol; // 768 pixels
public final int screenHeight = tileSize * maxScreenRow; // 576 pixels public final int screenHeight = tileSize * maxScreenRow; // 576 pixels
@@ -36,12 +34,12 @@ public class GamePanel extends JPanel implements Runnable {
// SYSTEM // SYSTEM
public TileManager tileM = new TileManager(this); public TileManager tileM = new TileManager(this);
public KeyHandler keyH = new KeyHandler(this); public KeyHandler keyH = new KeyHandler(this);
Sound se = new Sound();
Sound music = new Sound();
public CollisionHandler collisionH = new CollisionHandler(this); public CollisionHandler collisionH = new CollisionHandler(this);
public AssetSetter assetSetter = new AssetSetter(this); public AssetSetter assetSetter = new AssetSetter(this);
public UI ui = new UI(this); public UI ui = new UI(this);
public EventHandler eventH = new EventHandler(this); public EventHandler eventH = new EventHandler(this);
Sound se = new Sound();
Sound music = new Sound();
Thread gameThread; Thread gameThread;
// ENTITY AND OBJECT // ENTITY AND OBJECT
@@ -63,7 +61,6 @@ public class GamePanel extends JPanel implements Runnable {
} }
// GAME THREAD / CORE // GAME THREAD / CORE
public void startGameThread() { public void startGameThread() {
gameThread = new Thread(this); gameThread = new Thread(this);
gameThread.start(); gameThread.start();
@@ -103,7 +100,13 @@ public class GamePanel extends JPanel implements Runnable {
case PLAY: case PLAY:
player.update(); player.update();
for(Entity entity : npc) if(entity != null) entity.update(); for(Entity entity : npc) if(entity != null) entity.update();
for(Entity entity : monster) if(entity != null) entity.update(); for(int i = 0; i < monster.length; i++) {
Entity m = monster[i];
if(m != null) {
if(m.alive && !m.dying) m.update();
if(!m.alive) monster[i] = null;
}
}
break; break;
case PAUSE: case PAUSE:
break; break;

View File

@@ -14,7 +14,7 @@ public class UI {
Font arial_40, arial_80B; //TODO: Custom font loader: https://www.youtube.com/watch?v=g-wrebFVP3E Font arial_40, arial_80B; //TODO: Custom font loader: https://www.youtube.com/watch?v=g-wrebFVP3E
BufferedImage heart_full, heart_half, heart_blank; BufferedImage heart_full, heart_half, heart_blank;
public String currentDialogue; public String currentDialogue;
public int commandNum = 0; public int commandNum;
public UI(GamePanel panel) { public UI(GamePanel panel) {
this.panel = panel; this.panel = panel;

View File

@@ -24,6 +24,7 @@ public class Sound {
soundURL[4] = new File("assets/sounds/fanfare.wav").toURI().toURL(); soundURL[4] = new File("assets/sounds/fanfare.wav").toURI().toURL();
soundURL[5] = new File("assets/sounds/hitmonster.wav").toURI().toURL(); soundURL[5] = new File("assets/sounds/hitmonster.wav").toURI().toURL();
soundURL[6] = new File("assets/sounds/receivedamage.wav").toURI().toURL(); soundURL[6] = new File("assets/sounds/receivedamage.wav").toURI().toURL();
soundURL[7] = new File("assets/sounds/blocked.wav").toURI().toURL();
} catch(MalformedURLException e) { } catch(MalformedURLException e) {
Boot.logger.log(Level.SEVERE, e.getMessage()); Boot.logger.log(Level.SEVERE, e.getMessage());
} }

View File

@@ -15,29 +15,34 @@ import java.util.logging.Level;
public class Entity { public class Entity {
GamePanel panel; protected GamePanel panel;
public BufferedImage up1, up2, down1, down2, left1, left2, right1, right2; public BufferedImage up1, up2, down1, down2, left1, left2, right1, right2;
public BufferedImage attackUp1, attackUp2, attackDown1, attackDown2, attackLeft1, attackLeft2, attackRight1, attackRight2; public BufferedImage attackUp1, attackUp2, attackDown1, attackDown2, attackLeft1, attackLeft2, attackRight1, attackRight2;
public BufferedImage image, image2, image3; public BufferedImage image, image2, image3;
public Rectangle solidArea = new Rectangle(0, 0, 48, 48); public Rectangle solidArea = new Rectangle(0, 0, 48, 48);
public Rectangle attackArea = new Rectangle(0, 0, 0, 0); public Rectangle attackArea = new Rectangle(0, 0, 0, 0);
public int solidAreaDefaultX, solidAreaDefaultY; public int solidAreaDefaultX, solidAreaDefaultY;
public boolean collision = false; public boolean collision;
String[] dialogue = new String[20]; String[] dialogue = new String[20];
// STATE // STATE
public int worldX, worldY; public int worldX, worldY;
public Direction direction = Direction.DOWN; public Direction direction = Direction.DOWN;
public int spriteNum = 1; public int spriteNum = 1;
int dialogueIndex = 0; int dialogueIndex;
public boolean collisionOn = false; public boolean collisionOn;
public boolean invincible = false; public boolean invincible;
boolean attacking = false; //TODO: https://youtu.be/HL39xRzPpm4?t=551 boolean attacking;
public boolean alive = true;
public boolean dying;
public boolean hpBarOn;
// COUNTER // COUNTER
public int spriteCounter = 0; public int spriteCounter;
public int actionLock = 0; public int actionLock;
public int invincibleCount = 0; public int invincibleCount;
int dyingCount;
int hpBarCounter;
// ATTRIBUTES // ATTRIBUTES
public EntityType type; public EntityType type;
@@ -62,6 +67,7 @@ public class Entity {
if(this.type == EntityType.MONSTER && contactPlayer) { if(this.type == EntityType.MONSTER && contactPlayer) {
if(panel.player.invincible) return; if(panel.player.invincible) return;
panel.playSE(6);
panel.player.life -= 1; panel.player.life -= 1;
panel.player.invincible = true; panel.player.invincible = true;
} }
@@ -100,23 +106,43 @@ public class Entity {
worldY + panel.tileSize > panel.player.worldY - panel.player.screenY && worldY + panel.tileSize > panel.player.worldY - panel.player.screenY &&
worldY - panel.tileSize < panel.player.worldY + panel.player.screenY worldY - panel.tileSize < panel.player.worldY + panel.player.screenY
) { ) {
if(invincible) {
graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f)); // MONSTER HP-BAR
if(this.type == EntityType.MONSTER && hpBarOn) {
graphics2d.setColor(new Color(35, 35, 35));
graphics2d.fillRect(screenX-1, screenY-6, panel.tileSize+2, 12);
graphics2d.setColor(new Color(255, 0, 30));
graphics2d.fillRect(screenX, screenY-5, (int) ((double) panel.tileSize/maxLife)*life, 10);
hpBarCounter++;
if(hpBarCounter > 600) { //bar disappears after 10 seconds
hpBarCounter = 0;
hpBarOn = false;
}
} }
// only modify sprite render position for player because I dont know yet how monster attack sprite are gonna look // DRAW ENTITY
if(type == EntityType.PLAYER) { if(invincible) {
hpBarOn = true;
hpBarCounter = 0;
changeOpacity(graphics2d, 0.4f);
}
if(dying) dyingAnimation(graphics2d);
if(type == EntityType.PLAYER) { // only modify sprite render position for player because I dont know yet how monster attack sprite are gonna look
if(attacking) graphics2d.drawImage(parseSpriteATK(), if(attacking) graphics2d.drawImage(parseSpriteATK(),
(direction == Direction.LEFT) ? screenX - panel.tileSize : screenX, (direction == Direction.LEFT) ? screenX - panel.tileSize : screenX,
(direction == Direction.UP) ? screenY - panel.tileSize : screenY, null); (direction == Direction.UP) ? screenY - panel.tileSize : screenY, null);
else graphics2d.drawImage(parseSprite(), screenX, screenY, null); else graphics2d.drawImage(parseSprite(), screenX, screenY, null);
} else graphics2d.drawImage(parseSprite(), screenX, screenY, null); } else graphics2d.drawImage(parseSprite(), screenX, screenY, null);
graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f)); changeOpacity(graphics2d, 1f);
} }
} }
// INTERACTION // INTERACTION
public void setAction() {}
public void damageReaction() {};
public void speak() { public void speak() {
if(dialogue[dialogueIndex] == null) dialogueIndex = 0; if(dialogue[dialogueIndex] == null) dialogueIndex = 0;
panel.ui.currentDialogue = dialogue[dialogueIndex]; panel.ui.currentDialogue = dialogue[dialogueIndex];
@@ -129,7 +155,22 @@ public class Entity {
case RIGHT -> direction = Direction.LEFT; case RIGHT -> direction = Direction.LEFT;
} }
} }
public void setAction() {} public void dyingAnimation(Graphics2D graphics2d) {
dyingCount++;
int incr = 5;
if(dyingCount <= incr) changeOpacity(graphics2d, 0f);
if(dyingCount > incr && dyingCount <= incr*2) changeOpacity(graphics2d, 1f);
if(dyingCount > incr*2 && dyingCount <= incr*3) changeOpacity(graphics2d, 0f);
if(dyingCount > incr*3 && dyingCount <= incr*4) changeOpacity(graphics2d, 1f);
if(dyingCount > incr*4 && dyingCount <= incr*5) changeOpacity(graphics2d, 0f);
if(dyingCount > incr*5 && dyingCount <= incr*6) changeOpacity(graphics2d, 1f);
if(dyingCount > incr*6 && dyingCount <= incr*7) changeOpacity(graphics2d, 0f);
if(dyingCount > incr*7 && dyingCount <= incr*8) changeOpacity(graphics2d, 1f);
if(dyingCount > incr*8) {
dying = false;
alive = false;
}
}
// SETTING THINGS UP // SETTING THINGS UP
BufferedImage parseSprite() { BufferedImage parseSprite() {
@@ -164,5 +205,8 @@ public class Entity {
} }
return null; return null;
} }
public void changeOpacity(Graphics2D graphics2d, float opacity) {
graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity));
}
} }

View File

@@ -11,7 +11,6 @@ import java.awt.*;
public class Player extends Entity { public class Player extends Entity {
KeyHandler keyH; KeyHandler keyH;
public final int screenX; public final int screenX;
public final int screenY; public final int screenY;
@@ -156,12 +155,16 @@ public class Player extends Entity {
panel.monster[index].life -= 1; panel.monster[index].life -= 1;
panel.playSE(5); panel.playSE(5);
panel.monster[index].invincible = true; panel.monster[index].invincible = true;
if(panel.monster[index].life <= 0) panel.monster[index] = null; panel.monster[index].damageReaction();
if(panel.monster[index].life <= 0) panel.monster[index].dying = true;
} }
public void interactNPC(int index) { public void interactNPC(int index) {
if(index == 999) { if(index == 999) {
if(panel.keyH.spacePressed) attacking = true; if(panel.keyH.spacePressed) {
attacking = true;
//panel.playSE(7); //remains disabled because game does weird things while playing the sound
}
return; return;
} }
//if(!panel.keyH.spacePressed) return; //Only uncomment if text should only appear if player hits space //if(!panel.keyH.spacePressed) return; //Only uncomment if text should only appear if player hits space

View File

@@ -39,6 +39,10 @@ public class GreenSlimeMON extends Entity {
if(i > 75) direction = Direction.RIGHT; if(i > 75) direction = Direction.RIGHT;
actionLock = 0; actionLock = 0;
} }
public void damageReaction() {
actionLock = 0;
direction = panel.player.direction;
}
// SETTING THINGS UP // SETTING THINGS UP
public void getImage() { public void getImage() {