4school@info

package info;
import javax.swing.*;
import javax.imageio.*;

import java.io.*;
// .File
import java.util.*;
// HashMap, ArrayList
import java.awt.AWTException;
import java.awt.Desktop;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.Robot;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.event.*;
public class Main {
    static JFrame frame;
    static JLabel bufferLabel;
    static BufferedImage buffer, background;
    static Random zufall;
    
@SuppressWarnings("unchecked")// anders nicht möglich, außer per List...
static ArrayList<RohGebaeude>[] baumenue = new ArrayList[]{new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()};

    static JButton bauHaus, bauIndustrie;
    static int width=1600, height=900, mapSizeX=100, mapSizeY=100, deltaX=0, deltaY=0, mouseX, mouseY;
    static int baumenueW=1000, baumenueH=300, headerW=1000, headerH=170, aktivTab=-1, aktivBauX, aktivBauY, aktivBauW, aktivBauH;
    static Gebaeude[][] map = new Gebaeude[mapSizeX][mapSizeY];
    static RohGebaeude wald, sollBauen = null;
    static boolean sollZerstoeren = false;
    
    static final int RGB=1, ARGB=2;
    static BufferedImage[] a(BufferedImage... array){// nur ne Formsache - macht Platz -> übersichtlicher
        return array;
    }
    static Beduerfnis[] a(Beduerfnis... array){// nur ne Formsache - macht Platz -> übersichtlicher
        return array;
    }
    static Beduerfnis[] a0(){
        return new Beduerfnis[0];
    }
    public static void main(String[] args) throws Exception {

        bauAngebot.setRGB(0,0,0);
        // erstelle alle Gebaeudetypen...
        //String name, int typ, int w, int h, Beduerfnis[] beduerfnisse, BufferedImage[] imgs
        baumenue[0].add(new RohGebaeude("See", 0, 1, 1, a0(), a(load("Wasser.png"))));
        baumenue[0].add(wald=new RohGebaeude("Wald", 0, 1, 1, a0(), a(load("Wald.png"))));
        
        zufall = new Random(System.nanoTime()&0xffffffff);
        
        RessourcenKarte seen = new RessourcenKarte(System.currentTimeMillis());
        for(int i=0;i<mapSizeX;i++){
            for(int j=0;j<mapSizeY;j++){
                if(seen.get(i,j)<0.3){
                    map[i][j]=new Gebaeude(i,j,sollBauen);
                }
            }
        }
        
        nullImage = load("zero.png");
        buffer = new BufferedImage(width,height,ARGB);// erstmal nichts
        background = new BufferedImage(1,1,RGB);
        background.setRGB(0,0,0xff009000);// Grün des Hintergrunds
        frame = new JFrame("Stadtsimluation");
        frame.setContentPane(bufferLabel = new JLabel());
        frame.addMouseMotionListener(new MouseMotionListener(){
            
           @Override public void mouseDragged(MouseEvent e){
                int delX=mouseX-(mouseX=e.getX()), delY=mouseY-(mouseY=e.getY());
                deltaX-=delX;
                deltaY-=delY;
                repaint(false);
            }
            
           @Override public void mouseMoved(MouseEvent e){
                mouseX=e.getX();
                mouseY=e.getY();
            }
            
        });
        frame.addMouseListener(new MouseListener(){
           @Override public void mouseClicked(MouseEvent e){
                // reagiere auf das Klicken fürs Bauen
                int cx=e.getX(), cy=e.getY();
                switch(e.getButton()){
                case MouseEvent.BUTTON1://l
                    if(cy<headerH && cx<headerW){// Header, macht eigentlich nichts, vllt Extrastatistiken
                        sysout("click auf den Header");
                    } else if(cy<width-baumenueH || cx>=baumenueW){// Spielfeld
                        if(aktivTab>=0 && cx>=aktivBauX && cy>=aktivBauY && cx<aktivBauX+aktivBauW && cy<aktivBauY+aktivBauH){
                            // man möchte was bauen :)
                            if(sollBauen!=null){
                                int mx=(int)Math.round(a(cx, cy)), my=(int)Math.round(b(cx, cy));
                                if(mx>=0 && my>=0 && mx<mapSizeX && my<mapSizeY){// man möchte auf dem Spielplan bauen
                                    if(map[mx][my]==null){
                                        if(Gebaeude.baue(mx, my, sollBauen)){// Geld etc wurde schon abgehoben
                                            map[mx][my]=new Gebaeude(mx, my, sollBauen);
                                        }
                                    } else sysout("Bauen hier nicht möglich!");
                                } else sysout("Du kannst außerhalb des Feldes nicht bauen!");
                            } else if(sollZerstoeren){
                                // Lösche das Haus...
                                // also trage Bewohner aus und eventuell destroy...
                                int mx=(int)Math.round(a(cx, cy)), my=(int)Math.round(b(cx, cy));// oje... zoom noch einbinden!
                                if(mx>=0 && my>=0 && mx<mapSizeX && my<mapSizeY){// es wurde auf etwas geklickt, das nun gelöscht werden soll
                                    Gebaeude g = map[mx][my];
                                    if(g!=null){// das Gebaeude zum Löschen ist auch vorhanden
                                        g.verlasse();// trage Bewohner / Angestellte aus (+ gib die Hälfte der Resourcen dem Spieler wieder)
                                        map[mx][my]=null;
                                        repaint(true);// zeige die Veränderung -> später eventuell auch mal mit Abrisspartikeln -> je nach Zeit
                                    } else sysout("Nicht zu zerstören!");
                                } else sysout("Außerhalb des Feldes kann nichts zerstört werden!");
                            }
                        } else {
                            
                        }
                    } else {//Baumenü
                        // oder aufgeklappt... wie machen wir das eigentlich?
                        
                        aktivTab = cx*6/baumenueW;//
                        if(aktivTab==5){
                            sollZerstoeren = true;
                        } else {
                            sollZerstoeren = false;
                        }
                        
                        // repaint Baumenüe im Blickfeld
                        if(sollZerstoeren){
                            bauAngebot = new BufferedImage(1, 1, ARGB);
                            bauAngebot.setRGB(0,0,0);// unsichtbar und schwarz
                        } else {
                            bauAngebot = new BufferedImage(baumenueW, baumenueH, ARGB);
                            Graphics g = bauAngebot.getGraphics();
                            // angenommen es gibt nur soviele Objekte, dass die alle auf dem Schirm übereinander passen
                            for(int i=0;i<baumenue[aktivTab].size();i++){
                                // stelle das Haus schön dar... bei übergroßen eventuell ein Problem, solange nur 1x1 Felder belegt werden aber keines...
                                
                            }
                            g.dispose();
                        }
                        
                    }
                    break;
                case MouseEvent.BUTTON2://m
                    // nichts...
                    break;
                case MouseEvent.BUTTON3://r
                    // gib Infos über das sollGebäude, den Typ(RohGebaeude) oder das aktive Gebaeude
               
                    break;
                    
                }
                
            }
            
           @Override public void mouseEntered(MouseEvent e){}
           @Override public void mouseExited(MouseEvent e){}
            
           @Override public void mousePressed(MouseEvent e){
                
            }
            
           @Override public void mouseReleased(MouseEvent e){
                
            }
        });
        frame.addMouseWheelListener(new MouseWheelListener(){
           @Override public void mouseWheelMoved(MouseWheelEvent e){
                zoom/=Math.pow(1.1, e.getWheelRotation());
                if(zoom<minZoom){
                    zoom = minZoom;
                } else if(zoom>maxZoom){
                    zoom = maxZoom;
                }
                repaint(false);
            }
        });
        frame.addKeyListener(new KeyListener(){
           @Override public void keyPressed(KeyEvent e){}
           @Override public void keyReleased(KeyEvent e){}
           @Override public void keyTyped(KeyEvent e){
                switch(e.getKeyChar()){
                case '1':
                    zoom*=1.1;
                    if(zoom>maxZoom){
                        zoom = maxZoom;
                    }
                    repaint(false);
                    break;
                case '2':
                    zoom/=1.1;
                    if(zoom<minZoom){
                        zoom = minZoom;
                    }
                    repaint(false);
                    break;
                }
            }
        });
        
        frame.setBounds(0,0,width,height);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        
        repaint(true);
        
        while(true){
            Thread.sleep(300);//3 FPS
            tick();
            // male onEvent
        }
    }
    public static BufferedImage nullImage;
    public static double zoom=4, minZoom=0.25, maxZoom=10;
    public static int deltaMapX=1200, deltaMapY=1200;
    public static BufferedImage all, bauAngebot = new BufferedImage(1,1,ARGB);
    
