Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • cagnion/mp2_anahita_et_constance_2
1 result
Show changes
Showing
with 0 additions and 1469 deletions
package ch.epfl.cs107.play.areagame.area;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import ch.epfl.cs107.play.engine.actor.Draggable;
import ch.epfl.cs107.play.engine.actor.Droppable;
import ch.epfl.cs107.play.areagame.actor.Interactable;
import ch.epfl.cs107.play.areagame.actor.Interactor;
import ch.epfl.cs107.play.io.ResourcePath;
import ch.epfl.cs107.play.math.DiscreteCoordinates;
import ch.epfl.cs107.play.window.Image;
import ch.epfl.cs107.play.window.Window;
/**
* AreaBehavior is a basically a map made of Cells. Those cells are used for the game behavior
* Note: implementation from Interactable.Listener not excpected from students
*/
public abstract class AreaBehavior implements Interactable.Listener, Interactor.Listener{
/// The behavior is an Image of size height x width
private final Image behaviorMap;
private final int width, height;
/// We will convert the image into an array of cells
private final Cell[][] cells;
/**
* Default AreaBehavior Constructor
* @param window (Window): graphic context, not null
* @param name (String): name of the behavior image, not null
*/
public AreaBehavior(Window window, String name){
// Load the image
//System.out.println(ResourcePath.getBehavior(name));
behaviorMap = window.getImage(ResourcePath.getBehavior(name), null, false);
// Get the corresponding dimension and init the array
height = behaviorMap.getHeight();
width = behaviorMap.getWidth();
cells = new Cell[width][height];
}
public void dropInteractionOf(Draggable draggable, DiscreteCoordinates mouseCoordinates) {
if(mouseCoordinates.x >= 0 && mouseCoordinates.y >= 0 && mouseCoordinates.x < width && mouseCoordinates.y < height) {
cells[mouseCoordinates.x][mouseCoordinates.y].dropInteractionOf(draggable);
}
}
/// AreaBehavior implements Interactor.Listener
@Override
public void cellInteractionOf(Interactor interactor){
for(DiscreteCoordinates dc : interactor.getCurrentCells()){
if(dc.x < 0 || dc.y < 0 || dc.x >= width || dc.y >= height)
continue;
cells[dc.x][dc.y].cellInteractionOf(interactor);
}
}
@Override
public void viewInteractionOf(Interactor interactor){
for(DiscreteCoordinates dc : interactor.getFieldOfViewCells()){
if(dc.x < 0 || dc.y < 0 || dc.x >= width || dc.y >= height)
continue;
cells[dc.x][dc.y].viewInteractionOf(interactor);
}
}
protected void setCell(int x,int y, Cell cell) {
cells[x][y] = cell;
}
protected Cell getCell(int x, int y) {
return cells[x][y];
}
protected int getRGB(int r, int c) {
return behaviorMap.getRGB(r, c);
}
protected int getHeight() {
return height;
}
protected int getWidth() {
return width;
}
/// AreaBehavior implements Interactable.Listener
@Override
public boolean canLeave(Interactable entity, List<DiscreteCoordinates> coordinates) {
for(DiscreteCoordinates c : coordinates){
if(c.x < 0 || c.y < 0 || c.x >= width || c.y >= height)
return false;
if(!cells[c.x][c.y].canLeave(entity))
return false;
}
return true;
}
@Override
public boolean canEnter(Interactable entity, List<DiscreteCoordinates> coordinates) {
for(DiscreteCoordinates c : coordinates){
if(c.x < 0 || c.y < 0 || c.x >= width || c.y >= height)
return false;
if(!cells[c.x][c.y].canEnter(entity))
return false;
}
return true;
}
@Override
public void leave(Interactable entity, List<DiscreteCoordinates> coordinates) {
for(DiscreteCoordinates c : coordinates){
cells[c.x][c.y].leave(entity);
}
}
@Override
public void enter(Interactable entity, List<DiscreteCoordinates> coordinates) {
for(DiscreteCoordinates c : coordinates){
cells[c.x][c.y].enter(entity);
}
}
/**
* Each AreaGame will have its own Cell extension.
* At minimum a cell is linked to its content
*/
public abstract class Cell implements Interactable{
/// Content of the cell as a set of Interactable
protected Set<Interactable> entities;
protected DiscreteCoordinates coordinates;
/**
* Default Cell constructor
* @param x (int): x-coordinate of this cell
* @param y (int): y-coordinate of this cell
*/
protected Cell(int x, int y){
entities = new HashSet<>();
coordinates = new DiscreteCoordinates(x, y);
}
/**
* Do the given draggableAreaEntity interacts with all Droppable sharing the same cell
* @param draggable (Draggable), not null
*/
private void dropInteractionOf(Draggable draggable) {
for(Interactable interactable : entities){
if(interactable instanceof Droppable) {
Droppable droppable = (Droppable)interactable;
if(droppable.canDrop())
droppable.receiveDropFrom(draggable);
}
}
if(this instanceof Droppable) {
Droppable droppable = (Droppable)this;
if(droppable.canDrop())
droppable.receiveDropFrom(draggable);
}
}
/**
* Do the given interactor interacts with all Interactable sharing the same cell
* @param interactor (Interactor), not null
*/
private void cellInteractionOf(Interactor interactor){
interactor.interactWith(this, true);
for(Interactable interactable : entities){
if(interactable.isCellInteractable())
interactor.interactWith(interactable, true);
}
}
/**
* Do the given interactor interacts with all Interactable sharing the same cell
* @param interactor (Interactor), not null
*/
private void viewInteractionOf(Interactor interactor){
interactor.interactWith(this, false);
for(Interactable interactable : entities){
if(interactable.isViewInteractable())
interactor.interactWith(interactable, false);
}
}
/**
* Do the given interactable enter into this Cell
* @param entity (Interactable), not null
*/
protected void enter(Interactable entity) {
entities.add(entity);
}
/**
* Do the given Interactable leave this Cell
* @param entity (Interactable), not null
*/
protected void leave(Interactable entity) {
entities.remove(entity);
}
/**
* Indicate if the given Interactable can leave this Cell
* @param entity (Interactable), not null
* @return (boolean): true if entity can leave
*/
protected abstract boolean canLeave(Interactable entity);
/**
* Indicate if the given Interactable can enter this Cell
* @param entity (Interactable), not null
* @return (boolean): true if entity can enter
*/
protected abstract boolean canEnter(Interactable entity);
/// Cell implements Interactable
@Override
public boolean takeCellSpace(){
return false;
}
@Override
public void onLeaving(List<DiscreteCoordinates> coordinates) {}
@Override
public void onEntering(List<DiscreteCoordinates> coordinates) {}
@Override
public List<DiscreteCoordinates> getCurrentCells() {
return Collections.singletonList(coordinates);
}
}
}
package ch.epfl.cs107.play.areagame.area;
import ch.epfl.cs107.play.engine.PauseMenu;
import ch.epfl.cs107.play.io.FileSystem;
import ch.epfl.cs107.play.window.Window;
/**
* AreaPauseMenu extends standard a Pause menu. It is a context pause menu. Pause menu not of the game
* but of a specific area.
* When a game is in pause mode, you can update this instead of the currentArea
*/
public abstract class AreaPauseMenu extends PauseMenu {
/// Owner Area
private boolean isResumeRequested;
/** Setter for the resume request*/
protected void requestAreaResume(){
getOwner().requestResume();
isResumeRequested = true;
}
/** @return (boolean): true if the resume is requested*/
protected boolean isResumeRequested(){
return isResumeRequested;
}
@Override
public boolean begin(Window window, FileSystem fileSystem) {
isResumeRequested = false;
return super.begin(window, fileSystem);
}
@Override
public String getTitle() {
return "Area Pause Menu";
}
}
package ch.epfl.cs107.play.areagame.handler;
import ch.epfl.cs107.play.areagame.actor.Interactable;
public interface AreaInteractionVisitor {
/// Add Interaction method with all non Abstract Interactable
/**
* Default interaction between something and an interactable
* Notice: if this method is used, then you probably forget to cast the AreaInteractionVisitor into its correct child
* @param other (Interactable): interactable to interact with, not null
*/
default void interactWith(Interactable other, boolean isCellInteraction) {
System.out.println("Specific Interaction is not yet implemented or you simply forget a cast");
}
}
package ch.epfl.cs107.play.areagame.handler;
import java.util.NavigableMap;
import java.util.TreeMap;
public abstract class Inventory {
/// List of different pockets
private final Pocket[] pockets;
/// Inventory GUI
private GUI gui;
/**
* Default Inventory Constructor
*
* @param pocketNames (Array of String), not null
*/
public Inventory(String... pocketNames) {
pockets = new Pocket[pocketNames.length];
for (int i = 0; i < pocketNames.length; i++) {
pockets[i] = new Pocket(pocketNames[i]);
}
}
/**
* Add if possible the given quantity of the given item into the given pocket
* If done, notify listener of the change into the pocket
*
* @param item (InventoryItem): item to add, not null
* @param quantity (int): quantity of the item to add
* @return (boolean): true if the given quantity of the given item has been added
*/
public boolean addPocketItem(InventoryItem item, int quantity) {
int pocket = item.getPocketId();
if (pockets[pocket].addItem(item, quantity)) {
notifyPocketUpdated(pocket);
return true;
}
return false;
}
/**
* Remove if possible the given quantity of the given item from the given pocket
* If done, notify listener of the change into the pocket
*
* @param item (InventoryItem): item to remove, not null
* @param quantity (int): quantity of the item to remove
* @return (boolean): true if the given quantity of the given item has been removed
*/
public boolean removePocketItem(InventoryItem item, int quantity) {
int pocket = item.getPocketId();
if (pockets[pocket].removeItem(item, quantity)) {
notifyPocketUpdated(pocket);
return true;
}
return false;
}
/**
* Add a listener to this inventory
*
* @param gui (GUI): The listener to add
*/
public void setGui(GUI gui) {
this.gui = gui;
// Init the gui by indicating all pocket updated
for (int id = 0; id < pockets.length; id++) {
notifyPocketUpdated(id);
}
}
/**
* Notify the listener about the given pocket update
*
* @param id (int): Pocket id
*/
protected void notifyPocketUpdated(int id) {
if (gui != null)
gui.pocketUpdated(id, pockets[id].name, new TreeMap<>(pockets[id].items));
}
/**
* Boolean accessor to this inventory which indicate if one pocket possess the given object
*
* @param item (InventoryItem): the given object to check if contained, may be null
* @return (boolean): True if one pocket possess the given object
*/
public boolean contains(InventoryItem item) {
for (Pocket pocket : pockets) {
if (pocket.items.containsKey(item)) return true;
}
return false;
}
/**
* Can be implemented by all Inventory holder. Allow others to request if the holder possess items
*/
public interface Holder {
/**
* Boolean accessor to the Holder's inventory which indicate if it possess the given object
*
* @param item (InventoryItem): the given object to check, may be null
* @return (boolean): True if the holder possess the given object
*/
boolean possess(InventoryItem item);
}
public interface GUI {
/**
* Indicate Listeners a pocket has been updated
*
* @param id (int): id of the updated pocket
* @param name (String): name of the updated pocket, not null
* @param items (items): copy of the items contained in the pocket, not null
*/
void pocketUpdated(int id, String name, NavigableMap<InventoryItem, Integer> items);
}
private class Pocket {
private final String name;
private final NavigableMap<InventoryItem, Integer> items;
Pocket(String name) {
this.name = name;
this.items = new TreeMap<>();
}
boolean addItem(InventoryItem item, int quantity) {
// Get the current quantity of this specific item into the pocket (may be null)
Integer currentQuantity = items.get(item);
// If the current quantity does not exist simply add the item
// Otherwise simply update the quantity
if (currentQuantity == null) {
items.put(item, quantity);
} else {
items.replace(item, currentQuantity, currentQuantity + quantity);
}
return true;
}
boolean removeItem(InventoryItem item, int quantity) {
// Get the current quantity of this specific item into the pocket (may be null)
Integer currentQuantity = items.get(item);
// If the current quantity does not exist or if we want to remove more than available quantity we cannot remove it
// If we remove all of specific item, remove it from the pocket
// Otherwise update the quantity
if (currentQuantity == null || currentQuantity < quantity) {
return false;
} else if (currentQuantity == quantity) {
items.remove(item);
} else {
items.replace(item, currentQuantity, currentQuantity - quantity);
}
return true;
}
}
}
package ch.epfl.cs107.play.areagame.handler;
public interface InventoryItem {
/** @return (int): pocket id, the item is referred to */
int getPocketId();
/** @return (String): name of the item, not null */
String getName();
/**
* By default compare item by using there name
* @param item (InventoryItem): other items , not null
* @return (int): 1, 0, -1 following the comparison result
*/
default int compareTo(InventoryItem item) {
return getName().compareTo(item.getName());
}
}
package ch.epfl.cs107.play.data.json;
import java.util.ArrayList;
import java.util.List;
/** Used to represent json arrays */
public class JSONArray extends JSONValue {
/** The array stored in this object */
private final List<JSONValue> values;
public JSONArray() {
this.values = new ArrayList<>();
}
/**
* Returns the value at the given index
* @param index (int): The index of the value to return
* @return (JSONValue): The value at the given index
*/
public JSONValue get(int index) {
if (index < 0 || index >= values.size()) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + values.size());
}
return values.get(index);
}
/**
* Sets the value at the given index
* @param index (int): The index of the value to set
* @param value (JSONValue): The new value
*/
public void set(int index, JSONValue value) {
if (value == null) {
throw new IllegalArgumentException("Value cannot be null");
}
if (index < 0 || index >= values.size()) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + values.size());
}
values.set(index, value);
}
/**
* Appends the given value to the array
* @param value (JSONValue): The value to append
*/
public void append(JSONValue value) {
if (value == null) {
throw new IllegalArgumentException("Value cannot be null");
}
values.add(value);
}
/**
* Removes the value at the given index
* @param index (int): The index of the value to remove
*/
public void remove(int index) {
if (index < 0 || index >= values.size()) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + values.size());
}
values.remove(index);
}
/**
* Checks if the array contains the given value
* @param value (JSONValue): The value to check
* @return (boolean): True if the array contains the value, false otherwise
*/
public boolean contains(JSONValue value) {
if (value == null) {
throw new IllegalArgumentException("Value cannot be null");
}
return values.contains(value);
}
/**
* Returns the size of the array
* @return (int): The size of the array
*/
public int size() {
return values.size();
}
@Override
protected String toJSONString() {
if (values.isEmpty()) return "[]";
StringBuilder builder = new StringBuilder("[");
for (int i = 0; i < values.size(); i++) {
if (values.get(i) instanceof JSONArray) builder.append(values.get(i).toJSONString());
else builder.append(values.get(i).toString());
if (i < values.size() - 1) builder.append(", ");
}
return builder.append("]").toString();
}
/**
* Parses the given json into a JSONArray
* @param json (String): The json to parse
* @return (JSONArray): The parsed array
*/
protected static JSONArray parse(String json) {
json = json.trim().replaceAll("\\s{2,}", "");
if (!json.startsWith("[") || !(json.endsWith("]") || json.endsWith("],"))) {
throw new IllegalArgumentException("Invalid JSON array format");
}
// Remove the last comma if it exists
if (json.endsWith("],")) json = json.substring(0, json.length() - 1);
JSONArray array = new JSONArray();
// Removing the outermost brackets
json = json.substring(1, json.length() - 1);
List<String> parts = splitJsonArray(json);
for (String part : parts) {
// Recursive call for nested arrays
if (part.startsWith("[") && part.endsWith("]")) array.append(parse(part));
// Regular JSON value
else array.append(new JSONString(part));
}
return array;
}
/**
* Splits a json array into its parts
* @param json (String): The json array to split
* @return (List<String>): The parts of the json array
*/
private static List<String> splitJsonArray(String json) {
List<String> parts = new ArrayList<>();
int start = 0;
int bracketsCount = 0;
for (int i = 0; i < json.length(); i++) {
char ch = json.charAt(i);
// Check for nested arrays
if (ch == '[') bracketsCount++;
else if (ch == ']') bracketsCount--;
// Check for comma or end of string
if (bracketsCount == 0 && (ch == ',' || i == json.length() - 1)) {
String part = json.substring(start, ch == ',' ? i : i + 1).trim();
if (!part.isEmpty()) parts.add(part);
start = i + 1;
}
}
return parts;
}
}
\ No newline at end of file
package ch.epfl.cs107.play.data.json;
import java.security.InvalidParameterException;
import java.util.*;
/** Used to represent json objects */
public class JSONObject extends JSONValue {
/** The map of values stored in this object */
private final Map<String, JSONValue> values = new HashMap<>();
/**
* Adds a value to this object
* @param key (String): The key of the value to add
* @param value (JSONValue): The value to add
* @return (JSONObject): This object
*/
public JSONObject add(String key, JSONValue value) {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("Key cannot be null or empty");
}
if (value == null) {
throw new IllegalArgumentException("Value cannot be null");
}
if (values.containsKey(key)) {
throw new IllegalArgumentException("Duplicate key: " + key);
}
values.put(key, value);
return this;
}
/**
* Returns all the values in this object
* @return The value of this object
*/
public Map<String, JSONValue> get() {
return values;
}
/**
* Returns the value with the given key
* @param key (String): The key of the value to return
* @return (JSONValue): The value with the given key
*/
public JSONValue get(String key) {
if (!values.containsKey(key)) {
throw new IllegalArgumentException("Key not found: " + key);
}
return values.get(key);
}
/**
* Returns whether this object contains the given key
* @param key (String): The key to check
* @return (boolean): Whether this object contains the given key
*/
public boolean containsKey(String key) {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("Key cannot be null or empty");
}
return values.containsKey(key);
}
/**
* Removes the value with the given key
* @param key (String): The key of the value to remove
* @return (JSONObject): This object
*/
public JSONObject remove(String key) {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("Key cannot be null or empty");
}
if (!values.containsKey(key)) {
throw new IllegalArgumentException("Key not found: " + key);
}
values.remove(key);
return this;
}
/**
* Returns the set of keys in this object
* @return The set of keys in this object
*/
public Set<String> keySet() {
return values.keySet();
}
@Override
protected String toJSONString() {
if (values.isEmpty()) return "{}";
String indent = " ";
StringBuilder builder = new StringBuilder().append("{\n");
boolean firstEntry = true;
for (Map.Entry<String, JSONValue> entry : values.entrySet()) {
if (!firstEntry) builder.append(",\n");
builder.append(indent).append("\"").append(entry.getKey()).append("\": ");
if (entry.getValue() instanceof JSONObject || entry.getValue() instanceof JSONArray) {
// (?m)^ is a regex that activates multiline mode to match the start of each line
String nestedJson = (entry.getValue() + "").replaceAll("(?m)^", indent);
builder.append(nestedJson);
} else if (entry.getValue() instanceof JSONString value) {
if (JSONString.isInteger(value.toString()) || JSONString.isDouble(value.toString()) || JSONString.isBoolean(value.toString())) {
builder.append(value);
} else builder.append("\"").append(value).append("\"");
}
firstEntry = false;
}
return builder.append("\n}").toString().replaceAll(":\\s+", ": ");
}
/**
* Parses a list of lines into a JSONObject
* @param lines The lines to parse
* @return (JSONObject): The parsed JSONObject
*/
protected static JSONObject parse(List<String> lines) {
JSONObject json = new JSONObject();
// Variables used to parse a JSONObject
boolean inObject = false;
String objectKey = "";
List<String> objectLines = new ArrayList<>();
int braceCount = 0;
for (String line : lines) {
// Add the line to the correct list
if (inObject) objectLines.add(line);
// Check if we are looking at an empty object or array
if (!inObject && line.replaceAll("\\s+", "").contains("{}") || line.replaceAll("\\s+", "").contains("[]")) {
String[] parts = split(line);
json.add(parts[0], (line.contains("{") && line.contains("}")) ? new JSONObject() : new JSONArray());
}
// Check for one line object
else if (!inObject && line.contains("{") && line.contains("}")) {
String[] parts = split(line, false);
String key = parts[0];
json.add(key, JSONObject.parse(convertToMultiline(parts)));
}
// Check if we are at the start of an object
else if (line.contains("{")) {
braceCount++;
if (!inObject) {
inObject = true;
objectKey = split(line)[0];
}
}
// Check if we are at the start of a nested object
else if (inObject && line.contains("{")) ++braceCount;
// Check if we are at the end of an object
else if (line.contains("}")) {
braceCount--;
if (inObject && braceCount == 0) {
json.add(objectKey, JSONObject.parse(objectLines));
// Reset the variables
objectKey = "";
objectLines.clear();
inObject = false;
}
}
// Check for arrays
else if (!inObject && line.contains("[")) {
String[] parts = split(line, false);
json.add(parts[0], JSONArray.parse(parts[1]));
}
// Parse the line
else if (!inObject) {
String[] parts = split(line);
json.add(parts[0], new JSONString(parts[1]));
}
}
return json;
}
/**
* Splits a line into two parts: the key and the value
* @param line (String): the line to split
* @param removeCommas (boolean): whether to remove the commas from the value or not
* @return (String[]): the key and the value (in that order)
*/
private static String[] split(String line, boolean removeCommas) {
String[] parts = line.split(": ", 2);
if (parts.length != 2) throw new InvalidParameterException("Invalid JSON string: " + line);
if (removeCommas) parts[1] = parts[1].replaceAll(",", "");
return parts;
}
/**
* Splits a line into two parts: the key and the value
* @param line (String): the line to split
* @return (String[]): the key and the value (in that order)
*/
private static String[] split(String line) {
return split(line, true);
}
/**
* Converts a line with a linear object into a multiline object
* @param parts (String[]): the parts of the line
* @return (List<String>): the multiline object
*/
private static List<String> convertToMultiline(String[] parts) {
String newLine = parts[1].replaceAll("\\{", "").replaceAll("}", "");
ArrayList<String> lines = new ArrayList<>();
parts = newLine.split(",");
for (String s : parts) lines.add(s.trim());
return lines;
}
}
\ No newline at end of file
package ch.epfl.cs107.play.data.json;
import java.io.*;
import java.util.ArrayList;
/** Used to read JSON files and write to JSON files */
public class JSONParser {
/** The valid file extensions for JSON files */
private static final String[] FILE_EXTENSIONS = new String[] { ".json", ".txt" };
/**
* Used to read JSON files and get their contents as a JSONObject
* @param path (String): the path to the file
* @return (JSONObject): the contents of the file
*/
public static JSONObject readJSONFromFile(String path) {
validatePath(path);
// Read the file
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
ArrayList<String> lines = new ArrayList<>();
StringBuilder arrayContent = new StringBuilder();
int arrayDepth = 0;
String line;
while ((line = reader.readLine()) != null) {
line = line.trim().replaceAll("\"", "");
if (line.isEmpty() || line.equals("\n")) continue;
// Convert multiline arrays to single line arrays
if (arrayDepth > 0 || line.endsWith("[")) {
arrayContent.append(line);
// Update array depth count
arrayDepth += (int) line.chars().filter(ch -> ch == '[').count();
arrayDepth -= (int) line.chars().filter(ch -> ch == ']').count();
// If array ends, add to lines and reset arrayContent
if (arrayDepth == 0) {
lines.add(arrayContent.toString());
arrayContent = new StringBuilder();
}
} else lines.add(line);
}
// Remove the first and last line since they are just the container brackets
if (!lines.isEmpty()) {
lines.remove(0);
lines.remove(lines.size() - 1);
}
return JSONObject.parse(lines);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Used to write a JSONObject to a file
* @param path (String): the path to the file
* @param json (JSONObject): the JSONObject to write
*/
public static void writeJSONToFile(String path, JSONObject json) {
validatePath(path);
// Check if the JSONObject is null
if (json == null) {
throw new IllegalArgumentException("JSON cannot be null");
}
// Write to the file
try (BufferedWriter writer = new BufferedWriter(new FileWriter(path))) {
writer.write(json.toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Used to validate a file path
* @param path (String): the path to validate
*/
private static void validatePath(String path) {
// Check if the path is null or empty
if (path == null || path.isEmpty()) {
throw new IllegalArgumentException("Path cannot be null or empty");
}
// Check if the file extension is valid
boolean valid = false;
for (String extension : FILE_EXTENSIONS) {
int length = extension.length();
String ext = path.substring(path.length() - length);
if (ext.toLowerCase().equals(extension)) {
if (ext.equals(".txt")) {
System.err.println("Warning in file: " + path);
System.err.println("It is recommended to use the .json file extension for JSON files instead of .txt");
}
valid = true;
break;
}
}
if (!valid) throw new IllegalArgumentException("Invalid file extension: ." + path.split("\\.")[1]);
}
}
\ No newline at end of file
package ch.epfl.cs107.play.data.json;
/** Used to represent json strings, ints, doubles and booleans */
public class JSONString extends JSONValue {
/** The value of the string */
private String value;
/**
* Creates a new JSONString with the given value
* @param value (String): The value of the string
*/
public JSONString(String value) {
set(value);
}
/**
* Sets the value of this object
* @param value (String): The new value of this object
*/
public void set(String value) {
if (value == null) {
throw new IllegalArgumentException("Value cannot be null");
}
this.value = value;
}
/**
* Returns the value of this object
* @return (String): The value of this object as a String
*/
public String getString() {
return value;
}
/**
* Returns the value of this object
* @return (Integer): The value of this object as an Integer
*/
public Integer getInt() {
return isInteger(value) ? Integer.parseInt(value) : null;
}
/**
* Returns the value of this object
* @return (Double): The value of this object as a Double
*/
public Double getDouble() {
return isDouble(value) ? Double.parseDouble(value) : null;
}
/**
* Returns the value of this object
* @return (Boolean): The value of this object as a Boolean
*/
public Boolean getBoolean() {
return isBoolean(value) ? Boolean.parseBoolean(value) : null;
}
@Override
protected String toJSONString() {
return value;
}
/**
* Checks if the given string is an integer
* @param str (String): The string to check
* @return (boolean): True if the string is an integer, false otherwise
*/
public static boolean isInteger(String str) {
return str.matches("-?\\d+");
}
/**
* Checks if the given string is a boolean
* @param str (String): The string to check
* @return (boolean): True if the string is a boolean, false otherwise
*/
public static boolean isBoolean(String str) {
return "true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str);
}
/**
* Checks if the given string is a double
* @param str (String): The string to check
* @return (boolean): True if the string is a double, false otherwise
*/
public static boolean isDouble(String str) {
// This regex covers basic double representations but not scientific notation
return str.matches("[-+]?\\d*\\.?\\d+");
}
}
\ No newline at end of file
package ch.epfl.cs107.play.data.json;
/** Used to represent json values */
public abstract class JSONValue {
/**
* Serializes the implementing class to a JSON-formatted string.
* @return A JSON-formatted string representation of the object.
*/
protected abstract String toJSONString();
@Override
public String toString() {
return toJSONString();
}
}
\ No newline at end of file
package ch.epfl.cs107.play.engine;
import ch.epfl.cs107.play.engine.actor.Draggable;
public class DragHelper {
private static Draggable currentDraggedElement;
public static Draggable getCurrentDraggedElement() {
return currentDraggedElement;
}
public static void setCurrentDraggedElement(Draggable newElement) {
currentDraggedElement = newElement;
}
}
package ch.epfl.cs107.play.engine;
public interface Drawable {
void draw();
}
package ch.epfl.cs107.play.engine;
public interface Game extends Playable, Drawable {
/**
* Getter for the game frame rate
* @return (int): the desired number of frame per second
*/
default int getFrameRate() {
return 24;
}
}
package ch.epfl.cs107.play.engine;
import ch.epfl.cs107.play.engine.actor.Acoustics;
import ch.epfl.cs107.play.io.FileSystem;
import ch.epfl.cs107.play.math.Transform;
import ch.epfl.cs107.play.window.Audio;
import ch.epfl.cs107.play.window.Canvas;
import ch.epfl.cs107.play.window.Keyboard;
import ch.epfl.cs107.play.window.Window;
public abstract class PauseMenu implements Playable, Acoustics{
/**
* ???
*/
public interface Pausable{
void requestResume();
void requestPause();
boolean isPaused();
}
// Owner which called this pause. Initially null, need to call setOwner
private Pausable owner;
/// Display dimension scale
/// Context objects
private Window window;
//private FileSystem fileSystem; // TODO link it to save concept
protected static final float CAMERA_SCALE_FACTOR = 13;
/**
* Draw the entire menu (background, texts, etc.)
* @param c (Canvas): the context canvas : here the Window
*/
protected abstract void drawMenu(Canvas c);
/** @return (Keyboard): the Window Keyboard for inputs */
protected Keyboard getKeyboard(){
return window.getKeyboard();
}
/** @return (Pausable): the owner which starts this pause */
protected Pausable getOwner(){
return owner;
}
/**
* Set the owner Pausable
* @param owner (Pausable): the owner which starts this pause. Not null
*/
public void setOwner(Pausable owner){
this.owner = owner;
}
/// PauseMenu Implements Acoustics
@Override
public void bip(Audio audio) {
// By default there is nothing to bip
// Must be overridden by children who wants beep
}
/// PauseMenu implements Playable
@Override
public boolean begin(Window window, FileSystem fileSystem) {
this.window = window;
//this.fileSystem = fileSystem;
return true;
}
@Override
public void update(float deltaTime) {
// Center the view on the center of the menu
Transform viewTransform = Transform.I.scaled(CAMERA_SCALE_FACTOR).translated(CAMERA_SCALE_FACTOR /2, CAMERA_SCALE_FACTOR /2);
window.setRelativeTransform(viewTransform);
// Draw the menu
drawMenu(window);
bip(window);
}
@Override
public void end() {}
@Override
public String getTitle() {
return "Pause Menu";
}
}
package ch.epfl.cs107.play.engine;
import ch.epfl.cs107.play.io.FileSystem;
import ch.epfl.cs107.play.window.Window;
public interface Playable extends Updatable{
/**
* Initialises game state : display and controls
* Note: Need to be Override
* @param window (Window): display context. Not null
* @param fileSystem (FileSystem): given file system. Not null
* @return (boolean): whether the game was successfully started
*/
boolean begin(Window window, FileSystem fileSystem);
/** Cleans up things, called even if initialisation failed.
* Note: Need to be Override
*/
void end();
/**
* Getter for game title
* Note: Need to be Override
* @return (String) the game title
*/
String getTitle();
}
package ch.epfl.cs107.play.engine;
/**
* Represents a updatable element (which can be updated)
*/
public interface Updatable {
/**
* Simulates a single time step.
* Note: Need to be Override
* @param deltaTime elapsed time since last update, in seconds, non-negative
*/
void update(float deltaTime);
}
package ch.epfl.cs107.play.engine.actor;
import ch.epfl.cs107.play.window.Audio;
/**
* ???
*/
public interface Acoustics {
/**
* Play itself on specified Audio context.
* @param audio (Audio) target, not null
*/
void bip(Audio audio);
}
package ch.epfl.cs107.play.engine.actor;
import ch.epfl.cs107.play.engine.Updatable;
import ch.epfl.cs107.play.math.Positionable;
import ch.epfl.cs107.play.window.Audio;
import ch.epfl.cs107.play.window.Canvas;
/**
* Top game object, which is directly managed by the game.
* Smaller components and helpers are usually owned by actors themselves.
*/
public interface Actor extends Updatable, Graphics, Acoustics, Positionable{
@Override
default void update(float deltaTime) {
// By default, actors have nothing to update
}
@Override
default void bip(Audio audio){
// by default no sound is beeped for actor
}
@Override
default void draw(Canvas canvas) {
}
}
package ch.epfl.cs107.play.engine.actor;
import ch.epfl.cs107.play.engine.Updatable;
import ch.epfl.cs107.play.math.Orientation;
import ch.epfl.cs107.play.math.Positionable;import ch.epfl.cs107.play.math.Vector;
import ch.epfl.cs107.play.window.Canvas;
/**
* Animation is a Frames sequence of Sprite
*/
public class Animation implements Updatable, Graphics {
/// Duration of each frame (all frames have same duration)
private final int frameDuration;
/// Frames sequence of Sprite
private final Sprite[] frames;
private boolean repeat;
private boolean isCompleted;
/// Speed factor to reduce the frame duration
private int SPEED_FACTOR = 1;
/// counts for the animation
private int ANIMATION_COUNT = 1;
private int currentFrame = 0;
private boolean isPaused = false;
public Animation(String name, int nbFrames, float width, float height, Positionable parent, int regionWidth,
int regionHeight, Vector anchor, int frameDuration, boolean repeat) {
this(frameDuration,
Sprite.extractSprites(name, nbFrames, width, height, parent, anchor, regionWidth, regionHeight),
repeat);
}
public Animation(String name, int nbFrames, float width, float height, Positionable parent, int regionWidth,
int regionHeight, int frameDuration, boolean repeat) {
this(frameDuration,
Sprite.extractSprites(name, nbFrames, width, height, parent, regionWidth, regionHeight),
repeat);
}
/**
* Default Animation Constructor
*
* @param frameDuration (int): Duration of each frame (all frames have same duration)
* @param sprites (Sprite...): Array of sprite in the correct sequence order. Not null
* @param repeat (boolean): if the animation should be repeated after it is completed
*/
public Animation(int frameDuration, Sprite[] sprites, boolean repeat) {
this.frameDuration = frameDuration;
this.frames = sprites;
this.repeat = repeat;
}
/**
* Repeated animation constructor
*
* @param frameDuration (int): Duration of each frame (all frames have same duration)
* @param sprites (Sprite...): Array of sprite in the correct sequence order. Not null
*/
public Animation(int frameDuration, Sprite[] sprites) {
this(frameDuration, sprites, true);
}
/**
* Creates an array of 4 animations (one animation per orientation)
* the entry indexed by Orientation.dir.ordinal() is the animation corresponding
* to the orientation Orientation.dir
*
* @param animationDuration (int): the animation duration
* @param sprites (Sprite[][]): sprites to be played by each animation
* sprites[Orientation.dir.ordinal()] is the set of sprites to be played
* by the animation indexed by Orientation.dir.ordinal()
* @param repeat (boolean) : true if the animations must be repeated
* @return an array of 4 animations (one animation per orientation)
*/
public static Animation[] createAnimations(int animationDuration, Sprite[][] sprites, boolean repeat) {
Animation[] animations = new Animation[4];
for (Orientation direction : Orientation.values()) {
int index = direction.ordinal();
animations[index] = new Animation(animationDuration, sprites[index], repeat);
}
return animations;
}
/**
* Creates an array of 4 animations (one animation per orientation)
* the entry indexed by Orientation.dir.ordinal() is the animation corresponding
* to the orientation Orientation.dir. The animations are repeated by default.
*
* @param animationDuration (int): the animation duration
* @param sprites (Sprite[][]): sprites to be played by each animation
* sprites[Orientation.dir.ordinal()] is the set of sprites to be played by
* the animation indexed by Orientation.dir.ordinal()
* @return an array of 4 animations (one animation per orientation)
*/
public static Animation[] createAnimations(int animationDuration, Sprite[][] sprites) {
return createAnimations(animationDuration, sprites, true);
}
/**
* Update the speed factor of this Animation. Can be done on the fly.
* Note the speed factor is given between 1 (original speed) and frameDuration (maximal speed)
* Hence we cannot slow down the animation !
* TODO for next versions of the engine: make the animation compatible with slow down
*
* @param SPEED_FACTOR (int): new speed factor. Will be cropped between 1 and frameDuration
*/
public void setSpeedFactor(int SPEED_FACTOR) {
this.SPEED_FACTOR = Math.min(Math.max(1, SPEED_FACTOR), frameDuration);
}
/**
* ???
*
* @return ???
*/
public boolean isCompleted() {
return isCompleted;
}
/**
* ???
*
* @param anchor ???
*/
public void setAnchor(Vector anchor) {
for (Sprite sprite : frames) {
sprite.setAnchor(anchor);
}
}
/**
* ???
*
* @param width ???
*/
public void setWidth(float width) {
for (Sprite sprite : frames) {
sprite.setWidth(width);
}
}
public void setHeight(float height) {
for (Sprite sprite : frames) {
sprite.setHeight(height);
}
}
/// Animation implements Updatable
/**
* Reset this animation by setting the current frame to the first of the sequence
*/
public void reset() {
this.currentFrame = 0;
this.ANIMATION_COUNT = 1;
this.isPaused = false;
this.isCompleted = false;
}
/// Animation implements Graphics
public void switchPause() {
this.isPaused = !this.isPaused;
}
// Utilities for creating animations from sprites
@Override
public void update(float deltaTime) {
if (!isPaused && !isCompleted) {
// Count the rendering frames. And decide when changing the frame
ANIMATION_COUNT = (ANIMATION_COUNT + 1) % (frameDuration / (SPEED_FACTOR));
if (ANIMATION_COUNT == 0) {
currentFrame = (currentFrame + 1) % frames.length;
if (currentFrame == 0 && !repeat) {
isCompleted = true;
currentFrame = frames.length - 1; //Stay in the last frame state
}
}
}
}
@Override
public void draw(Canvas canvas) {
frames[currentFrame].draw(canvas);
}
}