Monday, August 09, 2021

My Journey So Far - Developing Dungeon Crawler RPG in Java

I've been developing a 3D dungeon crawler RPG called "Rayndia" in Java... for quite a while.

My first dungeon crawler game in Java


It's been a while since I started the development for sure. Actually I started it right after I started learning Java and programming back in 2015.


At first, I just thought learning programming by developing a game would be fun. And 3D dungeon crawler RPGs such as Wizardry was one of my favorite genre so the decision was quite simple. Although I had already made a couple of text-based adventure games at this point I wasn't sure how hard making a dungeon crawler would be and I wasn't sure if I could really make it.

Wizardry I (NES). My favorite game of all time.


At the time I didn't even know how to write programs with multiple classes! So the first prototype of Rayndia was written in a single class and I think the class had more than 15K lines in the end lol

The very early state of the game.
The program was written in a single class!


Then I learned how to split the code into multiple classes. And I think I created 4 or 5 floor maps that were different from the current maps. But then I stopped the development. I'm not really sure why I stopped it but I guess I was somehow satisfied. I could somehow succeed to draw a dungeon and move around in it. I could implement an inventory system and a battle system as well.

I started it to learn Java and I felt like I could learn what I wanted to learn so didn't really need to finish the development. 

I learned how to split code into multiple classes.


Also I wasn't sure how I should handle the monster graphics. I was using my crappy monster illustrations and I knew I needed to replace them at some point but I wasn't sure how to do it. I was developing Rayndia just to learn Java so asking someone to draw illustration felt a bit too much.

My crappy monster graphic lol 
And I was developing the game in Japanese at the time.


So I stopped it and didn't touch the program for like 2 years. 


Then I started my YouTube channel. One day, I talked about my dungeon crawler RPG that I stopped developing a while ago. It was just out of a whim. I just made the video because I just ran out of the topics for the video at the time.




But that video changed the future of Rayndia. To my surprise, the video was received very positively and viewed much more than I expected (13K views as of this writing).

 
The video was received surprisingly well



And a guy who watched the video contacted me via Twitter. His name was Henry (@_henry_wilcox) and he was an illustrator. 

In the video, I said how crappy my illustrations are and drawing monsters are the hardest part of the development. Henry said he can draw illustrations for my game so I said "That would be great!"

My slime illustration


I gave him my crappy sketches and told him what kind of illustrations I like (I showed him some Jun Suemi's work). Then he draw some monsters based on my sketches. 



Kobold by Henry

Firebat by Henry


They were really nice but I realized (or felt) that they didn't fit so well with the simple black and white dungeon graphics. 

A Henry's illustration with the simple dungeon graphics.


Henry's illustrations were good and I thought I should be satisfied considering how bad the original illustrations were. But in the end I asked him if he could try drawing everything in black and white. He said "I was thinking the same". He re-drew everything in black and white. They were beautiful and gave my game new life. 

The black and white monster illustrations fit the game really well.


I was pretty excited but then realized something at this point. 

"Oh wait, now I have to complete this game no matter what." 

Don't get me wrong. It was not a negative feeling but a positive feeling. The game was no longer my learning material. I felt like I got a new goal and motivation to push me further so Henry's work won't be wasted. At this point, Rayndia became a "serious" project for me.


Then one day, I found this picture when I was looking through the internet.

From https://www.ragingswanpress.com


I liked my game's early 80ish simple line-drawing dungeon background and wanting more felt a bit too much. But I couldn't help but imagine how my game would look like if it had a background like this picture. 


So I did some experiment on my end and created some mockup dungeon images (thankfully I had a moderate experience of Photoshop) and shared them with Henry. Based on those images, Henry drew some wall and floor images.

The images that Henry had drawn


I edited the images and merge them with the dungeon graphics. I also added some shadows.



When I saw the result, I was excited.



It was so beautiful and reminded me of those old choose your own adventure books such as The Warlock of Firetop Mountain. I loved it. I reconstructed my dungeon drawing code almost from the scratch for this new graphic style. 


I split the dungeon into multiple parts so the program only needs to draw the parts that need to be updated

I reconstructed the rendering system to optimize the process.


I also created a dungeon map editor to speed up the development (it worked wonderfully).
My dungeon map editor


The graphics are ready. Now I just need to finish the program. As of this writing, I have finished all the floor maps and the game is somewhat playable till the final boss although it's still in a rough state. I still need to adjust tons of parameters such as monster strength, weapon attack, EXP to level up etc. I'm finding this balancing much harder than I had expected. 

I'm using an Excel file to handle the parameters


Sometimes a monster is too strong or a new weapon is too weak... so you need to adjust the parameter... it is a pretty tiresome process since you need to play the game again and again to find a correct balance. But I'm getting there.


Although there were some intervals, it's been 5 years since I started programming this game. It's been a bit too long. It's about time to reach the goal.


*If you feel like following the development, please check my YouTube channel (RyiSnow). I've been posting vlog-style videos on this game :)







Tuesday, August 03, 2021

Creating Legend of Zelda in Java (and my struggle for it)

I tried to re-create NES version's Legend of Zelda in Java.

Legend of Zelda (NES, 1986)


It wasn't so easy but a fun experience. I had developed a few 2D games before but never really tackled on this kind of action-oriented 2D game.


The challenging part:

1. How to create a map

In my previous 2D games, I used a text file to create a map. It's a pretty common way to create a 2D map and you can find a lot of tutorials about this method. However, this is not a great method when you want to create a large map. Not only it requires tons of inputting, since all the tile information is described by numbers, it is kind of hard to visualize the actual map image and which makes editing difficult.

Each number represents tiles.


So I decided to create a map/tile editor. I know there are already many tile editors exist but I thought developing one by myself would be fun. 

My tile editor (version 1)

My tile editor (version 2)


And it is done and I succeeded to re-create the entire Zelda field map. The editor made this process a lot faster and I was really happy about it.

My tile editor (version 3)



2. How to move monsters?

Moving player character only is relatively easy and I had done it before. But moving multiple monsters at the same time while controlling your player character was a new thing for me. The key is how to implement monster movement into the game loop. 

I created a class called DrawMonster (drawM) and instantiated this as an array.


So whenever a monster spawns, create a class like drawM[0], drawM[1]... and when you killed a monster, the class becomes blank. 

The DrawMonster class has methods that handle monster movement, monster hit detection, attack/dying animation, drop items etc.



So the game loop checks these classes in every game loop and update every monster status.



I also created:

DrawPlayer class

DrawObject class

DrawTiles class

DrawUI class

DrawDialogue class

These classes are checked in every game loop and the program updates all the status on the screen.



And here are some screenshots from the resulted game.

Legend of Zelda in Java


Legend of Zelda in Java


Legend of Zelda in Java


If you are interested, please check my video on YouTube as well.


(My Java Zelda is done... kind of)


Creating 2D games from scratch is fun but also pretty confusing. I often felt I was writing the code while I was not 100% sure what I was doing. I was repeating tries and errors and it was like, "Oh, I guess this works (but I'm not really sure why it worked) so let's use this", "Hmm it seems this doesn't work so let's try some other ways..." It was tough to wrap my head around all the classes and understand the whole system. 

I'm hoping to create 2D game tutorial videos to record my progress but I guess I need to sort out what I have learned a bit more before do that. I also feel that there are better ways to handle it. My code works fine but not organized well enough.


Man, programming is fun but tough!


Friday, May 28, 2021

[Java Source Code] Blackjack Game

Here's a sample code to create a Blackjack game in Java.


Blackjack's rule is pretty simple so I think it's a good start for developing card games :)


Screenshots from the game:



The game screen 1


The game screen 2


The game screen 3


Title screen




I used some free images for the title screen and the playing cards. You don't really need the image for the title screen but please prepare playing card images and put them in the res folder.

I got the playing card image from here:

https://chicodeza.com/freeitems/torannpu-illust.html

You can also get images from websites such as pixabay: 
https://pixabay.com/ja/illustrations/search/playing%20cards/



Here's the video preview:



Code structure:





Here's the code:


Game.java
This class handles the gameplay.


import java.awt.Color;
import java.net.URL;
import java.util.Random;

import javax.swing.ImageIcon;

public class Game{
	
	// CLASS
	ActionHandler aHandler = new ActionHandler(this);
	UI ui = new UI(this);
	Cards cards = new Cards();
	Random random = new Random();
	AudioAsset aa = new AudioAsset();
	SE se = new SE();
	Music music = new Music();
	
