Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/com/monkey/animation/AnimationManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ public static void smoothStep(GameEnginePanel engine, double totalDist, int fram
double nextX = engine.monkeyX + dx;
double nextY = engine.monkeyY + dy;

if (CollisionChecker.isStoneCollision(engine, nextX, nextY)) {
if (CollisionChecker.isSolidCollision(engine, nextX, nextY)) {
break;
}

if (CollisionChecker.isWaterCollision(engine, nextX, nextY)) {
if (CollisionChecker.isDeadlyCollision(engine, nextX, nextY)) {
throw new RuntimeException("Monkey fell in the water!");
}

Expand Down
9 changes: 2 additions & 7 deletions src/com/monkey/animation/TurtleAnimation.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,29 @@ public void actionPerformed(java.awt.event.ActionEvent e) {
double nextX = turtle.x + dx;
double nextY = turtle.y + dy;

// 1. Turtle going on land fails the level
if (CollisionChecker.isTurtleLandCollision(engine, nextX, nextY)) {
throw new RuntimeException("Turtle walked onto land!");
}

// 2. Check Monkey Collisions
if (monkeyOnBoard) {
double nextMonkeyX = engine.monkeyX + dx;
double nextMonkeyY = engine.monkeyY + dy;

// --- CHANGED: Monkey hits stone while riding? Stop moving gently. ---
if (CollisionChecker.isStoneCollision(engine, nextMonkeyX, nextMonkeyY)) {
if (CollisionChecker.isSolidCollision(engine, nextMonkeyX, nextMonkeyY)) {
((Timer) e.getSource()).stop();
latch.countDown();
return;
}

// Monkey falls in water (shouldn't happen on a turtle, but just in case)
if (CollisionChecker.isWaterCollision(engine, nextMonkeyX, nextMonkeyY)) {
if (CollisionChecker.isDeadlyCollision(engine, nextMonkeyX, nextMonkeyY)) {
throw new RuntimeException("Monkey fell in the water!");
}

engine.monkeyX = nextMonkeyX;
engine.monkeyY = nextMonkeyY;
}

// Safely apply move
turtle.x = nextX;
turtle.y = nextY;

Expand All @@ -67,7 +63,6 @@ public void actionPerformed(java.awt.event.ActionEvent e) {
latch.countDown();
}
} catch (RuntimeException ex) {
// Deadly obstacle hit (water/land)! Stop the animation and signal failure.
hitDeadlyObstacle[0] = true;
((Timer) e.getSource()).stop();
latch.countDown();
Expand Down
7 changes: 6 additions & 1 deletion src/com/monkey/core/ITerrain.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package com.monkey.core;

import java.awt.Color;

public interface ITerrain extends IGameObject {

boolean isSolid(); // True for Stones (monkey can't walk through)

boolean isDeadly(); // True for Rivers (monkey drowns)

}
Color getColor(); // The color used to draw the terrain

int getSize(); // The radius/thickness of the terrain
}
6 changes: 6 additions & 0 deletions src/com/monkey/core/River.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.monkey.core;

import java.awt.Color;

public class River extends GameObject implements ITerrain {

public River(double x, double y) { super(x, y); }
Expand All @@ -9,4 +11,8 @@ public class River extends GameObject implements ITerrain {
@Override public boolean isSolid() { return false; }

@Override public boolean isDeadly() { return true; }

@Override public Color getColor() { return new Color(52, 152, 219); }

@Override public int getSize() { return 50; }
}
7 changes: 6 additions & 1 deletion src/com/monkey/core/Stone.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.monkey.core;

import java.awt.*;
import java.awt.Color;
import java.awt.Rectangle;

public class Stone extends GameObject implements ITerrain {

Expand All @@ -14,4 +15,8 @@ public Stone(double x, double y) {
@Override public boolean isSolid() { return true; }

@Override public boolean isDeadly() { return false; }

@Override public Color getColor() { return new Color(100, 100, 100); }

@Override public int getSize() { return 45; }
}
22 changes: 8 additions & 14 deletions src/com/monkey/design/TerrainDesign.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
package com.monkey.design;

import com.monkey.core.GameObject;
import com.monkey.core.ITerrain;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.List;

public class TerrainDesign {

public static void drawRivers(Graphics2D g2, List<GameObject> rivers) {
drawConnected(g2, rivers, new Color(52, 152, 219), 50);
}

public static void drawStones(Graphics2D g2, List<GameObject> stones) {
drawConnected(g2, stones, new Color(100, 100, 100), 45);
}
public static void drawTerrain(Graphics2D g2, List<GameObject> objects) {
if (objects.isEmpty()) return;

// Generic logic to connect adjacent objects
private static void drawConnected(Graphics2D g2, List<GameObject> objects, Color c, int size) {
g2.setColor(c);
ITerrain terrainType = (ITerrain) objects.get(0);
int size = terrainType.getSize();
int r = size / 2;

// 1. Draw the "Body" of each object
g2.setColor(terrainType.getColor());

for(GameObject o : objects) {
g2.fillOval((int)o.x - r, (int)o.y - r, size, size);
}

// 2. Draw "Connections" between close neighbors
// 2. Draw "Connections"
g2.setStroke(new BasicStroke(size, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));

for(int i = 0; i < objects.size(); i++) {
Expand All @@ -35,7 +30,6 @@ private static void drawConnected(Graphics2D g2, List<GameObject> objects, Color
GameObject b = objects.get(j);
double dist = Math.hypot(a.x - b.x, a.y - b.y);

// If they are close (adjacent grid cells + wiggle room)
if(dist < size + 20) {
g2.drawLine((int)a.x, (int)a.y, (int)b.x, (int)b.y);
}
Expand Down
95 changes: 67 additions & 28 deletions src/com/monkey/engine/CollisionChecker.java
Original file line number Diff line number Diff line change
@@ -1,58 +1,97 @@
package com.monkey.engine;

import com.monkey.core.GameObject;
import com.monkey.core.ITerrain;
import com.monkey.core.Turtle;
import com.monkey.gui.game.GameEnginePanel;

import java.util.ArrayList;
import java.util.List;

public class CollisionChecker {

// Hitbox sizes for the 50x50 tiles
private static final double OBSTACLE_RADIUS = 50.0;
private static final double RIVER_RADIUS = 50.0;
private static final double TURTLE_RADIUS = 50.0;
private static List<GameObject> getAllObjects(GameEnginePanel engine) {
List<GameObject> allObjects = new ArrayList<>();
allObjects.addAll(engine.stones);
allObjects.addAll(engine.rivers);
allObjects.addAll(engine.bananas);
allObjects.addAll(engine.turtles);

return allObjects;
}

public static boolean isSolidCollision(GameEnginePanel engine, double targetX, double targetY) {

for (GameObject obj : getAllObjects(engine)) {
if (obj instanceof ITerrain && ((ITerrain) obj).isSolid()) {

// Returns TRUE if the target position hits a stone
public static boolean isStoneCollision(GameEnginePanel engine, double targetX, double targetY) {
for (GameObject stone : engine.stones) {
if (Math.hypot(stone.x - targetX, stone.y - targetY) < OBSTACLE_RADIUS) {
return true;
if (obj.hitbox != null) {
if (obj.hitbox.contains(targetX, targetY)) {
return true;
}
}
else {
double radius = ((ITerrain) obj).getSize() / 2.0;
if (Math.hypot(obj.x - targetX, obj.y - targetY) < radius) {
return true;
}
}
}
}

return false;
}

// Returns TRUE if the monkey is drowning (in water without a turtle)
public static boolean isWaterCollision(GameEnginePanel engine, double targetX, double targetY) {
boolean overWater = false;
for (GameObject river : engine.rivers) {
if (Math.hypot(river.x - targetX, river.y - targetY) < RIVER_RADIUS) {
overWater = true;
break;
public static boolean isDeadlyCollision(GameEnginePanel engine, double targetX, double targetY) {
boolean overDeadlyGround = false;

for (GameObject obj : getAllObjects(engine)) {
if (obj instanceof ITerrain && ((ITerrain) obj).isDeadly()) {

if (obj.hitbox != null) {
if (obj.hitbox.contains(targetX, targetY)) overDeadlyGround = true;
} else {
double radius = ((ITerrain) obj).getSize() / 2.0;
if (Math.hypot(obj.x - targetX, obj.y - targetY) < radius) overDeadlyGround = true;
}
}

if (overDeadlyGround) break;
}

if (overWater) {
boolean onTurtle = false;
if (overDeadlyGround) {
for (Turtle turtle : engine.turtles) {
if (Math.hypot(turtle.x - targetX, turtle.y - targetY) < TURTLE_RADIUS) {
onTurtle = true;
break;
if (Math.hypot(turtle.x - targetX, turtle.y - targetY) < 40) {
return false;
}
}
return !onTurtle; // If over water but NOT on a turtle, it's a collision!
return true;
}

return false;
}

// Returns TRUE if the turtle walks onto land
public static boolean isTurtleLandCollision(GameEnginePanel engine, double targetX, double targetY) {
boolean onWater = false;
for (GameObject river : engine.rivers) {
if (Math.hypot(river.x - targetX, river.y - targetY) < RIVER_RADIUS) {
onWater = true;
break;

for (GameObject obj : getAllObjects(engine)) {
if (obj instanceof ITerrain && ((ITerrain) obj).isDeadly()) {

if (obj.hitbox != null) {
if (obj.hitbox.contains(targetX, targetY)) {
onWater = true;
break;
}
} else {
double radius = ((ITerrain) obj).getSize() / 2.0;
if (Math.hypot(obj.x - targetX, obj.y - targetY) < radius) {
onWater = true;
break;
}
}
}
}
return !onWater; // If NOT on water, it's a collision!

return !onWater;
}
}
34 changes: 30 additions & 4 deletions src/com/monkey/gui/editor/MapEditorLogic.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,39 @@ public void mousePressed(MouseEvent e) {
}

public void addObject(String type, int x, int y) {
// Your logic for adding a Banana, Stone, or Turtle to the lists
System.out.println("Added " + type + " at " + x + ", " + y);
switch (type) {
case "Banana":
engine.bananas.add(new com.monkey.core.Banana(x, y));
break;
case "Stone":
engine.stones.add(new com.monkey.core.Stone(x, y));
break;
case "River":
engine.rivers.add(new com.monkey.core.River(x, y));
break;
case "Turtle":
// Automatically assign the next available ID to the turtle (T0, T1, T2...)
int newId = 0;
for (Turtle t : engine.turtles) {
if (t.id >= newId) newId = t.id + 1;
}
engine.turtles.add(new Turtle(x, y, newId, 0, 0));
break;
case "Spawn":
engine.monkeyX = x;
engine.monkeyY = y;
// Update the starting location so the monkey resets here!
engine.saveInitialState();
break;
}
}

public void removeObject(int x, int y) {
// Your logic for looping through lists and removing objects at X, Y
System.out.println("Removed object at " + x + ", " + y);
// Remove anything within 20 pixels of the right-click
engine.bananas.removeIf(b -> Math.hypot(b.x - x, b.y - y) < 20);
engine.stones.removeIf(s -> Math.hypot(s.x - x, s.y - y) < 20);
engine.rivers.removeIf(r -> Math.hypot(r.x - x, r.y - y) < 20);
engine.turtles.removeIf(t -> Math.hypot(t.x - x, t.y - y) < 20);
}

public Object getGameObjectAt(int x, int y) {
Expand Down
4 changes: 2 additions & 2 deletions src/com/monkey/gui/game/GameRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ private void updateEffects() {
public void draw(Graphics2D g2) {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

TerrainDesign.drawRivers(g2, engine.rivers);
TerrainDesign.drawStones(g2, engine.stones);
TerrainDesign.drawTerrain(g2, engine.rivers);
TerrainDesign.drawTerrain(g2, engine.stones);

for (Turtle t : engine.turtles) t.draw(g2);
for (GameObject b : engine.bananas) ItemDesign.drawBanana(g2, b.x, b.y);
Expand Down