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