	// CARD STUFF
	int pickedCardNum;	
	int playerHas = 0;
	int dealerHas = 0;
	int playerCardNum[] = new int[6];
	int dealerCardNum[] = new int[6];	
	int playerCardValue[] = new int[6];
	int dealerCardValue[] = new int[6];	
	int playerTotalValue;
	int dealerTotalValue;	
	
	// OTHERS
	String situation = "";
	ImageIcon dealerSecondCard;
	

	public static void main(String[] args) {
		
		new Game();
	}	
	public void titleToGame() {
		
		ui.titlePanel.setVisible(false);
		ui.startB.setVisible(false);
		ui.exitB.setVisible(false);
		ui.table.setVisible(true);
		ui.dealerPanel.setVisible(true);
		ui.playerPanel.setVisible(true);
		ui.messageText.setVisible(true);
		ui.buttonPanel.setVisible(true);
		ui.getContentPane().setBackground(Color.black);
		
		playMusic(aa.bgm);
		
		startGame();
	}
	public void gameToTitle() {
				
		ui.titlePanel.setVisible(true);
		ui.table.setVisible(false);
		ui.dealerPanel.setVisible(false);
		ui.playerPanel.setVisible(false);
		ui.messageText.setVisible(false);
		ui.buttonPanel.setVisible(false);
		ui.getContentPane().setBackground(new Color(0,81,0));
		
		ui.titlePanel.alphaValue = 0f;
		ui.titlePanel.timer.start();
		
		stopMusic(aa.bgm);
	}
	public void startGame() {
				
		dealerDraw();
		playerDraw();
		dealerDraw();				
		ui.dealerCardLabel[2].setIcon(cards.front);
		ui.dealerScore.setText("Dealer: ?");
		playerTurn();		
	}
	public void dealerDraw() {

		playSE(aa.cardSound01);
		dealerHas++;
		
		ImageIcon pickedCard = pickRandomCard(); // pickRandomCard: return an ImageIcon
		if(dealerHas==2) {
			dealerSecondCard = pickedCard; // Save the second card for revealing it later
		}
		
		dealerCardNum[dealerHas] = pickedCardNum; // Register what card the dealer got		
		dealerCardValue[dealerHas] = checkCardValue();	// Register the card value. If it's from Jack to King, it returns 10
		
		ui.dealerCardLabel[dealerHas].setVisible(true);	
		ui.dealerCardLabel[dealerHas].setIcon(pickedCard);	// Display the card
		
		dealerTotalValue = dealerTotalValue();
		ui.dealerScore.setText("Dealer: " + dealerTotalValue);	
	}
	public void playerDraw() {

		playSE(aa.cardSound01);
		playerHas++;
		
		ImageIcon pickedCard = pickRandomCard();
				
		playerCardNum[playerHas] = pickedCardNum;		
		playerCardValue[playerHas] = checkCardValue();	
		
		ui.playerCardLabel[playerHas].setVisible(true);
		ui.playerCardLabel[playerHas].setIcon(pickedCard);
		
		playerTotalValue = playerTotalValue();	// Count total player card value	
		ui.playerScore.setText("You: " + playerTotalValue);
	}	
	public void playerTurn() {
		
		situation = "playerTurn";

		playerDraw(); // Hit a card
								
		if(playerTotalValue > 21) { // If the total value surpassed 21, the game is over
			dealerOpen();
		}
		else if(playerTotalValue == 21 && playerHas == 2) {
			playerNatural();
		}
		else {					
			if(playerHas > 1 && playerHas < 5) { // If the total value is less than 22 and has 4 or less cards, you can still hit
				ui.messageText.setTextPlus("Do you want to hit one more card?");
				ui.button[1].setVisible(true);
				ui.button[1].setText("Hit");
				ui.button[2].setVisible(true);
				ui.button[2].setText("Stand");	
			}
			if(playerHas == 5) { // If the player has 5 cards, the player's turn is over
				dealerOpen();
			}
		}		
	}
	public void playerNatural() {
		
		situation = "playerNatural";
		
		ui.messageText.setTextPlus("You got natural. Let's open the dealer's card.");
		ui.button[1].setVisible(true);
		ui.button[1].setText("Continue");
	}
	public void dealerOpen() {
		
		playSE(aa.cardSound01);
		ui.dealerCardLabel[2].setIcon(dealerSecondCard); // Reveal the second card	
		ui.dealerScore.setText("Dealer: " + dealerTotalValue);	
		
		if(playerHas == 2 && playerTotalValue == 21) { // If player got natural
			checkResult();
		}
		else if(dealerTotalValue < 17 && playerTotalValue <= 21) {
			dealerTurnContinue();
		}
		else {
			checkResult();
		}	
	}
	public void dealerTurn() {
						
		if(dealerTotalValue < 17) { // If it's less than 17, the dealer must hit one more card
			
			dealerDraw();
			
			if(dealerHas == 5 || dealerTotalValue >= 17) { // If the player has 5 cards, the player's turn is over
				checkResult();
			}
			else {
				dealerTurnContinue();
			}
		}	
		else {
			checkResult();
		}		
	}	
	public void dealerTurnContinue() {
		
		situation = "dealerTurnContinue";
		
		ui.messageText.setTextPlus("The dealer is going to hit another card.");
		ui.button[1].setVisible(true);
		ui.button[1].setText("Continue");
	}
	public void checkResult() {
		
		situation = "checkResult";		
		
		if(playerTotalValue > 21) {
			playSE(aa.youlost);
			ui.messageText.setTextPlus("You lost!");	
			gameFinished();
		}
		else {		
			if(playerTotalValue == 21 && dealerHas == 2) { // If player is natural
				if(dealerTotalValue == 21) {
					playSE(aa.draw);
					ui.messageText.setTextPlus("Draw!");	
					gameFinished();
				}
				else {
					playSE(aa.youwon);
					ui.messageText.setTextPlus("You won!");	
					gameFinished();
				}
			}
			else { 
				if(dealerTotalValue < 22 && dealerTotalValue > playerTotalValue) {
					playSE(aa.youlost);
					ui.messageText.setTextPlus("You lost!");	
					gameFinished();
				}
				else if(dealerTotalValue == playerTotalValue) {
					playSE(aa.draw);
					ui.messageText.setTextPlus("Draw!");	
					gameFinished();
				}
				else {
					playSE(aa.youwon);
					ui.messageText.setTextPlus("You won!");	
					gameFinished();
				}						
			}
		}
	}
	public void gameFinished() {

		situation = "gameFinished";		
		ui.button[1].setVisible(true);
		ui.button[1].setText("Play Again");
		ui.button[2].setVisible(true);		
		ui.button[2].setText("Quit");
	}
	public void resetEverything() {

		for(int i=1; i < 6; i++ ){
			ui.playerCardLabel[i].setVisible(false);
			ui.dealerCardLabel[i].setVisible(false);			
		}					
		for(int i=1; i < 6; i++) {
			playerCardNum[i]=0;
			playerCardValue[i]=0;
			dealerCardNum[i]=0;
			dealerCardValue[i]=0;
		}
		playerHas=0;		
		dealerHas=0;
		
		removeButtons();			
		startGame();
	}
	public int playerTotalValue() {
		
		playerTotalValue = playerCardValue[1] + playerCardValue[2] + playerCardValue[3] + playerCardValue[4] + playerCardValue[5];
		
		if(playerTotalValue > 21) {
			adjustPlayerAceValue();
		}
		playerTotalValue = playerCardValue[1] + playerCardValue[2] + playerCardValue[3] + playerCardValue[4] + playerCardValue[5];	
		return playerTotalValue;
	}
	public int dealerTotalValue() {
		
		dealerTotalValue = dealerCardValue[1] + dealerCardValue[2] + dealerCardValue[3] + dealerCardValue[4] + dealerCardValue[5];
		
		if(dealerTotalValue > 21) {
			adjustDealerAceValue();
		}
		dealerTotalValue = dealerCardValue[1] + dealerCardValue[2] + dealerCardValue[3] + dealerCardValue[4] + dealerCardValue[5];	
		return dealerTotalValue;
	}
	public void adjustPlayerAceValue() {
		
		for(int i=1; i<6; i++) {
			if(playerCardNum[i]==1) {
				playerCardValue[i]=1;	
				playerTotalValue = playerCardValue[1] + playerCardValue[2] + playerCardValue[3] + playerCardValue[4] + playerCardValue[5];
				if(playerTotalValue < 21) {
					break;
				}
			}
		}
	}
	public void adjustDealerAceValue() {
		
		for(int i=1; i<6; i++) {
			if(dealerCardNum[i]==1) {
				dealerCardValue[i]=1;	
				dealerTotalValue = dealerCardValue[1] + dealerCardValue[2] + dealerCardValue[3] + dealerCardValue[4] + dealerCardValue[5];
				if(dealerTotalValue < 21) {
					break;
				}
			}
		}
	}

