/*
* This file is part of the Haven & Hearth game client.
* Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and
* Björn Johannessen <johannessen.bjorn@gmail.com>
*
* Redistribution and/or modification of this file is subject to the
* terms of the GNU Lesser General Public License, version 3, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Other parts of this source tree adhere to other copying
* rights. Please see the file `COPYING' in the root directory of the
* source tree for details.
*
* A copy the GNU Lesser General Public License is distributed along
* with the source tree of which this file is a part in the file
* `doc/LPGL-3'. If it is missing for any reason, please see the Free
* Software Foundation's website at <http://www.fsf.org/>, or write
* to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package haven;
import static haven.MCache.tilesz;
import java.awt.Color;
import java.awt.event.KeyEvent;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import javax.media.opengl.GL;
public class MapView extends PView implements DTarget, Console.Directory {
public static final String DEFCAM = "sortho";
private final R2DWdg r2dwdg;
public long plgob = -1;
public Coord cc;
private final Glob glob;
private int view = 2;
private Collection<Delayed> delayed = new LinkedList<Delayed>();
private Collection<Delayed> delayed2 = new LinkedList<Delayed>();
private Collection<Rendered> extradraw = new LinkedList<Rendered>();
public Camera camera;
private Plob placing = null;
private int[] visol = new int[32];
private Grabber grab;
public static final Map<String, Class<? extends Camera>> camtypes = new HashMap<String, Class<? extends Camera>>();
{visol[4] = 1;}
{
camtypes.put("follow", FollowCam.class);
camtypes.put("sfollow", SmoothFollowCam.class);
camtypes.put("free", FreeCam.class);
camtypes.put("ortho", OrthoCam.class);
camtypes.put("sortho", SOrthoCam.class);
}
public interface Delayed {
public void run(GOut g);
}
public interface Grabber {
boolean mmousedown(Coord mc, int button);
boolean mmouseup(Coord mc, int button);
boolean mmousewheel(Coord mc, int amount);
void mmousemove(Coord mc);
}
public static abstract class Camera extends GLState.Abstract {
protected haven.Camera view = new haven.Camera(Matrix4f.identity());
protected Projection proj = new Projection(Matrix4f.identity());
protected MapView mv;
public Camera(MapView mv) {
this.mv = mv;
resized();
}
public boolean click(Coord sc) {
return(false);
}
public void drag(Coord sc) {}
public void release() {}
public boolean wheel(Coord sc, int amount) {
return(false);
}
public void fixangle() {}
public void resized() {
float field = 0.5f;
float aspect = ((float)mv.sz.y) / ((float)mv.sz.x);
proj.update(Projection.makefrustum(new Matrix4f(), -field, field, -aspect * field, aspect * field, 1, 5000));
}
public void prep(Buffer buf) {
proj.prep(buf);
view.prep(buf);
}
public abstract float angle();
public abstract void tick(double dt);
}
private static class FollowCam extends Camera {
public FollowCam(MapView mv) {
super(mv);
}
private final float fr = 0.0f, h = 10.0f;
private float ca, cd;
private Coord3f curc = null;
private float elev = (float)Math.PI / 6.0f;
private float angl = 0.0f;
private Coord dragorig = null;
private float anglorig;
public void resized() {
ca = (float)mv.sz.y / (float)mv.sz.x;
cd = 400.0f * ca;
}
public boolean click(Coord c) {
anglorig = angl;
dragorig = c;
return(true);
}
public void drag(Coord c) {
angl = anglorig + ((float)(c.x - dragorig.x) / 100.0f);
angl = angl % ((float)Math.PI * 2.0f);
}
private double f0 = 0.2, f1 = 0.5, f2 = 0.9;
private double fl = Math.sqrt(2);
private double fa = ((fl * (f1 - f0)) - (f2 - f0)) / (fl - 2);
private double fb = ((f2 - f0) - (2 * (f1 - f0))) / (fl - 2);
private float field(float elev) {
double a = elev / (Math.PI / 4);
return((float)(f0 + (fa * a) + (fb * Math.sqrt(a))));
}
private float dist(float elev) {
float da = (float)Math.atan(ca * field(elev));
return((float)(((cd - (h / Math.tan(elev))) * Math.sin(elev - da) / Math.sin(da)) - (h / Math.sin(elev))));
}
@Override
public void tick(double dt) {
Coord3f cc = mv.getcc();
cc.y = -cc.y;
if(curc == null)
curc = cc;
float dx = cc.x - curc.x, dy = cc.y - curc.y;
if(Math.sqrt((dx * dx) + (dy * dy)) > fr) {
Coord3f oc = curc;
float pd = (float)Math.cos(elev) * dist(elev);
Coord3f cambase = new Coord3f(curc.x + ((float)Math.cos(angl) * pd), curc.y + ((float)Math.sin(angl) * pd), 0.0f);
float a = cc.xyangle(curc);
float nx = cc.x + ((float)Math.cos(a) * fr), ny = cc.y + ((float)Math.sin(a) * fr);
curc = new Coord3f(nx, ny, cc.z);
angl = curc.xyangle(cambase);
}
float field = field(elev);
view.update(PointedCam.compute(curc.add(0.0f, 0.0f, h), dist(elev), elev, angl));
proj.update(Projection.makefrustum(new Matrix4f(), -field, field, -ca * field, ca * field, 1, 5000));
}
public float angle() {
return(angl);
}
private static final float maxang = (float)(Math.PI / 2 - 0.1);
private static final float mindist = 10.0f;
public boolean wheel(Coord c, int amount) {
float fe = elev;
elev += amount * elev * 0.02f;
if(elev > maxang)
elev = maxang;
if(dist(elev) < mindist)
elev = fe;
return(true);
}
}
private static class SmoothFollowCam extends Camera {
private final float fr = 0.0f, h = 10.0f;
private float ca, cd, da;
private Coord3f curc = null;
private float elev, telev;
private float angl, tangl;
private Coord dragorig = null;
private float anglorig;
public SmoothFollowCam(MapView mv) {
super(mv);
elev = telev = (float)Math.PI / 6.0f;
angl = tangl = 0.0f;
}
public void resized() {
ca = (float)mv.sz.y / (float)mv.sz.x;
cd = 400.0f * ca;
}
public boolean click(Coord c) {
anglorig = tangl;
dragorig = c;
return(true);
}
public void drag(Coord c) {
tangl = anglorig + ((float)(c.x - dragorig.x) / 100.0f);
tangl = tangl % ((float)Math.PI * 2.0f);
}
private double f0 = 0.2, f1 = 0.5, f2 = 0.9;
private double fl = Math.sqrt(2);
private double fa = ((fl * (f1 - f0)) - (f2 - f0)) / (fl - 2);
private double fb = ((f2 - f0) - (2 * (f1 - f0))) / (fl - 2);
private float field(float elev) {
double a = elev / (Math.PI / 4);
return((float)(f0 + (fa * a) + (fb * Math.sqrt(a))));
}
private float dist(float elev) {
float da = (float)Math.atan(ca * field(elev));
return((float)(((cd - (h / Math.tan(elev))) * Math.sin(elev - da) / Math.sin(da)) - (h / Math.sin(elev))));
}
public void tick(double dt) {
elev += (telev - elev) * (float)(1.0 - Math.pow(500, -dt));
if(Math.abs(telev - elev) < 0.0001)
elev = telev;
float dangl = tangl - angl;
while(dangl > Math.PI) dangl -= (float)(2 * Math.PI);
while(dangl < -Math.PI) dangl += (float)(2 * Math.PI);
angl += dangl * (float)(1.0 - Math.pow(500, -dt));
if(Math.abs(tangl - angl) < 0.0001)
angl = tangl;
Coord3f cc = mv.getcc();
cc.y = -cc.y;
if(curc == null)
curc = cc;
float dx = cc.x - curc.x, dy = cc.y - curc.y;
float dist = (float)Math.sqrt((dx * dx) + (dy * dy));
if(dist > 250) {
curc = cc;
} else if(dist > fr) {
Coord3f oc = curc;
float pd = (float)Math.cos(elev) * dist(elev);
Coord3f cambase = new Coord3f(curc.x + ((float)Math.cos(tangl) * pd), curc.y + ((float)Math.sin(tangl) * pd), 0.0f);
float a = cc.xyangle(curc);
float nx = cc.x + ((float)Math.cos(a) * fr), ny = cc.y + ((float)Math.sin(a) * fr);
Coord3f tgtc = new Coord3f(nx, ny, cc.z);
curc = curc.add(tgtc.sub(curc).mul((float)(1.0 - Math.pow(500, -dt))));
if(curc.dist(tgtc) < 0.01)
curc = tgtc;
tangl = curc.xyangle(cambase);
}
float field = field(elev);
view.update(PointedCam.compute(curc.add(0.0f, 0.0f, h), dist(elev), elev, angl));
proj.update(Projection.makefrustum(new Matrix4f(), -field, field, -ca * field, ca * field, 1, 5000));
}
public float angle() {
return(angl);
}
private static final float maxang = (float)(Math.PI / 2 - 0.1);
private static final float mindist = 50.0f;
public boolean wheel(Coord c, int amount) {
float fe = telev;
telev += amount * telev * 0.02f;
if(telev > maxang)
telev = maxang;
if(dist(telev) < mindist)
telev = fe;
return(true);
}
public String toString() {
return(String.format("%f %f %f", elev, dist(elev), field(elev)));
}
}
private static class FreeCam extends Camera {
public FreeCam(MapView mv) {
super(mv);
}
private float dist = 50.0f;
private float elev = (float)Math.PI / 4.0f;
private float angl = 0.0f;
private Coord dragorig = null;
private float elevorig, anglorig;
public void tick(double dt) {
Coord3f cc = mv.getcc();
cc.y = -cc.y;
view.update(PointedCam.compute(cc.add(0.0f, 0.0f, 15f), dist, elev, angl));
}
public float angle() {
return(angl);
}
public boolean click(Coord c) {
elevorig = elev;
anglorig = angl;
dragorig = c;
return(true);
}
public void drag(Coord c) {
elev = elevorig - ((float)(c.y - dragorig.y) / 100.0f);
if(elev < 0.0f) elev = 0.0f;
if(elev > (Math.PI / 2.0)) elev = (float)Math.PI / 2.0f;
angl = anglorig + ((float)(c.x - dragorig.x) / 100.0f);
angl = angl % ((float)Math.PI * 2.0f);
}
public boolean wheel(Coord c, int amount) {
float d = dist + (amount * 10);
if(d < 5)
d = 5;
dist = d;
return(true);
}
}
private static class SFreeCam extends Camera {
private float dist = 50.0f, tdist = dist;
private float elev = (float)Math.PI / 4.0f, telev = elev;
private float angl = 0.0f, tangl = angl;
private Coord dragorig = null;
private float elevorig, anglorig;
private final float pi2 = (float)(Math.PI * 2);
private Coord3f cc = null;
public SFreeCam(MapView mv) {
super(mv);
}
public void tick(double dt) {
angl = angl + ((tangl - angl) * (1f - (float)Math.pow(500, -dt)));
while(angl > pi2) {angl -= pi2; tangl -= pi2; anglorig -= pi2;}
while(angl < 0) {angl += pi2; tangl += pi2; anglorig += pi2;}
if(Math.abs(tangl - angl) < 0.0001) angl = tangl;
elev = elev + ((telev - elev) * (1f - (float)Math.pow(500, -dt)));
if(Math.abs(telev - elev) < 0.0001) elev = telev;
dist = dist + ((tdist - dist) * (1f - (float)Math.pow(500, -dt)));
if(Math.abs(tdist - dist) < 0.0001) dist = tdist;
Coord3f mc = mv.getcc();
mc.y = -mc.y;
if((cc == null) || (Math.hypot(mc.x - cc.x, mc.y - cc.y) > 250))
cc = mc;
else
cc = cc.add(mc.sub(cc).mul(1f - (float)Math.pow(500, -dt)));
view.update(PointedCam.compute(cc.add(0.0f, 0.0f, 15f), dist, elev, angl));
}
public float angle() {
return(angl);
}
public boolean click(Coord c) {
elevorig = elev;
anglorig = angl;
dragorig = c;
return(true);
}
public void drag(Coord c) {
telev = elevorig - ((float)(c.y - dragorig.y) / 100.0f);
if(telev < 0.0f) telev = 0.0f;
if(telev > (Math.PI / 2.0)) telev = (float)Math.PI / 2.0f;
tangl = anglorig + ((float)(c.x - dragorig.x) / 100.0f);
}
public boolean wheel(Coord c, int amount) {
float d = tdist + (amount * 5);
if(d < 5)
d = 5;
tdist = d;
return(true);
}
}
static {camtypes.put("best", SFreeCam.class);}
private static class OrthoCam extends Camera {
public static final float DEFANGLE = -(float) Math.PI / 4.0f;
public OrthoCam(MapView mv) {
super(mv);
}
protected float dist = 500.0f;
protected float elev = (float)Math.PI / 6.0f;
protected float angl = DEFANGLE;
protected float field = (float)(100 * Math.sqrt(2));
private Coord dragorig = null;
private float anglorig;
protected Coord3f cc;
public void tick2(double dt) {
Coord3f cc = mv.getcc();
cc.y = -cc.y;
this.cc = cc;
}
public void tick(double dt) {
tick2(dt);
float aspect = ((float)mv.sz.y) / ((float)mv.sz.x);
view.update(PointedCam.compute(cc.add(0.0f, 0.0f, 15f), dist, elev, angl));
proj.update(Projection.makeortho(new Matrix4f(), -field, field, -field * aspect, field * aspect, 1, 5000));
}
public float angle() {
return(angl);
}
public boolean click(Coord c) {
anglorig = angl;
dragorig = c;
return(true);
}
@Override
public void fixangle() {
angl = stepify(angl - DEFANGLE) + DEFANGLE;
}
protected float stepify(float a) {
if(Config.isocam_steps) {
a = Math.round(2 * a / Math.PI);
a = (float) (a * Math.PI / 2);
}
return a;
}
public void drag(Coord c) {
float delta = stepify((float) (c.x - dragorig.x) / 100.0f);
angl = anglorig + delta;
angl = angl % ((float)Math.PI * 2.0f);
}
public boolean wheel(Coord c, int amount) {
field += amount * 10;
field = Math.max(Math.min(field, 500), 50);
return(true);
}
public String toString() {
return(String.format("%f %f %f %f", dist, elev / Math.PI, angl / Math.PI, field));
}
}
private static class SOrthoCam extends OrthoCam {
public SOrthoCam(MapView mv) {
super(mv);
}
private Coord dragorig = null;
private float anglorig;
private float tangl = angl;
private float tfield = field;
private final float pi2 = (float)(Math.PI * 2);
public void tick2(double dt) {
Coord3f mc = mv.getcc();
mc.y = -mc.y;
if((cc == null) || (Math.hypot(mc.x - cc.x, mc.y - cc.y) > 250))
cc = mc;
else
cc = cc.add(mc.sub(cc).mul(1f - (float)Math.pow(500, -dt)));
angl = angl + ((tangl - angl) * (1f - (float)Math.pow(500, -dt)));
while(angl > pi2) {angl -= pi2; tangl -= pi2; anglorig -= pi2;}
while(angl < 0) {angl += pi2; tangl += pi2; anglorig += pi2;}
if(Math.abs(tangl - angl) < 0.0001)
angl = tangl = tangl % ((float)Math.PI * 2.0f);
field = field + ((tfield - field) * (1f - (float)Math.pow(500, -dt)));
if(Math.abs(tfield - field) < 0.0001)
field = tfield;
}
public boolean click(Coord c) {
anglorig = angl;
dragorig = c;
return(true);
}
@Override
public void fixangle() {
tangl = stepify(tangl - DEFANGLE) + DEFANGLE;
}
public void drag(Coord c) {
float delta = stepify((float) (c.x - dragorig.x) / 100.0f);
tangl = anglorig + delta;
}
public boolean wheel(Coord c, int amount) {
tfield += amount * 10;
tfield = Math.max(Math.min(tfield, 400), 50);
return(true);
}
}
@RName("mapview")
public static class $_ implements Factory {
public Widget create(Coord c, Widget parent, Object[] args) {
Coord sz = (Coord)args[0];
Coord mc = (Coord)args[1];
int pgob = -1;
if(args.length > 2)
pgob = (Integer)args[2];
return(new MapView(c, sz, parent, mc, pgob));
}
}
public MapView(Coord c, Coord sz, Widget parent, Coord cc, long plgob) {
super(c, sz, parent);
setcam(Utils.getpref("defcam", DEFCAM));
glob = ui.sess.glob;
this.cc = cc;
this.plgob = plgob;
setcanfocus(true);
r2dwdg = new R2DWdg(this);
}
public void enol(int... overlays) {
for(int ol : overlays)
visol[ol]++;
}
public void disol(int... overlays) {
for(int ol : overlays)
visol[ol]--;
}
public boolean visol(int ol) {
return(visol[ol] > 0);
}
private final Rendered map = new Rendered() {
public void draw(GOut g) {}
public boolean setup(RenderList rl) {
Coord cc = MapView.this.cc.div(tilesz).div(MCache.cutsz);
Coord o = new Coord();
for(o.y = -view; o.y <= view; o.y++) {
for(o.x = -view; o.x <= view; o.x++) {
Coord pc = cc.add(o).mul(MCache.cutsz).mul(tilesz);
MapMesh cut = glob.map.getcut(cc.add(o));
rl.add(cut, Location.xlate(new Coord3f(pc.x, -pc.y, 0)));
Collection<Gob> fol;
try {
fol = glob.map.getfo(cc.add(o));
} catch(Loading e) {
fol = Collections.emptyList();
}
for(Gob fo : fol)
addgob(rl, fo);
}
}
return(false);
}
};
public static final int WFOL = 18;
public static final Tex wftex = Resource.loadtex("gfx/hud/flat");
private final Rendered mapol = new Rendered() {
private final GLState[] mats;
{
mats = new GLState[32];
mats[0] = new Material(new Color(255, 0, 128, 32));
mats[1] = new Material(new Color(0, 0, 255, 32));
mats[2] = new Material(new Color(255, 0, 0, 32));
mats[3] = new Material(new Color(128, 0, 255, 32));
mats[4] = new Material(new Color(0, 0, 0, 64));
mats[16] = new Material(new Color(0, 255, 0, 32));
mats[17] = new Material(new Color(255, 0, 255, 32));
// mats[WFOL] = new Material(new Color(0, 255, 0, 255));
mats[WFOL] = new Material(wftex, true);
mats[WFOL] = new Material(wftex);
}
public void draw(GOut g) {}
public boolean setup(RenderList rl) {
Coord cc = MapView.this.cc.div(tilesz).div(MCache.cutsz);
Coord o = new Coord();
for(o.y = -view; o.y <= view; o.y++) {
for(o.x = -view; o.x <= view; o.x++) {
Coord pc = cc.add(o).mul(MCache.cutsz).mul(tilesz);
for(int i = 0; i < visol.length; i++) {
if(mats[i] == null)
continue;
if(visol[i] > 0) {
Rendered olcut;
olcut = glob.map.getolcut(i, cc.add(o));
if(olcut != null)
rl.add(olcut, GLState.compose(Location.xlate(new Coord3f(pc.x, -pc.y, 0)), mats[i]));
}
}
}
}
return(false);
}
};
void addgob(RenderList rl, final Gob gob) {
GLState xf;
try {
xf = Following.xf(gob);
} catch(Loading e) {
xf = null;
}
GLState extra = null;
if(xf == null) {
xf = gob.loc;
try {
Coord3f c = gob.getc();
Tiler tile = glob.map.tiler(glob.map.gettile(new Coord(c).div(tilesz)));
extra = tile.drawstate(glob, rl.cfg, c);
} catch(Loading e) {
extra = null;
}
}
if(extra != null)
rl.add(gob, GLState.compose(extra, xf, gob.olmod, gob.save));
else
rl.add(gob, GLState.compose(xf, gob.olmod, gob.save));
}
private final Rendered gobs = new Rendered() {
public void draw(GOut g) {}
public boolean setup(RenderList rl) {
synchronized(glob.oc) {
for(Gob gob : glob.oc)
addgob(rl, gob);
}
return(false);
}
};
public GLState camera() {return(camera);}
protected Projection makeproj() {return(null);}
private Coord3f smapcc = null;
private ShadowMap smap = null;
private long lsmch = 0;
private void updsmap(RenderList rl, DirLight light) {
if(rl.cfg.pref.lshadow.val) {
if(smap == null)
smap = new ShadowMap(new Coord(2048, 2048), 750, 5000, 1);
smap.light = light;
Coord3f dir = new Coord3f(-light.dir[0], -light.dir[1], -light.dir[2]);
Coord3f cc = getcc();
cc.y = -cc.y;
boolean ch = false;
long now = System.currentTimeMillis();
if((smapcc == null) || (smapcc.dist(cc) > 50)) {
smapcc = cc;
ch = true;
} else {
if(now - lsmch > 100)
ch = true;
}
if(ch) {
smap.setpos(smapcc.add(dir.neg().mul(1000f)), dir);
lsmch = now;
}
rl.prepc(smap);
} else {
if(smap != null)
smap.dispose();
smap = null;
smapcc = null;
}
}
private DropSky.ResSky sky1 = new DropSky.ResSky(null);
private DropSky.ResSky sky2 = new DropSky.ResSky(null);
public Light amb = null;
private Outlines outlines = new Outlines(false);
public void setup(RenderList rl) {
Gob pl = player();
if(pl != null)
this.cc = new Coord(pl.getc());
synchronized(glob) {
if(glob.lightamb != null) {
DirLight light = new DirLight(glob.lightamb, glob.lightdif, glob.lightspc, Coord3f.o.sadd((float)glob.lightelev, (float)glob.lightang, 1f));
rl.add(light, null);
updsmap(rl, light);
amb = light;
} else {
amb = null;
}
}
if(rl.cfg.pref.outline.val)
rl.add(outlines, null);
rl.add(map, null);
rl.add(mapol, null);
rl.add(gobs, null);
if(placing != null)
addgob(rl, placing);
synchronized(extradraw) {
for(Rendered extra : extradraw)
rl.add(extra, null);
extradraw.clear();
}
if(glob.sky1 != null) {
sky1.update(glob.sky1);
rl.add(sky1, Rendered.last);
if(glob.sky2 != null) {
sky2.update(glob.sky2);
sky2.alpha = glob.skyblend;
rl.add(sky2, Rendered.last);
}
}
}
public static final haven.glsl.Uniform amblight = new haven.glsl.Uniform.AutoApply(haven.glsl.Type.INT) {
public void apply(GOut g, int loc) {
int idx = -1;
RenderContext ctx = g.st.get(PView.ctx);
if(ctx instanceof WidgetContext) {
Widget wdg = ((WidgetContext)ctx).widget();
if(wdg instanceof MapView)
idx = g.st.get(Light.lights).index(((MapView)wdg).amb);
}
g.gl.glUniform1i(loc, idx);
}
};
public void drawadd(Rendered extra) {
synchronized(extradraw) {
extradraw.add(extra);
}
}
public Gob player() {
return(glob.oc.getgob(plgob));
}
public Coord3f getcc() {
Gob pl = player();
if(pl != null)
return(pl.getc());
else
return(new Coord3f(cc.x, cc.y, glob.map.getcz(cc)));
}
private final RenderContext clickctx = new RenderContext();
private GLState.Buffer clickbasic(GOut g) {
GLState.Buffer ret = basic(g);
clickctx.prep(ret);
return(ret);
}
private abstract static class Clicklist<T> extends RenderList {
private Map<Color, T> rmap = new HashMap<Color, T>();
private int i = 1;
private GLState.Buffer plain, bk;
abstract protected T map(Rendered r);
private Clicklist(GLState.Buffer plain) {
super(plain.cfg);
this.plain = plain;
this.bk = new GLState.Buffer(plain.cfg);
}
protected Color newcol(T t) {
int cr = ((i & 0x00000f) << 4) | ((i & 0x00f000) >> 12),
cg = ((i & 0x0000f0) << 0) | ((i & 0x0f0000) >> 16),
cb = ((i & 0x000f00) >> 4) | ((i & 0xf00000) >> 20);
Color col = new Color(cr, cg, cb);
i++;
rmap.put(col, t);
return(col);
}
protected void render(GOut g, Rendered r) {
try {
if(r instanceof FRendered)
((FRendered)r).drawflat(g);
} catch(RenderList.RLoad l) {
if(ignload) return; else throw(l);
}
}
public T get(GOut g, Coord c) {
return(rmap.get(g.getpixel(c)));
}
protected void setup(Slot s, Rendered r) {
T t = map(r);
super.setup(s, r);
s.os.copy(bk);
plain.copy(s.os);
bk.copy(s.os, GLState.Slot.Type.GEOM);
if(t != null) {
Color col = newcol(t);
new States.ColState(col).prep(s.os);
}
}
}
private static class Maplist extends Clicklist<MapMesh> {
private int mode = 0;
private MapMesh limit = null;
private Maplist(GLState.Buffer plain) {
super(plain);
}
protected MapMesh map(Rendered r) {
if(r instanceof MapMesh)
return((MapMesh)r);
return(null);
}
protected void render(GOut g, Rendered r) {
if(r instanceof MapMesh) {
MapMesh m = (MapMesh)r;
if(mode != 0)
g.state(States.vertexcolor);
if((limit == null) || (limit == m))
m.drawflat(g, mode);
}
}
}
private Coord checkmapclick(GOut g, Coord c) {
Maplist rl = new Maplist(clickbasic(g));
rl.setup(map, clickbasic(g));
rl.fin();
{
rl.render(g);
MapMesh hit = rl.get(g, c);
if(hit == null)
return(null);
rl.limit = (MapMesh)hit;
}
Coord tile;
{
rl.mode = 1;
rl.render(g);
Color hitcol = g.getpixel(c);
tile = new Coord(hitcol.getRed() - 1, hitcol.getGreen() - 1);
if(!tile.isect(Coord.z, rl.limit.sz))
return(null);
}
Coord pixel;
{
rl.mode = 2;
rl.render(g);
Color hitcol = g.getpixel(c);
if(hitcol.getBlue() != 0)
return(null);
pixel = new Coord((hitcol.getRed() * tilesz.x) / 255, (hitcol.getGreen() * tilesz.y) / 255);
}
return(rl.limit.ul.add(tile).mul(tilesz).add(pixel));
}
public static class ClickInfo {
Gob gob;
Gob.Overlay ol;
Rendered r;
ClickInfo(Gob gob, Gob.Overlay ol, Rendered r) {
this.gob = gob; this.ol = ol; this.r = r;
}
}
private ClickInfo checkgobclick(GOut g, Coord c) {
Clicklist<ClickInfo> rl = new Clicklist<ClickInfo>(clickbasic(g)) {
Gob curgob;
Gob.Overlay curol;
ClickInfo curinfo;
public ClickInfo map(Rendered r) {
return(curinfo);
}
public void add(Rendered r, GLState t) {
Gob prevg = curgob;
Gob.Overlay prevo = curol;
if(r instanceof Gob)
curgob = (Gob)r;
else if(r instanceof Gob.Overlay)
curol = (Gob.Overlay)r;
if((curgob == null) || !(r instanceof FRendered))
curinfo = null;
else
curinfo = new ClickInfo(curgob, curol, r);
super.add(r, t);
curgob = prevg;
curol = prevo;
}
};
rl.setup(gobs, clickbasic(g));
rl.fin();
rl.render(g);
return(rl.get(g, c));
}
public void delay(Delayed d) {
synchronized(delayed) {
delayed.add(d);
}
}
public void delay2(Delayed d) {
synchronized(delayed2) {
delayed2.add(d);
}
}
protected void undelay(Collection<Delayed> list, GOut g) {
synchronized(list) {
for(Delayed d : list)
d.run(g);
list.clear();
}
}
private static final Text.Furnace polownertf = new PUtils.BlurFurn(new Text.Foundry("serif", 30).aa(true), 3, 1, Color.BLACK);
private Text polownert = null;
private long polchtm = 0;
public void setpoltext(String text) {
polownert = polownertf.render(text);
polchtm = System.currentTimeMillis();
}
private void poldraw(GOut g) {
long now = System.currentTimeMillis();
long poldt = now - polchtm;
if((polownert != null) && (poldt < 6000)) {
int a;
if(poldt < 1000)
a = (int)((255 * poldt) / 1000);
else if(poldt < 4000)
a = 255;
else
a = (int)((255 * (2000 - (poldt - 4000))) / 2000);
g.chcolor(255, 255, 255, a);
g.aimage(polownert.tex(), sz.div(2), 0.5, 0.5);
g.chcolor();
}
}
private void drawarrow(GOut g, double a) {
Coord hsz = sz.div(2);
double ca = -Coord.z.angle(hsz);
Coord ac;
if((a > ca) && (a < -ca)) {
ac = new Coord(sz.x, hsz.y - (int)(Math.tan(a) * hsz.x));
} else if((a > -ca) && (a < Math.PI + ca)) {
ac = new Coord(hsz.x - (int)(Math.tan(a - Math.PI / 2) * hsz.y), 0);
} else if((a > -Math.PI - ca) && (a < ca)) {
ac = new Coord(hsz.x + (int)(Math.tan(a + Math.PI / 2) * hsz.y), sz.y);
} else {
ac = new Coord(0, hsz.y + (int)(Math.tan(a) * hsz.x));
}
Coord bc = ac.add(Coord.sc(a, -10));
g.line(bc, bc.add(Coord.sc(a, -40)), 2);
g.line(bc, bc.add(Coord.sc(a + Math.PI / 4, -10)), 2);
g.line(bc, bc.add(Coord.sc(a - Math.PI / 4, -10)), 2);
}
public double screenangle(Coord mc, boolean clip) {
Coord3f cc;
try {
cc = getcc();
} catch(Loading e) {
return(Double.NaN);
}
Coord3f mloc = new Coord3f(mc.x, -mc.y, cc.z);
float[] sloc = camera.proj.toclip(camera.view.fin(Matrix4f.id).mul4(mloc));
if(clip) {
float w = sloc[3];
if((sloc[0] > -w) && (sloc[0] < w) && (sloc[1] > -w) && (sloc[1] < w))
return(Double.NaN);
}
float a = ((float)sz.y) / ((float)sz.x);
return(Math.atan2(sloc[1] * a, sloc[0]));
}
private void partydraw(GOut g) {
for(Party.Member m : ui.sess.glob.party.memb.values()) {
if(m.gobid == this.plgob)
continue;
Coord mc = m.getc();
if(mc == null)
continue;
double a = screenangle(mc, true);
if(a == Double.NaN)
continue;
g.chcolor(m.col);
drawarrow(g, a);
}
g.chcolor();
}
private boolean camload = false;
private Loading lastload = null;
public void draw(GOut g) {
glob.map.sendreqs();
if((olftimer != 0) && (olftimer < System.currentTimeMillis()))
unflashol();
try {
if(camload)
throw(new MCache.LoadingMap());
undelay(delayed, g);
super.draw(g);
undelay(delayed2, g);
poldraw(g);
partydraw(g);
glob.map.reqarea(cc.div(tilesz).sub(MCache.cutsz.mul(view + 1)),
cc.div(tilesz).add(MCache.cutsz.mul(view + 1)));
} catch(Loading e) {
lastload = e;
String text = "Loading...";
g.chcolor(Color.BLACK);
g.frect(Coord.z, sz);
g.chcolor(Color.WHITE);
g.atext(text, sz.div(2), 0.5, 0.5);
}
}
public void tick(double dt) {
camload = false;
try {
camera.tick(dt);
} catch(Loading e) {
camload = true;
}
if(placing != null)
placing.ctick((int)(dt * 1000));
}
public void resize(Coord sz) {
super.resize(sz);
r2dwdg.resize(sz);
camera.resized();
}
@Override
protected void render2d(GOut g) {
//2d render will be done in r2dwdg
}
private class Plob extends Gob {
Coord lastmc = null;
boolean freerot = false;
private Plob(Indir<Resource> res, Message sdt) {
super(MapView.this.glob, Coord.z);
setattr(new ResDrawable(this, res, sdt));
if(ui.mc.isect(rootpos(), sz)) {
delay(new Adjust(ui.mc.sub(rootpos()), false));
}
}
private class Adjust extends Maptest {
boolean adjust;
Adjust(Coord c, boolean ta) {
super(c);
adjust = ta;
}
public void hit(Coord pc, Coord mc) {
rc = mc;
if(adjust)
rc = rc.div(tilesz).mul(tilesz).add(tilesz.div(2));
Gob pl = player();
if((pl != null) && !freerot)
a = rc.angle(pl.rc);
lastmc = pc;
}
}
}
private int olflash;
private long olftimer;
private void unflashol() {
for(int i = 0; i < visol.length; i++) {
if((olflash & (1 << i)) != 0)
visol[i]--;
}
olflash = 0;
olftimer = 0;
}
public void uimsg(String msg, Object... args) {
if(msg == "place") {
int a = 0;
Indir<Resource> res = ui.sess.getres((Integer)args[a++]);
Message sdt;
if((args.length > a) && (args[a] instanceof byte[]))
sdt = new Message(0, (byte[])args[a++]);
else
sdt = Message.nil;
placing = new Plob(res, sdt);
while(a < args.length) {
Indir<Resource> ores = ui.sess.getres((Integer)args[a++]);
Message odt;
if((args.length > a) && (args[a] instanceof byte[]))
odt = new Message(0, (byte[])args[a++]);
else
odt = Message.nil;
placing.ols.add(new Gob.Overlay(-1, ores, odt));
}
} else if(msg == "unplace") {
placing = null;
} else if(msg == "move") {
cc = (Coord)args[0];
} else if(msg == "flashol") {
unflashol();
olflash = (Integer)args[0];
for(int i = 0; i < visol.length; i++) {
if((olflash & (1 << i)) != 0)
visol[i]++;
}
olftimer = System.currentTimeMillis() + (Integer)args[1];
} else {
super.uimsg(msg, args);
}
}
private boolean camdrag = false;
public abstract class Maptest implements Delayed {
private final Coord pc;
public Maptest(Coord c) {
this.pc = c;
}
public void run(GOut g) {
GLState.Buffer bk = g.st.copy();
Coord mc;
try {
GL gl = g.gl;
g.st.set(clickbasic(g));
g.apply();
gl.glClear(GL.GL_DEPTH_BUFFER_BIT | GL.GL_COLOR_BUFFER_BIT);
mc = checkmapclick(g, pc);
} finally {
g.st.set(bk);
}
if(mc != null)
hit(pc, mc);
else
nohit(pc);
}
protected abstract void hit(Coord pc, Coord mc);
protected void nohit(Coord pc) {}
}
public abstract class Hittest implements Delayed {
private final Coord clickc;
public Hittest(Coord c) {
clickc = c;
}
public void run(GOut g) {
GLState.Buffer bk = g.st.copy();
Coord mapcl;
ClickInfo gobcl;
try {
GL gl = g.gl;
g.st.set(clickbasic(g));
g.apply();
gl.glClear(GL.GL_DEPTH_BUFFER_BIT | GL.GL_COLOR_BUFFER_BIT);
mapcl = checkmapclick(g, clickc);
g.st.set(bk);
g.st.set(clickbasic(g));
g.apply();
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
gobcl = checkgobclick(g, clickc);
} finally {
g.st.set(bk);
}
if(mapcl != null) {
if(gobcl == null)
hit(clickc, mapcl, null);
else
hit(clickc, mapcl, gobcl);
} else {
nohit(clickc);
}
}
protected abstract void hit(Coord pc, Coord mc, ClickInfo inf);
protected void nohit(Coord pc) {}
}
private static int getid(Rendered tgt) {
if(tgt instanceof ResPart)
return(((ResPart)tgt).partid());
return(-1);
}
private class Click extends Hittest {
int clickb;
private Click(Coord c, int b) {
super(c);
clickb = b;
}
protected void hit(Coord pc, Coord mc, ClickInfo inf) {
int modflags = ui.modflags();
if(inf == null) {
if(Config.center){mc = mc.div(11).mul(11).add(5, 5);}
wdgmsg("click", pc, mc, clickb, modflags);
} else {
if(ui.modmeta){
ChatUI.Channel channel = ui.gui.chat.sel;
if(channel != null && channel instanceof ChatUI.EntryChannel){
((ChatUI.EntryChannel)channel).send(String.format("$hl[%d]", inf.gob.id));
}
}
if(inf.ol == null) {
wdgmsg("click", pc, mc, clickb, modflags, 0, (int)inf.gob.id, inf.gob.rc, 0, getid(inf.r));
} else {
wdgmsg("click", pc, mc, clickb, modflags, 1, (int)inf.gob.id, inf.gob.rc, inf.ol.id, getid(inf.r));
}
}
}
}
public void grab(Grabber grab) {
this.grab = grab;
}
public void release(Grabber grab) {
if(this.grab == grab)
this.grab = null;
}
public boolean mousedown(Coord c, int button) {
parent.setfocus(this);
if(button == 2) {
if(((Camera)camera).click(c)) {
ui.grabmouse(this);
camdrag = true;
}
} else if(placing != null) {
if(placing.lastmc != null)
wdgmsg("place", placing.rc, (int)(placing.a * 180 / Math.PI), button, ui.modflags());
} else if((grab != null) && grab.mmousedown(c, button)) {
} else {
delay(new Click(c, button));
}
return(true);
}
public void mousemove(Coord c) {
if(grab != null)
grab.mmousemove(c);
if(camdrag) {
((Camera)camera).drag(c);
} else if(placing != null) {
if((placing.lastmc == null) || !placing.lastmc.equals(c)) {
delay(placing.new Adjust(c, !ui.modctrl));
}
}
}
public boolean mouseup(Coord c, int button) {
if(button == 2) {
if(camdrag) {
((Camera)camera).release();
ui.grabmouse(null);
camdrag = false;
}
} else if(grab != null) {
grab.mmouseup(c, button);
}
return(true);
}
public boolean mousewheel(Coord c, int amount) {
if((grab != null) && grab.mmousewheel(c, amount))
return(true);
if(ui.modshift) {
if(placing != null) {
placing.freerot = true;
if(!ui.modctrl)
placing.a = (Math.PI / 4) * Math.round((placing.a + (amount * Math.PI / 4)) / (Math.PI / 4));
else
placing.a += amount * Math.PI / 16;
}
return(true);
}
return(((Camera)camera).wheel(c, amount));
}
public boolean drop(final Coord cc, final Coord ul) {
delay(new Hittest(cc) {
public void hit(Coord pc, Coord mc, ClickInfo inf) {
wdgmsg("drop", pc, mc, ui.modflags());
}
});
return(true);
}
public boolean iteminteract(Coord cc, Coord ul) {
delay(new Hittest(cc) {
public void hit(Coord pc, Coord mc, ClickInfo inf) {
if(inf == null)
wdgmsg("itemact", pc, mc, ui.modflags());
else
wdgmsg("itemact", pc, mc, ui.modflags(), (int)inf.gob.id, inf.gob.rc, getid(inf.r));
}
});
return(true);
}
public boolean globtype(char c, java.awt.event.KeyEvent ev) {
int code = ev.getKeyCode();
if(code == KeyEvent.VK_ADD) {
return camera.wheel(Coord.z, 1);
} else if(code == KeyEvent.VK_SUBTRACT){
return camera.wheel(Coord.z, -1);
}
return(false);
}
public void setcam(String cam) {
try {
Constructor<? extends Camera> constructor;
constructor = camtypes.get(cam).getConstructor(MapView.class);
camera = Utils.construct(constructor, MapView.this);//constructor.newInstance(this);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
public class GrabXL implements Grabber {
private final Grabber bk;
public boolean mv = false;
public GrabXL(Grabber bk) {
this.bk = bk;
}
public boolean mmousedown(Coord cc, final int button) {
delay(new Hittest(cc) {
public void hit(Coord pc, Coord mc, ClickInfo inf) {
bk.mmousedown(mc, button);
}
});
return(true);
}
public boolean mmouseup(Coord cc, final int button) {
delay(new Hittest(cc) {
public void hit(Coord pc, Coord mc, ClickInfo inf) {
bk.mmouseup(mc, button);
}
});
return(true);
}
public boolean mmousewheel(Coord cc, final int amount) {
delay(new Hittest(cc) {
public void hit(Coord pc, Coord mc, ClickInfo inf) {
bk.mmousewheel(mc, amount);
}
});
return(true);
}
public void mmousemove(Coord cc) {
if(mv) {
delay(new Hittest(cc) {
public void hit(Coord pc, Coord mc, ClickInfo inf) {
bk.mmousemove(mc);
}
});
}
}
}
private Map<String, Console.Command> cmdmap = new TreeMap<String, Console.Command>();
{
cmdmap.put("cam", new Console.Command() {
public void run(Console cons, String[] args) throws Exception {
setcam(args[1]);
Class<? extends Camera> cc = camtypes.get(args[1]);
if(cc == null)
throw(new Exception("no such camera type: " + args[1]));
camera = Utils.construct(cc.getConstructor(MapView.class), MapView.this);
}
});
cmdmap.put("whyload", new Console.Command() {
public void run(Console cons, String[] args) throws Exception {
Loading l = lastload;
if(l == null)
throw(new Exception("Not loading"));
l.printStackTrace(cons.out);
}
});
}
public Map<String, Console.Command> findcmds() {
return(cmdmap);
}
}