public void addpart(Sprite.Part p) {
                p.effect = fx;
                if ((p.ul.x >= sz.x) || (p.ul.y >= sz.y) || (p.lr.x < 0)
                        || (p.lr.y < 0))
                    return;
                sprites.add(p);
                p.owner = cur;
                p.szo = szo;
            }
        }

        //Kerri
        if (KerriUtils.iObj.size() > 0) {
            g.chcolor(20, 20, 250, 108);
            synchronized (glob.oc) {
                for (Gob gob : glob.oc) {
                    if (KerriUtils.iObj.contains(Integer.valueOf(gob.id))) {
                        Resource.Neg neg = gob.getneg();
                        if (neg == null) {
                            continue;
                        }
                        if ((neg.bs.x > 0) && (neg.bs.y > 0)) {
                            Coord c1 = gob.position().add(neg.bc);
                            Coord c2 = c1.add(neg.bs);
                            g.frect(m2s(c1).add(oc), m2s(new Coord(c2.x, c1.y))
                                    .add(oc), m2s(c2).add(oc),
                                    m2s(new Coord(c1.x, c2.y)).add(oc));
                        }
                    }
                }
            }
            g.chcolor();
        }

        if (ui.modctrl) {
            g.chcolor(20, 20, 250, 108);
            synchronized (glob.oc) {
                for (Gob gob : glob.oc) {
                    if (gob.id == 0) {
                        Resource.Neg neg = gob.getneg();
                        if (neg == null) {
                            continue;
                        }
                        if ((neg.bs.x > 0) && (neg.bs.y > 0)) {
                            Coord c1 = gob.position().add(neg.bc);
                            Coord c2 = c1.add(neg.bs);
                            g.frect(m2s(c1).add(oc), m2s(new Coord(c2.x, c1.y))
                                    .add(oc), m2s(c2).add(oc),
                                    m2s(new Coord(c1.x, c2.y)).add(oc));
                        }
                        break;
                    }
                }
            }
            g.chcolor();
        }

        if (Config.showHidden && Config.hide) {
            if (!Config.aimacrazyman)
                g.chcolor(Config.hideColor);
            synchronized (glob.oc) {
                for (Gob gob : glob.oc) {
                    if (Config.aimacrazyman) {
                        Random r = new Random();
                        g.chcolor(r.nextInt(255), r.nextInt(255), r.nextInt(255), r.nextInt(255));
                    }
                    Resource res = gob.getres();
                    if (!Config.hide_all) {
                        if (!gob.hide || ((res != null) && (res.skiphighlight)))
                            continue;
                    }
                    if (res.name != null && res.name.contains("gfx/arch/sign")) {
                        Coord c1 = gob.position();
                        Coord c2 = c1.add(new Coord(2, 2));
                        g.frect(m2s(c1).add(oc), m2s(new Coord(c2.x, c1.y))
                                .add(oc), m2s(c2).add(oc),
                                m2s(new Coord(c1.x, c2.y)).add(oc));
                    }
                    Resource.Neg neg = gob.getneg();
                    if (neg == null) {
                        continue;
                    }
                    if ((neg.bs.x > 0) && (neg.bs.y > 0)) {
                        Coord c1 = gob.position().add(neg.bc);
                        Coord c2 = c1.add(neg.bs);
                        g.frect(m2s(c1).add(oc), m2s(new Coord(c2.x, c1.y))
                                .add(oc), m2s(c2).add(oc),
                                m2s(new Coord(c1.x, c2.y)).add(oc));
                    }
                }
            }
            g.chcolor();
        }

        GobMapper drawer = new GobMapper();
        synchronized (glob.oc) {
            for (Gob gob : glob.oc) {
                Integer tmp /*lol*/ = Integer.valueOf(gob.id);
                if (ui.modctrl && gob.id == 0) continue;
                if (KerriUtils.iObj.contains(tmp)) continue;
                Resource res = gob.getres();
                if (res != null) {
                    if (res.name.contains("gfx/terobjs/clue")) {
                        //scent checker
                        LinkedList<Gob.Overlay> olays = (LinkedList<haven.Gob.Overlay>) gob.ols;
                        int scent = olays.get(0).sdt.uint16K();
                        if (KerriUtils.isAssault(scent) && Config.hideAsslt) continue;
                        if (KerriUtils.isTheft(scent) && Config.hideTheft) continue;
                        if (KerriUtils.isTresspass(scent) && Config.hideTressp) continue;
                        if (KerriUtils.isVandalism(scent) && Config.hideVand) continue;
                        if (KerriUtils.isBattery(scent) && Config.hideBatt) continue;
                        if (KerriUtils.isMurder(scent) && Config.hideMurd) continue;
                        //System.out.println(scent);
                    }
                }
                drawer.chcur(gob);
                Coord dc = m2s(gob.position()).add(oc);
                gob.sc = dc;
                gob.drawsetup(drawer, dc, sz);
                Speaking s = gob.getattr(Speaking.class);
                if (s != null)
                    speaking.add(s);
                KinInfo k = gob.getattr(KinInfo.class);
                if (k != null)
                    kin.add(k);
            }
            if (curf != null)
                curf.tick("setup");
            Collections.sort(sprites, Sprite.partidcmp);
            {
                Sprite.Part[] clickable = new Sprite.Part[sprites.size()];
                for (int o = 0, u = clickable.length - 1; o < clickable.length; o++, u--)
                    clickable[u] = sprites.get(o);
                this.clickable = clickable;
            }
            if (curf != null)
                curf.tick("sort");
            //Kerri
            if (pmousepos != null)
                objectUnderMouse = gobatpos(pmousepos);
            else
                objectUnderMouse = null;
            obscured = findobsc();
            if (curf != null)
                curf.tick("obsc");
            for (Sprite.Part part : sprites) {
                if (part.effect != null)
                    part.draw(part.effect.apply(g));
                else
                    part.draw(g);
            }
            for (Sprite.Part part : obscured) {
                GOut g2 = new GOut(g);
                GobHealth hlt;
                if ((part.owner != null)
                        && (part.owner instanceof Gob)
                        && ((hlt = ((Gob) part.owner).getattr(GobHealth.class)) != null))
                    g2.chcolor(255, (int) (hlt.asfloat() * 255), 0, 255);
                else
                    g2.chcolor(255, 255, 0, 255);
                part.drawol(g2);
            }

            if (curf != null)
                curf.tick("draw");
            g.image(mask, Coord.z);
            long now = System.currentTimeMillis();
            RootWidget.names_ready = (RootWidget.screenshot && Config.sshot_nonames);
            if (!RootWidget.names_ready) {
                for (KinInfo k : kin) {
                    Tex t = k.rendered();
                    Coord gc = k.gob.sc;
                    String name = k.gob.resname();
                    boolean isother = name.contains("hearth")
                            || name.contains("skeleton");
                    if (gc.isect(Coord.z, sz)) {
                        if (k.seen == 0)
                            k.seen = now;
                        int tm = (int) (now - k.seen);
                        Color show = null;
                        boolean auto = (k.type & 1) == 0;
                        if ((isother && Config.showOtherNames)
                                || (!isother && Config.showNames)
                                || (k.gob == objectUnderMouse)) {
                            show = Color.WHITE;
                        } else if (auto && (tm < 7500)) {
                            show = Utils.clipcol(255, 255, 255,
                                    255 - ((255 * tm) / 7500));
                        }
                        if (show != null) {
                            g.chcolor(show);
                            g.image(t, gc.add(-t.sz().x / 2, -40 - t.sz().y));
                            g.chcolor();
                        }
                    } else {
                        k.seen = 0;
                    }
                }
            }
            for (Speaking s : speaking) {
                s.draw(g, s.gob.sc.add(s.off));
            }
            if (curf != null) {
                curf.tick("aux");
                curf.fin();
                curf = null;
            }
            if (Config.show_gob_health) {
                for (Gob gob : glob.oc) {
                    GobHealth hlt = gob.getattr(GobHealth.class);
                    if (hlt != null) {
                        int percent = (int) (hlt.asfloat() * 100);
                        if (percent < 100) {
                            g.atext(Integer.toString(percent) + "%", gob.sc, 0,
                                    1);
                        }
                    }
                }
            }
        }
    }

    public void drawarrows(GOut g) {
        Coord oc = viewoffset(sz, mc);
        Coord hsz = sz.div(2); //half size
        double ca = -Coord.z.angle(hsz);
        for (Party.Member partyMember : glob.party.memb.values()) {
            // Gob gob = glob.oc.getgob(id);
            Coord memberCoords = partyMember.getc();
            if (memberCoords == null)
                continue;
            Coord sc = m2s(memberCoords).add(oc);
            if (!sc.isect(Coord.z, sz)) {
                double a = -hsz.angle(sc);
                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));
                }
                g.chcolor(partyMember.col);
                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);
                g.chcolor(Color.WHITE);
            }
        }
    }

    private void checkplmove() {
        Gob pl;
        long now = System.currentTimeMillis();
        if ((playergob >= 0) && ((pl = glob.oc.getgob(playergob)) != null)
                && (pl.sc != null)) {
            Coord plp = pl.position();
            if ((plfpos == null) || !plfpos.equals(plp)) {
                lastmove = now;
                plfpos = plp;
                if ((obscpart != null)
                        && !obscpart.checkhit(pl.sc.add(obscgob.sc.inv()))) {
                    obscpart = null;
                    obscgob = null;
                }
            } else if (now - lastmove > 500) {
                for (Sprite.Part p : clickable) {
                    Gob gob = (Gob) p.owner;
                    if ((gob == null) || (gob.sc == null))
                        continue;
                    if (gob == pl)
                        break;
                    if (p.checkhit(pl.sc.add(gob.sc.inv()))) {
                        obscpart = p;
                        obscgob = gob;
                        break;
                    }
                }
            }
        }
        player_moving = ((now - lastmove) < 400);
    }

    private void checkmappos() {
        if (cam == null)
            return;
        Coord sz = this.sz;
        SlenHud slen = ui.slenhud;
        if (slen != null)
            sz = sz.add(0, -slen.foldheight());
        Gob player = glob.oc.getgob(playergob);
        if (player != null)
            cam.setpos(this, player, sz);
    }

    //Kerri
    Coord lastWASD = new Coord(0, 0);

    public void update(long dt) {
        if (Config.use_wasd) {
            Coord newWASD = APXUtils.updateWASD();
            if (!newWASD.equals(lastWASD)) {
                lastWASD = newWASD;
                map_click_ntf(lastWASD.x, lastWASD.y, 1, 0);
            }
        }

        Gob pl = glob.oc.getgob(playergob);
        Coord myCurrentCoord = null;
        if (pl != null) {
            myCurrentCoord = pl.position();
            if (myCurrentCoord != null && myLastCoord != null)
                if (myCurrentCoord.dist(myLastCoord) > 30 * 11 && cam != null) {
                    cam.reset();
                    APXUtils.onBigJumpDetected(polowner);
                }
        }
        myLastCoord = myCurrentCoord;

        update_pf_moving();
    }

    //Kerri
    private void drawTileSelection(GOut g, Coord tc, Coord sc) {
        Coord c1 = sc;
        Coord c2 = sc.add(m2s(new Coord(0, tileSize.y)));
        Coord c3 = sc.add(m2s(new Coord(tileSize.x, tileSize.y)));
        Coord c4 = sc.add(m2s(new Coord(tileSize.x, 0)));
        Color cl = Color.red;
        g.chcolor(new Color(cl.getRed(), cl.getGreen(), cl.getBlue(), 32));
        g.frect(c1, c2, c3, c4);
        g.chcolor(cl);
        g.line(c2, c1, 2);
        g.line(c1.add(1, 0), c4.add(1, 0), 2);
        g.line(c4.add(1, 0), c3.add(1, 0), 2);
        g.line(c3, c2, 2);
        g.chcolor();
    }

    private void drawJSRect(GOut g, Coord sc, Coord size) {
        Coord c1 = sc;
        Coord c2 = sc.add(m2s(new Coord(0, tileSize.y * size.y)));
        Coord c3 = sc.add(m2s(new Coord(tileSize.x * size.x, tileSize.y * size.y)));
        Coord c4 = sc.add(m2s(new Coord(tileSize.x * size.x, 0)));
        Color cl = Color.cyan;
        g.chcolor(new Color(cl.getRed(), cl.getGreen(), cl.getBlue(), 32));
        g.frect(c1, c2, c3, c4);
        g.chcolor(cl);
        g.line(c2, c1, 2);
        g.line(c1.add(1, 0), c4.add(1, 0), 2);
        g.line(c4.add(1, 0), c3.add(1, 0), 2);
        g.line(c3, c2, 2);
        g.chcolor();
    }

    public void drawQuad(GOut g, Coord sc, Color c) {
        Coord c1 = sc;
        Coord c2 = sc.add(m2s(new Coord(0, tileSize.y)));
        Coord c3 = sc.add(m2s(new Coord(tileSize.x, tileSize.y)));
        Coord c4 = sc.add(m2s(new Coord(tileSize.x, 0)));
        g.chcolor(c);
        g.frect(c1, c2, c3, c4);
        g.chcolor();
    }

    public void draw(GOut og) {
        if (!Config.render_enable) {
            Coord requl = mc.add(-500, -500).div(tileSize).div(cmaps);
            Coord reqbr = mc.add(500, 500).div(tileSize).div(cmaps);
            Coord cgc = new Coord(0, 0);
            for (cgc.y = requl.y; cgc.y <= reqbr.y; cgc.y++) {
                for (cgc.x = requl.x; cgc.x <= reqbr.x; cgc.x++) {
                    if (map.grids.get(cgc) == null)
                        map.request(new Coord(cgc));
                }
            }
            map.sendreqs();
            checkplmove();
            return;
        }
        if (moveto != null) {
            wdgmsg("click", moveto, moveto, 1, 0);
            moveto = null;
        }
        hsz = MainFrame.getInnerSize();
        sz = hsz.mul(1 / getScale());
        GOut g = og.reclip(Coord.z, sz);
        g.gl.glPushMatrix();
        g.scale(getScale());
        checkmappos();
        Coord requl = mc.add(-500, -500).div(tileSize).div(cmaps);
        Coord reqbr = mc.add(500, 500).div(tileSize).div(cmaps);
        Coord cgc = new Coord(0, 0);
        for (cgc.y = requl.y; cgc.y <= reqbr.y; cgc.y++) {
            for (cgc.x = requl.x; cgc.x <= reqbr.x; cgc.x++) {
                if (map.grids.get(cgc) == null)
                    map.request(new Coord(cgc));
            }
        }
        long now = System.currentTimeMillis();
        if ((olftimer != 0) && (olftimer < now))
            unflashol();
        map.sendreqs();
        checkplmove();
        // try {
        if (((mask.amb = glob.amblight) == null) || Config.nightvision)
            mask.amb = new Color(0, 0, 0, 0);
        drawmap(g);

        // movement highlight
        try {
            if (Config.showpath) {
                Coord oc = viewoffset(sz, mc);
                Coord pc, cc;
                synchronized (glob.oc) {
                    for (Gob gob : glob.oc) {
                        if (gob == null) continue;
                        if (gob.sc == null) continue;
                        if (!(gob.isPlayer() || gob.resname().contains("boat"))) continue;
                        Moving move_attr = gob.getattr(Moving.class);
                        if (move_attr == null) continue;
                        if (move_attr instanceof LinMove) {
                            LinMove lin_move = (LinMove) move_attr;
                            pc = m2s(lin_move.t).add(oc);
                            if (gob.id == playergob)
                                g.chcolor(Color.GREEN);
                            else
                                g.chcolor(Color.ORANGE);
                            g.line(gob.sc, pc, 2);
                            if (gob.id == playergob) {
                                for (Coord c : glob.oc.movequeue) {
                                    cc = m2s(c).add(oc);
                                    g.line(pc, cc, 2);
                                    pc = cc;
                                }
                            }
                        }
                    }
                }
                g.chcolor();
            }
        } catch (Exception ignored) {

        }
        // ###############
        // PF
        if (path != null && path.size() > 1) {
            Coord oc = viewoffset(sz, mc);
            g.chcolor(Color.green);
            for (int i = 0; i < path.size() - 1; i++) {
                g.line(m2s(path.get(i)).add(oc), m2s(path.get(i + 1)).add(oc), 2);
            }
            g.chcolor();
        }
        drawarrows(g);
        g.chcolor(Color.WHITE);

        // } catch(Loading l) {
        // String text = "Loading...";
        // g.chcolor(Color.BLACK);
        // g.frect(Coord.z, sz);
        // g.chcolor(Color.WHITE);
        // g.atext(text, sz, 0.5, 0.5);
        // }
        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();
        }
        g.gl.glPopMatrix();
        //Kerri. debug
        if (Config.showDebug) {
            int ay = 120;
            int margin = 15;
            g.atext("FPS: " + MainFrame.havenPanel.fps, new Coord(10, ay), 0, 1);
            ay = ay + margin;
            if (objectUnderMouse != null) {
                g.atext("Object id under mouse: " + objectUnderMouse.id + " Coords: "
                        + objectUnderMouse.position(), new Coord(10, ay), 0, 1);
                ay = ay + margin;
                g.atext("Resource name: " + objectUnderMouse.resname() +
                        " BLOB: " + objectUnderMouse.GetBlob(0), new Coord(10, ay), 0, 1);
                ay = ay + margin;
            } else {
                g.atext("Object id under mouse: NULL", new Coord(10, ay), 0, 1);
                ay = ay + margin;
            }
            if (mousepos != null) {
                g.atext("Mouse map pos: " + mousepos.toString() + " Tile: " + getTileFix(mousepos.div(tileSize)) + "TilifNS: " + tilefy_ns(mousepos).toString(), new Coord(10, ay), 0, 1);
                ay = ay + margin;
                g.atext("Tile coord: " + mouseAtTile.toString(), new Coord(10, ay), 0, 1);
                ay = ay + margin;
            }
            g.atext("Current cursor: " + UI.instance.root.cursor.name.replace("gfx/hud/curs/", ""),
                    new Coord(10, ay), 0, 1);
            ay = ay + margin;
            g.atext("Player id: " + playergob, new Coord(10, ay), 0, 1);
        }//debug
        if (Config.dbtext)
            g.atext(mc.toString(), new Coord(10, 560), 0, 1);
        super.draw(og);
    }

    public boolean drop(Coord cc, Coord ul) {
        wdgmsg("drop", ui.modflags());
        return (true);
    }

    public boolean iteminteract(Coord cc, Coord ul) {
        Coord cc0 = cc;
        cc = new Coord((int) (cc.x / getScale()), (int) (cc.y / getScale()));
        Gob hit = gobatpos(cc);
        Coord mc = s2m(cc.add(viewoffset(sz, this.mc).inv()));
        if (Config.assign_to_tile) mc = tilify(mc);
        if (hit == null)
            wdgmsg("itemact", cc0, mc, ui.modflags());
        else
            wdgmsg("itemact", cc0, mc, ui.modflags(), hit.id, hit.position());
        return (true);
    }

    private Map<String, Console.Command> cmdmap = new TreeMap<String, Console.Command>();

    {
        cmdmap.put("cam", new Console.Command() {
            public void run(Console cons, String[] args) {
                if (args.length >= 2) {
                    Class<? extends Camera> ct = camtypes.get(args[1]);
                    String[] cargs = new String[args.length - 2];
                    System.arraycopy(args, 2, cargs, 0, cargs.length);
                    if (ct != null) {
                        try {
                            MapView.this.cam = makecam(ct, cargs);
                            Utils.setpref("defcam", args[1]);
                            Utils.setprefb("camargs", Utils.serialize(cargs));
                        } catch (ClassNotFoundException e) {
                            throw (new RuntimeException("no such camera: "
                                    + args[1]));
                        }
                    } else {
                        throw (new RuntimeException("no such camera: "
                                + args[1]));
                    }
                }
            }
        });
        cmdmap.put("plol", new Console.Command() {
            public void run(Console cons, String[] args) {
                Indir<Resource> res = Resource.load(args[1]).indir();
                Message sdt;
                if (args.length > 2)
                    sdt = new Message(0, Utils.hex2byte(args[2]));
                else
                    sdt = new Message(0);
                Gob pl;
                if ((playergob >= 0)
                        && ((pl = glob.oc.getgob(playergob)) != null))
                    pl.ols.add(new Gob.Overlay(-1, res, sdt));
            }
        });
    }

    public Map<String, Console.Command> findcmds() {
        return (cmdmap);
    }

    ArrayList<Coord> path;
    int path_step = 0;
    boolean path_moving = false;
    Gob path_interact_object = null;
    boolean draw_pf_map = false;

    public void toggle_draw_pf() {
        if (!draw_pf_map) {
            draw_pf_map = true;
            APXUtils._pf_compute(0);
        } else {
            draw_pf_map = false;
        }
    }

    public int map_pf_move(Coord real_coord) {
        if (path != null) {
            path.clear();
            path_moving = false;
            path_step = 0;
            path_interact_object = null;
        }
        path = APXUtils._pf_find_path(real_coord, 0);
        update_pf_moving();
        return path == null ? -1 : path.size();
    }

    public int map_pf_interact(int id) {
        if (path != null) {
            path.clear();
            path_moving = false;
            path_step = 0;
            path_interact_object = null;
        }
        Gob pgob;
        synchronized (glob.oc) {
            pgob = glob.oc.getgob(id);
        }
        if (pgob == null)
            return -1;
        path_interact_object = pgob;
        path = APXUtils._pf_find_path(pgob.position(), id);
        update_pf_moving();
        return path == null ? -1 : path.size();
    }

    public void update_pf_moving() {
        if (path == null)
            return;
        if (JSBotUtils.isMoving()) return;

        if (path_step < path.size()) {
            if (myLastCoord.equals(path.get(path_step))) {
                path_moving = false;
                path_step++;
            }
        }
        if (path_moving) return;
        if (path_step < path.size()) {
            if (path.size() - path_step == 1 && path_interact_object != null) {
                //click, (261, 284), (-4062515, 1117484), 3, 0, -1725142871, (-4062515, 1117495)
                wdgmsg("click", JSBotUtils.getCenterScreenCoord(), path_interact_object.position(), 3, 0, path_interact_object.id, path_interact_object.position());
                path_step++;
            } else {
                path_moving = true;
                Coord togo = path.get(path_step);
                wdgmsg("click", JSBotUtils.getCenterScreenCoord(), tilify(togo), 1, 0);
            }
        } else {
            path = null;
            path_step = 0;
            path_moving = false;
        }
    }