	public ImageIcon pickRandomCard() {
		
		ImageIcon pickedCard = null;
		
		pickedCardNum = random.nextInt(13)+1;
		int pickedMark = random.nextInt(4)+1;
		
		switch(pickedMark) {
		case 1: pickedCard = cards.spade[pickedCardNum]; break;
		case 2: pickedCard = cards.heart[pickedCardNum]; break;
		case 3: pickedCard = cards.club[pickedCardNum]; break;
		case 4: pickedCard = cards.diamond[pickedCardNum]; break;
		}			
		return pickedCard;				
	}
	public int checkCardValue() {
		
		int cardValue = pickedCardNum;
		if(pickedCardNum==1) {
			cardValue=11;
		}
		if(pickedCardNum>10) {
			cardValue=10;
		}
		return cardValue;		
	}
	public void removeButtons() {
		
		ui.button[1].setVisible(false);
		ui.button[2].setVisible(false);
		ui.button[3].setVisible(false);
		ui.button[4].setVisible(false);
		ui.button[5].setVisible(false);
	}
	public void playSE(URL url) {
		
		se.setFile(url);
		se.play(url);
	}
	public void playMusic(URL url) {
		
		music.setFile(url);
		music.play(url);
		music.loop(url);
	}
	public void stopMusic(URL url) {
		
		music.stop(url);
	}
}



Cards.java
This class imports all the card images.

import javax.swing.ImageIcon;

public class Cards {

	ImageIcon front = new ImageIcon();
	ImageIcon spade[] = new ImageIcon[14];
	ImageIcon heart[] = new ImageIcon[14];
	ImageIcon club[] = new ImageIcon[14];
	ImageIcon diamond[] = new ImageIcon[14];

	public Cards() {
		
		front = new ImageIcon(getClass().getClassLoader().getResource("front.png"));

		// SPADE
		for(int num=1; num<14; num++) {
			spade[num]  = new ImageIcon(getClass().getClassLoader().getResource(num + "S.png"));
		}
		
		// HEART
		for(int num=1; num<14; num++) {
			heart[num]  = new ImageIcon(getClass().getClassLoader().getResource(num + "H.png"));
		}
		
		// CLUB
		for(int num=1; num<14; num++) {
			club[num]  = new ImageIcon(getClass().getClassLoader().getResource(num + "C.png"));
		}
		
		// DIAMOND
		for(int num=1; num<14; num++) {
			diamond[num]  = new ImageIcon(getClass().getClassLoader().getResource(num + "D.png"));
		}					
	}
}


UI.java

This class handles all the UI elements.


import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;


public class UI extends JFrame{
	
	// Title Screen UI
	PaintPanel titlePanel;
	JButton startB,exitB;
	
	// Table UI
	JPanel table;
	JPanel dealerPanel;
	JPanel playerPanel;
	JLabel playerCardLabel[] = new JLabel[6];
	JLabel dealerCardLabel[] = new JLabel[6];
	
	// Message UI
    JTextAreaPlus messageText;
	JPanel scorePanel;
	JLabel playerScore, dealerScore;
	JPanel buttonPanel = new JPanel();
	JButton button[] = new JButton[6];
	Game game;
	
	int cardWidth = 150;
	int cardHeight = 213;
	
	
	public UI(Game game) {
		
		this.game = game;
		
		this.setTitle("SUPER BlACKJACK");
		this.setIconImage(new ImageIcon(getClass().getClassLoader().getResource("ui_glass.png")).getImage());
		this.setSize(1200,900);
		this.setLocationRelativeTo(null);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setLayout(null);
		this.getContentPane().setBackground(new Color(0,81,0));
		
		createTitleScreen();
		createTableUI();	
		createOtherUI();
		this.setVisible(true);
	}
	public void createTitleScreen() {
			
		titlePanel = new PaintPanel(this);
		titlePanel.setBounds(0,0,1200,600);
		titlePanel.setOpaque(false);
		this.add(titlePanel);
		
		startB = new JButton("START");
		startB.setBounds(500,700,200,50);
		startB.setBorder(null);
		startB.setBackground(null);
		startB.setFocusPainted(false);
		startB.setForeground(Color.white);
		startB.setFont(new Font("Book Antiqua", Font.PLAIN,36));
		startB.setVisible(false);
		startB.addActionListener(game.aHandler);
		startB.setActionCommand("start");
		startB.setContentAreaFilled(false);
		this.add(startB);
		
		exitB = new JButton("EXIT");
		exitB.setBounds(500,750,200,50);
		exitB.setBorder(null);
		exitB.setBackground(null);
		exitB.setFocusPainted(false);
		exitB.setForeground(Color.white);
		exitB.setFont(new Font("Book Antiqua", Font.PLAIN,36));
		exitB.setVisible(false);
		exitB.addActionListener(game.aHandler);
		exitB.setActionCommand("exit");
		exitB.setContentAreaFilled(false);
		this.add(exitB);
		
		titlePanel.timer.start();
	}
	public void createTableUI() {
		
		table = new JPanel();
		table.setBackground(new Color(0,81,0));
		table.setBounds(50,50,850,600);
		table.setLayout(null);
		table.setVisible(false);
		
		dealerPanel = new JPanel();
		dealerPanel.setBounds(100,120,cardWidth*5,cardHeight);
		dealerPanel.setBackground(null);
		dealerPanel.setOpaque(false);
		dealerPanel.setLayout(new GridLayout(1,5));
		dealerPanel.setVisible(false);
		this.add(dealerPanel);
				
		playerPanel = new JPanel();
		playerPanel.setBounds(100,370,cardWidth*5,cardHeight);
		playerPanel.setOpaque(false);
		playerPanel.setLayout(new GridLayout(1,5));
		playerPanel.setVisible(false);
		this.add(playerPanel);

		for(int i = 1; i < 6; i++) {
			playerCardLabel[i] = new JLabel();
			playerCardLabel[i].setVisible(false);
			playerPanel.add(playerCardLabel[i]);
		}
		for(int i = 1; i < 6; i++) {
			dealerCardLabel[i] = new JLabel();
			dealerCardLabel[i].setVisible(false);
			dealerPanel.add(dealerCardLabel[i]);
		}	
		
		dealerScore = new JLabel();
		dealerScore.setBounds(50,10,200,50);
		dealerScore.setForeground(Color.white);
		dealerScore.setFont(new Font("Times New Roman", Font.PLAIN, 42));
		table.add(dealerScore);
		
		playerScore = new JLabel();
		playerScore.setBounds(50,540,200,50);
		playerScore.setForeground(Color.white);
		playerScore.setFont(new Font("Times New Roman", Font.PLAIN, 42));
		table.add(playerScore);
	
		this.add(table);
	}
	public void createOtherUI() {
		
		messageText = new JTextAreaPlus();
		messageText.setBounds(230,680,720,100);
		messageText.setBackground(null);
		messageText.setForeground(Color.white);
		messageText.setFont(new Font("Times New Roman", Font.PLAIN, 40));
		messageText.setEditable(false);
		this.add(messageText);
		
		buttonPanel = new JPanel();
		buttonPanel.setBounds(920,340,200,300);
		buttonPanel.setBackground(null);
		buttonPanel.setLayout(new GridLayout(6,1));
		this.add(buttonPanel);
		
		for(int i = 1; i < 6; i++) {
			button[i] = new JButton();
			button[i].setBackground(null);
			button[i].setForeground(Color.white);
			button[i].setFocusPainted(false);
			button[i].setBorder(null);
			button[i].setFont(new Font("Times New Roman", Font.PLAIN, 42));
			button[i].addActionListener(game.aHandler);
			button[i].setActionCommand(""+i);
			button[i].setVisible(false);
			buttonPanel.add(button[i]);
		}		
	}
}




PaintPanel.java

This class handles the title screen animation


import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.Timer;

public class PaintPanel extends JPanel implements ActionListener{
	
	public Timer timer = new Timer(30,this);
	float alphaValue = 0f;
	BufferedImage titleImage;
	
	UI ui;
	
	public PaintPanel(UI ui) {
		
		this.ui = ui;
		
		try {
			titleImage = ImageIO.read(getClass().getClassLoader().getResource("titleimage.png"));
			
		}catch(IOException e) {			
		}		
	}