    public static void repaint(boolean allD){
        // male den Bildschirm neu... //theoretisch sollten wir wohl einfach die ganze Karte 1x rendern lassen...
        Graphics g;
        if(allD){
            all = new BufferedImage(mapSizeX*50, mapSizeY*50,ARGB);
            g = all.getGraphics();
            
            for(int i=0;i<mapSizeX;i++){
                for(int j=0;j<mapSizeY;j++){
                    Gebaeude b;
                    int x=(int)x(i,j), y=(int)y(i,j);
                    if((b=map[i][j])!=null && b.typ!=null){
                        // wenn das Bild auf unserem Bild zu malen ist, male das...
                        BufferedImage icon = b.getIcon();
                        g.drawImage(icon,
                            (x-icon.getWidth()/2)+deltaMapX,
                            (y-icon.getHeight()/2)+deltaMapY, icon.getWidth(), icon.getHeight(), null);// male das Bild: von der Mitte bis
                    } else {
                        g.drawImage(nullImage,
                            (x-nullImage.getWidth()/2)+deltaMapX,
                            (y-nullImage.getHeight()/2)+deltaMapY, nullImage.getWidth(), nullImage.getHeight(), null);
                    }
                }
            }
            g.dispose();
        }
        
        
        
        g = buffer.getGraphics();
        g.drawImage(background,0,0,width,height,null);
        g.drawImage(all,(int)(deltaX-deltaMapX*zoom)+width/2,(int)(deltaY-deltaMapY*zoom)+height/2,(int)(all.getWidth()*zoom),(int)(all.getHeight()*zoom),null);
        
        // zeichne das Baumenü...
        
        
        g.dispose();
        bufferLabel.setIcon(new ImageIcon(buffer));
    }
    
