import greenfoot.*; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) import java.lang.*; import java.util.*; import java.lang.reflect.Method; /** * Anything that can move around in space and interact (fluctuate, collide, "hit") with other quantum objects. * All moving objects in this game have classes derived from QuantumObject. * * Unlike in real physics, QuantumObjects have an intrinsic speed that causes them to move around (implemented in act()). * They may choose to fall off the screen when reaching the border. * * Special coding magic enables it to have real dynamic method dispatch so that collisions with different classes of quantum objects * can be implemented. * * @author Jannis Andrija Schnitzer, Martin Schend * @version 2011-01-17 */ public class QuantumObject extends Actor { protected Vector speed; /** * Contains the mantissa that remains from speed calculations (since Greenfoot only allows it to do pixel-based movements). * * @see #move() */ protected Vector mantissa; /** * Whether the quantum object should fall off the world (i. e. be destroyed) when reaching the edge. */ protected boolean disappear; /** * Can be set to us by any caller within a given game turn. The next time this.act() is called after setting, * this object destroys itself. */ protected boolean destroysOnNextTurn = false; public QuantumObject() { speed = new Vector(0, 0); mantissa = new Vector(0, 0); disappear = true; } /** * A day in the life of a quantum object: * 1. Maybe commit suicide (i. e. destroys itself when destroysOnNextTurn is true) * 2. Do quantum object interactions with intersecting objects * 3. -- Err... are you missing point 3? */ public void act() { move(); if (destroysOnNextTurn) { getWorld().removeObject(this); return; } // quantum fluctuations: interact with objects that hit us. ArrayList objectsInRange = (ArrayList)this.getIntersectingObjects(QuantumObject.class); destroysOnNextTurn = false; int size = objectsInRange.size(); for (int i = 0; i < size; i++) { Actor otherObject = (QuantumObject)objectsInRange.get(i); if (!this.hit(otherObject)) // quantum effects caused us to be destroyed { destroysOnNextTurn = true; } } } public void setSpeed(Vector someSpeed) { speed = someSpeed; } public Vector getSpeed() { return speed; } public void setDestroysOnNextTurn(boolean destroys) { destroysOnNextTurn = destroys; } public boolean getDestroysOnNextTurn() { return destroysOnNextTurn; } /** * move. Cause the quantum object to move around based on its speed. * * Fun fact: speed values can also be non-integer. Because Greenfoot only allows for pixel-based movement, the mantissa of * the speed vector is kept seperately and summed up with the mantissa from the previous turn. That way, e. g. a vertical speed of * 2.2 causes the quantum object to move 2 pixels every game turn, and 3 pixels every 5th turn, because after each 5 turns the .2 * mantissae have added up to 1. * * @see #mantissa */ public void move() { int newX, newY; double x_speed, y_speed; // add old mantissa values x_speed = speed.getX()+mantissa.getX(); y_speed = speed.getY()+mantissa.getY(); newX = (int) Math.floor(x_speed); newY = (int) Math.floor(y_speed); // mantissa only gets the difference between the double values and the // int values in newX and newY mantissa.set(x_speed-(double)newX, y_speed-(double)newY); newX += getX(); newY += getY(); if (disappear && newX >= getWorld().getWidth() || newY >= getWorld().getHeight() || newX < 0 || newY < 0) { destroysOnNextTurn = true; } else { setLocation(newX, newY); } } /** * hit. Base method hit(Actor). Uses java.lang.reflect to dynamically find out which hit() method of the quantum object * to invoke based on which actual class "actor" has. Dynamic dispatch comes true! * * @param actor The object we are interacting with * * @return boolean. True if this quantum object should survive, false otherwise. */ public boolean hit(Actor actor) { Method hit = null; Class c = actor.getClass(); if (this.getClass() == c) return true; while (hit == null && c != null) { try { hit = this.getClass().getMethod("hit", new Class[] { c }); // get hit method for c's class } catch (NoSuchMethodException ex) { c = c.getSuperclass(); // called when there was no such method; check for c's superclass } } if (hit == null && c == null) { // obviously there's no hit method in this object that wants to handle actor return true; } try { return (Boolean)hit.invoke(this, new Object[] { actor }); } catch (Exception ex) { ex.printStackTrace(); } return true; } /** * strange quantum interaction effects occur... not. This method actually is redundant, but still left in place as an example for * how to write hit() methods. * * @param q quantum object that we hit. */ public boolean hit(QuantumObject q) { return true; } } // poop.