	public void paintComponent(Graphics g) {
		
		super.paintComponent(g);

		Graphics2D g2d = (Graphics2D)g;
		g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alphaValue));
		if(alphaValue < 0.99) {
			g2d.setColor(Color.white);
		}
		else {
			g2d.setColor(Color.yellow);
		}
		
		g2d.setFont(new Font("Book Antiqua", Font.PLAIN, 110));
		g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
		
		String text = "SUPER BLACKJACK";
		
		int stringLength = (int)g.getFontMetrics().getStringBounds(text, g).getWidth();
		int start = this.getWidth()/2 - stringLength/2;
		
		g.drawString(text, start, 200);		
		g.drawImage(titleImage, 280,320,640,280,null);
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
				
		alphaValue = alphaValue +0.01f;
		
		if(alphaValue > 1) {
			alphaValue = 1;
			timer.stop();
			ui.startB.setVisible(true);
			ui.exitB.setVisible(true);
		}
		repaint();
	}
}



ActionHandler.java

This class handles the button actions


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class ActionHandler implements ActionListener{
	
	Game game;
	
	public ActionHandler(Game game) {
		
		this.game = game;
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		
		String command = e.getActionCommand();
				
		game.removeButtons();
		
		switch(command) {
		case "start":
			game.titleToGame();
			break;
		case "exit":
			System.exit(0);
			break;
		case "1": 
			if(game.situation.equals("playerTurn")) {
				game.playerTurn();
			}
			else if(game.situation.equals("playerNatural")) {
				game.dealerOpen();
			}
			else if(game.situation.equals("dealerTurnContinue")) {
				game.dealerTurn();
			}
			else if(game.situation.equals("gameFinished")) {
				game.resetEverything();
			}
			break;
		case "2": 
			if(game.situation.equals("playerTurn")) {
				game.dealerOpen();
			}
			else if(game.situation.contentEquals("gameFinished")) {
				game.gameToTitle();
			}
			break;
		}			
	}
}


JTextAreaPlus.java

This is a modified version of JTextArea which can display texts letter by letter.


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JTextArea;
import javax.swing.Timer;

public class JTextAreaPlus extends JTextArea implements ActionListener{
	
	int i=0;
	String text;
	
	Timer timer = new Timer(10, this);
	
	public JTextAreaPlus() {
		
	}
	
	public JTextAreaPlus(String text) {
		super(text);
	}
	
	public void setTextPlus(String text) {
		
		this.text = text;
		setText("");
		timer.start();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		
		char character[] = text.toCharArray();
		int arrayNum = character.length;		
		String s = String.valueOf(character[i]);
		append(s);		
		i++;

		if(i == arrayNum){
			i = 0;
			timer.stop();
		}
	}
}



(Optional) If you want to play SE and Music

AudioAsset.java

This class imports all the sound data


import java.net.URL;

public class AudioAsset {
	
	URL cardSound01 = getClass().getClassLoader().getResource("cardSound01.wav");
	URL youwon = getClass().getClassLoader().getResource("Ryan_youwon.wav");
	URL youlost = getClass().getClassLoader().getResource("Ryan_youlost.wav");
	URL draw = getClass().getClassLoader().getResource("Ryan_draw.wav");
	URL isee = getClass().getClassLoader().getResource("Ryan_isee.wav");
	URL ok = getClass().getClassLoader().getResource("Ryan_ok.wav");
	URL hitanother = getClass().getClassLoader().getResource("Ryan_hitanother.wav");
	URL bgm = getClass().getClassLoader().getResource("bensound-theelevatorbossanova.wav");
}


SE.java


import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

public class SE {
	
	Clip clip;
	
	public void setFile(URL name) {
		
		try {
			AudioInputStream sound = AudioSystem.getAudioInputStream(name);
			clip = AudioSystem.getClip();
			clip.open(sound);
		}
		catch(Exception e) {
			
		}
	}
	
	public void play(URL name) {
		
		clip.setFramePosition(0);
		clip.start();
	}
	
	public void loop(URL name) {
		
		clip.loop(Clip.LOOP_CONTINUOUSLY);
	}
	public void stop(URL name) {
		
		clip.stop();
	}
}


Music.java


import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

public class Music {
	
	Clip clip;

	public void setFile(URL name) {
		
		try {
			AudioInputStream sound = AudioSystem.getAudioInputStream(name);
			clip = AudioSystem.getClip();
			clip.open(sound);
		}
		catch(Exception e) {
			
		}
	}
	
	public void play(URL name) {
		
		clip.setFramePosition(0);
		clip.start();
	}
	
	public void loop(URL name) {
		
		clip.loop(Clip.LOOP_CONTINUOUSLY);
	}
	public void stop(URL name) {
		
		clip.stop();
	}
}


end



Thursday, May 20, 2021

[Java Code Sample] Display all fonts in your computer with JList

Here's a sample code to create a font list which includes all the fonts in your computer.


You can see the preview by clicking an item on the list.


Font type: Arial




Font type: Book Antiqua Italic



Font type: Georgia




Please check the video tutorial for details:




Here's the code:



import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class Main {
	
	Font timesNewRoman = new Font("Comic Sans MS", Font.PLAIN, 28);

	public static void main(String[] args) {
		
		new Main();		
	}
	
	public Main(){
		
		JFrame window = new JFrame();
		window.setSize(800,600);
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		window.getContentPane().setBackground(Color.black);
		window.setLayout(null);
		
		JPanel textPanel = new JPanel();
		textPanel.setBounds(350, 50, 400, 500);
		textPanel.setBackground(Color.black);
		window.add(textPanel);
		
		JTextArea textArea = new JTextArea("It began with the forging of the Great Rings. Three were given to the Elves, immortal, wisest and fairest of all beings. Seven to the Dwarf-Lords, great miners and craftsmen of the mountain halls. And nine, nine rings were gifted to the race of Men, who above all else desire power.");
		textArea.setBounds(350, 50, 400, 500);
		textArea.setBackground(Color.black);
		textArea.setForeground(Color.white);
		textArea.setLineWrap(true);
		textArea.setWrapStyleWord(true);
		textArea.setFont(timesNewRoman);
		textPanel.add(textArea);
		
		JPanel scrollPanePanel = new JPanel();
		scrollPanePanel.setBounds(50, 50, 250, 500);
		scrollPanePanel.setBackground(Color.black);
		window.add(scrollPanePanel);
		
		
		GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
		
		Font allFonts[] = ge.getAllFonts();
		Vector<String> fontNames = new Vector<String>();
		
		for(int i =0; i < allFonts.length; i++){
			fontNames.addElement(allFonts[i].getName());
		}
		
		JList fontList = new JList(fontNames);
		fontList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		fontList.addListSelectionListener(new ListSelectionListener() {

			@Override
			public void valueChanged(ListSelectionEvent e) {
				String s = (String) fontList.getSelectedValue();
				System.out.println(s);
				textArea.setFont(new Font(s,Font.PLAIN,28));
			}		
		});
		
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setPreferredSize(new Dimension(250, 450));
		scrollPane.getViewport().setView(fontList);
		scrollPanePanel.add(scrollPane);

		window.setVisible(true);		
	}
}



end


[Java Code Sample] Create a Health Bar with JProgressBar

Here's a sample code to create a simple health bar with JProgressBar.


In this code, the bar represents player's HP and the meter decreases if you hit the "damage" button.


Check the video tutorial for details:








import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

public class Game {
	
	JFrame window;
	JPanel healthBarPanel, buttonPanel;
	Container con;
	JProgressBar healthBar;
	JButton button;
	int hp;
	
	DamageHandler damageHandler = new DamageHandler();


	public static void main(String[] args) {
		
		new Game();

	}
	
	public Game(){
			
		window = new JFrame();
		window.setSize(800,600);
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		window.getContentPane().setBackground(Color.black);
		window.setLayout(null); 	
		con = window.getContentPane();	
		
		healthBarPanel = new JPanel();
		healthBarPanel.setBounds(250, 250, 300, 30);
		healthBarPanel.setBackground(Color.black);
		con.add(healthBarPanel);
		
		healthBar = new JProgressBar(0,100);
		healthBar.setPreferredSize(new Dimension(300,30));
		healthBar.setValue(100);
		healthBarPanel.add(healthBar);
		
		buttonPanel = new JPanel();
		buttonPanel.setBounds(250, 300, 300, 40);
		buttonPanel.setBackground(Color.black);
		con.add(buttonPanel);
		
		button = new JButton("Receive Damage");
		button.setBackground(Color.black);
		button.setForeground(Color.white);
		button.setFocusPainted(false);
		button.addActionListener(damageHandler);
		buttonPanel.add(button);
		
		hp = 100;
			
		window.setVisible(true);
		
	}
	