    public static HashMap<String, BufferedImage> loaded = new HashMap<>();
    public static String imageStandartURL="gfx/";
    public static BufferedImage load(String name) throws IOException {
        BufferedImage img = loaded.get(name);
        if(img!=null)return img;
        return ImageIO.read(new File(imageStandartURL+name));
    }
    
    // Umwandlung der Positionen ineinander (fuer Malevents und Mausklicks auf unser Label)
    // 26.02.Abgabe :$, Dokumentation & Vorstellung 10min nicht vergessen
    public static double x5sq106 = 0.48564293117863209036780044642441, x9sq106 = 0.87415727612153776266204080356394;
    // MapCOO by BildCOO
    public static double a(double x, double y){return x9sq106*(x)+x5sq106*(y);}
    public static double b(double x, double y){return -x5sq106*(x)+x9sq106*(y);}
    // BildCOO by MapCOO
    public static double x(double a, double b){return 9*a-5*b;}
    public static double y(double a, double b){return 5*a+9*b;}
    public static void sysout(Object o){
        System.out.println(o);
    }
    public static void tick(){
        for(int i=0;i<mapSizeX;i++){
            for(int j=0;j<mapSizeY;j++){
                Gebaeude geb = map[i][j];
                if(geb!=null){
                if(geb.typ!=null){
                        if(geb.tick()){
                            geb.verlasse();
                            map[i][j]=null;
                        }
                    }
                } else if(zufall.next()*10000>1){// Zurückeroberung durch die Natur :)
                map[i][j] = new Gebaeude(i,j,wald);
                }
            }
        }
    }
    public static int multi1 = 256, multi2=multi1-1;
    public static Gebaeude leer = new Gebaeude(-1,-1,null);
    public static double expectedScore(double chance){
    double ges = 1, factorHalbe = Math.pow(((double)multi2)/multi1, chance*0.5), factor=factorHalbe*factorHalbe;
    for(int i=0;i<10;i++){// 10 Zyklen... -> das läuft auf eine Zahl zu, 10 Durchläufe reichen locker um sich dem anzunähern
    ges = ges*factor+1;// Zeit :)
    }
    return ges*factorHalbe;
    }
    public static class Gebaeude {
        
        public static boolean baue(int x, int y, RohGebaeude typ){
            // teste ob genug Geld etc da ist...
            // bei größreren Grundstücken vllt auch mehrere Felder überprüfen...
            for(int i=0;i<typ.w;i++){
            if(x+i>=mapSizeX) return false;
            for(int j=0;j<typ.h;j++){
            if(y+j>=mapSizeY) return false;
            if(map[x+i][y+j]!=null) return false;
            }
            }
            return true;// erfolgreich
        }
        
