package robombs.game.view; import java.util.*; import robombs.game.*; import robombs.game.util.*; import robombs.game.model.*; import robombs.clientserver.*; import com.threed.jpct.*; public class LevelLoader { private final static char WALL = '*'; private final static char NOTHING = ' '; private final static char OUTSIDE = '.'; private final static char CRATE = 'c'; private final static char BOMB_ITEM = 'b'; private final static char KICK_ITEM = 'k'; private final static char DISEASE_ITEM = 'd'; private final static char FIREPOWER_ITEM = 'f'; private final static String WALL_TEXTURE = "wall"; private final static String WALL_TEXTURE_DARK = "wall_dark"; private final static String FLOOR_TEXTURE = "floor"; private final static String TOP_TEXTURE = "top"; private final static String BORDER_TEXTURE = "border"; private final static String BLACK_TEXTURE = "black"; private static int tessellation = 1; private static SimpleVector s1 = new SimpleVector(); private static SimpleVector s2 = new SimpleVector(); private static SimpleVector s3 = new SimpleVector(); private static final Map<String, List<Object>> cache=new HashMap<String, List<Object>>(); private static void addVerticalPolygon(Object3D obj, float xMin, float xMax, float yMin, float yMax, float zMin, float zMax, int texID) { // texID++; int tessellation=LevelLoader.tessellation; if (texID==0) { tessellation=1; } float ft = (float) tessellation; float dX = (xMax - xMin) / ft; float dY = (yMax - yMin) / ft; float dZ = (zMax - zMin) / ft; float du = 1f / ft; float dv = du; int xTes = tessellation; int yTes = tessellation; int zTes = tessellation; if (dX == 0) { xTes = 1; } if (dZ == 0) { zTes = 1; } for (int xi = 0; xi < xTes; xi++) { float xs = xMin + dX * (float) xi; float xe = xs + dX; for (int yi = 0; yi < yTes; yi++) { float ys = yMin + dY * (float) yi; float ye = ys + dY; float vs = dv * (float) (yi); float ve = vs + dv; for (int zi = 0; zi < zTes; zi++) { float zs = zMin + dZ * (float) zi; float ze = zs + dZ; float zt = zi; if (zTes == 1) { zt = xi; } float us = du * zt; float ue = us + du; s1.set(xs, ye, zs); s2.set(xe, ye, ze); s3.set(xe, ys, ze); obj.addTriangle(s1, us, ve, s2, ue, ve, s3, ue, vs, texID); s2.set(xs, ys, zs); obj.addTriangle(s3, ue, vs, s2, us, vs, s1, us, ve, texID); } } } } private static void addHorizontalPolygon(Object3D obj, float xMin, float xMax, float y, float zMin, float zMax, int tesselation, int texID) { float ft = (float) tesselation; float dX = (xMax - xMin) / ft; float dZ = (zMax - zMin) / ft; float du = 1f / ft; float dv = du; for (int xi = 0; xi < tesselation; xi++) { float xs = xMin + dX * (float) xi; float xe = xs + dX; float us = du * xi; float ue = us + du; for (int zi = 0; zi < tesselation; zi++) { float zs = zMin + dZ * (float) zi; float ze = zs + dZ; float vs = dv * zi; float ve = vs + dv; s1.set(xs, y, ze); s2.set(xe, y, ze); s3.set(xe, y, zs); obj.addTriangle(s1, us, ve, s2, ue, ve, s3, ue, vs, texID); s2.set(xs, y, zs); obj.addTriangle(s3, ue, vs, s2, us, vs, s1, us, ve, texID); } } } private static int countElements(String level, char chr) { int cnt = 0; for (int i = 0; i < level.length(); i++) { if (level.charAt(i) == chr) { cnt++; } else { if (chr == '?') { char c = level.charAt(i); if (c == NOTHING || c==CRATE || c==BOMB_ITEM || c==FIREPOWER_ITEM || c==KICK_ITEM || c==DISEASE_ITEM) { // Alles andere.... cnt++; } } } } return cnt; } static synchronized List<Object> loadMap(String file, String set) throws Exception { set+="_"; long t=Ticker.getTime(); List<Object> fc=getFromCache(file); if (fc!=null) { NetLogger.log("Got level data for '"+file+"' from cache in "+(Ticker.getTime()-t)+"ms."); return fc; } SimpleVector minBB = new SimpleVector(99999999, 0, 99999999); SimpleVector maxBB = new SimpleVector(-99999999, 0, -99999999); SimpleStream stream = new SimpleStream(file); String level = Loader.loadTextFile(stream.getStream()); stream.close(); int wallCnt = countElements(level, WALL); int tesQ = tessellation * tessellation; // Die Zahlen bei den Walls hier sind irgendwie geraten...:-) int size=wallCnt * tesQ * 4 + 1000; Object3D walls = new Object3D(size); // Shadows is a special, simplified mesh inside the walls that is used for // shadow casting. Otherwise the walls would have to be caster and receiver, // which will cause shadow acne on most systems. Object3D shadows = new Object3D(size); float soffs=Globals.shadowMeshOffset; Object3D floor = new Object3D(countElements(level, '?') * tesQ * 2+100); int topCnt = 0; TextureManager texMan = TextureManager.getInstance(); StringTokenizer toky = new StringTokenizer(level, "\n"); String[] lines = new String[toky.countTokens() + 2]; int cnt = 0; int max = -1; StringBuffer buf = new StringBuffer(40); StringBuffer buf2 = new StringBuffer(40); while (toky.hasMoreTokens()) { String tok = toky.nextToken(); buf.setLength(0); buf2.setLength(0); int start = 0; int end = 0; if (max < tok.length() + 1) { max = tok.length() + 1; } buf.append(OUTSIDE); topCnt++; final int endy = tok.length(); for (int i = 0; i < endy; i++) { char c = tok.charAt(i); if (c == WALL) { start = i; break; } else { buf.append(OUTSIDE); topCnt++; } } for (int i = endy - 1; i >= 0; i--) { char c = tok.charAt(i); if (c == WALL) { end = i; break; } else { buf2.append(OUTSIDE); topCnt++; } } buf.append(tok.substring(start, end + 1)).append(buf2); lines[cnt + 1] = buf.toString(); cnt++; } StringBuffer sb = new StringBuffer(max); for (int i = 0; i < max; i++) { sb.append(OUTSIDE); } String sbs = sb.toString(); lines[0] = sbs; lines[cnt + 1] = sbs; int endy = lines.length; for (int i = 0; i < endy; i++) { String l = lines[i]; if (l.length() < max) { lines[i] += sbs.substring(0, max - l.length()); topCnt += max - l.length(); } lines[i]=flip(lines[i]); System.out.println(lines[i]); } level = null; String tmpLine = null; Object3D top = new Object3D(topCnt * 2 + wallCnt * 2); MapMask mask = new MapMask(max, lines.length); int texID_dark; texID_dark = texMan.getTextureID(set+WALL_TEXTURE_DARK); for (int i = 1; i < cnt + 1; i++) { String line = lines[i]; int z = i * MapMask.TILE_SIZE; boolean foundWall = false; endy = line.length(); for (int p = 0; p < endy; p++) { char c = line.charAt(p); if (foundWall || c == WALL) { int x = p * MapMask.TILE_SIZE; String topy=TOP_TEXTURE; int height=7; if (i==1 || i==cnt || p==1 || p==endy-2) { height=8; // 10 topy=BORDER_TEXTURE; } switch (c) { case (WALL): { int cto=NOTHING; int ctu=NOTHING; int ctl=NOTHING; int ctr=NOTHING; // oben: if (i > 0) { tmpLine = lines[i - 1]; if (tmpLine.length() > p) { cto = tmpLine.charAt(p); } } if (i < cnt + 1) { tmpLine = lines[i + 1]; if (tmpLine.length() > p) { ctu = tmpLine.charAt(p); } } if (p > 0) { ctl = line.charAt(p - 1); } if (p < line.length() - 1) { ctr = line.charAt(p + 1); } int texID = texMan.getTextureID(set+topy); // "Dach" malen...da reicht einfache // Geometrie-Aufl�sung, da es // nicht beleuchtet wird addHorizontalPolygon(top, x, x + MapMask.TILE_SIZE, -height, z, z - MapMask.TILE_SIZE, 1, texID); addHorizontalPolygon(shadows, x + MapMask.TILE_SIZE -(ctr!=WALL?soffs/10f:0), x+(ctl!=WALL?soffs/10f:0), -height, z-(ctu!=WALL?soffs/10f:0), z- MapMask.TILE_SIZE+(cto!=WALL?soffs/10f:0), 1, texID); texID = texMan.getTextureID(set+WALL_TEXTURE); extendBB(minBB, maxBB, x, x + MapMask.TILE_SIZE, z, z - MapMask.TILE_SIZE); // mask.setMaskAt(p,i,MapMask.UNKNOWN); int sideCnt = 0; if (cto != WALL || i==lines.length-2) { addVerticalPolygon(walls, x, x + MapMask.TILE_SIZE, -height, 0, z - MapMask.TILE_SIZE, z - MapMask.TILE_SIZE, texID); addVerticalPolygon(shadows, x+(ctl!=WALL?soffs/10f:0), x + MapMask.TILE_SIZE-(ctr!=WALL?soffs/10f:0), -height, 1, z - MapMask.TILE_SIZE+soffs/10f, z - MapMask.TILE_SIZE+soffs/10f, 0); sideCnt++; } // unten: if (ctu != WALL || i==1) { addVerticalPolygon(walls, x + MapMask.TILE_SIZE, x, -height, 0, z, z, texID_dark); addVerticalPolygon(shadows, x + MapMask.TILE_SIZE-(ctr!=WALL?soffs/10f:0), x+(ctl!=WALL?soffs/10f:0), -height, 1, z-soffs/10f, z-soffs/10f, 0); sideCnt++; } // links: if (ctl != WALL || p==line.length()-2) { addVerticalPolygon(walls, x, x, -height, 0, z, z - MapMask.TILE_SIZE, texID_dark); addVerticalPolygon(shadows, x+soffs/10f, x+soffs/10f, -height, 1, z-(ctu!=WALL?soffs/10f:0), z - MapMask.TILE_SIZE+(cto!=WALL?soffs/10f:0), 0); sideCnt++; } // rechts if (ctr != WALL || p==1) { addVerticalPolygon(walls, x + MapMask.TILE_SIZE, x + MapMask.TILE_SIZE, -height, 0, z - MapMask.TILE_SIZE, z, texID); addVerticalPolygon(shadows, x + MapMask.TILE_SIZE-soffs/10f, x + MapMask.TILE_SIZE-soffs/10f, -height, 1, z - MapMask.TILE_SIZE+(cto!=WALL?soffs/10f:0), z-(ctu!=WALL?soffs/10f:0), 0); sideCnt++; } break; } default: { // "Boden" malen if (c != OUTSIDE) { int texID = 0; if (c == NOTHING || c==CRATE || c==BOMB_ITEM || c==KICK_ITEM || c==FIREPOWER_ITEM || c==DISEASE_ITEM || (c>='1' && c<='9')) { String floorTex = set+FLOOR_TEXTURE; int f = (int) (Math.random()*100); if (f<12) { floorTex += 4; } else { if (f<24) { floorTex += 3; } else { if (f<36) { floorTex += 2; } } } texID = texMan.getTextureID(floorTex); if (c==CRATE) { mask.setMaskAt(p, i, MapMask.CRATE); } if (c==BOMB_ITEM) { mask.setMaskAt(p, i, MapMask.BOMB_ITEM); } if (c==FIREPOWER_ITEM) { mask.setMaskAt(p, i, MapMask.FIREPOWER_ITEM); } if (c==KICK_ITEM) { mask.setMaskAt(p, i, MapMask.KICK_ITEM); } if (c==DISEASE_ITEM) { mask.setMaskAt(p, i, MapMask.DISEASE_ITEM); } if (c==NOTHING) { mask.setMaskAt(p, i, MapMask.FLOOR); } if (c>='1' && c<='9') { mask.setMaskAt(p,i,((int)c)+10000); } } addHorizontalPolygon(floor, x, x + MapMask.TILE_SIZE, 0, z, z - MapMask.TILE_SIZE, tessellation, texID); } else { if (p > 0&& p < endy - 1) { // Die Kanten brauchen das nicht... int texID = texMan.getTextureID(set+BLACK_TEXTURE); addHorizontalPolygon(top, x, x + MapMask.TILE_SIZE, -10, z, z - MapMask.TILE_SIZE, 1, texID); } } } } } foundWall = true; } } List<Object> v = new ArrayList<Object>(6); v.add(new LevelPart(walls)); v.add(new LevelPart(top)); v.add(new LevelPart(floor)); v.add(mask); v.add(minBB); v.add(maxBB); shadows.setTransparency(-1); if (!Globals.shadowCulling) { shadows.invert(); } v.add(new LevelPart(shadows)); storeInCache(file, v); NetLogger.log("Loaded level data for '"+file+"' in "+(Ticker.getTime()-t)+"ms"); return v; } private static String flip(String line) { StringBuffer sb=new StringBuffer(); for (int i=line.length()-1; i>=0; i--) { sb.append(line.charAt(i)); } return sb.toString(); } private static void storeInCache(String name, List<Object> v) { List<Object> c=new ArrayList<Object>(v); copy(c); cache.clear(); // The cache holds one entry only ATM cache.put(name, c); } private static List<Object> getFromCache(String name) { List<Object> c=cache.get(name); if (c!=null) { c=new ArrayList<Object>(c); copy(c); } return c; } private static void copy(List<Object> c) { for (int i=0;i<3; i++) { LevelPart lp=new LevelPart((LevelPart) c.get(i)); lp.setMesh(lp.getMesh().cloneMesh(true)); c.set(i, lp); } c.set(3, ((MapMask) c.get(3)).cloneMask()); c.set(4, new SimpleVector((SimpleVector) c.get(4))); c.set(5, new SimpleVector((SimpleVector) c.get(5))); } private static void extendBB(SimpleVector min, SimpleVector max, float x1, float x2, float z1, float z2) { if (min.x > x1) { min.x = x1; } if (min.x > x2) { min.x = x2; } if (min.z > z1) { min.z = z1; } if (min.z > z2) { min.z = z2; } if (max.x < x1) { max.x = x1; } if (max.x < x2) { max.x = x2; } if (max.z < z1) { max.z = z1; } if (max.z < z2) { max.z = z2; } } }