	public void damageReceived(){
		
		hp = hp -10;
		healthBar.setValue(hp);
	}
	

	public class DamageHandler implements ActionListener{
		
		public void actionPerformed(ActionEvent event){
			
			damageReceived();
		}
	}

}

Here's the result:


*If you press the button, the HP decreases.


Sunday, May 16, 2021

[Java Code Sample] Placing color chooser palette

Here's a sample code to place color palette (color chooser) on your window.

In this sample code, you can choose a color from the palette and apply to the panel.


Please check my video tutorial for the details:





import java.awt.Color;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Main implements ChangeListener{
	
	JFrame window;
	JColorChooser cc;
	JPanel colorChooserPanel, colorPanel;

	public static void main(String[] args) {
		
		new Main();
	}
	
	public Main(){
		
		window = new JFrame();
		window.setSize(800, 600);
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		window.getContentPane().setBackground(Color.black);
		window.setLayout(null);
		
		colorChooserPanel = new JPanel();
		colorChooserPanel.setBounds(100, 50, 600, 350);
		colorChooserPanel.setBackground(Color.black);
		window.add(colorChooserPanel);
		
		cc = new JColorChooser();
		cc.getSelectionModel().addChangeListener(this);
		
		// to remove preview panel
		cc.setPreviewPanel(new JPanel());
		
		// to remove the panes
//		cc.removeChooserPanel(cc.getChooserPanels()[4]); // CMYK
//		cc.removeChooserPanel(cc.getChooserPanels()[3]); // RGB
//		cc.removeChooserPanel(cc.getChooserPanels()[2]); // HSL
//		cc.removeChooserPanel(cc.getChooserPanels()[1]); // HSV
//		cc.removeChooserPanel(cc.getChooserPanels()[0]); // Swatch
		
		colorChooserPanel.add(cc);
		
		colorPanel = new JPanel();
		colorPanel.setBounds(200, 420, 400, 100);
		colorPanel.setBackground(Color.white);
		window.add(colorPanel);
				
		window.setVisible(true);
	}
	
	public void stateChanged(ChangeEvent e){
		
		Color newColor = cc.getColor();
		colorPanel.setBackground(newColor);
	}	
}


Result:



Saturday, May 15, 2021

[Java Code Sample] Adding background image to JTextArea

Unlike JLabel, JTextArea doesn't have a function to set image on its background.

So let's create a "new" JTextArea which can display images.



Please check this video tutorial for the detailed explanation:





Main class


import java.awt.Color;
import java.awt.Font;
import javax.swing.ImageIcon;
import javax.swing.JFrame;


public class Main {

	public static void main(String[] args) {
		
		new Main();

	}
	public Main() {
		
		JFrame window = new JFrame();
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		window.setSize(1280,840);
		

		JTextAreaPlus textAreaPlus = new JTextAreaPlus("This is a sample text");
		textAreaPlus.setFont(new Font("Times New Roman", Font.PLAIN, 36));
		ImageIcon icon = new ImageIcon(getClass().getClassLoader().getResource("forest01.jpg"));
		textAreaPlus.setImage(icon,0,0,1280,840);
		textAreaPlus.setForeground(Color.white);
		
		window.add(textAreaPlus);
		
		window.setVisible(true);
	}
}

JTextAreaPlus class


import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JTextArea;

public class JTextAreaPlus extends JTextArea{
	
	Image image;
	int x, y, width, height;

	public JTextAreaPlus() {
		super();
	}
	public JTextAreaPlus(String text) {
		super(text);
	}
	
	public void setImage(ImageIcon icon, int x, int y, int width, int height) {
		
		this.image = icon.getImage();
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		setOpaque(false);
		repaint();
	}
	public void paint(Graphics g) {
		
		g.drawImage(image,x,y,width,height,null);	
		super.paint(g);
	}
}


You can set an image, the image's location and its size with the "setImage" method.

Result:


Saturday, April 17, 2021

Discarded idea for my point and click adventure game

 

This is the alternative version of my point and click adventure game.


I added a battle sequence but it didn't fit well with the point and click game mechanics so I discarded the idea.


I made a video about it:



and received a request for the code so here it is:

Class structure



GameManager



package Main;

import java.net.URL;
import Event.Event01;
import Event.Event02;
import Event.Event03;

public class GameManager{
		
	// MAIN CLASS
	ActionHandler aHandler = new ActionHandler(this);	// Instantiate before UI class otherwise action won't work
	public UI ui = new UI(this);
	public Player player = new Player(this);
	public SceneChanger sChanger = new SceneChanger(this);		
	Music music = new Music();
	SE se = new SE();
	public BattleManager bm = new BattleManager(this);
	
	// EVENT CLASS
	public Event01 ev1 = new Event01(this);
	public Event02 ev2 = new Event02(this);
	public Event03 ev3 = new Event03(this);
		
	// SOUND
	public URL fieldMusic  = getClass().getClassLoader().getResource("audio//bensound-acousticbreeze voldown2.wav");
	public URL fieldMusic2  = getClass().getClassLoader().getResource("audio//bensound-ofeliasdream.wav");
	public URL battleMusic  = getClass().getClassLoader().getResource("audio//bensound-epic-down.wav");
	public URL deathSound  = getClass().getClassLoader().getResource("audio//darksoulsdeath.wav");
	public URL hitSound  = getClass().getClassLoader().getResource("audio//hit.wav");
	public URL healSound  = getClass().getClassLoader().getResource("audio//healSound.wav");
	public URL guard01  = getClass().getClassLoader().getResource("audio//guard01.wav");
	public URL guard02  = getClass().getClassLoader().getResource("audio//guard02.wav");
	public URL guard03  = getClass().getClassLoader().getResource("audio//guard03.wav");
	public URL guard03b  = getClass().getClassLoader().getResource("audio//guard03b.wav");
	public URL guard04  = getClass().getClassLoader().getResource("audio//guard04.wav");
	public URL guard05  = getClass().getClassLoader().getResource("audio//guard05.wav");
	public URL guard06  = getClass().getClassLoader().getResource("audio//guard06.wav");
	public URL awesome  = getClass().getClassLoader().getResource("audio//awesome.wav");
	public URL chestopen  = getClass().getClassLoader().getResource("audio//dqchestopen.wav");
	public URL enter  = getClass().getClassLoader().getResource("audio//dqstairs.wav");
	URL currentMusic;
	

	public static void main(String[] args) {
		
		new GameManager();	
	}
	public GameManager() {
		
		// SET MUSIC
		currentMusic = fieldMusic; // Do this before showScreen1 otherwise you get NullPointer to stop the current audio
		playMusic(currentMusic);
				
		// SET SCREEN
		player.setPlayerDefaultStatus();
		sChanger.showScene1();	
		
		ui.window.setVisible(true);
	}
	public void playSE(URL url){

		se.setFile(url);
		se.play(url);
	}
	public void playMusic(URL url) {
		
		music.setFile(url);
		music.play(url);
		music.loop(url);
	}
	public void stopMusic(URL url) {
		
		music.stop(url);
	}

}


UI



package Main;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

public class UI {
	
	// MAIN UI
	JFrame window;
	public JTextArea messageText;	
	public JPanel bgPanel[] = new JPanel[20];
	JLabel bgLabel[] = new JLabel[20];
	public JButton choiceB1,choiceB2;
	
	// PLAYER UI
	JPanel lifePanel;
	JLabel life[] = new JLabel[6];	
	JPanel inventoryPanel;
	public JLabel sword;
	public JLabel lantern;
	public JLabel shield;
	
	// GAME OVER UI
	public JLabel titleLabel;
	public JButton restartButton;	
	
	// CLASS
	GameManager gm;
	