        public RohGebaeude typ;
        public int x, y, score, maxscore, pro256, id, problemIndex;
        public Mensch[] nutzer;
        public String[] problems = new String[]{"","","","","","","",""};
        /**
        IDs:
        Typ
        0=Deko
        1=
        
        Nummer 0-unendl.
        */
        
        public Gebaeude(int x, int y, RohGebaeude typ){
        if(typ==null) return;
            this.x=x;this.y=y;this.typ=typ;
            nutzer = new Mensch[typ.nutzer];
            for(Beduerfnis b:typ.beduerfnisse){
           
            // fülle eventuell schonmal deine Bedürfnisse auf Standardwerte
            // da das nicht ganz so einfach zu berechnen ist...
            double ges = 1, factor = Math.pow(((double)multi2)/multi1, b.chance);
            for(int i=0;i<10;i++){// 10 Zyklen... -> das läuft auf eine Zahl zu, 10 Durchläufe reichen locker um sich dem anzunähern
            ges = ges*factor+1;// Zeit :)
            }
           
            score+=(b.id/1000 == 1 || b.id/1000 == 3 ? b.score2 : b.teste(this, x, y)) * expectedScore(b.chance);// Einwohner kommen schon noch :)
            }
        }
        
        public boolean tick(){
            if(typ.typ==0){
                return false;
            }
            for(Beduerfnis beduerfnis:typ.beduerfnisse){
                if(beduerfnis.chance * zufall.next()<1){// spart Division
                int delta = beduerfnis.teste(this,x,y);
                if(delta==0){
                problems[problemIndex++&7]=beduerfnis.problem;
                } else {
                score+=delta;
                }
                }
            }
            if(typ.typ==1){// Wohnhaus
                int l = nutzer.length;
                for(int i=0;i<l;i++){
                    Mensch bewohner=nutzer[i];
                    if(bewohner!=null && bewohner.alter++*zufall.next()>3200){// Gesundheit noch beachten!!!
                        // loesche den Nutzer aus der Beschaeftigung und dem Wohnhaus -> wird dann geloescht...
                        int l2 = bewohner.beschaeftigung.nutzer.length;
                        for(int j=0;j<l2;j++){
                            if(bewohner.equals(bewohner.beschaeftigung.nutzer[j])){
                                bewohner.beschaeftigung.nutzer[j]=null;
                                break;
                            }
                        }
                    }
                }
            } else {
            for(Mensch m:nutzer){
            if(m!=null && m.zuhause==null){
            // finde dem Arbeiter ein neues Zuhause...
            // trennt die Familien auf... etwas unrealistisch aber nicht zu unrealistisch
            boolean notok=true;
            br:for(int i=0;i<mapSizeX;i++){
            for(int j=0;j<mapSizeY;j++){
            Gebaeude g = map[i][j];
            if(g!=null && g.typ.typ==1){// Wohnhaus?
            for(int k=0;k<g.nutzer.length;k++){
            Mensch mn = g.nutzer[k];
            if(mn==null){
            g.nutzer[k] = m;// eingemietet / eingekauft
            m.geld*=0.3;// Geldverlust
            notok=false;
            break br;
            }
            }
            }
            }
            }
            if(notok){
            // der Arbeiter konnte kein Zuhause finden...
            // -> Arbeiter verlässt die Stadt...
            for(int i=0;i<m.beschaeftigung.nutzer.length;i++){
            if(m.beschaeftigung.nutzer[i]==m){
            m.beschaeftigung.nutzer[i]=null;
            }
            }
            }
            }
            }
            }
            score=score*multi2/multi1;// "Bedürfnisse" steigen
            pro256 = (int) (score/typ.maxscore);
            return pro256<2;// loesche das Gebaeude wenn es ihm zu schlecht geht
        }
        
        public void verlasse(){
            if(typ.typ==1){// Wohnhaus
            if(nutzer!=null){
            for(Mensch m:nutzer){
            if(m!=null){
            // suche dann von der Arbeitsstelle neue Wohnorte (Leute wohnen bei Kollegen / Freunden)
            m.zuhause = null;
            }
            }
            }
            } else {// Beschaeftigung
                if(nutzer!=null){
                for(Mensch m:nutzer){
                if(m!=null){
                m.beschaeftigung = null;
                }
                }
                }
            }
        }
        
