/*
* 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 java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.font.TextAttribute;
import java.awt.image.BufferedImage;
import haven.GobbleInfo.Event;
import haven.ItemInfo.Tip;
import haven.Resource.AButton;
import haven.Glob.Pagina;
import haven.ItemInfo.InfoFactory;
import java.util.*;
import org.ender.wiki.Item;
import org.ender.wiki.Wiki;
public class MenuGrid extends Widget {
public final static Tex bg = Resource.loadtex("gfx/hud/invsq");
public final static Coord bgsz = bg.sz().add(-1, -1);
private final Pagina CRAFT;
public final Pagina next = paginafor(Resource.load("gfx/hud/sc-next").loadwait());
public final Pagina bk = paginafor(Resource.load("gfx/hud/sc-back").loadwait());
public final static RichText.Foundry ttfnd = new RichText.Foundry(TextAttribute.FAMILY, "SansSerif", TextAttribute.SIZE, 10);
private static Coord gsz = new Coord(4, 4);
public Pagina cur, pressed, dragging, layout[][] = new Pagina[gsz.x][gsz.y];
private int curoff = 0;
private int pagseq = 0;
private boolean loading = true;
private Map<Character, Pagina> hotmap = new TreeMap<Character, Pagina>();
@RName("scm")
public static class $_ implements Factory {
public Widget create(Coord c, Widget parent, Object[] args) {
return(new MenuGrid(c, parent));
}
}
public class PaginaException extends RuntimeException {
public Pagina pag;
public PaginaException(Pagina p) {
super("Invalid pagina: " + p.res().name);
pag = p;
}
}
public boolean cons(Pagina p, Collection<Pagina> buf) {
Pagina[] cp = new Pagina[0];
Collection<Pagina> open, close = new HashSet<Pagina>();
synchronized(ui.sess.glob.paginae) {
open = new LinkedList<Pagina>();
for(Pagina pag : ui.sess.glob.paginae) {
if(pag.newp == 2) {
pag.newp = 0;
pag.fstart = 0;
}
open.add(pag);
}
for(Pagina pag : ui.sess.glob.pmap.values()) {
if(pag.newp == 2) {
pag.newp = 0;
pag.fstart = 0;
}
}
}
boolean ret = true;
while(!open.isEmpty()) {
Iterator<Pagina> iter = open.iterator();
Pagina pag = iter.next();
iter.remove();
try {
Resource r = pag.res();
AButton ad = r.layer(Resource.action);
if(ad == null)
throw(new PaginaException(pag));
Pagina parent = paginafor(ad.parent);
if((pag.newp != 0) && (parent != null) && (parent.newp == 0)) {
parent.newp = 2;
parent.fstart = (parent.fstart == 0)?pag.fstart:Math.min(parent.fstart, pag.fstart);
}
if(parent == p)
buf.add(pag);
else if((parent != null) && !close.contains(parent) && !open.contains(parent))
open.add(parent);
close.add(pag);
} catch(Loading e) {
ret = false;
}
}
return(ret);
}
public MenuGrid(Coord c, Widget parent) {
super(c, Inventory.invsz(gsz), parent);
ui.mnu = this;
CRAFT = paginafor(Resource.load("paginae/act/craft"));
Glob glob = ui.sess.glob;
Collection<Pagina> p = glob.paginae;
p.add(glob.paginafor(Resource.load("paginae/act/add")));
p.add(glob.paginafor(Resource.load("paginae/add/timer")));
p.add(glob.paginafor(Resource.load("paginae/add/wiki")));
p.add(glob.paginafor(Resource.load("paginae/add/craft")));
p.add(glob.paginafor(Resource.load("paginae/add/radius")));
// p.add(glob.paginafor(Resource.load("paginae/add/anime/raeg")));
// p.add(glob.paginafor(Resource.load("paginae/add/anime/facepalm")));
//cons(null);
}
public static Comparator<Pagina> sorter = new Comparator<Pagina>() {
public int compare(Pagina a, Pagina b) {
AButton aa = a.act(), ab = b.act();
if((aa.ad.length == 0) && (ab.ad.length > 0))
return(-1);
if((aa.ad.length > 0) && (ab.ad.length == 0))
return(1);
return(aa.name.compareTo(ab.name));
}
};
private void updlayout() {
synchronized(ui.sess.glob.paginae) {
List<Pagina> cur = new ArrayList<Pagina>();
loading = !cons(this.cur, cur);
Collections.sort(cur, sorter);
int i = curoff;
hotmap.clear();
for(int y = 0; y < gsz.y; y++) {
for(int x = 0; x < gsz.x; x++) {
Pagina btn = null;
if((this.cur != null) && (x == gsz.x - 1) && (y == gsz.y - 1)) {
btn = bk;
} else if((cur.size() > ((gsz.x * gsz.y) - 1)) && (x == gsz.x - 2) && (y == gsz.y - 1)) {
btn = next;
} else if(i < cur.size()) {
Resource.AButton ad = cur.get(i).act();
if(ad.hk != 0)
hotmap.put(Character.toUpperCase(ad.hk), cur.get(i));
btn = cur.get(i++);
}
layout[x][y] = btn;
}
}
pagseq = ui.sess.glob.pagseq;
}
}
public static BufferedImage getXPgain(String name){
try {
Item itm = Wiki.get(name);
if(itm != null){
Map<String, Integer> props = itm.attgive;
if(props != null){
int n = props.size();
String attrs[] = new String[n];
int exp[] = new int[n];
n = 0;
for(String attr : props.keySet()){
Integer val = props.get(attr);
attrs[n] = attr;
exp[n] = val;
n ++;
}
Inspiration i = new Inspiration(null, attrs, exp);
return i.longtip();
}
}
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static float[] safeFloat(Float[] input, float[] def){
if(input == null){return def;}
int n = input.length;
float[] output = new float[n];
for(int i=0; i<n; i++){
if(input[i] != null){
output[i] = input[i];
} else {
output[i] = 0;
}
}
return output;
}
public static BufferedImage getFood(String name){
try {
Item itm = Wiki.get(name);
if(itm != null){
Map<String, Float[]> food = itm.food;
if(food != null){
float[] def = new float[]{0, 0, 0, 0};
float[] heal = safeFloat(food.get("Heals"), def);
float[] gmax = safeFloat(food.get("GluttonMax"), def);
float[] gmin = safeFloat(food.get("GluttonMin"), def);
int[] low = new int[4];
int[] high = new int[4];
int[] tempers = new int[4];
for(int i=0; i<4; i++){
tempers[i] = (int) (1000*heal[i]);
high[i] = (int) (1000*gmax[i]);
low[i] = (int) (1000*gmin[i]);
}
FoodInfo fi = new FoodInfo(null, tempers);
int ft = itm.food_full*60;
GobbleInfo gi = new GobbleInfo(null, low, high, new int[0], ft, new LinkedList<Event>());
String uses = String.format("$b{$col[192,192,64]{Uses: %d}}\n", itm.food_uses);
BufferedImage uses_img = RichText.stdf.render(uses).img;
return ItemInfo.catimgs(3, fi.longtip(), gi.longtip(), uses_img);
}
}
} catch (Exception e) {e.printStackTrace();}
return null;
}
public static BufferedImage getArtifact(String name){
try{
Item itm = Wiki.get(name);
if(itm == null || itm.art_profs == null || itm.art_profs.length == 0){return null;}
Resource res = Resource.load("ui/tt/slot");
if(res == null){return null;}
InfoFactory f = res.layer(Resource.CodeEntry.class).get(InfoFactory.class);
Session sess = UI.instance.sess;
int rid = sess.getresid("ui/tt/dattr");
if(rid == 0){return null;}
Object[] bonuses = itm.getArtBonuses();
bonuses[0] = rid;
Object[] args = new Object[4 + itm.art_profs.length];//{0, itm.art_pmin, itm.art_pmax, "arts", "faith", new Object[]{bonuses}};
int i=0;
args[i++] = 0;
args[i++] = itm.art_pmin;
args[i++] = itm.art_pmax;
for(String prof : itm.art_profs){
args[i++] = CharWnd.attrbyname(prof);
}
args[i++] = new Object[]{bonuses};
ItemInfo.Tip tip = (Tip) f.build(sess, args);
List<ItemInfo> list = new LinkedList<ItemInfo>();
list.add(tip);
return tip.longtip();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static BufferedImage getSlots(String name){
try {
Item itm = Wiki.get(name);
if(itm == null || itm.cloth_slots == 0){return null;}
Object[] args = new Object[5 + itm.cloth_profs.length];
int i = 0;
args[i++] = 0;
args[i++] = itm.cloth_slots;
args[i++] = itm.cloth_pmin;
args[i++] = itm.cloth_pmax;
for(String prof : itm.cloth_profs){
args[i++] = CharWnd.attrbyname(prof);
}
args[i++] = 0;
Resource res = Resource.load("ui/tt/slots");
if(res == null){return null;}
InfoFactory f = res.layer(Resource.CodeEntry.class).get(InfoFactory.class);
ItemInfo.Tip tip = (Tip) f.build(null, args);
return tip.longtip();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static Tex rendertt(Resource res, boolean withpg, boolean hotkey) {
Resource.AButton ad = res.layer(Resource.action);
Resource.Pagina pg = res.layer(Resource.pagina);
String tt = ad.name;
BufferedImage xp = null, food = null, slots = null, art = null;
if(hotkey){
int pos = tt.toUpperCase().indexOf(Character.toUpperCase(ad.hk));
if(pos >= 0)
tt = tt.substring(0, pos) + "$col[255,255,0]{" + tt.charAt(pos) + "}" + tt.substring(pos + 1);
else if(ad.hk != 0)
tt += " [" + ad.hk + "]";
}
if(withpg) {
if(pg != null){tt += "\n\n" + pg.text;}
xp = getXPgain(ad.name);
food = getFood(ad.name);
slots = getSlots(ad.name);
art = getArtifact(ad.name);
}
BufferedImage img = ttfnd.render(tt, 300).img;
if(xp != null){
img = ItemInfo.catimgs(3, img, xp);
}
if(food != null){
img = ItemInfo.catimgs(3, img, food);
}
if(slots != null){
img = ItemInfo.catimgs(3, img, slots);
}
if(art != null){
img = ItemInfo.catimgs(3, img, art);
}
return(new TexI(img));
}
private static Map<Pagina, Tex> glowmasks = new WeakHashMap<Pagina, Tex>();
private Tex glowmask(Pagina pag) {
Tex ret = glowmasks.get(pag);
if(ret == null) {
ret = new TexI(PUtils.glowmask(PUtils.glowmask(pag.res().layer(Resource.imgc).img.getRaster()), 4, new Color(32, 255, 32)));
glowmasks.put(pag, ret);
}
return(ret);
}
public void draw(GOut g) {
long now = System.currentTimeMillis();
Inventory.invsq(g, Coord.z, gsz);
for(int y = 0; y < gsz.y; y++) {
for(int x = 0; x < gsz.x; x++) {
Coord p = Inventory.sqoff(new Coord(x, y));
Pagina btn = layout[x][y];
if(btn != null) {
Tex btex = btn.img.tex();
g.image(btex, p);
if(btn.meter > 0) {
double m = btn.meter / 1000.0;
if(btn.dtime > 0)
m += (1 - m) * (double)(now - btn.gettime) / (double)btn.dtime;
m = Utils.clip(m, 0, 1);
g.chcolor(255, 255, 255, 128);
g.fellipse(p.add(Inventory.isqsz.div(2)), Inventory.isqsz.div(2), 90, (int)(90 + (360 * m)));
g.chcolor();
}
if(btn.newp != 0) {
if(btn.fstart == 0) {
btn.fstart = now;
} else {
double ph = ((now - btn.fstart) / 1000.0) - (((x + (y * gsz.x)) * 0.15) % 1.0);
if(ph < 1.25) {
g.chcolor(255, 255, 255, (int)(255 * ((Math.cos(ph * Math.PI * 2) * -0.5) + 0.5)));
g.image(glowmask(btn), p.sub(4, 4));
g.chcolor();
} else {
g.chcolor(255, 255, 255, 128);
g.image(glowmask(btn), p.sub(4, 4));
g.chcolor();
}
}
}
if(btn == pressed) {
g.chcolor(new Color(0, 0, 0, 128));
g.frect(p, btex.sz());
g.chcolor();
}
}
}
}
super.draw(g);
if(dragging != null) {
final Tex dt = dragging.img.tex();
ui.drawafter(new UI.AfterDraw() {
public void draw(GOut g) {
g.image(dt, ui.mc.add(dt.sz().div(2).inv()));
}
});
}
}
private Pagina curttp = null;
private boolean curttl = false;
Item ttitem = null;
private Tex curtt = null;
private long hoverstart;
public Object tooltip(Coord c, Widget prev) {
Pagina pag = bhit(c);
long now = System.currentTimeMillis();
if((pag != null) && (pag.act() != null)) {
if(prev != this)
hoverstart = now;
boolean ttl = (now - hoverstart) > 500;
Item itm = Wiki.get(pag.res().layer(Resource.action).name);
if((pag != curttp) || (ttl != curttl) || itm != ttitem) {
ttitem = itm;
curtt = rendertt(pag.res(), ttl, true);
curttp = pag;
curttl = ttl;
}
return(curtt);
} else {
hoverstart = now;
return("");
}
}
private Pagina bhit(Coord c) {
Coord bc = Inventory.sqroff(c);
if((bc.x >= 0) && (bc.y >= 0) && (bc.x < gsz.x) && (bc.y < gsz.y))
return(layout[bc.x][bc.y]);
else
return(null);
}
public boolean mousedown(Coord c, int button) {
Pagina h = bhit(c);
if((button == 1) && (h != null)) {
pressed = h;
ui.grabmouse(this);
}
return(true);
}
public void mousemove(Coord c) {
if((dragging == null) && (pressed != null)) {
Pagina h = bhit(c);
if(h != pressed)
dragging = pressed;
}
}
private Pagina paginafor(Resource res) {
return(ui.sess.glob.paginafor(res));
}
public Pagina paginafor(String name){
Set<Pagina> pags = ui.sess.glob.paginae;
for(Pagina p : pags){
Resource res = p.res();
if(res == null){continue;}
AButton act = res.layer(Resource.action);
if(act == null){continue;}
if(name.equals(act.name)){
return p;
}
}
return null;
}
public void useres(Resource r){
use(paginafor(r));
}
public void use(Pagina r) {
Collection<Pagina> sub = new LinkedList<Pagina>(),
cur = new LinkedList<Pagina>();
cons(r, sub);
cons(this.cur, cur);
if(isCrafting(r)){ui.gui.showCraftWnd();}
selectCraft(r);
if(sub.size() > 0) {
this.cur = r;
curoff = 0;
} else if(r == bk) {
this.cur = paginafor(this.cur.act().parent);
this.curoff = 0;
selectCraft(this.cur);
} else if(r == next) {
int off = gsz.x*gsz.y - 2;
if((curoff + off) >= cur.size())
curoff = 0;
else
curoff += off;
} else {
r.newp = 0;
if (!senduse(r)) return;
if(Config.menugrid_resets){
this.cur = null;
curoff = 0;
}
}
updlayout();
}
public boolean senduse(Pagina r) {
String [] ad = r.act().ad;
if((ad == null) || (ad.length < 1)){
return false;
}
if(ad[0].equals("@")) {
usecustom(ad);
} else {
wdgmsg("act", (Object[])ad);
}
return true;
}
private void selectCraft(Pagina r) {
if(r == null){
return;
}
if(ui.gui.craftwnd != null){
ui.gui.craftwnd.select(r, true);
}
}
public void tick(double dt) {
if(loading || (pagseq != ui.sess.glob.pagseq))
updlayout();
}
private void usecustom(String[] ad) {
if(ad[1].equals("act")) {
String[] args = new String[ad.length - 2];
System.arraycopy(ad, 2, args, 0, args.length);
ui.gui.wdgmsg("act", (Object[])args);
} else if(ad[1].equals("timers")) {
TimerPanel.toggle();
} else if(ad[1].equals("wiki")) {
WikiBrowser.toggle();
} else if(ad[1].equals("craft")) {
ui.gui.toggleCraftWnd();
} else if(ad[1].equals("radius")) {
Config.toggleRadius();
}
use(null);
}
public boolean mouseup(Coord c, int button) {
Pagina h = bhit(c);
if(button == 1) {
if(dragging != null) {
ui.dropthing(ui.root, ui.mc, dragging.res());
dragging = pressed = null;
} else if(pressed != null) {
if(pressed == h)
use(h);
pressed = null;
}
ui.grabmouse(null);
}
return(true);
}
public void uimsg(String msg, Object... args) {
if(msg == "goto") {
String res = (String)args[0];
if(res.equals(""))
cur = null;
else
cur = paginafor(Resource.load(res));
curoff = 0;
updlayout();
}
}
public boolean globtype(char k, KeyEvent ev) {
if(ev.isAltDown() || ev.isControlDown() || k == 0){return false;}
k = (char) ev.getKeyCode();
if(Character.toUpperCase(k) != k){return false;}
if((k == 27) && (this.cur != null)) {
this.cur = null;
curoff = 0;
updlayout();
return(true);
} else if((k == KeyEvent.VK_N) && (layout[gsz.x - 2][gsz.y - 1] == next)) {
use(next);
return(true);
}
Pagina r = hotmap.get(k);
if(r != null) {
use(r);
return(true);
}
return(false);
}
public boolean isCrafting(Pagina p) {
return isChildOf(p, CRAFT) || CRAFT == p;
}
public boolean isCrafting(Resource res){
return isCrafting(paginafor(res));
}
public Pagina getParent(Pagina p){
if(p == null){
return null;
}
try {
Resource res = p.res();
Resource.AButton ad = res.layer(Resource.action);
if (ad == null)
return null;
return paginafor(ad.parent);
} catch (Loading e){
return null;
}
}
public boolean isChildOf(Pagina item, Pagina parent) {
Pagina p;
while((p = getParent(item)) != null){
if(p == parent){ return true; }
item = p;
}
return false;
}
}