	public UI(GameManager gm) {
		
		this.gm = gm;
		
		createMainField();
		createPlayerField();
		createGameOverField();
		generateScreen();							
	}
	public void createMainField() {
		
		window = new JFrame("Awesome Quest III");
		window.setSize(800,600);
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		window.getContentPane().setBackground(Color.black);
		window.setLayout(null);
							
		messageText = new JTextArea();
		messageText.setBounds(50,410,700,150);
		messageText.setBackground(Color.black);
		messageText.setForeground(Color.white);
		messageText.setEditable(false);
		messageText.setLineWrap(true);
		messageText.setWrapStyleWord(true); 
		messageText.setFont(new Font("Times New Roman", Font.PLAIN, 26));
		window.add(messageText);
		choiceB1 = new JButton("Add to your wishlist");
		choiceB1.setBounds(190,320,200,50);
		choiceB1.setBackground(new Color(39,67,89));
		choiceB1.setForeground(new Color(97,195,240));
		choiceB1.setFocusPainted(false);
		choiceB1.setFont(new Font("Arial", Font.PLAIN, 17));
		choiceB1.setVisible(false);
		window.add(choiceB1);
		choiceB2 = new JButton("No way");
		choiceB2.setBounds(400,320,200,50);
		choiceB2.setBackground(new Color(39,67,89));
		choiceB2.setForeground(new Color(97,195,240));
		choiceB2.setFocusPainted(false);
		choiceB2.setFont(new Font("Arial", Font.PLAIN, 17));
		choiceB2.addActionListener(gm.aHandler);
		choiceB2.setActionCommand("noway");
		choiceB2.setVisible(false);
		window.add(choiceB2);
		
	}
	public void createBackgroundImage(int num, String bgFileName) {
		
		bgPanel[num] = new JPanel();
		bgPanel[num].setBounds(50,50,700,350);
		bgPanel[num].setBackground(Color.black);
		bgPanel[num].setLayout(null);
		bgPanel[num].setVisible(false); // So panel 2 or later panels doesn't show up at the beginning
		window.add(bgPanel[num]);
		
		bgLabel[num] = new JLabel();
		bgLabel[num].setBounds(0,0,700,350);	
		
		ImageIcon bgIcon = new ImageIcon(getClass().getClassLoader().getResource(bgFileName));
		Image image = bgIcon.getImage().getScaledInstance(700, 350, Image.SCALE_DEFAULT); // Adjust the size to the label
		bgIcon = new ImageIcon(image);
		bgLabel[num].setIcon(bgIcon);
		
//		bgPanel[num].add(bgLabel[num]);  DON'T ADD Background image to the panel yet!!!!
	}
	public void createObject(int bgNum, int objX, int objY, int objWidth, int objHeight, String objFileName, 
			String choice1Name, String choice2Name, String choice3Name, String choice1Command, String choice2Command, String choice3Command) {
		
		// CREATE POP MENU
		JPopupMenu popMenu = new JPopupMenu(); 
		
		// CREATE POP MENU ITEMS
		JMenuItem menuItem[] = new JMenuItem[4]; // Use [1], [2], [3] 				
		menuItem[1] = new JMenuItem(choice1Name);
		menuItem[1].addActionListener(gm.aHandler);
		menuItem[1].setActionCommand(choice1Command);
		popMenu.add(menuItem[1]);
		
		menuItem[2] = new JMenuItem(choice2Name);
		menuItem[2].addActionListener(gm.aHandler);
		menuItem[2].setActionCommand(choice2Command);
		popMenu.add(menuItem[2]);
		
		menuItem[3] = new JMenuItem(choice3Name);
		menuItem[3].addActionListener(gm.aHandler);
		menuItem[3].setActionCommand(choice3Command);
		popMenu.add(menuItem[3]);
		
		// CREATE OBJECTS
		JLabel objectLabel = new JLabel();
		objectLabel.setBounds(objX,objY,objWidth,objHeight);
//		objectLabel.setOpaque(true);  // Use this when you want to check where the blank object is
//		objectLabel.setBackground(Color.blue);  // Use this when you want to check where the blank object is
		
		ImageIcon objectIcon = new ImageIcon(getClass().getClassLoader().getResource(objFileName));
		Image image = objectIcon.getImage().getScaledInstance(objWidth, objHeight, Image.SCALE_DEFAULT);
		objectIcon = new ImageIcon(image);
		objectLabel.setIcon(objectIcon);
		
		objectLabel.addMouseListener(new MouseListener() {

			public void mouseClicked(MouseEvent e) {}
			public void mousePressed(MouseEvent e) {				
				if(SwingUtilities.isRightMouseButton(e)){					
					popMenu.show(objectLabel, e.getX(), e.getY());
				}	
			}
			public void mouseReleased(MouseEvent e) {}
			public void mouseEntered(MouseEvent e) {}
			public void mouseExited(MouseEvent e) {}
			
		});
		bgPanel[bgNum].add(objectLabel);						
	}
	public void createArrowButton(int bgNum, int x, int y, int width, int height, String arrowFileName, String command) {
				
		ImageIcon arrowIcon = new ImageIcon(getClass().getClassLoader().getResource(arrowFileName));
		Image image = arrowIcon.getImage().getScaledInstance(50, 50, Image.SCALE_DEFAULT);
		arrowIcon = new ImageIcon(image);
		
		JButton arrowButton = new JButton();
		arrowButton.setBounds(x,y,width,height);
		arrowButton.setBackground(Color.black);
		arrowButton.setContentAreaFilled(false);
		arrowButton.setIcon(arrowIcon);
		arrowButton.setBorderPainted(false);
		arrowButton.addActionListener(gm.aHandler);
		arrowButton.setActionCommand(command);
		arrowButton.setVisible(true);
		
		bgPanel[bgNum].add(arrowButton);
	}
	public void createPlayerField() {
		
		lifePanel = new JPanel();
		lifePanel.setBounds(50,0,250,50);
		lifePanel.setBackground(Color.black);
		lifePanel.setLayout(new GridLayout(1,5));
		window.add(lifePanel);
		
		ImageIcon lifeICon = new ImageIcon(getClass().getClassLoader().getResource("image//pixelheart.png"));
		Image image = lifeICon.getImage().getScaledInstance(35, 35, Image.SCALE_DEFAULT);
		lifeICon = new ImageIcon(image);
		
		int i =1;
		while(i<6) {
			life[i] = new JLabel();
			life[i].setIcon(lifeICon);
			lifePanel.add(life[i]);
			i++;
		}				
		
		inventoryPanel = new JPanel();
		inventoryPanel.setBounds(650,0,100,50);
		inventoryPanel.setBackground(Color.black);
		inventoryPanel.setLayout(new GridLayout(1,3));
		window.add(inventoryPanel);
				
		// CREATE SWORD
		sword = new JLabel();
		ImageIcon swordIcon = new ImageIcon(getClass().getClassLoader().getResource("image//plain-dagger.png"));
		image = swordIcon.getImage().getScaledInstance(35, 35, Image.SCALE_DEFAULT);
		swordIcon = new ImageIcon(image);
		sword.setIcon(swordIcon);
		inventoryPanel.add(sword);
		
		shield = new JLabel();
		ImageIcon shieldIcon = new ImageIcon(getClass().getClassLoader().getResource("image//dragon-shield.png"));
		image = shieldIcon.getImage().getScaledInstance(35, 35, Image.SCALE_DEFAULT);
		shieldIcon = new ImageIcon(image);
		shield.setIcon(shieldIcon);
		inventoryPanel.add(shield);
		
		lantern = new JLabel();
		ImageIcon lanternIcon = new ImageIcon(getClass().getClassLoader().getResource("image//lanternflame.png"));
		image = lanternIcon.getImage().getScaledInstance(35, 35, Image.SCALE_DEFAULT);
		lanternIcon = new ImageIcon(image);
		lantern.setIcon(lanternIcon);
		inventoryPanel.add(lantern);
	}
	public void createGameOverField() {
		
		titleLabel = new JLabel("",JLabel.CENTER);
		titleLabel.setBounds(200,150,400,200);
		titleLabel.setFont(new Font("Times New Roman",Font.PLAIN,70));
		titleLabel.setForeground(Color.red);
		titleLabel.setVisible(false);
		window.add(titleLabel);
		
		restartButton = new JButton("Click here to restart");
		restartButton.setBounds(340,320,120,50);
		restartButton.setBorder(null);
		restartButton.setBackground(null);
		restartButton.setForeground(Color.white);
		restartButton.setFocusPainted(false);
		restartButton.addActionListener(gm.aHandler);
		restartButton.setActionCommand("restart");
		restartButton.setVisible(false);
		window.add(restartButton);	
	}
	
