Work through the AI book

Signed-off-by: Chris Cromer <chris@cromer.cl>
This commit is contained in:
Chris Cromer 2019-09-21 20:41:21 -03:00
parent 86d6dcb4ab
commit ca46991813
46 changed files with 4588 additions and 175 deletions

5
.gitignore vendored
View File

@ -1,2 +1,3 @@
out
doc
/out/
/doc/
/log/

View File

@ -0,0 +1,12 @@
<component name="ArtifactManager">
<artifact type="jar" name="Game:jar">
<output-path>$PROJECT_DIR$/out/artifacts/Game_jar</output-path>
<root id="archive" name="Game.jar">
<element id="module-output" name="Game" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/json-simple-3.1.0.jar" path-in-jar="/" />
<element id="directory" name="javadoc">
<element id="dir-copy" path="$PROJECT_DIR$/doc" />
</element>
</root>
</artifact>
</component>

View File

@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="cromer">
<words>
<w>cromer</w>
</words>
</dictionary>
</component>

10
.idea/libraries/lib.xml Normal file
View File

@ -0,0 +1,10 @@
<component name="libraryTable">
<library name="lib">
<CLASSES>
<root url="file://$PROJECT_DIR$/lib" />
</CLASSES>
<JAVADOC />
<SOURCES />
<jarDirectory url="file://$PROJECT_DIR$/lib" recursive="false" />
</library>
</component>

View File

@ -2,8 +2,9 @@
<project version="4">
<component name="JavadocGenerationManager">
<option name="OUTPUT_DIRECTORY" value="$PROJECT_DIR$/doc" />
<option name="OPTION_SCOPE" value="private" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_12" default="true" project-jdk-name="12" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11 OpenJDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

124
.idea/uiDesigner.xml Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

View File

@ -2,11 +2,16 @@
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<javadoc-paths>
<root url="file://$MODULE_DIR$/doc" />
</javadoc-paths>
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/doc" />
<excludeFolder url="file://$MODULE_DIR$/log" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="lib" level="project" />
</component>
</module>

BIN
lib/json-simple-3.1.0.jar Normal file

Binary file not shown.

3
src/META-INF/MANIFEST.MF Normal file
View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: cl.cromer.game.Main

View File

@ -15,9 +15,18 @@
package cl.cromer.game;
import java.awt.*;
import javax.swing.JComponent;
import cl.cromer.game.sprite.Animation;
import cl.cromer.game.sprite.AnimationException;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.logging.Logger;
/**
* This class is a cell that will contain a game element such as a player, enemy, prize, etc
*/
public class Celda extends JComponent implements Constantes {
/**
* The x coordinate of the cell
@ -31,18 +40,22 @@ public class Celda extends JComponent implements Constantes {
* The type of cell
*/
private Type type = Type.SPACE;
/**
* The possible types of cell that this could be
* If the cell is selected
*/
protected enum Type {
SPACE,
PLAYER,
END,
ENEMY,
OBSTACLE,
PRIZE
}
private boolean selected = false;
/**
* The sprites that can be used in the cell
*/
private Animation animation = null;
/**
* The tiles to show in this cell
*/
private ArrayList<BufferedImage> tiles = new ArrayList<>();
/**
* The logger
*/
private Logger logger;
/**
* Initialize the cell with its coordinates
@ -52,6 +65,85 @@ public class Celda extends JComponent implements Constantes {
public Celda(int x, int y) {
this.x = x;
this.y = y;
logger = getLogger(this.getClass(), CELDA_LOG_LEVEL);
}
/**
* Set the x and y coordinates of the cell
*
* @param x The x coordinate
* @param y The y coordinate
*/
public void setCoords(int x, int y) {
this.x = x;
this.y = y;
}
/**
* Get the x coordinate of the cell
*
* @return Returns the x coordinate
*/
public int getX() {
return x;
}
/**
* Set the x coordinate for the cell
*
* @param x The new x coordinate
*/
public void setX(int x) {
this.x = x;
}
/**
* Get the y coordinate for the cell
*
* @return Returns the y coordinate
*/
public int getY() {
return y;
}
/**
* Set the y coordinate for the cell
*
* @param y The new y coordinate
*/
public void setY(int y) {
this.y = y;
}
/**
* Get the sprite for the cell
*
* @return Return the sprite in use
*/
public Animation getAnimation() {
return animation;
}
/**
* Set which sprite to use for this cell
*
* @param animation The sprite to show
*/
public void setAnimation(Animation animation) {
this.animation = animation;
}
public void addTile(BufferedImage tile) {
tiles.add(tile);
}
/**
* Get the current type of this cell
*
* @return Returns the type of cell
*/
public Type getType() {
return this.type;
}
/**
@ -63,61 +155,102 @@ public class Celda extends JComponent implements Constantes {
}
/**
* Get the current type of this cell
* @return Returns the type of cell
*/
private Type getType() {
return this.type;
}
/**
* Override the paintComponent method of JComponent to pain the cell based on type
* Override the paintComponent method of JComponent to paint the cell based on type
* @param g The graphics object to paint
*/
@Override
public void paintComponent(Graphics g) {
// Draw the borders
g.setColor(Color.black);
g.drawRect(x, y, CELL_PIXELS, CELL_PIXELS);
update(g);
}
/**
* Override the update method of JComponent to do double buffering
* @param g The graphics object to paint
*/
@Override
public void update(Graphics g) {
// Set the text font
g.setFont(new Font("monospaced", Font.BOLD, 10));
// Fill in the cell
for (BufferedImage tile : tiles) {
if (tile != null) {
g.drawImage(tile, x, y, null);
}
}
// Draw a sprite in the cell if needed
switch (getType()) {
case SPACE:
g.setColor(Color.orange);
g.fillRect(x + 1, y + 1, CELL_PIXELS - 1, CELL_PIXELS - 1);
break;
case PLAYER:
g.setColor(Color.green);
g.fillRect(x + 1, y + 1, CELL_PIXELS - 1, CELL_PIXELS - 1);
g.setColor(Color.black);
g.drawString(String.valueOf(PLAYER), x + (CELL_PIXELS / 2), y + (CELL_PIXELS / 2));
case ENEMY:
case CHEST:
try {
if (animation != null && animation.getFrame() != null) {
g.drawImage(animation.getFrame(), x + animation.getXOffset(), y + animation.getYOffset(), null);
}
}
catch (AnimationException e) {
logger.warning(e.getMessage());
}
break;
case END:
case PORTAL:
g.setColor(Color.pink);
g.fillRect(x + 1, y + 1, CELL_PIXELS - 1, CELL_PIXELS - 1);
g.setColor(Color.black);
g.drawString(String.valueOf(END), x + (CELL_PIXELS / 2), y + (CELL_PIXELS / 2));
break;
case PRIZE:
g.setColor(Color.red);
g.fillRect(x + 1, y + 1, CELL_PIXELS - 1, CELL_PIXELS - 1);
g.setColor(Color.black);
g.drawString(String.valueOf(PRIZE), x + (CELL_PIXELS / 2), y + (CELL_PIXELS / 2));
break;
case ENEMY:
g.setColor(Color.blue);
g.fillRect(x + 1, y + 1, CELL_PIXELS - 1, CELL_PIXELS - 1);
g.setColor(Color.black);
g.drawString(String.valueOf(ENEMY), x + (CELL_PIXELS / 2), y + (CELL_PIXELS / 2));
break;
case OBSTACLE:
g.setColor(Color.black);
g.fillRect(x + 1, y + 1, CELL_PIXELS - 1, CELL_PIXELS - 1);
g.setColor(Color.white);
g.drawString(String.valueOf(OBSTACLE), x + (CELL_PIXELS / 2), y + (CELL_PIXELS / 2));
break;
}
// The cell is selected
if (isSelected()) {
g.setColor(Color.black);
g.drawRect(x, y, CELL_PIXELS - 1, CELL_PIXELS - 1);
}
}
/**
* Check if the cell is selected
*
* @return Returns true if the cell is selected
*/
public boolean isSelected() {
return selected;
}
/**
* Set the call as selected or unselected
*
* @param selected True if the cell is selected
*/
public void setSelected(boolean selected) {
this.selected = selected;
}
/**
* Called when the player uses mouse to select cell
*
* @param clickX The x coordinate
* @param clickY They y coordinate
* @return Returns true if the cell is selected
*/
public boolean selected(int clickX, int clickY) {
Rectangle rectangle = new Rectangle(x, y, CELL_PIXELS, CELL_PIXELS);
if (rectangle.contains(new Point(clickX, clickY))) {
selected = !selected;
return true;
}
return false;
}
/**
* The possible types of cell that this could be
*/
public enum Type {
PLAYER,
ENEMY,
SPACE,
PORTAL,
OBSTACLE,
CHEST,
KEY
}
}

View File

@ -15,67 +15,179 @@
package cl.cromer.game;
import cl.cromer.game.logging.HtmlFormatter;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Constants used in the game
*/
public interface Constantes {
/**
* The name of the game
*/
String TITLE = "La Aventura de Azaraka";
/**
* The level of logs to record
*/
Level GLOBAL_LOG_LEVEL = Level.WARNING;
Level MAIN_LOG_LEVEL = Level.WARNING;
Level VENTANA_PRINCIPAL_LOG_LEVEL = Level.WARNING;
Level LIENZO_LOG_LEVEL = Level.WARNING;
Level ESCENARIO_LOG_LEVEL = Level.WARNING;
Level ENEMY_LOG_LEVEL = Level.WARNING;
Level CONFIG_LOG_LEVEL = Level.WARNING;
Level SOUND_LOG_LEVEL = Level.WARNING;
Level IMAGE_LOG_LEVEL = Level.WARNING;
Level CELDA_LOG_LEVEL = Level.WARNING;
/**
* Use a global log if true or individual logs if false
*/
boolean GLOBAL_LOG = true;
/**
* Append to the logs if true or make a new log if false
*/
boolean APPEND_LOGS = false;
/**
* The size in pixels of the cells
*/
int CELL_PIXELS = 32;
int CELL_PIXELS = 64;
/**
* The number of cells to draw horizontally
*/
int HORIZONTAL_CELLS = 40;
int HORIZONTAL_CELLS = 18;
/**
* The number of cells to draw vertically
*/
int VERTICAL_CELLS = 20;
/**
* The window border width
*/
int WINDOW_BORDER_WIDTH = 30;
/**
* The window border height
*/
int WINDOW_BORDER_HEIGHT = 50;
int VERTICAL_CELLS = 10;
/**
* The scene width
*/
int SCENE_WIDTH = (CELL_PIXELS * HORIZONTAL_CELLS) + WINDOW_BORDER_WIDTH;
/**
* The scene height
*/
int SCENE_HEIGHT = (CELL_PIXELS * VERTICAL_CELLS) + WINDOW_BORDER_HEIGHT;
int TOP_MARGIN = 40;
int LEFT_MARGIN = 40;
Dimension SCREEN_SIZE = Toolkit.getDefaultToolkit().getScreenSize();
/**
* The letter that represents the player
*/
char PLAYER = 'P';
/**
* The letter that represents the end
*/
char END = 'F';
/**
* The letter that represents the prize
* The amount of chests to draw
*/
char PRIZE = 'G';
/**
* The letter that represents the enemy
*/
char ENEMY = 'E';
/**
* The letter that represents the obstacle
*/
char OBSTACLE = 'O';
/**
* The amount of prizes to draw
*/
int PRIZES = 5;
int CHESTS = 3;
/**
* The amount of enemies to draw
*/
int ENEMIES = 3;
int ENEMIES = 2;
int PLAYER_START_X = 2;
int PLAYER_START_Y = 1;
int FONT_SIZE = 12;
Font BOLD_FONT = new Font("monospaced", Font.BOLD, FONT_SIZE);
Font FONT = new Font("monospaced", Font.PLAIN, FONT_SIZE);
int MINIMUM_SPEED = 100;
int MAXIMUM_SPEED = 500;
int DEFAULT_SPEED = 100;
int MINIMUM_VOLUME = 0;
int MAXIMUM_VOLUME = 100;
int DEFAULT_VOLUME = 100;
boolean GENERATE_SCENE = false;
boolean EXPORT_SCENE = false;
/**
* Generate a random number between given min and max
*
* @param min Minimum number in range
* @param max Maximum number in range
* @return Returns a random number
*/
default int random(int min, int max) {
Random random = new Random();
return random.nextInt((max - min) + 1) + min;
}
/**
* Initialize the logger and assign a html handler
*
* @param logClass The class to be initialized
*/
default void initializeLogger(Class logClass) {
String className = logClass.getName();
Logger logger;
if (GLOBAL_LOG) {
logger = Logger.getGlobal();
}
else {
logger = Logger.getLogger(className);
}
FileHandler fileHandler = null;
File directory = new File("log");
if (!directory.exists()) {
if (!directory.mkdir()) {
System.out.println("Could not make directory \"log\"");
}
}
try {
if (GLOBAL_LOG) {
fileHandler = new FileHandler("log/log.html", APPEND_LOGS);
}
else {
fileHandler = new FileHandler("log/" + className + ".html", APPEND_LOGS);
}
}
catch (IOException e) {
e.printStackTrace();
}
if (fileHandler != null) {
logger.addHandler(fileHandler);
}
Formatter formatter = new HtmlFormatter();
if (fileHandler != null) {
fileHandler.setFormatter(formatter);
}
}
/**
* Get a logger object to use for debugging
*
* @param logClass The class that is in need of a logger
* @param level What log level to use
* @return Returns the logger
*/
default Logger getLogger(Class logClass, Level level) {
String className = logClass.getName();
Logger logger;
if (GLOBAL_LOG) {
logger = Logger.getGlobal();
}
else {
logger = Logger.getLogger(className);
}
if (logger.getHandlers().length == 0) {
initializeLogger(logClass);
}
if (GLOBAL_LOG) {
logger.setLevel(GLOBAL_LOG_LEVEL);
}
else {
logger.setLevel(level);
}
return logger;
}
/**
* The sprite type
*/
enum SpriteType {
PLAYER,
ENEMY,
CHEST
}
}

