package ch.sahits.game.graphic.display.gameplay;
import java.awt.Color;
import java.awt.FontFormatException;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.font.GlyphVector;
import java.awt.image.BufferedImage;
import java.io.IOException;
import ch.sahits.game.event.EViewChangeEvent;
import ch.sahits.game.event.Event;
import ch.sahits.game.event.IEventListener;
import ch.sahits.game.event.KeyPressEvent;
import ch.sahits.game.event.MouseClickEvent;
import ch.sahits.game.event.ViewChangeEvent;
import ch.sahits.game.graphic.display.model.CityPlayerProxy;
import ch.sahits.game.graphic.display.util.ClickablePolygons;
import ch.sahits.game.graphic.display.util.NamedPolygon;
import ch.sahits.game.graphic.image.ImagesLoader;
import ch.sahits.game.graphic.image.OpenPatricianPainter;
import ch.sahits.game.image.ImageUtil;
import ch.sahits.game.openpatrician.model.city.ICity;
import ch.sahits.game.openpatrician.model.product.AmountablePrice;
import ch.sahits.game.openpatrician.model.product.EWare;
import ch.sahits.game.openpatrician.model.ship.IShip;
import ch.sahits.game.rendering.RenderablePart;
/**
* Trade dialog for buying/selling from/to the city involving either a ship or the branch office.
* @author Andi Hotz, (c) Sahits GmbH, 2011
* Created on Nov 18, 2011
*
*/
public class TradeDialog extends OpenPatricianGameDialog implements
RenderablePart, IEventListener {
private boolean enabled = true;
/** Reference to the city view model */
private final CityPlayerProxy city;
/** The amount that is moved with one click */
private ETransferAmount movableAmount = ETransferAmount.ONE;
private ClickablePolygons polygons;
private ClickablePolygons footerPolygons;
/** Hold all the positiononing information, initialized in initBackgroundImage */
private DialogPositions positions;
public TradeDialog(Point topLeft, ImagesLoader loader,double scale, CityPlayerProxy CityPlayerProxy) {
super(topLeft, loader,scale);
city = CityPlayerProxy;
Event.add(this);
}
@Override
protected void initiatePolygons() {
polygons = new ClickablePolygons();
footerPolygons = new ClickablePolygons();
}
/**
* Retrieve the amount that is to be transferred. This method does
* not consider how much is available.
* @param maxMount
* @return
*/
private int getAmount(int maxMount){
switch (movableAmount){
case ONE:
return 1;
case FIVE:
return 5;
case MAX:
return maxMount;
default:
return 1;
}
}
@Override
public void gameUpdate(Event e, Object notice) {
if (e instanceof KeyPressEvent){
KeyEvent key= ((KeyPressEvent)e).getKey();
if (key.getKeyCode()==KeyEvent.VK_ESCAPE){
new ViewChangeEvent(MainView.class).notify(EViewChangeEvent.CLOSE_DIALOG);
Event.remove(this);
}
}
if (e instanceof MouseClickEvent){
polygons.testAndExecute((Point) notice);
footerPolygons.testAndExecute((Point) notice);
}
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public void setEnabled(boolean flag) {
enabled=flag;
}
@Override
protected BufferedImage initBackgroundImage(ImagesLoader loader,Point topLeft) {
BufferedImage bg = super.initBackgroundImage(loader,topLeft);
Graphics2D g2d = bg.createGraphics();
g2d.setColor(Color.BLACK);
try {
GlyphVector gv = OpenPatricianPainter.createGlyphVector(g2d, "Trade goods", 24); // TODO externalize
double titleHeight = gv.getVisualBounds().getHeight();
int length = (int) Math.rint(gv.getVisualBounds().getWidth());
int x = ((bg.getWidth()-getInset().left-getInset().right)-length)/2;
int y = getInset().top+10+(int)Math.rint(titleHeight*2/3);
g2d.drawGlyphVector(gv, x, y);
// Dynamic row
y += titleHeight;
y += (int)Math.rint(titleHeight*2/3);
BufferedImage waxSeal = loader.getImage("waxseal");
int leftBorderX = topLeft.x+getInset().left;
int xWare = leftBorderX+100; // right aligned
int xCity = leftBorderX+110;
int xBuy = leftBorderX+160; // size of the image + 10
int xSell = leftBorderX+210;// size of the image + 10
int xShip = leftBorderX+260;
int xPrice = leftBorderX+310;
int yLineDiff = waxSeal.getHeight();
// Initialize the position relative to the frame
positions = new DialogPositions(titleHeight, yLineDiff, leftBorderX, xWare, xCity, xBuy, xSell, xShip, xPrice);
leftBorderX = getInset().left; // Use local coordinates
xWare = leftBorderX+100; // right aligned
xCity = leftBorderX+110;
xBuy = leftBorderX+160; // size of the image + 10
xSell = leftBorderX+210;// size of the image + 10
xShip = leftBorderX+260;
xPrice = leftBorderX+310;
// draw the table header
gv = OpenPatricianPainter.createGlyphVector(g2d, "Ware", 19); // TODO externalize
x = xWare-(int)Math.rint(gv.getVisualBounds().getWidth());
y += positions.lineHeight; // more space to the title
y += positions.lineHeight;
g2d.drawGlyphVector(gv, x, y);
gv = OpenPatricianPainter.createGlyphVector(g2d, "City", 19);
g2d.drawGlyphVector(gv, xCity, y);
gv = OpenPatricianPainter.createGlyphVector(g2d, "Buy", 19);
g2d.drawGlyphVector(gv, xBuy, y);
gv = OpenPatricianPainter.createGlyphVector(g2d, "Sell", 19);
g2d.drawGlyphVector(gv, xSell, y);
gv = OpenPatricianPainter.createGlyphVector(g2d, "Ship", 19); // TODO differenciate between ship and convoi
g2d.drawGlyphVector(gv, xShip, y);
gv = OpenPatricianPainter.createGlyphVector(g2d, Character.toChars(0x00D8)[0]+"-Price", 19); // TODO do better
g2d.drawGlyphVector(gv, xPrice, y);
// Table
EWare[] wares = EWare.values();
for (EWare ware : wares) {
// Ware name
y += positions.lineHeight;
gv = OpenPatricianPainter.createGlyphVector(g2d, ware.getLocalDisplayName(), 18);
x = xWare-(int)Math.rint(gv.getVisualBounds().getWidth());
g2d.drawGlyphVector(gv, x, y);
// Amount available
// Buy button
g2d.drawImage(waxSeal, xBuy,y-(int)(positions.lineHeight*0.8), null);
NamedPolygon polygon = new NamedPolygon(ware.name()+"Buy");
// The positions of the polygons must be global
polygon.addPoint(positions.xBuy, topLeft.y+y-(int)(positions.lineHeight*0.8));
polygon.addPoint(positions.xBuy+waxSeal.getWidth(), topLeft.y+y-(int)(positions.lineHeight*0.8));
polygon.addPoint(positions.xBuy+waxSeal.getWidth(), topLeft.y+y-(int)(positions.lineHeight*0.8)+waxSeal.getHeight());
polygon.addPoint(positions.xBuy, topLeft.y+y-(int)(positions.lineHeight*0.8)+waxSeal.getHeight());
polygons.add(polygon, new City2ShipAction(ware));
// Sell button
g2d.drawImage(waxSeal, xSell,y-(int)(positions.lineHeight*0.8), null);
polygon = new NamedPolygon(ware.name()+"Sell");
polygon.addPoint(positions.xSell, topLeft.y+y-(int)(positions.lineHeight*0.8));
polygon.addPoint(positions.xSell+waxSeal.getWidth(), topLeft.y+y-(int)(positions.lineHeight*0.8));
polygon.addPoint(positions.xSell+waxSeal.getWidth(), topLeft.y+y-(int)(positions.lineHeight*0.8)+waxSeal.getHeight());
polygon.addPoint(positions.xSell, topLeft.y+y-(int)(positions.lineHeight*0.8)+waxSeal.getHeight());
polygons.add(polygon, new Ship2CityAction(ware));
}
// Footer buttons
y = bg.getHeight()-getInset().bottom-positions.lineHeight;
x = getInset().left+30;
int footerWidth = bg.getWidth()-getInset().left-getInset().right-2*30;
// 1 item
g2d.setColor(new Color(0xEA,0xC1,0x17)); // Gold
NamedPolygon polygon;
g2d.drawImage(waxSeal, x,y-(int)(positions.lineHeight*0.8), null);
polygon = new NamedPolygon("One");
polygon.addPoint(topLeft.x+x, topLeft.y+y-(int)(positions.lineHeight*0.8));
polygon.addPoint(topLeft.x+x+waxSeal.getWidth(), topLeft.y+y-(int)(positions.lineHeight*0.8));
polygon.addPoint(topLeft.x+x+waxSeal.getWidth(), topLeft.y+y-(int)(positions.lineHeight*0.8)+waxSeal.getHeight());
polygon.addPoint(topLeft.x+x, topLeft.y+y-(int)(positions.lineHeight*0.8)+waxSeal.getHeight());
footerPolygons.add(polygon, new TransferAmountChangeAction(ETransferAmount.ONE));
gv = OpenPatricianPainter.createGlyphVector(g2d, "1", 18);
int xPadding = ImageUtil.computeCenterAlignX(x, waxSeal.getWidth(), (int)gv.getVisualBounds().getWidth());
g2d.drawGlyphVector(gv, xPadding, y);
x += footerWidth/4;
// 5 items
g2d.drawImage(waxSeal, x,y-(int)(positions.lineHeight*0.8), null);
polygon = new NamedPolygon("five");
polygon.addPoint(topLeft.x+x, topLeft.y+y-(int)(positions.lineHeight*0.8));
polygon.addPoint(topLeft.x+x+waxSeal.getWidth(), topLeft.y+y-(int)(positions.lineHeight*0.8));
polygon.addPoint(topLeft.x+x+waxSeal.getWidth(), topLeft.y+y-(int)(positions.lineHeight*0.8)+waxSeal.getHeight());
polygon.addPoint(topLeft.x+x, topLeft.y+y-(int)(positions.lineHeight*0.8)+waxSeal.getHeight());
footerPolygons.add(polygon, new TransferAmountChangeAction(ETransferAmount.FIVE));
gv = OpenPatricianPainter.createGlyphVector(g2d, "5", 18);
xPadding = ImageUtil.computeCenterAlignX(x, waxSeal.getWidth(), (int)gv.getVisualBounds().getWidth());
g2d.drawGlyphVector(gv, xPadding, y); // centeralign
x += footerWidth/4;
// Max item
g2d.drawImage(waxSeal, x,y-(int)(positions.lineHeight*0.8), null);
polygon = new NamedPolygon("Max");
polygon.addPoint(topLeft.x+x, topLeft.y+y-(int)(positions.lineHeight*0.8));
polygon.addPoint(topLeft.x+x+waxSeal.getWidth(), topLeft.y+y-(int)(positions.lineHeight*0.8));
polygon.addPoint(topLeft.x+x+waxSeal.getWidth(), topLeft.y+y-(int)(positions.lineHeight*0.8)+waxSeal.getHeight());
polygon.addPoint(topLeft.x+x, topLeft.y+y-(int)(positions.lineHeight*0.8)+waxSeal.getHeight());
footerPolygons.add(polygon, new TransferAmountChangeAction(ETransferAmount.MAX));
gv = OpenPatricianPainter.createGlyphVector(g2d, "Max", 18); //externalize
xPadding = ImageUtil.computeCenterAlignX(x, waxSeal.getWidth(), (int)gv.getVisualBounds().getWidth());
g2d.drawGlyphVector(gv, xPadding, y); // centeralign
x += footerWidth/4;
// close
g2d.drawImage(waxSeal, x,y-(int)(positions.lineHeight*0.8), null);
polygon = new NamedPolygon("Close");
polygon.addPoint(topLeft.x+x, topLeft.y+y-(int)(positions.lineHeight*0.8));
polygon.addPoint(topLeft.x+x+waxSeal.getWidth(), topLeft.y+y-(int)(positions.lineHeight*0.8));
polygon.addPoint(topLeft.x+x+waxSeal.getWidth(), topLeft.y+y-(int)(positions.lineHeight*0.8)+waxSeal.getHeight());
polygon.addPoint(topLeft.x+x, topLeft.y+y-(int)(positions.lineHeight*0.8)+waxSeal.getHeight());
footerPolygons.add(polygon, new CloseAction(this));
gv = OpenPatricianPainter.createGlyphVector(g2d, "X", 18);
xPadding = ImageUtil.computeCenterAlignX(x, waxSeal.getWidth(), (int)gv.getVisualBounds().getWidth());
g2d.drawGlyphVector(gv, xPadding, y); // centeralign
} catch (FontFormatException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
return bg;
}
@Override
public void gameRender(Graphics gScr) {
Color oldColor = gScr.getColor();
super.gameRender(gScr);
gScr.setColor(Color.BLACK);
Graphics2D g2d = (Graphics2D) gScr;
try {
// Dialog title
int y = getBounds().y+getInset().top+10+(int)Math.rint(positions.titleHeigth*2/3);
// Sub title (dynamic)
// City name
y += positions.titleHeigth;
int leftBorderX = getBounds().x+getInset().left;
int x = leftBorderX + 20;
ICity cityModel = city.getCity();
GlyphVector gv = OpenPatricianPainter.createGlyphVector(g2d, cityModel.getName()+" "+cityModel.getPopulation(), 18);
y += (int)Math.rint(positions.titleHeigth*2/3);
g2d.drawGlyphVector(gv, x, y);
// Barrel icon
x += (int)Math.rint(gv.getVisualBounds().getWidth())+50;
BufferedImage barrel = loader.getImage("barrel_icon");
BufferedImage bale = loader.getImage("bale_icon");
g2d.drawImage(barrel, x, y-(int)Math.rint(positions.titleHeigth), null); // Is y correct ???
// loaded on ship and ship capacity
x += 50;
IShip ship = city.getActiveShip();
StringBuilder sb = new StringBuilder();
sb.append(ship.getLoad()).append(" / ").append(ship.getSize()).append(" ").append(ship.getName());
gv = OpenPatricianPainter.createGlyphVector(g2d, sb.toString(), 18);
g2d.drawGlyphVector(gv, x, y); // TODO this is a dynamic line
BufferedImage waxSeal = loader.getImage("waxseal");
// Table header
y += positions.lineHeight; // more space to the title
y += positions.lineHeight;
// Table
EWare[] wares = EWare.values();
for (EWare ware : wares) {
// Ware name
y += positions.lineHeight;
// Available amount in the city
AmountablePrice<EWare> wareInCity = cityModel.getWare(ware);
int availableAmount = wareInCity.getAmount();
int value = availableAmount; // amount available
if (value>0){
gv = OpenPatricianPainter.createGlyphVector(g2d, String.valueOf(value), 18);
g2d.drawGlyphVector(gv, positions.xCity, y);
// draw barrel or burden
if (ware.getSizeAsBarrels()==1){
// barrel
g2d.drawImage(barrel, (int)(positions.xCity+gv.getVisualBounds().getWidth())+5, y-positions.lineHeight*2/3, null);
} else {
g2d.drawImage(bale, (int)(positions.xCity+gv.getVisualBounds().getWidth())+5, y-positions.lineHeight*2/3, null);
}
}
g2d.setColor(new Color(0xEA,0xC1,0x17)); // Gold
// buy
if (availableAmount>0){
value = ware.computeBuyPrice(availableAmount, getAmount(availableAmount));
} else {
value = 0; // cannot buy anything if nothing is there
}
gv = OpenPatricianPainter.createGlyphVector(g2d, String.valueOf(value), 18);
int xPadding = ImageUtil.computeCenterAlignX(positions.xBuy, waxSeal.getWidth(), (int)gv.getVisualBounds().getWidth());
g2d.drawGlyphVector(gv, xPadding, y);
// sell
if (movableAmount==ETransferAmount.MAX){
value = ware.getMaxValueSell();
} else {
value = ware.computeSellPrice(availableAmount, getAmount(availableAmount));
}
gv = OpenPatricianPainter.createGlyphVector(g2d, String.valueOf(value), 18);
xPadding = ImageUtil.computeCenterAlignX(positions.xSell, waxSeal.getWidth(), (int)gv.getVisualBounds().getWidth());
g2d.drawGlyphVector(gv, xPadding, y);
g2d.setColor(Color.BLACK);
// amount loaded
value = city.getActiveShip().getWare(ware).getAmount(); // differ between ship and convoi
if (value>0){
gv = OpenPatricianPainter.createGlyphVector(g2d, String.valueOf(value), 18);
g2d.drawGlyphVector(gv, positions.xShip, y); // this value is dynamic
// draw barrel or burden
}
if (ship.getWare(ware).getAmount()>0){
value = ship.getWare(ware).getAVGPrice();
gv = OpenPatricianPainter.createGlyphVector(g2d, String.valueOf(value), 18);
g2d.drawGlyphVector(gv, positions.xPrice+10, y);
}
} // end for wares
} catch (FontFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
gScr.setColor(oldColor);
}
/**
* Action of buying stuff from the city onto the ship
* @author Andi Hotz, (c) Sahits GmbH, 2011
* Created on Nov 23, 2011
*
*/
private class City2ShipAction implements Runnable{
private final EWare ware;
public City2ShipAction(EWare ware) {
super();
this.ware = ware;
}
@Override
public void run() {
int availableAmountCity = city.getCity().getWare(ware).getAmount();
if (availableAmountCity>0){
int amount2Move = getAmount(availableAmountCity); // This is war specific size
System.out.println("Amount in the city: "+amount2Move);
// TODO consider available cash and capacity
final short sizeAsBarrels = ware.getSizeAsBarrels();
if (sizeAsBarrels==1){
amount2Move = Math.min(amount2Move, city.getActiveShip().getCapacity());
} else {
int temp = Math.min(amount2Move*sizeAsBarrels, city.getActiveShip().getCapacity());
amount2Move = temp/sizeAsBarrels;
}
System.out.println("Amount to be bought: "+amount2Move);
int avgPrice = ware.computeBuyPrice(availableAmountCity, amount2Move);
int movedAmount = city.getCity().move(ware, -amount2Move);
if (amount2Move!=movedAmount){
System.out.println("Calc new price: "+amount2Move+","+avgPrice+","+movedAmount);
avgPrice = ware.computeBuyPrice(city.getCity().getWare(ware).getAmount()+movedAmount, movedAmount);
amount2Move=movedAmount;
}
int loaded = city.getActiveShip().load(ware, amount2Move, avgPrice);
System.out.println("Loaded onto the ship: "+loaded);
city.getPlayer().updateCash(-avgPrice*movedAmount);
}
}
}
/**
* Action of selling stuff to the city from the ship
* @author Andi Hotz, (c) Sahits GmbH, 2011
* Created on Nov 23, 2011
*
*/
private class Ship2CityAction implements Runnable{
private final EWare ware;
public Ship2CityAction(EWare ware) {
super();
this.ware = ware;
}
@Override
public void run() {
if (city.getActiveShip().getWare(ware).getAmount()>0){
int availableAmountCity = city.getCity().getWare(ware).getAmount();
int amount2Move = getAmount(city.getActiveShip().getWare(ware).getAmount());
final int avgPrice;
if (movableAmount==ETransferAmount.MAX){
avgPrice = ware.getMaxValueSell();
} else {
avgPrice = ware.computeSellPrice(availableAmountCity, amount2Move);
}
city.getCity().move(ware, amount2Move); // The price here is irrelevant
city.getActiveShip().unload(ware, -amount2Move);
city.getPlayer().updateCash(avgPrice*amount2Move);
}
}
}
/**
* Action to change the chunck size that is to be moved.
* @author Andi Hotz, (c) Sahits GmbH, 2011
* Created on Nov 25, 2011
*
*/
private class TransferAmountChangeAction implements Runnable{
private final ETransferAmount size;
public TransferAmountChangeAction(ETransferAmount size) {
super();
this.size = size;
}
@Override
public void run() {
movableAmount=size;
// Change the visual of the pressed button
}
}
/**
* Enumeration over the transferable amount
* @author Andi Hotz, (c) Sahits GmbH, 2011
* Created on Nov 25, 2011
*
*/
private enum ETransferAmount{
/** Tranfer one item at the time */
ONE,
/** Transfer 5 items at the time */
FIVE,
/** Transfer all items at the time */
MAX
}
/**
* This class holds the positions where different elements are
* placed on the dialog.
* @author Andi Hotz, (c) Sahits GmbH, 2011
* Created on Dec 9, 2011
*
*/
private static class DialogPositions{
private final double titleHeigth;
private final int lineHeight;
private final int leftBorder;
private final int xWare;
private final int xCity;
private final int xBuy;
private final int xSell;
private final int xShip;
private final int xPrice;
public DialogPositions(double titleHeigth, int lineHeight,
int leftBorder, int xWare, int xCity, int xBuy, int xSell,
int xShip, int xPrice) {
super();
this.titleHeigth = titleHeigth;
this.lineHeight = lineHeight;
this.leftBorder = leftBorder;
this.xWare = xWare;
this.xCity = xCity;
this.xBuy = xBuy;
this.xSell = xSell;
this.xShip = xShip;
this.xPrice = xPrice;
}
}
}