	// SCREEN GENERATION
	public void generateScreen() {
		// SCREEN1
		createBackgroundImage(1,"image//greenbg700x400.png"); // bgPanelNum, fileName
		createObject(1,450,130,250,200,"image//hut01 200x200.png","Look","Talk","Rest","lookCabin","talkCabin","restCabin");
		createObject(1,300,285,70,52,"image//chest01 x70.png","Look","Talk","Open","lookChest","talkChest","openChest");
		createObject(1,70,180,136,150,"image//guard150.png","Look","Talk","Attack","lookGuard","talkGuard","attackGuard");
		createArrowButton(1,0,170,50,50,"image//leftarrow50x50.png","goScreen2"); 			
		bgPanel[1].add(bgLabel[1]);  // IMPORTANT!  ADD background after objects otherwise it hides all the objects. ADD background image IN THE END!
	    
		// SCREEN2
		createBackgroundImage(2,"image//cave700x400.jpg");
		createObject(2,0,100,70,300,"image//blank100x100.png","Look","Talk","Enter","lookCave","talkCave","enterCave");
		createObject(2,355,250,50,50,"image//blank100x100.png","Look","Talk","Search","lookRoot","talkRoot","searchRoot");
		createArrowButton(2,650,170,50,50,"image//rightarrow50x50.png","goScreen1"); 			
		bgPanel[2].add(bgLabel[2]);	
		
		// SCREEN3
		createBackgroundImage(3,"image//cave.png");
//		createObject(3,250,80,270,250,"image//halloween-3973250_1280.png","Look","Talk","Enter","lookCave","talkCave","enterCave");
		createObject(3,250,80,200,250,"image//werewolf 200x300.png","Look","Talk","Attack","lookMonster","talkMonster","attackMonster");
		createArrowButton(3,650,170,50,50,"image//rightarrow50x50.png","goScreen2"); 		
//		createArrowButton(3,325,50,50,50,"uparrow50x50.png","goScreen1"); 		
		bgPanel[3].add(bgLabel[3]);	
	}

}


SceneChanger



package Main;

public class SceneChanger{
	
	GameManager gm;
	
	public SceneChanger(GameManager gm) {
		
		this.gm = gm;
	}

	public void showScene1() {		

		gm.ui.bgPanel[1].setVisible(true);
		gm.ui.bgPanel[2].setVisible(false);
		gm.ui.messageText.setText("Let's defeat the Demon Lord and save the world!");
			
		gm.stopMusic(gm.currentMusic);
		gm.currentMusic = gm.fieldMusic;
		gm.playMusic(gm.currentMusic);
	}
	public void showScene2() {

		gm.ui.bgPanel[1].setVisible(false);
		gm.ui.bgPanel[3].setVisible(false);
		gm.ui.bgPanel[2].setVisible(true);	
		gm.ui.messageText.setText("");
		
		gm.stopMusic(gm.currentMusic);
		gm.currentMusic = gm.fieldMusic2;
		gm.playMusic(gm.currentMusic);
		
		gm.bm.resetMonsterLife();
	}
	public void showScene3() {

		gm.ui.bgPanel[2].setVisible(false);
		gm.ui.bgPanel[3].setVisible(true);	
					
		gm.stopMusic(gm.currentMusic);	
		
		if(gm.player.defeatWerewolf==false) {
			gm.ui.messageText.setText("OMG the cave is inhabited by a werewolf!");
			gm.currentMusic = gm.battleMusic;
			gm.playMusic(gm.currentMusic);
		}
		else {
			gm.ui.messageText.setText("");
					
		}	
//		gm.ui.messageText.setText("You enter the cave. What is waiting for you inside...?\n\n"
//		+ "*** This is the end of the Demo. Thank you for playing! ***");
//		gm.ui.choiceB1.setVisible(true);
//		gm.ui.choiceB2.setVisible(true);
	}
	public void showGameOverScreen(int currentBgNum) {
		
		gm.ui.bgPanel[currentBgNum].setVisible(false);
		gm.ui.titleLabel.setVisible(true);
		gm.ui.titleLabel.setText("YOU DIED");
		gm.ui.restartButton.setVisible(true);
//		gm.ui.choiceB1.setVisible(false);
//		gm.ui.choiceB2.setVisible(false);
		
		gm.stopMusic(gm.fieldMusic);
		gm.playSE(gm.deathSound);
	}
	public void exitGameOverScreen() {
		
		gm.ui.titleLabel.setVisible(false);
		gm.ui.restartButton.setVisible(false);
		gm.bm.resetMonsterLife();
		gm.player.setPlayerDefaultStatus();
	}
	public void showEndScreen(int currentBgNum) {
		
		gm.ui.bgPanel[currentBgNum].setVisible(false);
		gm.ui.titleLabel.setVisible(true);
		gm.ui.titleLabel.setText("Congratz!");
		gm.ui.messageText.setText("You have completed the Awesome Quest III!\nThanks for playing!");
//		gm.ui.restartButton.setVisible(true);
		
		gm.stopMusic(gm.fieldMusic);
		gm.playSE(gm.awesome);
	}
}


ActionHandler



package Main;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class ActionHandler implements ActionListener{
	
	GameManager gm;

	public ActionHandler(GameManager gm) {
		
		this.gm = gm;
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		
		System.out.println("here?");
		
		String yourChoice = e.getActionCommand();
		
		switch(yourChoice) {
		// Screen 1
		case "lookCabin": gm.ev1.lookCabin();break;
		case "restCabin": gm.ev1.restCabin();break;
		case "talkCabin": gm.ev1.talkCabin();break;
		case "lookChest": gm.ev1.lookChest();break;
		case "openChest": gm.ev1.openChest();break;
		case "talkChest": gm.ev1.talkChest();break;
		case "lookGuard": gm.ev1.lookGuard();break;
		case "attackGuard": gm.ev1.attackGuard();break;
		case "talkGuard": gm.ev1.talkGuard();break;			
		// Screen 2
		case "lookCave": gm.ev2.lookCave();break;
		case "enterCave": gm.ev2.enterCave();break;
		case "talkCave": gm.ev2.talkCave();break;
		case "lookRoot": gm.ev2.lookRoot();break;
		case "searchRoot": gm.ev2.searchRoot();break;
		case "talkRoot": gm.ev2.talkRoot();break;
		// Scene 3
//		case "noway": gm.sChanger.showGameOverScreen(3); gm.ui.messageText.setText("What a fool...");break;
		case "lookMonster": gm.ev3.lookMonster();break;
		case "talkMonster": gm.ev3.talkMonster();break;
		case "attackMonster": gm.ev3.attackMonster();break;
		// Change Screen
		case "goScreen1": gm.sChanger.showScene1();break;
		case "goScreen2": gm.sChanger.showScene2();break;
		// Others
		case "restart": gm.sChanger.exitGameOverScreen(); gm.player.setPlayerDefaultStatus(); gm.sChanger.showScene1();break;			
		}
	}

}


Player



package Main;

public class Player {
	
	GameManager gm;
	
	// PLAYER STATUS
	public int playerMaxLife = 5;
	public int playerLife = 3;
	public int playerStrength = 3;
	public int playerEndurance = 1;
	public int playerAttack;
	public int playerDefense;
	
	public int hasSword;
	public int hasShield;
	public int hasLantern;
	public int weaponPower;
	public int shieldPower;
	
	public boolean defeatWerewolf = false;

	
	public Player(GameManager gm) {
		
		this.gm = gm;		
	}
	public void setPlayerDefaultStatus() {
		
		playerMaxLife = 5;
		playerLife=3;
		playerStrength = 3;
		playerEndurance = 1;
		hasSword=0;
		hasShield=0;
		hasLantern=0;
		weaponPower=0;
		shieldPower=0;
		
		defeatWerewolf = false;
		updatePlayerStatus();
	}
	public void updatePlayerStatus() {
		
		// REMOVE ALL LIFE FIRST
		int i=1;
		while(i<6) { 
			gm.ui.life[i].setVisible(false);
			i++;
		}	
		// RESET LIFE
		int lifeCount = playerLife;
		while(lifeCount!=0) {
			gm.ui.life[lifeCount].setVisible(true);
			lifeCount--;
		}
		playerAttack = playerStrength + weaponPower;
		playerDefense = playerEndurance + shieldPower;
		
		// CHECK PLAYER ITEMS
		if(hasSword==0) {
			gm.ui.sword.setVisible(false);			
		}
		if(hasSword==1) {
			gm.ui.sword.setVisible(true);
		}
		if(hasShield==0) {
			gm.ui.shield.setVisible(false);
		}
		if(hasShield==1) {
			gm.ui.shield.setVisible(true);
		}
		if(hasLantern==0) {
			gm.ui.lantern.setVisible(false);
		}
		if(hasLantern==1) {
			gm.ui.lantern.setVisible(true);
		}
	}

}


SE



package Main;
import java.net.URL;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

public class SE {
	
	Clip clip;

	public void setFile(URL name)
	{
		try{
			//File file = new File(name);
			AudioInputStream sound = AudioSystem.getAudioInputStream(name);
			clip = AudioSystem.getClip();
			clip.open(sound);
		}
		catch(Exception e)
		{
			
		}
	}
	
	public void play(URL name)
	{
		clip.setFramePosition(0);
		clip.start();
	}
	