        public BufferedImage getIcon(){
            return typ.imgs[(typ.imgs.length*pro256)/pro256];
        }
    }
    
    public static class Mensch {
        public int alter, geld;// Alter: 16 = 1 Jahr(erstmal)
        public boolean mann;
        public Gebaeude zuhause, beschaeftigung;
        public Mensch(int alter, boolean mann, Gebaeude zuhause){
            this.alter=alter;this.mann=mann;this.geld=0;this.zuhause=zuhause;
        }
    }
    
    public static class RohGebaeude {
        public int w, h;
        public String name;
        public int typ, nutzer;
        public double maxscore = 0;
        public RohGebaeude(String name, int typ, int w, int h, Beduerfnis[] beduerfnisse, BufferedImage[] imgs){
            this.name=name;this.beduerfnisse=beduerfnisse;this.imgs=imgs;this.typ=typ;this.w=w;this.h=h;
            // berechne den Glücklichkeitsscore...
            for(Beduerfnis b:beduerfnisse){
            maxscore += b.score2 * expectedScore(b.chance);
            }
        }
        public Beduerfnis[] beduerfnisse;
        public BufferedImage[] imgs;
    }
    
    public static class Beduerfnis {
        int rad1, rad2, score1, score2, id, chance;
        String name;
        String problem = "";
        
        public Beduerfnis(int r1, int r2, int s1, int s2, int id, String name, int chance){
            this.name=name;rad1=r1;rad2=r2;score1=s1;score2=s2;this.id=id;this.chance=chance;
        }
        
        public int teste(Gebaeude g, int x, int y){// score...
       
        switch(id/1000){
        case 0:// deko
        break;
        case 1:// Mitarbeiter
        break;
        case 2:// Haus / Gebäude
        break;
        case 3:// Bewohner
        break;
        }
            return 0;
        }
    }
    
    public static class RessourcenKarte {
        Perlin2D a, b, c, d;
        public RessourcenKarte(long seed){
            Random r = new Random(seed);
            a = new Perlin2D((long)(r.next()*100000.0), 8);
            b = new Perlin2D((long)(r.next()*100000.0),16);
            c = new Perlin2D((long)(r.next()*100000.0),32);
            d = new Perlin2D((long)(r.next()*100000.0),64);
        }
        
        public double get(double x, double y){
            return sq((a.getNoiseAt(x,y)+b.getNoiseAt(x,y)+c.getNoiseAt(x,y)+d.getNoiseAt(x,y))*0.25);
        }
        
        public static double sq(double d){
            return d*d;
        }
    }
    
    public static class Perlin2D {
        private long seed;
        private Random rand;
        private double frequency, period;
         
         public Perlin2D(long seed, double octave){
            this.seed = seed;
            this.frequency = octave;
            this.period = 1f/this.frequency;
            this.rand = new Random(0);
         }
        public double getNoiseAt(double x, double z){
            int xmin = (int) Math.floor(x * period);
            int xmax = xmin + 1;
            int zmin = (int) Math.floor(z * period);
            int zmax = zmin + 1;
            return cosineInterpolate(
           cosineInterpolate(
               getRandomAtPosition(xmin, zmin),
               getRandomAtPosition(xmax, zmin),
               (x - xmin * frequency) * period
           ),
           cosineInterpolate(
               getRandomAtPosition(xmin, zmax),
               getRandomAtPosition(xmax, zmax),
               (x - xmin * frequency) * period
           ),
           (z - zmin * frequency) * period
            );
        }
         
        private double getRandomAtPosition(int x, int z){
            rand.seed = seed ^ (445213215641L*x) ^ (14757127171L*z);
            return rand.next();
        }
        
        public static double cosineInterpolate(double d, double e, double x){
            double f = (1.0 - Math.cos(x * Math.PI)) * 0.5;
            return d * (1.0 - f) + e * f;
        }
    }
    
    public static class Random {
        public long seed;
        public Random(long seed){
            this.seed=seed;
        }
        
        public double next(){
            seed = (214013*seed+2531011)&0xffffffffffL;
            return ((int)(seed>>16)&0xffff)*0.0000152587890625;
        }
    }
    
}

118464159s ago, by Antonio