diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..198fccd --- /dev/null +++ b/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..01befa6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/resources/* \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 0000000..44decaf --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + MiniMario + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..bb35fa0 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/res/icon_luigi.png b/res/icon_luigi.png new file mode 100644 index 0000000..e755d01 Binary files /dev/null and b/res/icon_luigi.png differ diff --git a/res/icon_luigi_fire.png b/res/icon_luigi_fire.png new file mode 100644 index 0000000..5d5c385 Binary files /dev/null and b/res/icon_luigi_fire.png differ diff --git a/res/icon_mario.png b/res/icon_mario.png new file mode 100644 index 0000000..c2c49a3 Binary files /dev/null and b/res/icon_mario.png differ diff --git a/res/icon_mario_fire.png b/res/icon_mario_fire.png new file mode 100644 index 0000000..7951949 Binary files /dev/null and b/res/icon_mario_fire.png differ diff --git a/res/luigi.png b/res/luigi.png new file mode 100644 index 0000000..111e47d Binary files /dev/null and b/res/luigi.png differ diff --git a/res/luigi_fire.png b/res/luigi_fire.png new file mode 100644 index 0000000..9b52489 Binary files /dev/null and b/res/luigi_fire.png differ diff --git a/res/mario.png b/res/mario.png new file mode 100644 index 0000000..cdeac22 Binary files /dev/null and b/res/mario.png differ diff --git a/res/mario_fire.png b/res/mario_fire.png new file mode 100644 index 0000000..e47f57b Binary files /dev/null and b/res/mario_fire.png differ diff --git a/src/fr/klemek/minimario/Launch.java b/src/fr/klemek/minimario/Launch.java new file mode 100644 index 0000000..c8e3a55 --- /dev/null +++ b/src/fr/klemek/minimario/Launch.java @@ -0,0 +1,23 @@ +package fr.klemek.minimario; + +import java.io.IOException; + +import javax.swing.SwingUtilities; + +public class Launch { + + public static void main(String[] args) { + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + try { + new MarioWindow(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } + +} diff --git a/src/fr/klemek/minimario/MarioAI.java b/src/fr/klemek/minimario/MarioAI.java new file mode 100644 index 0000000..0d433ec --- /dev/null +++ b/src/fr/klemek/minimario/MarioAI.java @@ -0,0 +1,258 @@ +package fr.klemek.minimario; + +import java.awt.Point; +import java.awt.geom.Point2D; +import java.util.Random; + +public class MarioAI { + + //times + private static final int MIN_STATE_TIME = 500; + private static final int MAX_STILL_TIME = 10000-MIN_STATE_TIME; + private static final int MAX_WALKING_TIME = 5000-MIN_STATE_TIME; + private static final int MAX_RUNNING_TIME = 3000-MIN_STATE_TIME; + + //speeds + private static final float WALK_SPEED = 0.5f; + private static final float RUN_SPEED = 1.5f; + private static final float JUMP_SPEED_X = 0.25f; + private static final float GRAVITY = 0.25f; + private static final float MAX_JUMP_SPEED_Y = 5f; + + //tiles + private static final int MARIO_STILL = 0; + private static final int MARIO_WALKING_2 = 1; + private static final int MARIO_JUMPING = 2; + private static final int MARIO_FALLING = 3; + private static final int MARIO_TURNING = 4; + private static final int MARIO_BACK = 5; + private static final int MARIO_DUCK = 6; + private static final int MARIO_WIN = 7; + private static final int MARIO_RUNNING_1 = 8; + private static final int MARIO_RUNNING_2 = 9; + private static final int MARIO_LOOK_UP = 10; + private static final int MARIO_LOOSING = 11; + + private enum State{ + STILL, STILL_BACK, STILL_WIN, STILL_DUCK, STILL_LOOK_UP, WALKING, RUNNING, JUMPING, LOOSING + } + + private State state; + private int time, time2; + private float spdy, maxspdy; + + private Point2D.Float pos; + + private boolean left, wait, turn; + private Random rand; + + private final int minx,maxx; + private int sizex, sizey, speedf; + + public MarioAI(int sizex, int sizey, int speedf){ + this.minx = Utils.getMinX(); + this.maxx = Utils.getMaxX(); + this.sizex = sizex; + this.sizey = sizey; + this.speedf = speedf; + this.state = State.STILL; + this.time = 0; + this.pos = new Point2D.Float(); + this.rand = new Random(); + } + + public void setSize(int sizex, int sizey, int speedf){ + this.sizex = sizex; + this.sizey = sizey; + this.speedf = speedf; + } + + public void setPos(Point newpos){ + this.pos = new Point2D.Float(newpos.x, newpos.y); + } + + public void moved(Point newpos){ + this.pos = new Point2D.Float(newpos.x, newpos.y); + this.state = State.LOOSING; + this.time2 = time; + } + + public Point refresh(int refresh){ + time += refresh; + + Point2D.Float speed = new Point2D.Float(); + + switch(this.state){ + case JUMPING: + speed.x = (this.left?-1f:1f)*speedf*JUMP_SPEED_X;; + this.spdy += GRAVITY*speedf; + speed.y = spdy; + if(this.spdy>=this.maxspdy){ + int randi = rand.nextInt(100); + if(randi<40){ //0-39 - 40% + this.state = State.STILL; + }else if(randi<70){ //40-69 - 30% + this.state = State.WALKING; + }else{ //70-99 - 30% + this.state = State.RUNNING; + } + } + break; + case STILL_BACK: + case STILL_LOOK_UP: + case STILL_WIN: + case STILL_DUCK: + case STILL: + if(this.time2100){ + this.time2 = this.time+rand.nextInt(MAX_STILL_TIME)+MIN_STATE_TIME; + this.wait = true; + this.state = State.STILL; + } + break; + } + + this.pos = Utils.add(this.pos, speed); + + if(pos.x<=minx && this.left || pos.x+sizex>=maxx && !this.left){ + this.turn = true; + this.left = !this.left; + } + + int[] ybounds = Utils.getYBounds((int) pos.x); + + if(this.state != State.JUMPING){ + if(pos.yybounds[1]){ + this.spdy = -rand.nextFloat()*speedf*MAX_JUMP_SPEED_Y; + this.maxspdy = 0f; + this.state = State.JUMPING; + } + } + + return new Point((int)this.pos.x, (int)this.pos.y); + } + + public int getTile(){ + if(this.turn){ + if(this.time%200>=100){ + this.turn = false; + } + return MARIO_TURNING; + }else{ + switch(this.state){ + case LOOSING: + return MARIO_LOOSING; + case WALKING: + return this.time%400<200?MARIO_STILL:MARIO_WALKING_2; + case RUNNING: + return this.time%150<75?MARIO_RUNNING_1:MARIO_RUNNING_2; + case JUMPING: + return this.spdy<=1f?MARIO_JUMPING:MARIO_FALLING; + case STILL_LOOK_UP: + return MARIO_LOOK_UP; + case STILL_WIN: + return MARIO_WIN; + case STILL_BACK: + return MARIO_BACK; + case STILL_DUCK: + return MARIO_DUCK; + case STILL: + default: + return MARIO_STILL; + } + } + } + + public boolean isReversed(){ + return this.left; + } + +} diff --git a/src/fr/klemek/minimario/MarioWindow.java b/src/fr/klemek/minimario/MarioWindow.java new file mode 100644 index 0000000..bd5f4f0 --- /dev/null +++ b/src/fr/klemek/minimario/MarioWindow.java @@ -0,0 +1,168 @@ +package fr.klemek.minimario; + +import java.awt.AWTException; +import java.awt.Color; +import java.awt.Menu; +import java.awt.MenuItem; +import java.awt.Point; +import java.awt.PopupMenu; +import java.awt.SystemTray; +import java.awt.TrayIcon; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.Random; + +import javax.imageio.ImageIO; +import javax.swing.JWindow; +import javax.swing.Timer; + +public class MarioWindow extends JWindow implements ActionListener { + + private static final long serialVersionUID = 1L; + + private static final String VERSION = "1.5"; + + private static final int REFRESH_MS = 20; + private static final int TILE_W = 20; + private static final int TILE_H = 24; + + private TilePanel p; + private final Timer refresh; + + private int factor = 2; + + private MarioAI ai; + + private Point initialClick; + + public MarioWindow() throws IOException { + + this.setBackground(new Color(0, 0, 0, 0)); + this.setLocationRelativeTo(null); + + this.ai = new MarioAI(TILE_W*factor,TILE_H*factor, factor); + + Random r = new Random(); + String tileset_name = "mario"; + if(r.nextInt(100)>=90){ //90-99 - 10% + tileset_name = "luigi"; + } + + if(r.nextInt(100)>=99){ //99 - 1% + tileset_name += "_fire"; + } + + + BufferedImage tileset = ImageIO.read(this.getClass().getResource("/"+tileset_name+".png")); + this.p = new TilePanel(this, tileset, TILE_W, TILE_H, factor); + + this.p.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + initialClick = e.getPoint(); + getComponentAt(initialClick); + } + }); + + this.p.addMouseMotionListener(new MouseMotionAdapter() { + @Override + public void mouseDragged(MouseEvent e) { + + // get location of Window + int thisX = getLocation().x; + int thisY = getLocation().y; + + // Determine how much the mouse moved since the initial click + int xMoved = (thisX + e.getX()) - (thisX + initialClick.x); + int yMoved = (thisY + e.getY()) - (thisY + initialClick.y); + + // Move window to this position + int X = thisX + xMoved; + int Y = thisY + yMoved; + setLocation(X, Y); + ai.moved(getLocation()); + } + }); + + this.add(this.p); + + this.refresh = new Timer(REFRESH_MS, this); + + this.pack(); + this.setAlwaysOnTop(true); + + if (!SystemTray.isSupported()) { + System.out.println("SystemTray is not supported"); + return; + } + + TrayIcon trayIcon = new TrayIcon(Utils.createImage("/icon_"+tileset_name+".png", "icon")); + trayIcon.setImageAutoSize(true); + + final PopupMenu popup = new PopupMenu(); + + + + Menu sizeMenu = new Menu("Change size"); + popup.add(sizeMenu); + + for(int i = 0; i < 5; i++){ + final int f = (int)Math.pow(2, i); + MenuItem size = new MenuItem(f+"x"); + size.addActionListener(new ActionListener(){ + @Override + public void actionPerformed(ActionEvent e) { + setFactor(f); + } + }); + sizeMenu.add(size); + } + + MenuItem exit = new MenuItem("Kill it !"); + exit.addActionListener(new ActionListener(){ + @Override + public void actionPerformed(ActionEvent e) { + System.exit(0); + } + }); + popup.add(exit); + + trayIcon.setPopupMenu(popup); + trayIcon.setToolTip("MiniMario ! (version "+VERSION+")\nBy Klemek"); + + final SystemTray tray = SystemTray.getSystemTray(); + try { + tray.add(trayIcon); + } catch (AWTException e) { + System.out.println("TrayIcon could not be added."); + } + + + this.setVisible(true); + + this.ai.setPos(this.getLocation()); + + this.refresh.start(); + } + + private void setFactor(int factor){ + this.setVisible(false); + this.factor = factor; + this.ai.setSize(TILE_W*factor,TILE_H*factor, factor); + this.p.setFactor(factor); + this.pack(); + this.setVisible(true); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource().equals(this.refresh)) { + this.setLocation(this.ai.refresh(REFRESH_MS)); + this.p.setTile(this.ai.getTile(), this.ai.isReversed()); + } + } +} diff --git a/src/fr/klemek/minimario/TilePanel.java b/src/fr/klemek/minimario/TilePanel.java new file mode 100644 index 0000000..2602d40 --- /dev/null +++ b/src/fr/klemek/minimario/TilePanel.java @@ -0,0 +1,66 @@ +package fr.klemek.minimario; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; + +import javax.swing.JPanel; +import javax.swing.JWindow; + +public class TilePanel extends JPanel { + + private static final long serialVersionUID = 1L; + + private final int tileWidth, tileHeight, imageWidth, imageHeight, columns, rows; + private int id, factor; + private BufferedImage image; + private boolean reversed = false; + + + + public TilePanel(JWindow parent, BufferedImage image, int tileWidth, int tileHeight, int factor) { + super(); + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.image = image; + this.imageWidth = image.getWidth(); + this.imageHeight = image.getHeight(); + this.columns = this.imageWidth / this.tileWidth; + this.rows = this.imageHeight / this.tileHeight; + System.out.println("Tileset : "+this.columns+"x"+this.rows); + this.setFactor(factor); + this.setBackground(new Color(0, 0, 0, 0)); + this.setOpaque(false); + } + + public void setFactor(int factor){ + this.factor = factor; + Dimension size = new Dimension(tileWidth * factor, tileHeight * factor); + this.setPreferredSize(size); + this.setMinimumSize(size); + this.setMaximumSize(size); + } + + @Override + public void paintComponent(Graphics g) { + g.clearRect(0, 0, tileWidth * factor, tileHeight * factor); + if (id >= 0 && id < rows * columns) { + if (reversed) { + g.drawImage(image, 0, 0, tileWidth * factor, tileHeight * factor, (1 + id % columns) * tileWidth, + (id / columns) * tileHeight, (id % columns) * tileWidth, (1 + id / columns) * tileHeight, this); + } else { + g.drawImage(image, 0, 0, tileWidth * factor, tileHeight * factor, (id % columns) * tileWidth, + (id / columns) * tileHeight, (1 + id % columns) * tileWidth, (1 + id / columns) * tileHeight, this); + } + } + g.dispose(); + } + + public void setTile(int id, boolean reversed) { + this.id = id; + this.reversed = reversed; + this.repaint(); + } + +} diff --git a/src/fr/klemek/minimario/Utils.java b/src/fr/klemek/minimario/Utils.java new file mode 100644 index 0000000..189b7c1 --- /dev/null +++ b/src/fr/klemek/minimario/Utils.java @@ -0,0 +1,93 @@ +package fr.klemek.minimario; + +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.geom.Point2D; +import java.net.URL; + +import javax.swing.ImageIcon; + +public abstract class Utils { + + private static Rectangle[] bounds = new Rectangle[0]; + + public static Point2D.Float add(Point2D.Float p1, Point2D.Float p2) { + return new Point2D.Float(p1.x + p2.x, p1.y + p2.y); + } + + public static int getMaxX() { + int maxx = 0; + + GraphicsDevice[] gds = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); + if(bounds.length == 0){ + bounds = new Rectangle[gds.length]; + } + for (int i = 0; i < gds.length; i++) { + GraphicsDevice gd = gds[i]; + bounds[i] = gd.getDefaultConfiguration().getBounds(); + if(bounds[i].getMaxX()>maxx){ + maxx=(int) bounds[i].getMaxX(); + } + } + return maxx; + } + + public static int getMinX(){ + int minx = 0; + GraphicsDevice[] gds = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); + if(bounds.length == 0){ + bounds = new Rectangle[gds.length]; + } + for (int i = 0; i < gds.length; i++) { + GraphicsDevice gd = gds[i]; + bounds[i] = gd.getDefaultConfiguration().getBounds(); + if(bounds[i].getMinX()=b.getMinX()&&x<=b.getMaxX()){ + if(out == null){ + out = new int[]{(int)b.getMinY(),(int)b.getMaxY()}; + }else{ + if(b.getMinY()out[1]) + out[1] = (int) b.getMaxY(); + } + } + } + if(out == null) + out = new int[]{0,0}; + return out; + } + + //Obtain the image URL + public static Image createImage(String path, String description) { + URL imageURL = Utils.class.getResource(path); + + if (imageURL == null) { + System.err.println("Resource not found: " + path); + return null; + } else { + return (new ImageIcon(imageURL, description)).getImage(); + } + } +} \ No newline at end of file