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;
}
}
}
270510437s ago, by Antonio