	public void loop(URL name)
	{
		clip.loop(Clip.LOOP_CONTINUOUSLY);
	}
	
	public void stop(URL name)
	{
		clip.stop();
	}	

}


Music



package Main;
import java.net.URL;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

public class Music {

	Clip clip;
	
	public void setFile(URL name)
	{
		try{
			AudioInputStream sound = AudioSystem.getAudioInputStream(name);
			clip = AudioSystem.getClip();
			clip.open(sound);
		}
		catch(Exception e)
		{			
		}
	}	
	public void play(URL name)
	{
//		fc.setValue(volume);
		clip.setFramePosition(0);
		clip.start();
	}	
	public void loop(URL name)
	{
		clip.loop(Clip.LOOP_CONTINUOUSLY);
	}	
	public void stop(URL name)
	{		
		clip.stop();
		clip.close();
	}
}


BattleManager



package Main;

import java.util.Random;

import Monster.Monster;
import Monster.Werewolf;

public class BattleManager {
	
	GameManager gm;
	Monster monster;
	Werewolf werewolf = new Werewolf();
	Random randomDie = new Random();
	
	public BattleManager(GameManager gm) {
		
		this.gm = gm;
	}
	public void resetMonsterLife() {
		// add all the monsters if needed
		werewolf.monsterLife = werewolf.monsterMaxLife;
	}
	
	public void setMonster() {		
		monster = werewolf;
		attack();
	}
	
	public void attack() {
		
		int playerDamage = randomDie.nextInt(gm.player.playerAttack)- monster.monsterDefense;
		if(playerDamage<0) {
			playerDamage=0;
		}
		gm.ui.messageText.setText("");
		gm.ui.messageText.append("You attack the " + monster.monsterName + " and give " + playerDamage + " damage!");
		monster.monsterLife -= playerDamage;
		if(monster.monsterLife<1) {
			win();
		}
		else {			
			int monsterDamage = randomDie.nextInt(monster.monsterAttack)-gm.player.playerDefense;
			if(monsterDamage<0) {
				monsterDamage=0;
			}
			gm.player.playerLife -= monsterDamage;
			gm.ui.messageText.append("\nThe " + monster.monsterName + " attacks you and give " + monsterDamage + " damage!");
			if(gm.player.playerLife<1) {
				gm.player.playerLife =0;
				gm.player.updatePlayerStatus();
				lose();
			}	
			else {
				gm.player.updatePlayerStatus();
			}					
		}
		gm.playSE(gm.hitSound);
	}
	public void win() {
		
		gm.ui.messageText.append("\nYou have defeated the monster!");
		
		if(monster==werewolf) {
			gm.player.defeatWerewolf = true;
			gm.stopMusic(gm.currentMusic);
		}
		else {			
		}
	}
	public void lose() {
	
		gm.ui.messageText.append("\nYou died!!");
		gm.sChanger.showGameOverScreen(3);
	}

}


Event01



package Event;

import Main.GameManager;

public class Event01{
	
	public GameManager gm;

	public Event01(GameManager gm) {
		
		this.gm = gm;
	}
	
	public void lookCabin() {
		gm.ui.messageText.setText("This is your home.");
	}
	public void talkCabin() {
		gm.ui.messageText.setText("Who are you talking to?");
	}
	public void restCabin() {
		
		if(gm.player.defeatWerewolf==true) {
			gm.sChanger.showEndScreen(1);
		}		 
		else {
			if(gm.player.playerLife != gm.player.playerMaxLife) {
				gm.ui.messageText.setText("You rest at the home.\n(Your LIFE has recovered)");
				gm.player.playerLife++; 
				gm.player.updatePlayerStatus();
				
				gm.playSE(gm.healSound);
			}	
			else {
				gm.ui.messageText.setText("Your LIFE is full.");
			}
		}
	}
	public void lookChest() {
		gm.ui.messageText.setText("A chest is on the ground.");
	}
	public void talkChest() {
		gm.ui.messageText.setText("You talk to the chest but it says nothing.");
	}
	public void openChest() {
		gm.ui.messageText.setText("You open the chest and find a sword!"); 
		gm.ui.sword.setVisible(true);
		gm.player.hasSword=1;
		gm.player.weaponPower=2;
		gm.player.updatePlayerStatus();
		gm.playSE(gm.chestopen);
	}
	public void lookGuard() {
		gm.ui.messageText.setText("A guard is standing in front of you.");
	}
	public void talkGuard() {
		if(gm.player.hasSword==0) {
			gm.ui.messageText.setText("Guard: Don't go any further without a weapon! \nYou should check the chest over there."); 
			gm.playSE(gm.guard01);
		}
		else {
			gm.ui.messageText.setText("Guard: Don't go any further without a weapon! \n...Oh never mind. It seems you already have it."); 
			gm.playSE(gm.guard06);
		}
	}
	public void attackGuard() {
		if(gm.player.hasShield==0) {
			if(gm.player.hasSword==0) {
				if(gm.player.playerLife!=1) {
					gm.ui.messageText.setText("Guard: Hey, don't be stupid!\n(The guard hits you back and your life decreases by 1)");
					gm.player.playerLife--;
//					gm.player.updatePlayerStatus();
					
					gm.playSE(gm.hitSound);
					gm.playSE(gm.guard02);
				}
				else if(gm.player.playerLife==1) {
					gm.ui.messageText.setText("Guard: What a fool...");
					gm.player.playerLife--;
//					gm.player.updatePlayerStatus();
					gm.sChanger.showGameOverScreen(1);
//					gm.playSE(gm.guard03);
				}
			}
			else {
				gm.ui.messageText.setText("Guard: Oh, shit!\n(You defeated the guard and got his shield!)");
				gm.ui.shield.setVisible(true);
				gm.player.hasShield=1;
				gm.player.shieldPower=1;
//				gm.player.updatePlayerStatus();
				gm.playSE(gm.hitSound);
				gm.playSE(gm.guard04);
			}
			gm.player.updatePlayerStatus();
		}
		else {
			gm.ui.messageText.setText("Guard: Just leave me alone.");
			gm.playSE(gm.guard05);
		}
	}	
}


Event02



package Event;

import Main.GameManager;

public class Event02 {
	
	public GameManager gm;
	
	public Event02(GameManager gm) {
		
		this.gm = gm;		
	}
	
	public void lookCave() {
		gm.ui.messageText.setText("It's a cave!"); 
	}
	public void talkCave() {
		gm.ui.messageText.setText("You hear the echo of your voice.");
	}
	public void enterCave() {
		if(gm.player.hasLantern==0) {
			gm.ui.messageText.setText("It's too dark to enter.");
		}
		else {
			gm.sChanger.showScene3();
//			gm.playSE(gm.enter);
		}
	}
	public void lookRoot() {
		gm.ui.messageText.setText("There is something at the tree.");
	}
	public void talkRoot() {
		gm.ui.messageText.setText("They say plants grow well if you talk to them but this tree doesn't look like it needs encouragement.");
	}	
	public void searchRoot() {
		gm.ui.messageText.setText("You find a lantern!"); 
		gm.ui.lantern.setVisible(true);
		gm.player.hasLantern=1;
		gm.playSE(gm.chestopen);
	}
		
}


Event03



package Event;

import Main.GameManager;

public class Event03 {
	
	GameManager gm;
	
	public Event03(GameManager gm) {
		
		this.gm = gm;		
	}
	
	public void lookMonster() {
		
		if(gm.player.defeatWerewolf==true) {
			gm.ui.messageText.setText("The monster is dead...");
		}
		else {
			gm.ui.messageText.setText("It's a monster!");
		}
	}
	public void talkMonster() {
		
		if(gm.player.defeatWerewolf==true) {
			gm.ui.messageText.setText("Monster: .......");
		}
		else {
			gm.ui.messageText.setText("Monster: DIE YOU FOOL!");
		}
	}
	public void attackMonster() {
		
		if(gm.player.defeatWerewolf==true) {
			gm.ui.messageText.setText("He is already dead...");
		}
		else {
			gm.bm.setMonster();
		}
		
	}
}

Monster



package Monster;

public class Monster {
	
	public String monsterName;
//	public String status;
	public int monsterMaxLife;
	public int monsterLife;
	public int monsterAttack;
	public int monsterDefense;

}


Werewolf



package Monster;

public class Werewolf extends Monster{
		
	public Werewolf() {
		
		monsterName = "Werewolf";
//		status = "alive";
		monsterMaxLife = 5;
		monsterLife = 5;
		monsterAttack = 5; // 0-4
		monsterDefense = 1;
	}
}