View File

@ -15,87 +15,908 @@
package cl.cromer.game;
import java.awt.Graphics;
import cl.cromer.game.sound.Sound;
import cl.cromer.game.sound.SoundException;
import cl.cromer.game.sprite.*;
import com.github.cliftonlabs.json_simple.JsonArray;
import com.github.cliftonlabs.json_simple.JsonObject;
import com.github.cliftonlabs.json_simple.Jsoner;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.io.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JComponent;
import java.util.Map;
import java.util.logging.Logger;
/**
* The scene used for the game
*/
public class Escenario extends JComponent implements Constantes {
/**
* The width of the scene
*/
protected int width = CELL_PIXELS * HORIZONTAL_CELLS;
/**
* The height of the scene
*/
protected int height = CELL_PIXELS * VERTICAL_CELLS;
/**
* The canvas
*/
private Lienzo canvas;
/**
* The cells of the game
*/
private Celda[][] celdas;
/**
* The cell that contains the player
*/
private Celda player;
/**
* A hashmap that contains all the sprites for the game
*/
private Map<SpriteType, Animation> sprites = new AnimationMap();
/**
* A collection of tiles that can be used in the scene
*/
private Sheet textureSheet;
/**
* Whether or not the door is closed yet
*/
private boolean doorClosed = false;
/**
* The logger
*/
private Logger logger;
/**
* Initialize the scene
*/
public Escenario() {
Random random = new Random();
final int cells = (HORIZONTAL_CELLS * VERTICAL_CELLS);
final int obstacles = (int) Math.floor((double) cells * 0.1);
int random_value;
ArrayList<RandomPositionList> arrayList = new ArrayList<>();
for (int i = 0; i < ENEMIES; i++) {
random_value = random.nextInt(cells - 1) + 1;
while (arrayList.contains(new RandomPositionList(random_value, Celda.Type.ENEMY))) {
random_value = random.nextInt(cells - 1) + 1;
}
arrayList.add(new RandomPositionList(random_value, Celda.Type.ENEMY));
}
for (int i = 0; i < PRIZES; i++) {
random_value = random.nextInt(cells - 1) + 1;
while (arrayList.contains(new RandomPositionList(random_value, Celda.Type.PRIZE))) {
random_value = random.nextInt(cells - 1) + 1;
}
arrayList.add(new RandomPositionList(random_value, Celda.Type.PRIZE));
}
for (int i = 0; i < obstacles; i++) {
random_value = random.nextInt(cells - 1) + 1;
while (arrayList.contains(new RandomPositionList(random_value, Celda.Type.OBSTACLE))) {
random_value = random.nextInt(cells - 1) + 1;
}
arrayList.add(new RandomPositionList(random_value, Celda.Type.OBSTACLE));
}
random_value = random.nextInt(cells - 1) + 1;
while (arrayList.contains(new RandomPositionList(random_value, Celda.Type.END))) {
random_value = random.nextInt(cells - 1) + 1;
}
arrayList.add(new RandomPositionList(random_value, Celda.Type.END));
public Escenario(Lienzo canvas) {
logger = getLogger(this.getClass(), ESCENARIO_LOG_LEVEL);
this.canvas = canvas;
loadResources();
player = new Celda(PLAYER_START_X, PLAYER_START_Y);
celdas = new Celda[HORIZONTAL_CELLS][VERTICAL_CELLS];
int cell_count = 0;
StringBuilder stringBuilder;
if (!GENERATE_SCENE) {
stringBuilder = new StringBuilder();
InputStream inputStream = getClass().getResourceAsStream("/res/scene.json");
try {
String line;
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line);
}
}
catch (IOException e) {
logger.warning(e.getMessage());
}
}
int cellCount = 0;
for (int i = 0; i < HORIZONTAL_CELLS; i++) {
for (int j = 0; j < VERTICAL_CELLS; j++) {
celdas[i][j] = new Celda(i * CELL_PIXELS + 10, j * CELL_PIXELS + 10);
if (i == 0 && j == 0) {
celdas[i][j].setType(Celda.Type.PLAYER);
celdas[i][j] = new Celda((i * CELL_PIXELS) + LEFT_MARGIN, (j * CELL_PIXELS) + TOP_MARGIN);
if (GENERATE_SCENE) {
generateScene(i, j);
}
else {
for (RandomPositionList randomList : arrayList) {
if (cell_count == randomList.getPosition()) {
celdas[i][j].setType(randomList.getType());
break;
}
}
loadScene(i, j, stringBuilder, cellCount);
cellCount++;
}
cell_count++;
}
}
if (EXPORT_SCENE) {
exportScene();
}
generateRandomObjects();
}
private void generateRandomObjects() {
final int cells = (HORIZONTAL_CELLS * VERTICAL_CELLS);
final int obstacles = (int) Math.floor((double) cells * 0.05);
int random_x;
int random_y;
ArrayList<RandomPositionList> arrayList = new ArrayList<>();
for (int i = 0; i < ENEMIES; i++) {
random_x = random(0, HORIZONTAL_CELLS - 1);
random_y = random(0, VERTICAL_CELLS - 1);
while (arrayList.contains(new RandomPositionList(random_x, random_y, Celda.Type.ENEMY)) || celdas[random_x][random_y].getType() != Celda.Type.SPACE) {
random_x = random(0, HORIZONTAL_CELLS - 1);
random_y = random(0, VERTICAL_CELLS - 1);
}
arrayList.add(new RandomPositionList(random_x, random_y, Celda.Type.ENEMY));
}
for (int i = 0; i < obstacles; i++) {
random_x = random(0, HORIZONTAL_CELLS - 1);
random_y = random(0, VERTICAL_CELLS - 1);
while (arrayList.contains(new RandomPositionList(random_x, random_y, Celda.Type.OBSTACLE)) || celdas[random_x][random_y].getType() != Celda.Type.SPACE) {
random_x = random(0, HORIZONTAL_CELLS - 1);
random_y = random(0, VERTICAL_CELLS - 1);
}
arrayList.add(new RandomPositionList(random_x, random_y, Celda.Type.OBSTACLE));
}
for (int i = 0; i < CHESTS; i++) {
random_x = random(0, HORIZONTAL_CELLS - 1);
random_y = random(0, VERTICAL_CELLS - 1);
// Don't put a chest if it can't be opened
while (arrayList.contains(new RandomPositionList(random_x, random_y, Celda.Type.CHEST)) || arrayList.contains(new RandomPositionList(random_x, random_y + 1, Celda.Type.CHEST)) || celdas[random_x][random_y].getType() != Celda.Type.SPACE || celdas[random_x][random_y + 1].getType() != Celda.Type.SPACE) {
random_x = random(0, HORIZONTAL_CELLS - 1);
random_y = random(0, VERTICAL_CELLS - 1);
}
arrayList.add(new RandomPositionList(random_x, random_y, Celda.Type.CHEST));
}
/*random_value = random(1, cells);
while (arrayList.contains(new RandomPositionList(random_value, Celda.Type.PORTAL))) {
random_value = random(1, cells);
}
arrayList.add(new RandomPositionList(random_value, Celda.Type.PORTAL));*/
for (RandomPositionList randomList : arrayList) {
int x = randomList.getX();
int y = randomList.getY();
celdas[x][y].setType(randomList.getType());
switch (randomList.getType()) {
case ENEMY:
celdas[x][y].setAnimation(sprites.get(SpriteType.ENEMY));
break;
case CHEST:
celdas[x][y].setAnimation(sprites.get(SpriteType.CHEST));
break;
case OBSTACLE:
try {
celdas[x][y].addTile(textureSheet.getTile(30));
}
catch (SheetException e) {
e.printStackTrace();
}
break;
}
}
}
/**
* Override the painComponent method of JComponent to paint the scene
* Generate the scene manually without the JSON file
*
* @param x The cell x position
* @param y The cell y position
*/
private void generateScene(int x, int y) {
logger.info("Generate cell x: " + x + " y: " + y + " manually");
try {
celdas[x][y].addTile(textureSheet.getTile(0));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
if (x == 0 && y == 0) {
// Top left corner
celdas[x][y].setType(Celda.Type.OBSTACLE);
try {
celdas[x][y].addTile(textureSheet.getTile(33));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else if (x == 4 && y == 3) {
// Obstacle on floor
try {
celdas[x][y].setType(Celda.Type.OBSTACLE);
celdas[x][y].addTile(textureSheet.getTile(30));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else if (x == 6 && y == 6) {
// Blood on floor
try {
celdas[x][y].addTile(textureSheet.getTile(12));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else if (x == HORIZONTAL_CELLS - 1 && y == 0) {
// Top right corner
celdas[x][y].setType(Celda.Type.OBSTACLE);
try {
celdas[x][y].addTile(textureSheet.getTile(37));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else if (x == 0 && y == VERTICAL_CELLS - 1) {
// Bottom left corner
celdas[x][y].setType(Celda.Type.OBSTACLE);
try {
celdas[x][y].addTile(textureSheet.getTile(97));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else if (x == HORIZONTAL_CELLS - 1 && y == VERTICAL_CELLS - 1) {
// Bottom right corner
celdas[x][y].setType(Celda.Type.OBSTACLE);
try {
celdas[x][y].addTile(textureSheet.getTile(101));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else if (y == 0) {
// Top wall
celdas[x][y].setType(Celda.Type.OBSTACLE);
if (x == 1) {
// Left door frame
try {
celdas[x][y].addTile(textureSheet.getTile(144));
celdas[x][y].addTile(textureSheet.getTile(192));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else if (x == 2) {
// Door
try {
celdas[x][y].addTile(textureSheet.getTile(145));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else if (x == 3) {
// Right door frame
try {
celdas[x][y].addTile(textureSheet.getTile(146));
celdas[x][y].addTile(textureSheet.getTile(194));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else if (x == 8) {
// Broken wall piece
try {
celdas[x][y].addTile(textureSheet.getTile(105));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else if (x % 2 == 0) {
try {
celdas[x][y].addTile(textureSheet.getTile(34));
celdas[x][y].addTile(textureSheet.getTile(222));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else {
try {
celdas[x][y].addTile(textureSheet.getTile(35));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
}
else if (x == 0) {
// Left wall
celdas[x][y].setType(Celda.Type.OBSTACLE);
if (y % 2 == 0) {
try {
celdas[x][y].addTile(textureSheet.getTile(49));
celdas[x][y].addTile(textureSheet.getTile(255));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else {
try {
celdas[x][y].addTile(textureSheet.getTile(65));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
}
else if (x == HORIZONTAL_CELLS - 1) {
// Right wall
celdas[x][y].setType(Celda.Type.OBSTACLE);
if (y % 2 == 0) {
try {
celdas[x][y].addTile(textureSheet.getTile(53));
celdas[x][y].addTile(textureSheet.getTile(238));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else {
try {
celdas[x][y].addTile(textureSheet.getTile(69));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
}
else if (y == VERTICAL_CELLS - 1) {
// Bottom wall
celdas[x][y].setType(Celda.Type.OBSTACLE);
if (x % 2 == 0) {
try {
celdas[x][y].addTile(textureSheet.getTile(98));
celdas[x][y].addTile(textureSheet.getTile(207));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
else {
try {
celdas[x][y].addTile(textureSheet.getTile(99));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
}
if (x == PLAYER_START_X && y == PLAYER_START_Y) {
celdas[x][y].setType(Celda.Type.PLAYER);
celdas[x][y].setAnimation(sprites.get(SpriteType.PLAYER));
}
else if (x == 10 && y == 3) {
celdas[x][y].setType(Celda.Type.ENEMY);
celdas[x][y].setAnimation(sprites.get(SpriteType.ENEMY));
}
else if (x == 10 && y == 7) {
celdas[x][y].setType(Celda.Type.ENEMY);
celdas[x][y].setAnimation(sprites.get(SpriteType.ENEMY));
}
/*else if (x == 16 && y == 1) {
celdas[x][y].setType(Celda.Type.CHEST);
celdas[x][y].setAnimation(sprites.get(SpriteType.CHEST));
}
else if (x == 12 && y == 7) {
celdas[x][y].setType(Celda.Type.CHEST);
celdas[x][y].setAnimation(sprites.get(SpriteType.CHEST));
}*/
/*else {
for (RandomPositionList randomList : arrayList) {
if (cellCount == randomList.getPosition()) {
celdas[i][j].setType(randomList.getType());
switch (randomList.getType()) {
case ENEMY:
celdas[i][j].setAnimation(sprites.get(SpriteType.ENEMY));
break;
case CHEST:
Animation chestSprite = sprites.get(SpriteType.CHEST);
celdas[i][j].setAnimation(chestSprite);
break;
}
break;
}
}
}*/
}
/**
* Load the cell for the scene
*
* @param x The cell x position
* @param y The cell y position
* @param stringBuilder The string builder which contains the JSON
* @param cellCount Which cell to get out of the JSON
*/
private void loadScene(int x, int y, StringBuilder stringBuilder, int cellCount) {
logger.info("Load cell x: " + x + " y: " + y + " from JSON file");
JsonArray jsonArray = Jsoner.deserialize(stringBuilder.toString(), new JsonArray());
// Get the cell
JsonObject cell = (JsonObject) jsonArray.get(cellCount);
// Get the textures
JsonObject textures = (JsonObject) cell.get("textures");
// Get the type
BigDecimal bigDecimal = (BigDecimal) cell.get("type");
int type = bigDecimal.intValue();
// Create the textures needed
for (int k = 0; k < textures.size(); k++) {
int tile = Integer.parseInt(textures.get(String.valueOf(k)).toString());
try {
celdas[x][y].addTile(textureSheet.getTile(tile));
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
}
// Set the type and animation
if (type == Celda.Type.PLAYER.ordinal()) {
celdas[x][y].setType(Celda.Type.PLAYER);
celdas[x][y].setAnimation(sprites.get(SpriteType.PLAYER));
}
else if (type == Celda.Type.ENEMY.ordinal()) {
celdas[x][y].setType(Celda.Type.ENEMY);
celdas[x][y].setAnimation(sprites.get(SpriteType.ENEMY));
}
else if (type == Celda.Type.CHEST.ordinal()) {
celdas[x][y].setType(Celda.Type.CHEST);
celdas[x][y].setAnimation(sprites.get(SpriteType.CHEST));
}
else if (type == Celda.Type.OBSTACLE.ordinal()) {
celdas[x][y].setType(Celda.Type.OBSTACLE);
}
}
/**
* Export the scene to a JSON file
*/
private void exportScene() {
logger.info("Export scene to JSON");
JsonObject textures;
JsonObject cell;
JsonArray cells = new JsonArray();
for (int i = 0; i < HORIZONTAL_CELLS; i++) {
for (int j = 0; j < VERTICAL_CELLS; j++) {
cell = new JsonObject();
textures = new JsonObject();
textures.put("0", 0);
if (i == 0 && j == 0) {
// Top left corner
textures.put("1", 33);
cell.put("type", Celda.Type.OBSTACLE.ordinal());
}
else if (i == 4 && j == 3) {
// Obstacle on floor
textures.put("1", 30);
cell.put("type", Celda.Type.OBSTACLE.ordinal());
}
else if (i == 6 && j == 6) {
// Blood on floor
textures.put("1", 12);
cell.put("type", Celda.Type.SPACE.ordinal());
}
else if (i == HORIZONTAL_CELLS - 1 && j == 0) {
// Top right corner
textures.put("1", 37);
cell.put("type", Celda.Type.OBSTACLE.ordinal());
}
else if (i == 0 && j == VERTICAL_CELLS - 1) {
// Bottom left corner
textures.put("1", 97);
cell.put("type", Celda.Type.OBSTACLE.ordinal());
}
else if (i == HORIZONTAL_CELLS - 1 && j == VERTICAL_CELLS - 1) {
// Bottom right corner
textures.put("1", 101);
cell.put("type", Celda.Type.OBSTACLE.ordinal());
}
else if (j == 0) {
// Top wall
if (i == 1) {
// Left door frame
textures.put("1", 144);
textures.put("2", 192);
}
else if (i == 2) {
// Door
textures.put("1", 145);
}
else if (i == 3) {
// Right door frame
textures.put("1", 146);
textures.put("2", 194);
}
else if (i == 8) {
// Broken wall piece
textures.put("1", 105);
}
else if (i % 2 == 0) {
textures.put("1", 34);
textures.put("2", 222);
}
else {
textures.put("1", 35);
}
cell.put("type", Celda.Type.OBSTACLE.ordinal());
}
else if (i == 0) {
// Left wall
if (j % 2 == 0) {
textures.put("1", 49);
textures.put("2", 255);
}
else {
textures.put("1", 65);
}
cell.put("type", Celda.Type.OBSTACLE.ordinal());
}
else if (i == HORIZONTAL_CELLS - 1) {
// Right wall
if (j % 2 == 0) {
textures.put("1", 53);
textures.put("2", 238);
}
else {
textures.put("1", 69);
}
cell.put("type", Celda.Type.OBSTACLE.ordinal());
}
else if (j == VERTICAL_CELLS - 1) {
// Bottom wall
if (i % 2 == 0) {
textures.put("1", 98);
textures.put("2", 207);
}
else {
textures.put("1", 99);
}
cell.put("type", Celda.Type.OBSTACLE.ordinal());
}
else {
if (i == PLAYER_START_X && j == PLAYER_START_Y) {
cell.put("type", Celda.Type.PLAYER.ordinal());
}
else if (i == 10 && j == 3) {
cell.put("type", Celda.Type.ENEMY.ordinal());
}
else if (i == 10 && j == 7) {
cell.put("type", Celda.Type.ENEMY.ordinal());
}
/*else if (i == 16 && j == 1) {
cell.put("type", Celda.Type.CHEST.ordinal());
}
else if (i == 12 && j == 7) {
cell.put("type", Celda.Type.CHEST.ordinal());
}*/
else {
cell.put("type", Celda.Type.SPACE.ordinal());
}
}
cell.put("textures", textures);
cells.add(cell);
}
}
// Save the new json file
File file = new File("src/res/scene.json");
try {
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(Jsoner.prettyPrint(cells.toJson()).getBytes());
fileOutputStream.close();
}
catch (IOException e) {
logger.warning(e.getMessage());
}
}
/**
* Load all the images that will be shown in the game
*/
private void loadResources() {
Animation animation;
// Load player animations
Sheet characterSheet = new Sheet("/res/img/player/chara2.png", 54, 39);
int character = 6;
try {
animation = new Animation();
animation.setCurrentDirection(Animation.Direction.DOWN);
loadCharacter(animation, characterSheet, character);
sprites.put(SpriteType.PLAYER, animation);
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
// Load enemy animations
characterSheet = new Sheet("/res/img/enemy/chara4.png", 54, 39);
character = 57;
try {
animation = new Animation();
animation.setCurrentDirection(Animation.Direction.LEFT);
loadCharacter(animation, characterSheet, character);
sprites.put(SpriteType.ENEMY, animation);
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
// Load the chest animation
Sheet chestSheet = new Sheet("/res/img/chest/chests.png", 54, 63);
try {
animation = new Animation();
animation.addImage(Animation.Direction.NONE, chestSheet.getTile(54));
animation.addImage(Animation.Direction.NONE, chestSheet.getTile(66));
animation.addImage(Animation.Direction.NONE, chestSheet.getTile(78));
animation.addImage(Animation.Direction.NONE, chestSheet.getTile(80));
animation.setYOffset(0);
sprites.put(SpriteType.CHEST, animation);
}
catch (SheetException e) {
logger.warning(e.getMessage());
}
// Load the background textures
textureSheet = new Sheet("/res/img/textures/3.png", 64, 64);
}
/**
* Load a character animation
*
* @param animation The animation object
* @param characterSheet The sheet to load from
* @param character The position in the character sheet to start from
* @throws SheetException Thrown if there is a problem loading images from the sheet
*/
private void loadCharacter(Animation animation, Sheet characterSheet, int character) throws SheetException {
animation.addImage(Animation.Direction.DOWN, characterSheet.getTile(character));
animation.addImage(Animation.Direction.DOWN, characterSheet.getTile(character + 2));
character = character + 12;
animation.addImage(Animation.Direction.LEFT, characterSheet.getTile(character));
animation.addImage(Animation.Direction.LEFT, characterSheet.getTile(character + 2));
character = character + 12;
animation.addImage(Animation.Direction.RIGHT, characterSheet.getTile(character));
animation.addImage(Animation.Direction.RIGHT, characterSheet.getTile(character + 2));
character = character + 12;
animation.addImage(Animation.Direction.UP, characterSheet.getTile(character));
animation.addImage(Animation.Direction.UP, characterSheet.getTile(character + 2));
animation.setYOffset(0);
}
/**
* This method will remove the selected attribute from all cells
*/
public void emptyEscenario() {
for (int i = 0; i < HORIZONTAL_CELLS; i++) {
for (int j = 0; j < VERTICAL_CELLS; j++) {
if (celdas[i][j].isSelected()) {
celdas[i][j].setSelected(false);
}
}
}
}
/**
* Get the cells of the game
*
* @return Returns a matrix of the cells of the game
*/
public Celda[][] getCeldas() {
return celdas;
}
/**
* Handle keys being pressed in the game
*
* @param event The event from the keyboard
*/
public void keyPressed(KeyEvent event) {
if (!doorClosed) {
try {
celdas[2][0].addTile(textureSheet.getTile(193));
}
catch (SheetException e) {
e.printStackTrace();
}
doorClosed = true;
}
switch (event.getKeyCode()) {
case KeyEvent.VK_UP:
moveUp();
break;
case KeyEvent.VK_DOWN:
moveDown();
break;
case KeyEvent.VK_LEFT:
moveLeft();
break;
case KeyEvent.VK_RIGHT:
moveRight();
break;
case KeyEvent.VK_SPACE:
interact();
break;
}
}
/**
* Move the player up
*/
private void moveUp() {
logger.info("Up key pressed");
int x = player.getX();
int y = player.getY();
if (y > 0 && celdas[x][y - 1].getType() == Celda.Type.SPACE) {
celdas[x][y].setType(Celda.Type.SPACE);
player.setY(y - 1);
celdas[x][y - 1].setType(Celda.Type.PLAYER);
if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.UP) {
celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.UP);
}
celdas[x][y - 1].setAnimation(celdas[x][y].getAnimation());
celdas[x][y].setAnimation(null);
}
else {
if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.UP) {
celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.UP);
}
}
}
/**
* Move the player down
*/
private void moveDown() {
logger.info("Down key pressed");
int x = player.getX();
int y = player.getY();
if (y < (VERTICAL_CELLS - 1) && celdas[x][y + 1].getType() == Celda.Type.SPACE) {
celdas[x][y].setType(Celda.Type.SPACE);
player.setY(y + 1);
celdas[x][y + 1].setType(Celda.Type.PLAYER);
if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.DOWN) {
celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.DOWN);
}
celdas[x][y + 1].setAnimation(celdas[x][y].getAnimation());
celdas[x][y].setAnimation(null);
}
else {
if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.DOWN) {
celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.DOWN);
}
}
}
/**
* Move the player to the left
*/
private void moveLeft() {
logger.info("Left key pressed");
int x = player.getX();
int y = player.getY();
if (x > 0 && celdas[x - 1][y].getType() == Celda.Type.SPACE) {
celdas[x][y].setType(Celda.Type.SPACE);
player.setX(x - 1);
celdas[x - 1][y].setType(Celda.Type.PLAYER);
if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.LEFT) {
celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.LEFT);
}
celdas[x - 1][y].setAnimation(celdas[x][y].getAnimation());
celdas[x][y].setAnimation(null);
}
else {
if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.LEFT) {
celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.LEFT);
}
}
}
/**
* Move the player to the right
*/
private void moveRight() {
logger.info("Right key pressed");
int x = player.getX();
int y = player.getY();
if (x < (HORIZONTAL_CELLS - 1) && celdas[x + 1][y].getType() == Celda.Type.SPACE) {
celdas[x][y].setType(Celda.Type.SPACE);
player.setX(x + 1);
celdas[x + 1][y].setType(Celda.Type.PLAYER);
if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.RIGHT) {
celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.RIGHT);
}
celdas[x + 1][y].setAnimation(celdas[x][y].getAnimation());
celdas[x][y].setAnimation(null);
}
else {
if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.RIGHT) {
celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.RIGHT);
}
}
}
/**
* Interact with an object in the game
*/
private void interact() {
logger.info("Space bar pressed");
int x = player.getX();
int y = player.getY();
if (celdas[x][y].getAnimation().getCurrentDirection() == Animation.Direction.UP) {
if (celdas[x][y - 1].getType() == Celda.Type.CHEST) {
logger.info("Opened chest");
try {
Sound chestOpenSound = new Sound("/res/snd/OpenChest.wav");
chestOpenSound.play();
}
catch (SoundException e) {
logger.warning(e.getMessage());
}
try {
celdas[x][y - 1].getAnimation().setFrame(3);
}
catch (AnimationException e) {
logger.warning(e.getMessage());
}
}
}
}
/**
* Override the paintComponent method of JComponent to paint the scene
* @param g The graphics object to paint
*/
@Override
public void paintComponent(Graphics g) {
update(g);
}
/**
* Override the update method of JComponent to do double buffering
* @param g The graphics object
*/
@Override
public void update(Graphics g) {
for (int i = 0; i < HORIZONTAL_CELLS; i++) {
for (int j = 0; j < VERTICAL_CELLS; j++) {
celdas[i][j].paintComponent(g);
}
}
}
/**
* Get the player cell
* @return Returns a cell that contains the player
*/
public Celda getPlayer() {
return player;
}
/**
* Get the parent canvas of this scene
* @return Returns the parent canvas
*/
public Lienzo getCanvas() {
return canvas;
}
}

View File

@ -15,21 +15,186 @@
package cl.cromer.game;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import cl.cromer.game.object.Enemy;
import cl.cromer.game.sound.Sound;
import cl.cromer.game.sound.SoundException;
import cl.cromer.game.sprite.AnimationException;
import javax.sound.sampled.Clip;
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
/**
* This class extends the canvas to make drawing and listening easier
*/
public class Lienzo extends Canvas implements Constantes {
/**
* The game scene
*/
private Escenario escenario;
/**
* Check if the mouse button is being held down or not
*/
private boolean holdMouseButton = false;
/**
* Check if the selected cell is the player or not
*/
private boolean playerSelected = false;
/**
* The current mouse x position
*/
private int mouseX;
/**
* The current mouse y position
*/
private int mouseY;
/**
* The graphics buffer
*/
private Graphics graphicBuffer;
/**
* The image buffer
*/
private Image imageBuffer;
/**
* The first enemy
*/
private Enemy enemy;
/**
* The second enemy
*/
private Enemy enemy2;
/**
* The logger
*/
private Logger logger;
/**
* The background music of the game
*/
private Sound backgroundMusic;
/**
* Initialize the canvas
*/
public Lienzo() {
escenario = new Escenario();
this.setBackground(Color.orange);
this.setSize(SCENE_WIDTH, SCENE_HEIGHT);
logger = getLogger(this.getClass(), LIENZO_LOG_LEVEL);
escenario = new Escenario(this);
setBackground(Color.black);
setSize(escenario.width, escenario.height);
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent event) {
super.mouseClicked(event);
if (event.getButton() == MouseEvent.BUTTON1) {
escenario.emptyEscenario();
setMousePosition(event);
holdMouseButton = true;
repaint();
}
}
@Override
public void mouseReleased(MouseEvent event) {
super.mouseClicked(event);
if (event.getButton() == MouseEvent.BUTTON1) {
escenario.emptyEscenario();
setMousePosition(event);
holdMouseButton = false;
activateCell(event);
repaint();
}
}
});
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent event) {
super.mouseDragged(event);
escenario.emptyEscenario();
setMousePosition(event);
repaint();
}
});
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent event) {
super.keyPressed(event);
escenario.emptyEscenario();
playerSelected = false;
escenario.keyPressed(event);
repaint();
}
});
final Lock lock = new ReentrantLock(true);
enemy = new Enemy(escenario, lock);
enemy.setCoordinates(10, 3);
enemy2 = new Enemy(escenario, lock);
enemy2.setCoordinates(10, 7);
enemy2.setDirection(Enemy.Direction.DOWN);
Thread thread = new Thread(enemy);
Thread thread2 = new Thread(enemy2);
thread.start();
thread2.start();
try {
backgroundMusic = new Sound("/res/snd/GameLoop.wav");
backgroundMusic.setLoops(Clip.LOOP_CONTINUOUSLY);
backgroundMusic.play();
}
catch (SoundException e) {
logger.warning(e.getMessage());
}
}
/**
* Activate the cell that was clicked
*
* @param event The mouse click event
*/
private void activateCell(MouseEvent event) {
for (int i = 0; i < HORIZONTAL_CELLS; i++) {
for (int j = 0; j < VERTICAL_CELLS; j++) {
if (escenario.getCeldas()[i][j].selected(event.getX(), event.getY())) {
logger.info("Cell x: " + i + " y: " + j + " selected");
escenario.getCeldas()[i][j].setSelected(true);
if (playerSelected) {
if (escenario.getCeldas()[i][j].getType() == Celda.Type.SPACE) {
int x = escenario.getPlayer().getX();
int y = escenario.getPlayer().getY();
// Put the player in the new place
escenario.getCeldas()[i][j].setType(Celda.Type.PLAYER);
escenario.getCeldas()[i][j].setAnimation(escenario.getCeldas()[x][y].getAnimation());
escenario.getPlayer().setCoords(i, j);
playerSelected = false;
// Remove the player from previous place
escenario.getCeldas()[x][y].setType(Celda.Type.SPACE);
escenario.getCeldas()[x][y].setAnimation(null);
escenario.emptyEscenario();
break;
}
}
if (escenario.getCeldas()[i][j].getType() == Celda.Type.PLAYER) {
playerSelected = true;
}
break;
}
}
}
}
/**
@ -38,6 +203,78 @@ public class Lienzo extends Canvas implements Constantes {
*/
@Override
public void paint(Graphics g) {
escenario.paintComponent(g);
update(g);
}
/**
* Override the update method of Canvas to update using a double buffer
*
* @param g The graphics object to paint
*/
@Override
public void update(Graphics g) {
if (graphicBuffer == null) {
imageBuffer = createImage(this.getWidth(), this.getHeight());
graphicBuffer = imageBuffer.getGraphics();
}
graphicBuffer.setColor(getBackground());
graphicBuffer.fillRect(0, 0, this.getWidth(), this.getHeight());
//graphicBuffer.drawImage()
escenario.paintComponent(graphicBuffer);
g.drawImage(imageBuffer, 0, 0, null);
if (holdMouseButton && playerSelected) {
try {
int x = escenario.getPlayer().getX();
int y = escenario.getPlayer().getY();
Celda celda = escenario.getCeldas()[x][y];
if (celda.getAnimation() != null) {
g.drawImage(celda.getAnimation().getNextFrame(), mouseX, mouseY, null);
}
}
catch (AnimationException e) {
logger.warning(e.getMessage());
}
}
}
/**
* Set the position of the mouse while it is being dragged
*
* @param event The event
*/
private void setMousePosition(MouseEvent event) {
mouseX = event.getX();
mouseY = event.getY();
}
/**
* Change the speed of the enemies
*
* @param speed The new speed
*/
public void changeSpeed(int speed) {
if (speed <= 0) {
speed = 1;
}
enemy.setSpeed(speed);
enemy2.setSpeed(speed);
requestFocus();
}
/**
* Change the volume of the game background music
*
* @param volume The new volume
*/
public void changeVolume(float volume) {
try {
backgroundMusic.setVolume(volume);
}
catch (SoundException e) {
logger.warning(e.getMessage());
}
}
}

View File

@ -15,20 +15,33 @@
package cl.cromer.game;
import javax.swing.JFrame;
import javax.swing.*;
import java.util.logging.Logger;
/**
* The main class of the game
*/
public class Main {
public class Main implements Constantes {
/**
* Initialize the main class
*/
public Main() {
Logger logger = getLogger(this.getClass(), MAIN_LOG_LEVEL);
logger.info("Load main window");
VentanaPrincipal ventanaPrincipal = new VentanaPrincipal();
ventanaPrincipal.setVisible(true);
ventanaPrincipal.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
logger.info("Finished loading main window");
}
/**
* Open the main window
* @param args The arguments passed to the application
*/
public static void main (String[]args) {
VentanaPrincipal ventanaPrincipal = new VentanaPrincipal();
ventanaPrincipal.setVisible(true);
ventanaPrincipal.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
new Main();
}
}

View File

@ -20,9 +20,13 @@ package cl.cromer.game;
*/
public class RandomPositionList {
/**
* The position
* The x position
*/
private int position;
private int x;
/**
* The y position
*/
private int y;
/**
* The type
*/
@ -30,20 +34,31 @@ public class RandomPositionList {
/**
* Initialize the position and type of the list
* @param position The position
* @param x The x position
* @param y The y position
* @param type The type
*/
public RandomPositionList(int position, Celda.Type type) {
this.position = position;
public RandomPositionList(int x, int y, Celda.Type type) {
this.x = x;
this.y = y;
this.type = type;
}
/**
* Return the position that the object should be drawn
* @return The position
* Get the x position
* @return Returns the x position
*/
public int getPosition() {
return position;
public int getX() {
return x;
}
/**
* Get the y position
*
* @return Returns the y position
*/
public int getY() {
return y;
}
/**
@ -68,6 +83,6 @@ public class RandomPositionList {
return false;
}
RandomPositionList that = (RandomPositionList) o;
return position == that.position;
return (x == that.x && y == that.y);
}
}

View File

@ -15,22 +15,58 @@
package cl.cromer.game;
import javax.swing.JFrame;
import java.awt.Dimension;
import cl.cromer.game.panel.Config;
import cl.cromer.game.panel.Game;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Logger;
/**
* The main window of the game
*/
public class VentanaPrincipal extends JFrame implements Constantes {
/**
* Initialize the main window
*/
public VentanaPrincipal() {
Lienzo lienzo = new Lienzo();
Dimension screenSize = super.getToolkit().getScreenSize();
this.getContentPane().add(lienzo);
this.setSize(screenSize.width, screenSize.height);
this.setExtendedState(this.getExtendedState() | JFrame.MAXIMIZED_BOTH);
}
Logger logger = getLogger(this.getClass(), VENTANA_PRINCIPAL_LOG_LEVEL);
logger.info("Create panels");
JSplitPane panelSeparator = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
panelSeparator.setOneTouchExpandable(true);
Game gamePanel = new Game();
Config configPanel = new Config(gamePanel);
panelSeparator.setLeftComponent(gamePanel);
panelSeparator.setRightComponent(configPanel);
panelSeparator.setDividerLocation(gamePanel.getWidth() + (LEFT_MARGIN * 2));
panelSeparator.setDividerSize(0);
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(panelSeparator, BorderLayout.CENTER);
setExtendedState(this.getExtendedState() | JFrame.MAXIMIZED_BOTH);
setSize(SCREEN_SIZE.width - 50, SCREEN_SIZE.height - 50);
setTitle(TITLE);
String icon = "/res/img/icon.png";
try {
BufferedImage image = ImageIO.read(getClass().getResourceAsStream(icon));
this.setIconImage(image);
}
catch (IOException | IllegalArgumentException e) {
logger.warning("Failed to load icon: " + icon);
logger.warning(e.getMessage());
}
logger.info("Finished creating panels");
}
}

View File

@ -0,0 +1,119 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.logging;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
/**
* This class handles formatting the log into a html table
*/
public class HtmlFormatter extends Formatter {
/**
* Create a log row in the html table
*
* @param logRecord The record to insert into the table
* @return Returns the log row in html
*/
public String format(LogRecord logRecord) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("<tr>\n");
// colorize any levels >= WARNING in red
if (logRecord.getLevel().intValue() >= Level.WARNING.intValue()) {
stringBuilder.append("\t<td style=\"color:red\">");
stringBuilder.append("<b>");
stringBuilder.append(StringUtils.encodeHtml(logRecord.getLevel().getName()));
stringBuilder.append("</b>");
}
else {
stringBuilder.append("\t<td>");
stringBuilder.append(StringUtils.encodeHtml(logRecord.getLevel().getName()));
}
stringBuilder.append("</td>\n");
stringBuilder.append("\t<td>");
stringBuilder.append(StringUtils.encodeHtml(calculateDate(logRecord.getMillis())));
stringBuilder.append("</td>\n");
stringBuilder.append("\t<td>");
stringBuilder.append(StringUtils.encodeHtml(logRecord.getSourceClassName()));
stringBuilder.append("</td>\n");
stringBuilder.append("\t<td>");
stringBuilder.append(StringUtils.encodeHtml(logRecord.getSourceMethodName()));
stringBuilder.append("</td>\n");
stringBuilder.append("\t<td>");
stringBuilder.append(StringUtils.encodeHtml(formatMessage(logRecord)));
stringBuilder.append("</td>\n");
stringBuilder.append("</tr>\n");
return stringBuilder.toString();
}
/**
* Calculate the date and time for the log row
*
* @param milliseconds The time the log record happened in milliseconds
* @return Returns the formatted time
*/
private String calculateDate(long milliseconds) {
SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd,yyyy HH:mm:ss");
Date resultDate = new Date(milliseconds);
return dateFormat.format(resultDate);
}
/**
* This method is called to get the head of the log
*
* @param handler The handler
* @return Returns the header of the log
*/
public String getHead(Handler handler) {
return "<!DOCTYPE html>\n<head>\n<style>\n"
+ "table { width: 100%; border: 1px solid black; border-collapse: collapse; }\n"
+ "th { font:bold 10pt Tahoma; border: 1px solid black; border-collapse: collapse; }\n"
+ "td { font:normal 10pt Tahoma; border: 1px solid black; border-collapse: collapse; }\n"
+ "h1 {font:normal 11pt Tahoma;}\n"
+ "</style>\n"
+ "</head>\n"
+ "<body>\n"
+ "<h1>" + (StringUtils.encodeHtml(new Date().toString())) + "</h1>\n"
+ "<table border=\"0\" cellpadding=\"5\" cellspacing=\"3\">\n"
+ "<tr align=\"left\">\n"
+ "\t<th style=\"width:10%\">Loglevel</th>\n"
+ "\t<th style=\"width:15%\">Time</th>\n"
+ "\t<th style=\"width:10%\">Class</th>\n"
+ "\t<th style=\"width:10%\">Method</th>\n"
+ "\t<th style=\"width:55%\">Log Message</th>\n"
+ "</tr>\n";
}
/**
* This method is called to get the tail of the log
*
* @param handler The handler
* @return Returns the tail of the log
*/
public String getTail(Handler handler) {
return "</table>\n</body>\n</html>";
}
}

View File

@ -0,0 +1,333 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.logging;
import java.util.HashMap;
/**
* This class handles string formatting for html
*/
public class StringUtils {
/**
* A map of all the characters that need to be encoded
*/
private static final HashMap<Character, String> htmlEncodeChars = new HashMap<>();
static {
// Special characters for HTML
htmlEncodeChars.put('\u0026', "&amp;");
htmlEncodeChars.put('\u003C', "&lt;");
htmlEncodeChars.put('\u003E', "&gt;");
htmlEncodeChars.put('\u0022', "&quot;");
htmlEncodeChars.put('\u0152', "&OElig;");
htmlEncodeChars.put('\u0153', "&oelig;");
htmlEncodeChars.put('\u0160', "&Scaron;");
htmlEncodeChars.put('\u0161', "&scaron;");
htmlEncodeChars.put('\u0178', "&Yuml;");
htmlEncodeChars.put('\u02C6', "&circ;");
htmlEncodeChars.put('\u02DC', "&tilde;");
htmlEncodeChars.put('\u2002', "&ensp;");
htmlEncodeChars.put('\u2003', "&emsp;");
htmlEncodeChars.put('\u2009', "&thinsp;");
htmlEncodeChars.put('\u200C', "&zwnj;");
htmlEncodeChars.put('\u200D', "&zwj;");
htmlEncodeChars.put('\u200E', "&lrm;");
htmlEncodeChars.put('\u200F', "&rlm;");
htmlEncodeChars.put('\u2013', "&ndash;");
htmlEncodeChars.put('\u2014', "&mdash;");
htmlEncodeChars.put('\u2018', "&lsquo;");
htmlEncodeChars.put('\u2019', "&rsquo;");
htmlEncodeChars.put('\u201A', "&sbquo;");
htmlEncodeChars.put('\u201C', "&ldquo;");
htmlEncodeChars.put('\u201D', "&rdquo;");
htmlEncodeChars.put('\u201E', "&bdquo;");
htmlEncodeChars.put('\u2020', "&dagger;");
htmlEncodeChars.put('\u2021', "&Dagger;");
htmlEncodeChars.put('\u2030', "&permil;");
htmlEncodeChars.put('\u2039', "&lsaquo;");
htmlEncodeChars.put('\u203A', "&rsaquo;");
htmlEncodeChars.put('\u20AC', "&euro;");
// Character entity references for ISO 8859-1 characters
htmlEncodeChars.put('\u00A0', "&nbsp;");
htmlEncodeChars.put('\u00A1', "&iexcl;");
htmlEncodeChars.put('\u00A2', "&cent;");
htmlEncodeChars.put('\u00A3', "&pound;");
htmlEncodeChars.put('\u00A4', "&curren;");
htmlEncodeChars.put('\u00A5', "&yen;");
htmlEncodeChars.put('\u00A6', "&brvbar;");
htmlEncodeChars.put('\u00A7', "&sect;");
htmlEncodeChars.put('\u00A8', "&uml;");
htmlEncodeChars.put('\u00A9', "&copy;");
htmlEncodeChars.put('\u00AA', "&ordf;");
htmlEncodeChars.put('\u00AB', "&laquo;");
htmlEncodeChars.put('\u00AC', "&not;");
htmlEncodeChars.put('\u00AD', "&shy;");
htmlEncodeChars.put('\u00AE', "&reg;");
htmlEncodeChars.put('\u00AF', "&macr;");
htmlEncodeChars.put('\u00B0', "&deg;");
htmlEncodeChars.put('\u00B1', "&plusmn;");
htmlEncodeChars.put('\u00B2', "&sup2;");
htmlEncodeChars.put('\u00B3', "&sup3;");
htmlEncodeChars.put('\u00B4', "&acute;");
htmlEncodeChars.put('\u00B5', "&micro;");
htmlEncodeChars.put('\u00B6', "&para;");
htmlEncodeChars.put('\u00B7', "&middot;");
htmlEncodeChars.put('\u00B8', "&cedil;");
htmlEncodeChars.put('\u00B9', "&sup1;");
htmlEncodeChars.put('\u00BA', "&ordm;");
htmlEncodeChars.put('\u00BB', "&raquo;");
htmlEncodeChars.put('\u00BC', "&frac14;");
htmlEncodeChars.put('\u00BD', "&frac12;");
htmlEncodeChars.put('\u00BE', "&frac34;");
htmlEncodeChars.put('\u00BF', "&iquest;");
htmlEncodeChars.put('\u00C0', "&Agrave;");
htmlEncodeChars.put('\u00C1', "&Aacute;");
htmlEncodeChars.put('\u00C2', "&Acirc;");
htmlEncodeChars.put('\u00C3', "&Atilde;");
htmlEncodeChars.put('\u00C4', "&Auml;");
htmlEncodeChars.put('\u00C5', "&Aring;");
htmlEncodeChars.put('\u00C6', "&AElig;");
htmlEncodeChars.put('\u00C7', "&Ccedil;");
htmlEncodeChars.put('\u00C8', "&Egrave;");
htmlEncodeChars.put('\u00C9', "&Eacute;");
htmlEncodeChars.put('\u00CA', "&Ecirc;");
htmlEncodeChars.put('\u00CB', "&Euml;");
htmlEncodeChars.put('\u00CC', "&Igrave;");
htmlEncodeChars.put('\u00CD', "&Iacute;");
htmlEncodeChars.put('\u00CE', "&Icirc;");
htmlEncodeChars.put('\u00CF', "&Iuml;");
htmlEncodeChars.put('\u00D0', "&ETH;");
htmlEncodeChars.put('\u00D1', "&Ntilde;");
htmlEncodeChars.put('\u00D2', "&Ograve;");
htmlEncodeChars.put('\u00D3', "&Oacute;");
htmlEncodeChars.put('\u00D4', "&Ocirc;");
htmlEncodeChars.put('\u00D5', "&Otilde;");
htmlEncodeChars.put('\u00D6', "&Ouml;");
htmlEncodeChars.put('\u00D7', "&times;");
htmlEncodeChars.put('\u00D8', "&Oslash;");
htmlEncodeChars.put('\u00D9', "&Ugrave;");
htmlEncodeChars.put('\u00DA', "&Uacute;");
htmlEncodeChars.put('\u00DB', "&Ucirc;");
htmlEncodeChars.put('\u00DC', "&Uuml;");
htmlEncodeChars.put('\u00DD', "&Yacute;");
htmlEncodeChars.put('\u00DE', "&THORN;");
htmlEncodeChars.put('\u00DF', "&szlig;");
htmlEncodeChars.put('\u00E0', "&agrave;");
htmlEncodeChars.put('\u00E1', "&aacute;");
htmlEncodeChars.put('\u00E2', "&acirc;");
htmlEncodeChars.put('\u00E3', "&atilde;");
htmlEncodeChars.put('\u00E4', "&auml;");
htmlEncodeChars.put('\u00E5', "&aring;");
htmlEncodeChars.put('\u00E6', "&aelig;");
htmlEncodeChars.put('\u00E7', "&ccedil;");
htmlEncodeChars.put('\u00E8', "&egrave;");
htmlEncodeChars.put('\u00E9', "&eacute;");
htmlEncodeChars.put('\u00EA', "&ecirc;");
htmlEncodeChars.put('\u00EB', "&euml;");
htmlEncodeChars.put('\u00EC', "&igrave;");
htmlEncodeChars.put('\u00ED', "&iacute;");
htmlEncodeChars.put('\u00EE', "&icirc;");
htmlEncodeChars.put('\u00EF', "&iuml;");
htmlEncodeChars.put('\u00F0', "&eth;");
htmlEncodeChars.put('\u00F1', "&ntilde;");
htmlEncodeChars.put('\u00F2', "&ograve;");
htmlEncodeChars.put('\u00F3', "&oacute;");
htmlEncodeChars.put('\u00F4', "&ocirc;");
htmlEncodeChars.put('\u00F5', "&otilde;");
htmlEncodeChars.put('\u00F6', "&ouml;");
htmlEncodeChars.put('\u00F7', "&divide;");
htmlEncodeChars.put('\u00F8', "&oslash;");
htmlEncodeChars.put('\u00F9', "&ugrave;");
htmlEncodeChars.put('\u00FA', "&uacute;");
htmlEncodeChars.put('\u00FB', "&ucirc;");
htmlEncodeChars.put('\u00FC', "&uuml;");
htmlEncodeChars.put('\u00FD', "&yacute;");
htmlEncodeChars.put('\u00FE', "&thorn;");
htmlEncodeChars.put('\u00FF', "&yuml;");
// Mathematical, Greek and Symbolic characters for HTML
htmlEncodeChars.put('\u0192', "&fnof;");
htmlEncodeChars.put('\u0391', "&Alpha;");
htmlEncodeChars.put('\u0392', "&Beta;");
htmlEncodeChars.put('\u0393', "&Gamma;");
htmlEncodeChars.put('\u0394', "&Delta;");
htmlEncodeChars.put('\u0395', "&Epsilon;");
htmlEncodeChars.put('\u0396', "&Zeta;");
htmlEncodeChars.put('\u0397', "&Eta;");
htmlEncodeChars.put('\u0398', "&Theta;");
htmlEncodeChars.put('\u0399', "&Iota;");
htmlEncodeChars.put('\u039A', "&Kappa;");
htmlEncodeChars.put('\u039B', "&Lambda;");
htmlEncodeChars.put('\u039C', "&Mu;");
htmlEncodeChars.put('\u039D', "&Nu;");
htmlEncodeChars.put('\u039E', "&Xi;");
htmlEncodeChars.put('\u039F', "&Omicron;");
htmlEncodeChars.put('\u03A0', "&Pi;");
htmlEncodeChars.put('\u03A1', "&Rho;");
htmlEncodeChars.put('\u03A3', "&Sigma;");
htmlEncodeChars.put('\u03A4', "&Tau;");
htmlEncodeChars.put('\u03A5', "&Upsilon;");
htmlEncodeChars.put('\u03A6', "&Phi;");
htmlEncodeChars.put('\u03A7', "&Chi;");
htmlEncodeChars.put('\u03A8', "&Psi;");
htmlEncodeChars.put('\u03A9', "&Omega;");
htmlEncodeChars.put('\u03B1', "&alpha;");
htmlEncodeChars.put('\u03B2', "&beta;");
htmlEncodeChars.put('\u03B3', "&gamma;");
htmlEncodeChars.put('\u03B4', "&delta;");
htmlEncodeChars.put('\u03B5', "&epsilon;");
htmlEncodeChars.put('\u03B6', "&zeta;");
htmlEncodeChars.put('\u03B7', "&eta;");
htmlEncodeChars.put('\u03B8', "&theta;");
htmlEncodeChars.put('\u03B9', "&iota;");
htmlEncodeChars.put('\u03BA', "&kappa;");
htmlEncodeChars.put('\u03BB', "&lambda;");
htmlEncodeChars.put('\u03BC', "&mu;");
htmlEncodeChars.put('\u03BD', "&nu;");
htmlEncodeChars.put('\u03BE', "&xi;");
htmlEncodeChars.put('\u03BF', "&omicron;");
htmlEncodeChars.put('\u03C0', "&pi;");
htmlEncodeChars.put('\u03C1', "&rho;");
htmlEncodeChars.put('\u03C2', "&sigmaf;");
htmlEncodeChars.put('\u03C3', "&sigma;");
htmlEncodeChars.put('\u03C4', "&tau;");
htmlEncodeChars.put('\u03C5', "&upsilon;");
htmlEncodeChars.put('\u03C6', "&phi;");
htmlEncodeChars.put('\u03C7', "&chi;");
htmlEncodeChars.put('\u03C8', "&psi;");
htmlEncodeChars.put('\u03C9', "&omega;");
htmlEncodeChars.put('\u03D1', "&thetasym;");
htmlEncodeChars.put('\u03D2', "&upsih;");
htmlEncodeChars.put('\u03D6', "&piv;");
htmlEncodeChars.put('\u2022', "&bull;");
htmlEncodeChars.put('\u2026', "&hellip;");
htmlEncodeChars.put('\u2032', "&prime;");
htmlEncodeChars.put('\u2033', "&Prime;");
htmlEncodeChars.put('\u203E', "&oline;");
htmlEncodeChars.put('\u2044', "&frasl;");
htmlEncodeChars.put('\u2118', "&weierp;");
htmlEncodeChars.put('\u2111', "&image;");
htmlEncodeChars.put('\u211C', "&real;");
htmlEncodeChars.put('\u2122', "&trade;");
htmlEncodeChars.put('\u2135', "&alefsym;");
htmlEncodeChars.put('\u2190', "&larr;");
htmlEncodeChars.put('\u2191', "&uarr;");
htmlEncodeChars.put('\u2192', "&rarr;");
htmlEncodeChars.put('\u2193', "&darr;");
htmlEncodeChars.put('\u2194', "&harr;");
htmlEncodeChars.put('\u21B5', "&crarr;");
htmlEncodeChars.put('\u21D0', "&lArr;");
htmlEncodeChars.put('\u21D1', "&uArr;");
htmlEncodeChars.put('\u21D2', "&rArr;");
htmlEncodeChars.put('\u21D3', "&dArr;");
htmlEncodeChars.put('\u21D4', "&hArr;");
htmlEncodeChars.put('\u2200', "&forall;");
htmlEncodeChars.put('\u2202', "&part;");
htmlEncodeChars.put('\u2203', "&exist;");
htmlEncodeChars.put('\u2205', "&empty;");
htmlEncodeChars.put('\u2207', "&nabla;");
htmlEncodeChars.put('\u2208', "&isin;");
htmlEncodeChars.put('\u2209', "&notin;");
htmlEncodeChars.put('\u220B', "&ni;");
htmlEncodeChars.put('\u220F', "&prod;");
htmlEncodeChars.put('\u2211', "&sum;");
htmlEncodeChars.put('\u2212', "&minus;");
htmlEncodeChars.put('\u2217', "&lowast;");
htmlEncodeChars.put('\u221A', "&radic;");
htmlEncodeChars.put('\u221D', "&prop;");
htmlEncodeChars.put('\u221E', "&infin;");
htmlEncodeChars.put('\u2220', "&ang;");
htmlEncodeChars.put('\u2227', "&and;");
htmlEncodeChars.put('\u2228', "&or;");
htmlEncodeChars.put('\u2229', "&cap;");
htmlEncodeChars.put('\u222A', "&cup;");
htmlEncodeChars.put('\u222B', "&int;");
htmlEncodeChars.put('\u2234', "&there4;");
htmlEncodeChars.put('\u223C', "&sim;");
htmlEncodeChars.put('\u2245', "&cong;");
htmlEncodeChars.put('\u2248', "&asymp;");
htmlEncodeChars.put('\u2260', "&ne;");
htmlEncodeChars.put('\u2261', "&equiv;");
htmlEncodeChars.put('\u2264', "&le;");
htmlEncodeChars.put('\u2265', "&ge;");
htmlEncodeChars.put('\u2282', "&sub;");
htmlEncodeChars.put('\u2283', "&sup;");
htmlEncodeChars.put('\u2284', "&nsub;");
htmlEncodeChars.put('\u2286', "&sube;");
htmlEncodeChars.put('\u2287', "&supe;");
htmlEncodeChars.put('\u2295', "&oplus;");
htmlEncodeChars.put('\u2297', "&otimes;");
htmlEncodeChars.put('\u22A5', "&perp;");
htmlEncodeChars.put('\u22C5', "&sdot;");
htmlEncodeChars.put('\u2308', "&lceil;");
htmlEncodeChars.put('\u2309', "&rceil;");
htmlEncodeChars.put('\u230A', "&lfloor;");
htmlEncodeChars.put('\u230B', "&rfloor;");
htmlEncodeChars.put('\u2329', "&lang;");
htmlEncodeChars.put('\u232A', "&rang;");
htmlEncodeChars.put('\u25CA', "&loz;");
htmlEncodeChars.put('\u2660', "&spades;");
htmlEncodeChars.put('\u2663', "&clubs;");
htmlEncodeChars.put('\u2665', "&hearts;");
htmlEncodeChars.put('\u2666', "&diams;");
}
/**
* Encode the string for html characters
*
* @param source The string to encode
* @return Returns a new string that is encoded with html characters
*/
public static String encodeHtml(String source) {
if (source == null) {
return null;
}
StringBuffer encoded_string = null;
char[] string_to_encode_array = source.toCharArray();
int last_match = -1;
int difference;
for (int i = 0; i < string_to_encode_array.length; i++) {
char char_to_encode = string_to_encode_array[i];
if (StringUtils.htmlEncodeChars.containsKey(char_to_encode)) {
if (encoded_string == null) {
encoded_string = new StringBuffer(source.length());
}
difference = i - (last_match + 1);
if (difference > 0) {
encoded_string.append(string_to_encode_array, last_match + 1, difference);
}
encoded_string.append(StringUtils.htmlEncodeChars.get(char_to_encode));
last_match = i;
}
}
if (encoded_string == null) {
return source;
}
else {
difference = string_to_encode_array.length - (last_match + 1);
if (difference > 0) {
encoded_string.append(string_to_encode_array, last_match + 1, difference);
}
return encoded_string.toString();
}
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.object;
/**
* This class handles the chests
*/
public class Chest {
/**
* The current state of the chest
*/
private State state = State.CLOSED;
/**
* The possible states of the chest
*/
private enum State {
/**
* The chest is closed
*/
CLOSED,
/**
* The chest is opened
*/
OPENED
}
}

View File

@ -0,0 +1,237 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.object;
import cl.cromer.game.Celda;
import cl.cromer.game.Constantes;
import cl.cromer.game.Escenario;
import cl.cromer.game.sprite.Animation;
import cl.cromer.game.sprite.AnimationException;
import java.util.concurrent.locks.Lock;
import java.util.logging.Logger;
/**
* This class handles the enemy object
*/
public class Enemy implements Runnable, Constantes {
/**
* The scene that contains the enemy
*/
private Escenario escenario;
/**
* The cell that contains the enemy
*/
private Celda celda;
/**
* The current x position of the enemy
*/
private int x = 0;
/**
* The current y position of the enemy
*/
private int y = 0;
/**
* The current direction the enemy is facing
*/
private Direction direction = Direction.LEFT;
/**
* The logger for this class
*/
private Logger logger;
/**
* If the enemy is alive
*/
private boolean alive = true;
/**
* The speed of the enemy
*/
private int speed = 500;
/**
* The lock helps prevent race conditions when checking positioning
*/
private Lock lock;
/**
* Initialize the enemy
*
* @param escenario The scene the enemy is in
*/
public Enemy(Escenario escenario, Lock lock) {
this.lock = lock;
logger = getLogger(this.getClass(), ENEMY_LOG_LEVEL);
this.escenario = escenario;
celda = new Celda(x, y);
celda.setType(Celda.Type.ENEMY);
}
/**
* Set the x and y coordinate of the enemy
*
* @param x The x coordinate
* @param y The y coordinate
*/
public void setCoordinates(int x, int y) {
this.x = x;
this.y = y;
celda.setCoords(x, y);
}
/**
* Set the direction of the enemy
*
* @param direction The direction the enemy is facing
*/
public void setDirection(Direction direction) {
this.direction = direction;
}
/**
* This method handles the enemy's movements
*/
private void move() {
if (direction == Direction.LEFT) {
if (x > 0 && escenario.getCeldas()[x - 1][y].getType() == Celda.Type.SPACE) {
escenario.getCeldas()[x - 1][y].setType(Celda.Type.ENEMY);
escenario.getCeldas()[x - 1][y].setAnimation(escenario.getCeldas()[x][y].getAnimation());
escenario.getCeldas()[x][y].setType(Celda.Type.SPACE);
escenario.getCeldas()[x][y].setAnimation(null);
try {
escenario.getCeldas()[x - 1][y].getAnimation().getNextFrame();
}
catch (AnimationException e) {
logger.warning(e.getMessage());
}
x--;
logger.info("Move left to x: " + x + " y: " + y);
}
else {
logger.info("Change to right direction");
escenario.getCeldas()[x][y].getAnimation().setCurrentDirection(Animation.Direction.RIGHT);
direction = Direction.RIGHT;
}
}
else if (direction == Direction.RIGHT) {
if (x < (HORIZONTAL_CELLS) - 1 && escenario.getCeldas()[x + 1][y].getType() == Celda.Type.SPACE) {
escenario.getCeldas()[x + 1][y].setType(Celda.Type.ENEMY);
escenario.getCeldas()[x + 1][y].setAnimation(escenario.getCeldas()[x][y].getAnimation());
escenario.getCeldas()[x][y].setType(Celda.Type.SPACE);
escenario.getCeldas()[x][y].setAnimation(null);
try {
escenario.getCeldas()[x + 1][y].getAnimation().getNextFrame();
}
catch (AnimationException e) {
logger.warning(e.getMessage());
}
x++;
logger.info("Move right to x: " + x + " y: " + y);
}
else {
logger.info("Change to left direction");
escenario.getCeldas()[x][y].getAnimation().setCurrentDirection(Animation.Direction.LEFT);
direction = Direction.LEFT;
}
}
else if (direction == Direction.DOWN) {
if (y < (VERTICAL_CELLS) - 1 && escenario.getCeldas()[x][y + 1].getType() == Celda.Type.SPACE) {
escenario.getCeldas()[x][y + 1].setType(Celda.Type.ENEMY);
escenario.getCeldas()[x][y + 1].setAnimation(escenario.getCeldas()[x][y].getAnimation());
escenario.getCeldas()[x][y].setType(Celda.Type.SPACE);
escenario.getCeldas()[x][y].setAnimation(null);
try {
escenario.getCeldas()[x][y + 1].getAnimation().getNextFrame();
}
catch (AnimationException e) {
logger.warning(e.getMessage());
}
y++;
logger.info("Move down to x: " + x + " y: " + y);
}
else {
logger.info("Change to up direction");
escenario.getCeldas()[x][y].getAnimation().setCurrentDirection(Animation.Direction.UP);
direction = Direction.UP;
}
}
else if (direction == Direction.UP) {
if (y > 0 && escenario.getCeldas()[x][y - 1].getType() == Celda.Type.SPACE) {
escenario.getCeldas()[x][y - 1].setType(Celda.Type.ENEMY);
escenario.getCeldas()[x][y - 1].setAnimation(escenario.getCeldas()[x][y].getAnimation());
escenario.getCeldas()[x][y].setType(Celda.Type.SPACE);
escenario.getCeldas()[x][y].setAnimation(null);
try {
escenario.getCeldas()[x][y - 1].getAnimation().getNextFrame();
}
catch (AnimationException e) {
logger.warning(e.getMessage());
}
y--;
logger.info("Move up to x: " + x + " y: " + y);
}
else {
logger.info("Change to down direction");
escenario.getCeldas()[x][y].getAnimation().setCurrentDirection(Animation.Direction.DOWN);
direction = Direction.DOWN;
}
}
}
/**
* This method is run constantly by the runnable
*/
public void run() {
while (alive) {
try {
Thread.sleep(speed);
}
catch (InterruptedException e) {
logger.warning(e.getMessage());
}
synchronized (this) {
lock.lock();
move();
escenario.getCanvas().repaint();
lock.unlock();
}
}
}
/**
* Kill the enemy
*/
public void kill() {
alive = false;
}
/**
* Set the speed of the enemy
*
* @param speed The new speed
*/
public void setSpeed(int speed) {
this.speed = speed;
}
/**
* The possible directions the enemy can face
*/
public enum Direction {
UP,
DOWN,
LEFT,
RIGHT
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.object;
/**
* This class contains the gem
*/
public class Gem {
/**
* The current state of the gem
*/
private State state = State.TAINTED;
/**
* The possible states of the gem
*/
private enum State {
/**
* The gem is tainted
*/
TAINTED,
/**
* The gem has been purified
*/
PURIFIED
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.object;
/**
* This class contains the key
*/
public class Key {
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.object;
import java.util.ArrayList;
/**
* This class contains the player
*/
public class Player {
/**
* The maximum health of the player
*/
private final int MAX_HEALTH = 10;
/**
* The current health of the player
*/
private int health = MAX_HEALTH;
/**
* The current x position of the player
*/
private int x;
/**
* The current y position of the player
*/
private int y;
/**
* Objects that the player is carrying
*/
private ArrayList<Object> carrying;
}

View File

@ -0,0 +1,104 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.panel;
import cl.cromer.game.Constantes;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import java.awt.*;
import java.util.logging.Logger;
/**
* The config panel that is shown on the right
*/
public class Config extends JPanel implements Constantes {
/**
* The game panel to modify with the new configuration
*/
private Game gamePanel;
/**
* The logger
*/
private Logger logger;
/**
* The game panel used to modify with the new config
*
* @param gamePanel The game panel
*/
public Config(Game gamePanel) {
this.gamePanel = gamePanel;
logger = getLogger(this.getClass(), CONFIG_LOG_LEVEL);
JLabel speed = new JLabel("Speed");
speed.setForeground(Color.yellow);
speed.setFont(FONT);
speed.setHorizontalAlignment(JLabel.CENTER);
speed.setBackground(Color.gray);
JLabel volume = new JLabel("Volume");
volume.setForeground(Color.yellow);
volume.setFont(FONT);
volume.setHorizontalAlignment(JLabel.CENTER);
volume.setBackground(Color.gray);
JSlider changeVolume = new JSlider(JSlider.HORIZONTAL, MINIMUM_VOLUME, MAXIMUM_VOLUME, DEFAULT_VOLUME);
changeVolume.addChangeListener(this::volumeSliderListener);
changeVolume.setMajorTickSpacing(10);
changeVolume.setPaintTicks(true);
changeVolume.setBackground(Color.gray);
JSlider changeSpeed = new JSlider(JSlider.HORIZONTAL, MINIMUM_SPEED, MAXIMUM_SPEED, DEFAULT_SPEED);
changeSpeed.addChangeListener(this::speedSliderListener);
changeSpeed.setMajorTickSpacing(100);
changeSpeed.setPaintTicks(true);
changeSpeed.setBackground(Color.gray);
setLayout(new GridLayout(2, 2, 5, 5));
setBackground(Color.gray);
add(speed);
add(changeSpeed);
add(volume);
add(changeVolume);
}
/**
* Listener for the speed slider control
*
* @param changeEvent The event that caused the listener to fire
*/
private void speedSliderListener(ChangeEvent changeEvent) {
JSlider jSlider = (JSlider) changeEvent.getSource();
int speed = 500 - jSlider.getValue() + 100;
logger.info("Speed slider adjusted: " + speed);
gamePanel.getCanvas().changeSpeed(speed);
}
/**
* Listener for the volume slider control
*
* @param changeEvent The event that caused the listener to fire
*/
private void volumeSliderListener(ChangeEvent changeEvent) {
JSlider jSlider = (JSlider) changeEvent.getSource();
float volume = (float) jSlider.getValue() / 100;
gamePanel.getCanvas().changeVolume(volume);
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.panel;
import cl.cromer.game.Constantes;
import cl.cromer.game.Lienzo;
import javax.swing.*;
import java.awt.*;
/**
* The game panel
*/
public class Game extends JPanel implements Constantes {
/**
* The canvas
*/
private Lienzo canvas;
/**
* Initialize the game panel
*/
public Game() {
setLayout(new BorderLayout());
canvas = new Lienzo();
canvas.setFocusable(true);
canvas.requestFocus();
add(canvas);
setSize(canvas.getWidth(), canvas.getHeight());
}
/**
* Get the canvas
*
* @return Returns the game canvas
*/
public Lienzo getCanvas() {
return canvas;
}
/**
* The language to use in the game
*/
public enum Language {
ENGLISH,
SPANISH
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.panel;
import cl.cromer.game.Constantes;
import javax.swing.*;
import java.awt.*;
/**
* The menu panel
*/
public class Menu extends JPanel implements Constantes {
/**
* Initialize the menu panel
*
* @param ventanaPrincipal The main window
*/
public Menu(JFrame ventanaPrincipal) {
this.setName("La Aventura de Azaraka");
this.setLayout(null);
this.setSize(SCREEN_SIZE.width, SCREEN_SIZE.height);
JLabel name = new JLabel("NAME: ");
name.setFont(new Font("monospaced", Font.BOLD, 40));
name.setForeground(Color.orange);
name.setBounds(20, 20, 400, 200);
add(name);
}
}

View File

@ -0,0 +1,149 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.sound;
import cl.cromer.game.Constantes;
import javax.sound.sampled.*;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Logger;
/**
* This class handles sound
*/
public class Sound implements Constantes {
/**
* The sound clip to play
*/
private Clip sound;
/**
* The path to the sound
*/
private String path;
/**
* The logger
*/
private Logger logger;
/**
* Load the sound
*
* @param path The path to the sound resource
*/
public Sound(String path) throws SoundException {
this.path = path;
logger = getLogger(this.getClass(), SOUND_LOG_LEVEL);
InputStream inputStream = this.getClass().getResourceAsStream(path);
if (inputStream == null) {
throw new SoundException("Could not load sound: " + path);
}
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(inputStream));
}
catch (UnsupportedAudioFileException | IOException e) {
logger.warning(e.getMessage());
}
try {
/*DataLine.Info info = null;
if (audioInputStream != null) {
info = new DataLine.Info(Clip.class, audioInputStream.getFormat());
}
sound = (Clip) AudioSystem.getLine(info);*/
sound = AudioSystem.getClip();
}
catch (LineUnavailableException e) {
logger.warning(e.getMessage());
}
try {
if (audioInputStream != null) {
sound.open(audioInputStream);
}
}
catch (LineUnavailableException | IOException e) {
logger.warning(e.getMessage());
}
logger.info("Opened sound: " + path);
}
/**
* Play the sound
*/
public void play() throws SoundException {
if (sound == null) {
throw new SoundException("Sound is null!");
}
sound.start();
logger.info("Play sound: " + path);
}
/**
* Stop the sound
*/
public void stop() throws SoundException {
if (sound == null) {
throw new SoundException("Sound is null!");
}
sound.stop();
logger.info("Stop sound: " + path);
}
public void setLoops(int loops) throws SoundException {
if (sound == null) {
throw new SoundException("Sound is null!");
}
sound.loop(loops);
}
/**
* Set the volume of the sound
*
* @param volume Volume between 0f and 1f
*/
public void setVolume(float volume) throws SoundException {
if (sound == null) {
throw new SoundException("Sound is null!");
}
if (volume < 0f || volume > 1f) {
throw new SoundException("Invalid sound range!");
}
if (sound.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
FloatControl gainControl = (FloatControl) sound.getControl(FloatControl.Type.MASTER_GAIN);
float dB = (float) (Math.log(volume) / Math.log(10.0) * 20.0);
logger.info("Set volume " + path + ": " + dB);
gainControl.setValue(dB);
}
else if (sound.isControlSupported(FloatControl.Type.VOLUME)) {
logger.info("Gain control not supported");
float newVolume = 0;
if (volume > 0f) {
newVolume = ((volume * 100) * 65536) / 100;
}
FloatControl volumeControl = (FloatControl) sound.getControl(FloatControl.Type.VOLUME);
logger.info("Set volume " + path + ": " + newVolume);
volumeControl.setValue(newVolume);
}
else {
logger.info("No control to modify volume");
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.sound;
/**
* This exception is thrown when there are problems with a sound
*/
public class SoundException extends Exception {
/**
* Throw an error with a message
*
* @param errorMessage The message
*/
public SoundException(String errorMessage) {
super(errorMessage);
}
}

View File

@ -0,0 +1,310 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.sprite;
import cl.cromer.game.Constantes;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Logger;
/**
* This class handles loading the images and animating the sprite
*/
public class Animation implements Cloneable, Constantes {
/**
* The current frame the sprite should be showing
*/
private int currentFrame = 0;
/**
* The offset in pixels from the left side of the cell to draw the sprite
*/
private int xOffset = 0;
/**
* The offset in pixels from the top of the cell to draw the sprite
*/
private int yOffset = 0;
/**
* The direction of the image to show
*/
private Direction currentDirection = Direction.NONE;
/**
* The collection of all the images that make up the object
*/
private HashMap<Direction, ArrayList<BufferedImage>> imageHash;
/**
* The logger
*/
private Logger logger;
/**
* Initialize the sprite
*/
public Animation() {
imageHash = new HashMap<>();
logger = getLogger(this.getClass(), IMAGE_LOG_LEVEL);
}
/**
* Scale an image
*
* @param image The image to scale
* @return Returns the scaled image
*/
static public BufferedImage scaleImage(BufferedImage image) {
Image tmp = image.getScaledInstance(16, 16, BufferedImage.SCALE_SMOOTH);
BufferedImage resized = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = resized.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
image = resized;
return image;
}
/**
* Get the offset of x for the sprite
*
* @return Returns the x offset in pixels
*/
public int getXOffset() {
return xOffset;
}
/**
* Get the offset of y for the sprite
*
* @return Returns the y offset in pixels
*/
public int getYOffset() {
return yOffset;
}
/**
* Set the y offset manually instead of using the automatically detected value
*
* @param yOffset The new y offset
*/
public void setYOffset(int yOffset) {
this.yOffset = yOffset;
}
/**
* Set the offset of x for the sprite
*
* @param x The width x of the image
*/
private void calculateXOffset(int x) {
if (x == CELL_PIXELS) {
x = 0;
}
else {
x = (CELL_PIXELS - x) / 2;
}
this.xOffset = x;
}
/**
* Set the offset of y for the sprite
*
* @param y The height y of the image
*/
private void calculateYOffset(int y) {
if (y == CELL_PIXELS) {
y = 0;
}
else {
y = (CELL_PIXELS - y) / 2;
}
this.yOffset = y;
}
/**
* Add an image to the sprite
*
* @param path The path to the sprite e.g. res/player/image.png
*/
public void addImage(Direction direction, String path) {
try {
BufferedImage bufferedImage = ImageIO.read(getClass().getResourceAsStream(path));
addImageToList(direction, bufferedImage);
}
catch (IOException | IllegalArgumentException e) {
logger.warning("Failed to load image: " + path);
logger.warning(e.getMessage());
}
}
/**
* Add an image to the sprite
*
* @param bufferedImage The path to the sprite e.g. res/player/image.png
*/
public void addImage(Direction direction, BufferedImage bufferedImage) {
addImageToList(direction, bufferedImage);
}
private void addImageToList(Direction direction, BufferedImage bufferedImage) {
calculateXOffset(bufferedImage.getWidth());
calculateYOffset(bufferedImage.getHeight());
ArrayList<BufferedImage> images;
if (imageHash.containsKey(direction)) {
images = imageHash.get(direction);
}
else {
images = new ArrayList<>();
}
images.add(bufferedImage);
imageHash.put(direction, images);
}
/**
* Returns the current frame in the sprite
*
* @return Returns the current frame
* @throws AnimationException Thrown when there are no images in the sprite
*/
public BufferedImage getFrame() throws AnimationException {
ArrayList<BufferedImage> images = getImagesFromHash();
if (currentFrame >= images.size()) {
throw new AnimationException("Animation does not have frame: " + currentFrame);
}
return images.get(currentFrame);
}
/**
* Set which frame is to be shown in the sprite manually
*
* @param frame The frame to show
* @throws AnimationException Thrown if the frame number does not exist
*/
public void setFrame(int frame) throws AnimationException {
ArrayList<BufferedImage> images;
if (imageHash.containsKey(currentDirection)) {
images = imageHash.get(currentDirection);
}
else {
throw new AnimationException("The direction has no images assigned!");
}
if (frame < 0) {
throw new AnimationException("The frame number passed is invalid!");
}
if (frame > images.size() - 1) {
throw new AnimationException("The frame does not exist inside the sprite!");
}
currentFrame = frame;
}
/**
* Returns the next frame in the sprite
*
* @return Returns the next frame in the sprite
* @throws AnimationException Thrown when there are no images in the sprite
*/
public BufferedImage getNextFrame() throws AnimationException {
ArrayList<BufferedImage> images = getImagesFromHash();
currentFrame++;
if (currentFrame >= images.size()) {
currentFrame = 0;
}
return images.get(currentFrame);
}
/**
* Get the images from the HashMap
*
* @return The images for the specific direction
* @throws AnimationException Thrown if there are no images in the hash or no images in the sprite
*/
private ArrayList<BufferedImage> getImagesFromHash() throws AnimationException {
ArrayList<BufferedImage> images;
if (imageHash.containsKey(currentDirection)) {
images = imageHash.get(currentDirection);
}
else {
throw new AnimationException("The direction has no images assigned!");
}
if (images.size() == 0) {
throw new AnimationException("There are no images in the sprite!");
}
return images;
}
/**
* Get the current direction that the animation is using
*
* @return Returns the current direction
*/
public Direction getCurrentDirection() {
return currentDirection;
}
/**
* Change the animation to the new direction and set the current frame to 0
*
* @param currentDirection The new direction
*/
public void setCurrentDirection(Direction currentDirection) {
this.currentDirection = currentDirection;
currentFrame = 0;
}
/**
* Enable the sprite to be cloned into various cells
*
* @return The cloned sprite
* @throws CloneNotSupportedException Thrown if the object does not support cloning
*/
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* The directions that the image can use
*/
public enum Direction {
/**
* The image does not belong in a direction
*/
NONE,
/**
* The image is facing up
*/
UP,
/**
* The image is facing down
*/
DOWN,
/**
* The image is facing left
*/
LEFT,
/**
* The image is facing right
*/
RIGHT
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.sprite;
/**
* This exception is thrown when there are problems with a sprite
*/
public class AnimationException extends Exception {
/**
* Throw an error with a message
*
* @param errorMessage The message
*/
public AnimationException(String errorMessage) {
super(errorMessage);
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.sprite;
import cl.cromer.game.Constantes;
import java.util.HashMap;
import java.util.logging.Logger;
/**
* This class is used to copy the sprite into a new sprite object so that the sprite doesn't get passed by reference
* This is important because 2 cells share the same sprite, but not the same frame of animation
*/
public class AnimationMap extends HashMap<Constantes.SpriteType, Animation> implements Constantes {
/**
* Clone the sprite object when returning
*
* @param key The key used to get the object
* @return Return the clone of the sprite
*/
@Override
public Animation get(Object key) {
try {
return (Animation) super.get(key).clone();
}
catch (CloneNotSupportedException e) {
Logger logger = getLogger(this.getClass(), IMAGE_LOG_LEVEL);
logger.warning(e.getMessage());
}
return null;
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.sprite;
import cl.cromer.game.Constantes;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Logger;
/**
* This class handles loading the images and subimages
*/
public class Sheet implements Constantes {
/**
* The logger
*/
Logger logger;
/**
* A list of all the tile in the collection
*/
private ArrayList<BufferedImage> images;
/**
* Initialize the tile collection and add all of its images
*
* @param path The path to the image
*/
public Sheet(String path, int height, int width) {
images = new ArrayList<>();
logger = getLogger(this.getClass(), IMAGE_LOG_LEVEL);
try {
BufferedImage image = ImageIO.read(getClass().getResourceAsStream(path));
int columns = image.getWidth() / width;
int rows = image.getHeight() / height;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
BufferedImage subImage = image.getSubimage(j * width, i * height, width, height);
images.add(subImage);
}
}
}
catch (IOException | IllegalArgumentException e) {
logger.warning("Failed to load image: " + path);
logger.warning(e.getMessage());
}
}
/**
* Returns the selected tile
*
* @return Returns the current tile
* @throws SheetException Thrown when there are no images in the tile
*/
public BufferedImage getTile(int tileNumber) throws SheetException {
if (images.size() == 0) {
throw new SheetException("There are no images in the tile collection!");
}
if (tileNumber < 0 || tileNumber > images.size() - 1) {
throw new SheetException("Invalid tile number!");
}
return images.get(tileNumber);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package cl.cromer.game.sprite;
/**
* This exception is thrown when there are problems with a sprite
*/
public class SheetException extends Exception {
/**
* Throw an error with a message
*
* @param errorMessage The message
*/
public SheetException(String errorMessage) {
super(errorMessage);
}
}

BIN
src/res/img/chest/chests.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
src/res/img/enemy/chara4.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

BIN
src/res/img/enemy/emote4.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
src/res/img/icon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

BIN
src/res/img/player/chara2.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

BIN
src/res/img/player/emote2.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
src/res/img/textures/1.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 919 KiB

BIN
src/res/img/textures/2.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 KiB

BIN
src/res/img/textures/3.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 KiB

1160
src/res/scene.json Normal file

File diff suppressed because it is too large Load Diff

BIN
src/res/snd/GameLoop.wav Normal file

Binary file not shown.

BIN
src/res/snd/OpenChest.wav Normal file

Binary file not shown.

BIN
src/res/snd/TitleLoop.wav Normal file

Binary file not shown.