/******************************************************************************* * Rhythos Editor is a game editor and project management tool for making RPGs on top of the Rhythos Game system. * * Copyright (C) 2013 David Maletz * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package mrpg.display; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JPanel; import javax.swing.Scrollable; import javax.swing.Timer; import mrpg.editor.MapEditor; import mrpg.editor.TilesetViewer; import mrpg.world.Cell; import mrpg.world.Tile; import mrpg.world.World; public class WorldPanel extends JPanel implements ActionListener, Scrollable { private static final long serialVersionUID = 6141752899325918664L; private final Rectangle rect = new Rectangle(); public Overlay overlay = null; private World world = null; public int left = 0, top = 0, bgleft = 0, bgtop = 0; private int width, height; private int frame_num = 0; private Timer timer; private boolean show_grid = false; private double scale = 1; private int edit_level = 0, show_level = -1; private static final Color transparent = new Color(0, 0, 0, 80); public int tile_size = TilesetViewer.TILE_SIZE; public boolean animate = false; public WorldPanel(int _width, int _height){worldSize(_width, _height); timer = new Timer(83, this);} public void startAnim(){timer.start();} public void worldSize(int _width, int _height){ width = _width; height = _height; Dimension size = new Dimension(width,height); if(show_grid){size.width += 1; size.height += 1;} size.width *= scale; size.height *= scale; setPreferredSize(size); setMinimumSize(size); revalidate(); repaint(); } public void setScale(double s){if(scale != s){scale = s; worldSize(width, height);}} public double getScale(){return scale;} public int getEditLevel(){return edit_level;} public void setEditLevel(int level){edit_level = level;} public void showLevel(int level){if(level != show_level){show_level = level; repaint();}} public void setWorld(World w, int ts){world = w; if(ts != 0) tile_size = ts; repaint();} public World getWorld(){return world;} public void setShowGrid(boolean b){show_grid = b; worldSize(width, height);} public void paint(Graphics g){ Graphics gold=g; if(scale != 1){ g = g.create(); ((Graphics2D)g).scale(scale, scale); } g.getClipBounds(rect); if(rect.x+rect.width > width) rect.width = width-rect.x; if(rect.y+rect.height > height) rect.height = height-rect.y; g.clearRect(rect.x, rect.y, rect.width, rect.height); if(world == null) return; if(!world.wrapX){left = Math.min(world.getWidth()*tile_size-width, Math.max(0, left));} if(!world.wrapY){top = Math.min(world.getHeight()*tile_size-height, Math.max(0, top));} if(world.background != null) renderBG(g); renderCells(g); if(show_grid){ gold.setColor(Color.black); int rw = rect.x+rect.width, rh = rect.y+rect.height; for(int y=(int)Math.ceil(rect.y*1.0/tile_size)*tile_size; y<=rh; y+=tile_size){ int y2 = (int)Math.floor(y*scale); gold.drawLine((int)Math.floor(rect.x*scale), y2, (int)Math.ceil((rw-1)*scale), y2); } for(int x=(int)Math.ceil(rect.x*1.0/tile_size)*tile_size; x<=rw; x+=tile_size){ int x2 = (int)Math.floor(x*scale); gold.drawLine(x2, (int)Math.floor(rect.y*scale), x2, (int)Math.ceil(rh*scale)); } } if(overlay != null) overlay.paintOverlay(g, rect, gold, scale); } private void lowerLevel(Graphics g, int dx, int dy, int w, int h){ Color old = g.getColor(); g.setColor(transparent); g.fillRect(dx, dy, w, h); g.setColor(old); } private final void renderCell(Graphics g, int x, int y, int dx, int dy, int sx, int sy, int w, int h){ Cell c = world.getCell(x,y); if(c == null || !c.hasTiles()){ if(show_level >= 0) lowerLevel(g, dx, dy, w, h); return; } Composite com = null; if(show_level >= 0) com = ((Graphics2D)g).getComposite(); int level = 0; for(Tile tile : c){ if(level == show_level) lowerLevel(g, dx, dy, w, h); tile.paint(g, frame_num, dx, dy, sx, sy, w, h, this); if(level == show_level) ((Graphics2D)g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); level++; } if(com != null) ((Graphics2D)g).setComposite(com); if(level <= show_level) lowerLevel(g, dx, dy, w, h); } private final void renderCellClamped(Graphics g, int x, int y, int dx, int dy, int sx, int sy, int w, int h){ w = Math.min(rect.x+rect.width-dx, w); if(w <= 0) return; h = Math.min(rect.y+rect.height-dy, h); if(h <= 0) return; renderCell(g, x, y, dx, dy, sx, sy, w, h); } private final void renderCells(Graphics g){ int stx = (int)Math.floor(((double)(rect.x+left))/tile_size), sty = (int)Math.floor(((double)(rect.y+top))/tile_size), endx = (int)Math.floor(((double)(rect.x+rect.width+left))/tile_size), endy = (int)Math.floor(((double)(rect.y+rect.height+top))/tile_size); int dl = rect.x-(stx*tile_size-left); int dr = rect.x+rect.width-(endx*tile_size-left); int dt = rect.y-(sty*tile_size-top); int db = rect.y+rect.height-(endy*tile_size-top); renderCellClamped(g, stx, sty, rect.x, rect.y, dl, dt, tile_size-dl, tile_size-dt); for(int x=stx+1; x<endx; x++) renderCellClamped(g, x, sty, x*tile_size-left, rect.y, 0, dt, tile_size, tile_size-dt); if(stx != endx) renderCellClamped(g, endx, sty, rect.x+rect.width-dr, rect.y, 0, dt, dr, tile_size-dt); for(int y=sty+1; y<endy; y++){ renderCellClamped(g, stx, y, rect.x, y*tile_size-top, dl, 0, tile_size-dl, tile_size); for(int x=stx+1; x<endx; x++) renderCell(g, x, y, x*tile_size-left, y*tile_size-top, 0, 0, tile_size, tile_size); if(stx != endx) renderCellClamped(g, endx, y, rect.x+rect.width-dr, y*tile_size-top, 0, 0, dr, tile_size); } if(sty != endy){ renderCellClamped(g, stx, endy, rect.x, rect.y+rect.height-db, dl, 0, tile_size-dl, db); for(int x=stx+1; x<endx; x++) renderCellClamped(g, x, endy, x*tile_size-left, rect.y+rect.height-db, 0, 0, tile_size, db); if(stx != endx) renderCellClamped(g, endx, endy, rect.x+rect.width-dr, rect.y+rect.height-db, 0, 0, dr, db); } } private final void renderBGClamped(Graphics g, int x, int y, int dx, int dy, int sx, int sy, int w, int h){ w = Math.min(rect.x+rect.width-dx, w); if(w <= 0) return; h = Math.min(rect.y+rect.height-dy, h); if(h <= 0) return; g.drawImage(world.background, dx, dy, dx+w, dy+h, sx, sy, sx+w, sy+h, this); } private final void renderBG(Graphics g, int x, int y, int dx, int dy, int sx, int sy, int w, int h){ g.drawImage(world.background, dx, dy, dx+w, dy+h, sx, sy, sx+w, sy+h, this); } private final void renderBG(Graphics g){ int ww = world.background.getWidth(this), wh = world.background.getHeight(this); int stx = (int)Math.floor(((double)(rect.x+bgleft))/ww), sty = (int)Math.floor(((double)(rect.y+bgtop))/wh), endx = (int)Math.floor(((double)(rect.x+rect.width+bgleft))/ww), endy = (int)Math.floor(((double)(rect.y+rect.height+bgtop))/wh); int dl = rect.x-(stx*ww-bgleft); int dr = rect.x+rect.width-(endx*ww-bgleft); int dt = rect.y-(sty*wh-bgtop); int db = rect.y+rect.height-(endy*wh-bgtop); renderBGClamped(g, stx, sty, rect.x, rect.y, dl, dt, ww-dl, wh-dt); for(int x=stx+1; x<endx; x++) renderBGClamped(g, x, sty, x*ww-bgleft, rect.y, 0, dt, ww, wh-dt); if(stx != endx) renderBGClamped(g, endx, sty, rect.x+rect.width-dr, rect.y, 0, dt, dr, wh-dt); for(int y=sty+1; y<endy; y++){ renderBGClamped(g, stx, y, rect.x, y*wh-bgtop, dl, 0, ww-dl, wh); for(int x=stx+1; x<endx; x++) renderBG(g, x, y, x*ww-bgleft, y*wh-bgtop, 0, 0, ww, wh); if(stx != endx) renderBGClamped(g, endx, y, rect.x+rect.width-dr, y*wh-bgtop, 0, 0, dr, wh); } if(sty != endy){ renderBGClamped(g, stx, endy, rect.x, rect.y+rect.height-db, dl, 0, ww-dl, db); for(int x=stx+1; x<endx; x++) renderBGClamped(g, x, endy, x*ww-bgleft, rect.y+rect.height-db, 0, 0, ww, db); if(stx != endx) renderBGClamped(g, endx, endy, rect.x+rect.width-dr, rect.y+rect.height-db, 0, 0, dr, db); } } public void actionPerformed(ActionEvent e) { if(isDisplayable() && animate){ frame_num++; if(world != null) repaint(); } if(MapEditor.instance == null) timer.stop(); } public int getFrame(){return frame_num;} public int getScrollableUnitIncrement(Rectangle r, int o, int d){return (int)Math.floor(tile_size*scale);} public int getScrollableBlockIncrement(Rectangle r, int o, int d){return (int)Math.floor(tile_size*scale);} public Dimension getPreferredScrollableViewportSize(){return getPreferredSize();} public boolean getScrollableTracksViewportWidth(){return false;} public boolean getScrollableTracksViewportHeight(){return false;} }