Whole bunches of stuff. The game flow should do the right thing from start

to finish (ending the game when the last tile is played). Added a turn
indicator view that displays whose turn it is, the tile being placed and
the number of tiles remaining to be played. Added code to create
geometries for each tile which dictates which parts of it are farms, roads
and cities, respectively. These are currently used to render the tiles but
will eventually just be used to identify where the user wants to place
their piecen.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@358 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2001-10-16 01:41:55 +00:00
parent 3445af5ce7
commit df898f60d1
10 changed files with 392 additions and 66 deletions
@@ -1,5 +1,5 @@
//
// $Id: TileGeometryTest.java,v 1.1 2001/10/15 19:55:15 mdb Exp $
// $Id: TileGeometryTest.java,v 1.2 2001/10/16 01:41:55 mdb Exp $
package com.threerings.venison;
@@ -23,7 +23,7 @@ public class TileGeometryTest
ArrayList colors = new ArrayList();
// create polygons from the various tile features
for (int i = 1; i < TileUtil.TILE_FEATURES.length; i++) {
for (int i = 0; i < TileUtil.TILE_FEATURES.length; i++) {
// convert tile index into x and y coordinates (in tile
// feature coords which will be converted to screen coords)
int x = 4 * (i % 5), y = 4 * (i / 5);
@@ -1,5 +1,5 @@
//
// $Id: AtlantiBoard.java,v 1.5 2001/10/15 19:54:53 mdb Exp $
// $Id: AtlantiBoard.java,v 1.6 2001/10/16 01:41:55 mdb Exp $
package com.threerings.venison;
@@ -85,14 +85,18 @@ public class VenisonBoard
* the mouse while the tile is in a placeable position, a
* <code>TILE_PLACED</code> command will be dispatched to the
* controller in scope. The coordinates and orientation of the tile
* will have been set to the values specified by the user.
* will be available by fetching the tile back via {@link
* #getPlacedTile}. The tile provided to this method will not be
* modified.
*
* @param tile the new tile to be placed or null if no tile is to
* currently be placed.
*/
public void setTileToBePlaced (VenisonTile tile)
{
_placingTile = tile;
// make a copy of this tile so that we can play with it
_placingTile = (VenisonTile)tile.clone();;
// update our internal state based on this new placing tile
if (_placingTile != null) {
updatePlacingInfo(true);
@@ -101,6 +105,14 @@ public class VenisonBoard
repaint();
}
/**
* Returns the last tile placed by the user.
*/
public VenisonTile getPlacedTile ()
{
return _placedTile;
}
// documentation inherited
public void layout ()
{
@@ -115,6 +127,7 @@ public class VenisonBoard
public void paintComponent (Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
// center the tile display if we are bigger than we need to be
g.translate(_tx, _ty);
@@ -124,17 +137,17 @@ public class VenisonBoard
Iterator iter = _tiles.elements();
while (iter.hasNext()) {
VenisonTile tile = (VenisonTile)iter.next();
tile.paint(g, _origX, _origY);
tile.paint(g2, _origX, _origY);
}
}
// if we have a placing tile, draw that one as well
if (_placingTile != null && _validPlacement) {
// if the current position is valid, draw the placing tile
_placingTile.paint(g, _origX, _origY);
_placingTile.paint(g2, _origX, _origY);
// draw a green rectangle around the placing tile
g.setColor(Color.green);
g.setColor(Color.blue);
int sx = (_placingTile.x + _origX) * TILE_WIDTH;
int sy = (_placingTile.y + _origY) * TILE_HEIGHT;
g.drawRect(sx, sy, TILE_WIDTH, TILE_HEIGHT);
@@ -166,7 +179,8 @@ public class VenisonBoard
// to dispatch an action letting the controller know that the user
// placed it
if (_placingTile != null && _validPlacement) {
// clear out the placing tile
// move the placing tile to the placed tile
_placedTile = _placingTile;
_placingTile = null;
// post the action
@@ -330,8 +344,8 @@ public class VenisonBoard
TestDSet set = new TestDSet();
set.addTile(new VenisonTile(CITY_TWO, false, WEST, 0, 0));
set.addTile(new VenisonTile(CITY_FOUR, false, NORTH, 0, 1));
set.addTile(new VenisonTile(CITY_THREE, false, NORTH, 1, 1));
set.addTile(new VenisonTile(CITY_THREE_ROAD, false, NORTH, 1, 2));
set.addTile(new VenisonTile(CITY_THREE, false, WEST, 1, 1));
set.addTile(new VenisonTile(CITY_THREE_ROAD, false, EAST, 1, 2));
set.addTile(new VenisonTile(CITY_THREE, false, NORTH, -1, 0));
set.addTile(new VenisonTile(CITY_FOUR, false, NORTH, -2, 0));
board.setTiles(set);
@@ -364,6 +378,9 @@ public class VenisonBoard
/** The tile currently being placed by the user. */
protected VenisonTile _placingTile;
/** The last tile being placed by the user. */
protected VenisonTile _placedTile;
/** Whether or not the current position and orientation of the placing
* tile is valid. */
protected boolean _validPlacement = false;
@@ -81,11 +81,10 @@ public class VenisonController
public boolean handleAction (ActionEvent action)
{
if (action.getActionCommand().equals(TILE_PLACED)) {
// the user placed the tile into a valid location. the board
// will have updated the position and orientation information
// in the tile object accordingly, so we simply submit our
// move to the server
Object[] args = new Object[] { _venobj.currentTile };
// the user placed the tile into a valid location. grab the
// placed tile from the board and submit it to the server as
// our move
Object[] args = new Object[] { _panel.board.getPlacedTile() };
MessageEvent mevt = new MessageEvent(
_venobj.getOid(), PLACE_TILE_REQUEST, args);
_ctx.getDObjectManager().postEvent(mevt);
@@ -8,6 +8,8 @@ import javax.swing.*;
import com.samskivert.swing.Controller;
import com.samskivert.swing.ControllerProvider;
import com.samskivert.swing.HGroupLayout;
import com.samskivert.swing.VGroupLayout;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.client.PlaceView;
@@ -28,9 +30,27 @@ public class VenisonPanel
*/
public VenisonPanel (ParlorContext ctx, VenisonController controller)
{
// add the board
HGroupLayout gl = new HGroupLayout(HGroupLayout.STRETCH);
gl.setOffAxisPolicy(HGroupLayout.STRETCH);
setLayout(gl);
// create the board
board = new VenisonBoard();
add(board, BorderLayout.CENTER);
// create a scroll area to contain the board
JScrollPane scrolly = new JScrollPane(board);
add(scrolly);
// create our side panel
VGroupLayout sgl = new VGroupLayout(VGroupLayout.STRETCH);
sgl.setOffAxisPolicy(VGroupLayout.STRETCH);
JPanel sidePanel = new JPanel(sgl);
// add a turn indicator to the side panel
sidePanel.add(new TurnIndicatorView(), VGroupLayout.FIXED);
// add our side panel to the main display
add(sidePanel, HGroupLayout.FIXED);
// we'll need this later to provide it
_controller = controller;
@@ -0,0 +1,50 @@
//
// $Id: TileLabel.java,v 1.1 2001/10/16 01:41:55 mdb Exp $
package com.threerings.venison;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
/**
* Displays a single tile in a Swing component.
*/
public class TileLabel
extends JComponent implements TileCodes
{
/**
* Configures the component to display the specified tile.
*
* @param tile a reference to the tile to display or null if no tile
* should be displayed.
*/
public void setTile (VenisonTile tile)
{
_tile = tile;
repaint();
}
// documentation inherited
public void paintComponent (Graphics g)
{
super.paintComponent(g);
// simply paint the tile if we have one
if (_tile != null) {
_tile.paint((Graphics2D)g, 0, 0);
}
}
// documentation inherited
public Dimension getPreferredSize ()
{
return new Dimension(TILE_WIDTH, TILE_HEIGHT);
}
/** The tile we are displaying. */
protected VenisonTile _tile;
}
@@ -0,0 +1,115 @@
//
// $Id: TurnIndicatorView.java,v 1.1 2001/10/16 01:41:55 mdb Exp $
package com.threerings.venison;
import javax.swing.JLabel;
import javax.swing.JPanel;
import com.samskivert.swing.VGroupLayout;
import com.threerings.presents.dobj.AttributeChangeListener;
import com.threerings.presents.dobj.AttributeChangedEvent;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.client.PlaceView;
/**
* Displays who the current turn holder is as well as the tile they are
* currently placing.
*/
public class TurnIndicatorView
extends JPanel implements PlaceView, AttributeChangeListener
{
/**
* Creates a new turn indicator view which in turn creates its
* subcomponents.
*/
public TurnIndicatorView ()
{
setLayout(new VGroupLayout());
// add our turn holder display
_whoLabel = new JLabel();
add(_whoLabel);
// and add our tile display
_tileLabel = new TileLabel();
add(_tileLabel);
// and add a tile's remaining label
_countLabel = new JLabel();
add(_countLabel);
}
// documentation inherited
public void willEnterPlace (PlaceObject plobj)
{
// we want to grab a reference to the game object and add
// ourselves as an attribute change listener
_venobj = (VenisonObject)plobj;
_venobj.addListener(this);
}
// documentation inherited
public void didLeavePlace (PlaceObject plobj)
{
// remove our listening self
_venobj.removeListener(this);
_venobj = null;
}
// documentation inherited
public void attributeChanged (AttributeChangedEvent event)
{
// we care about the current tile display
if (event.getName().equals(VenisonObject.CURRENT_TILE)) {
// display the tile to be placed
_tileLabel.setTile(_venobj.currentTile);
} else if (event.getName().equals(VenisonObject.STATE)) {
switch (_venobj.state) {
case VenisonObject.AWAITING_PLAYERS:
_whoLabel.setText("Awaiting players...");
_tileLabel.setTile(null);
break;
case VenisonObject.GAME_OVER:
_whoLabel.setText("Game over.");
_tileLabel.setTile(null);
break;
case VenisonObject.CANCELLED:
_whoLabel.setText("Cancelled.");
_tileLabel.setTile(null);
break;
}
// update the tiles remaining
updateRemainingTiles();
} else if (event.getName().equals(VenisonObject.TURN_HOLDER)) {
if (_venobj.state == VenisonObject.IN_PLAY) {
_whoLabel.setText(_venobj.turnHolder);
}
// update the tiles remaining when the turn changes
updateRemainingTiles();
}
}
protected void updateRemainingTiles ()
{
_countLabel.setText("Tiles remaining: " + (72-_venobj.tiles.size()));
}
/** The label displaying whose turn it is. */
protected JLabel _whoLabel;
/** The label displaying the tile to be placed. */
protected TileLabel _tileLabel;
/** The label displaying the number of tiles remaining. */
protected JLabel _countLabel;
/** A reference to the game object. */
protected VenisonObject _venobj;
}
@@ -1,15 +1,23 @@
//
// $Id: AtlantiTile.java,v 1.4 2001/10/12 20:34:13 mdb Exp $
// $Id: AtlantiTile.java,v 1.5 2001/10/16 01:41:55 mdb Exp $
package com.threerings.venison;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.util.ArrayList;
import com.samskivert.util.IntTuple;
import com.threerings.presents.dobj.DSet;
/**
@@ -71,51 +79,35 @@ public class VenisonTile
* @param yoff the offset (in tile units) of the origin in the y
* direction.
*/
public void paint (Graphics g, int xoff, int yoff)
public void paint (Graphics2D g, int xoff, int yoff)
{
int tidx = type-1;
// create our shapes if we haven't already
if (_shapes[tidx][orientation] == null) {
createShapes();
}
// compute our screen coordinates
int sx = (x + xoff) * TILE_WIDTH;
int sy = (y + yoff) * TILE_HEIGHT;
// draw a rectangle
g.setColor(Color.black);
g.drawRect(sx, sy, TILE_WIDTH, TILE_HEIGHT);
// translate to our screen coordinates
g.translate(sx, sy);
// and draw our tile id in the middle for now (we'll eventually
// draw tile images)
String txt = type + "/" + x + "/" + y + "/" +
ORIENT_NAMES[orientation];
g.drawString(txt, sx + 20, sy + 20);
// draw little dots indicating the color of our sides
for (int i = 0; i < 4; i++) {
// adjust for our orientation
switch (TileUtil.getEdge(type, (i+(4-orientation))%4)) {
case GRASS:
g.setColor(Color.green);
break;
case ROAD:
g.setColor(Color.white);
break;
case CITY:
g.setColor(Color.red);
break;
}
switch (i) {
case NORTH:
g.fillOval(sx + TILE_WIDTH/2 - 2, sy + 2, 4, 4);
break;
case EAST:
g.fillOval(sx + TILE_WIDTH - 6, sy + TILE_HEIGHT/2 - 2, 4, 4);
break;
case SOUTH:
g.fillOval(sx + TILE_WIDTH/2 - 2, sy + TILE_HEIGHT - 6, 4, 4);
break;
case WEST:
g.fillOval(sx + 2, sy + TILE_HEIGHT/2 - 2, 4, 4);
break;
}
// draw our shapes using the proper orientation
GeneralPath[] paths = _shapes[tidx][orientation];
for (int i = 0; i < paths.length; i++) {
g.setColor(COLOR_MAP[_types[tidx][i].left]);
g.fill(paths[i]);
}
// draw a rectangular outline
g.setColor(Color.black);
g.drawRect(0, 0, TILE_WIDTH, TILE_HEIGHT);
// translate back out
g.translate(-sx, -sy);
}
/**
@@ -155,10 +147,112 @@ public class VenisonTile
y = in.readInt();
}
/**
* Creates the path objects that describe the shapes that make up this
* tile and sticks it into the appropriate slot in the shapes array.
*/
protected void createShapes ()
{
System.out.println("Creating shapes " + this + ".");
int tidx = type-1;
ArrayList polys = new ArrayList();
ArrayList types = new ArrayList();
// the first feature is the background color
Object[] features = (Object[])TileUtil.TILE_FEATURES[tidx];
IntTuple base = (IntTuple)features[0];
// add a polygon containing the whole tile
types.add(base);
Polygon poly = new Polygon();
poly.addPoint(0, 0);
poly.addPoint(TILE_WIDTH, 0);
poly.addPoint(TILE_WIDTH, TILE_HEIGHT);
poly.addPoint(0, TILE_HEIGHT);
polys.add(poly);
// the remainder are tuple/coordinate pairs
for (int f = 1; f < features.length; f += 2) {
IntTuple ftype = (IntTuple)features[f];
int[] coords = (int[])features[f+1];
// keep track of this shape's type
types.add(ftype);
// if this is a road segment, we need to create a special
// polygon
if (ftype.left == ROAD) {
poly = TileUtil.roadSegmentToPolygon(
coords[0], coords[1], coords[2], coords[3]);
} else {
// otherwise create the polygon directly from the coords
poly = new Polygon();
for (int c = 0; c < coords.length; c += 2) {
// scale the coords accordingly
int fx = (coords[c] * TILE_WIDTH) / 4;
int fy = (coords[c+1] * TILE_HEIGHT) / 4;
poly.addPoint(fx, fy);
}
}
polys.add(poly);
}
// now create general paths from our polygons and convert
// everything into the appropriate arrays
GeneralPath[] paths = new GeneralPath[polys.size()];
for (int i = 0; i < polys.size(); i++) {
GeneralPath path = new GeneralPath();
path.append(((Polygon)polys.get(i)).getPathIterator(null), false);
path.closePath();
paths[i] = path;
}
// keep the first one around
_shapes[tidx][NORTH] = paths;
// and rotate it three times to get the other orientations
AffineTransform xform = new AffineTransform();
for (int o = 1; o < 4; o++) {
xform.translate(TILE_WIDTH, 0);
xform.rotate(Math.PI/2);
GeneralPath[] rpaths = new GeneralPath[paths.length];
for (int i = 0; i < paths.length; i++) {
rpaths[i] = (GeneralPath)paths[i].clone();
rpaths[i].transform(xform);
}
_shapes[tidx][o] = rpaths;
}
// also fill in the feature type info
_types[tidx] = new IntTuple[types.size()];
types.toArray(_types[tidx]);
}
/**
* Generates a string representation of this tile instance.
*/
public String toString ()
{
return "[type=" + type + ", shield=" + hasShield +
", orient=" + ORIENT_NAMES[orientation] +
", pos=" + x + "/" + y + "]";
}
/** Path objects that describe closed shapes which be used to render
* the tiles (one for each orientation of each tile type). */
protected static GeneralPath[][][] _shapes =
new GeneralPath[TILE_TYPES][4][];
/** The feature type of each shape in shapes array. */
protected static IntTuple[][] _types = new IntTuple[TILE_TYPES][];
/** Maps feature types to colors. */
protected static Color[] COLOR_MAP = {
Color.red, // CITY
Color.green, // GRASS
Color.black // ROAD
};
}
@@ -1,5 +1,5 @@
//
// $Id: TileCodes.java,v 1.2 2001/10/10 06:14:57 mdb Exp $
// $Id: TileCodes.java,v 1.3 2001/10/16 01:41:55 mdb Exp $
package com.threerings.venison;
@@ -75,6 +75,8 @@ public interface TileCodes
/** A curved road segment. */
public static final int CURVED_ROAD = 19;
/** The number of different tile types. */
public static final int TILE_TYPES = 19;
/** A tile orientation constant indicating the tile is in its default
* orientation. */
@@ -1,5 +1,5 @@
//
// $Id: AtlantiManager.java,v 1.5 2001/10/12 20:34:13 mdb Exp $
// $Id: AtlantiManager.java,v 1.6 2001/10/16 01:41:55 mdb Exp $
package com.threerings.venison;
@@ -62,12 +62,39 @@ public class VenisonManager
protected void turnWillStart ()
{
super.turnWillStart();
// let the players know what the next tile is that should be
// played
VenisonTile tile = (VenisonTile)_tiles.remove(0);
_venobj.setCurrentTile(tile);
}
protected void turnDidEnd ()
{
super.turnDidEnd();
// if there are no tiles left, we end the game
if (_tiles.size() == 0) {
endGame();
}
}
/**
* Continue the game until we're out of tiles.
*/
protected void setNextTurnHolder ()
{
// if we have tiles left, we move to the next player as normal
if (_tiles.size() > 0) {
super.setNextTurnHolder();
} else {
// if we don't, we ensure that a new turn isn't started by
// setting _turnIdx to -1
_turnIdx = -1;
}
}
/** Handles place tile requests. */
protected class PlaceTileHandler implements MessageHandler
{
@@ -76,7 +103,7 @@ public class VenisonManager
VenisonTile tile = (VenisonTile)event.getArgs()[0];
// don't do no checking at present
_venobj.addToTiles(tile);
// and since they placed their tile, we end the turn
// end the turn
endTurn();
}
}
@@ -1,5 +1,5 @@
//
// $Id: TileUtil.java,v 1.3 2001/10/15 19:55:15 mdb Exp $
// $Id: TileUtil.java,v 1.4 2001/10/16 01:41:55 mdb Exp $
package com.threerings.venison;
@@ -230,6 +230,8 @@ public class TileUtil implements TileCodes
poly.addPoint(x2 + dx, y2 - dy);
} else { // diagonal
dx = 6; // widen these a bit so that the diagonal line appears
dy = 6; // to have the same weight as the straight ones
poly.addPoint(x1 - dx, y1);
poly.addPoint(x1 + dx, y1);
poly.addPoint(x2, y2 + dy);
@@ -321,7 +323,7 @@ public class TileUtil implements TileCodes
/** A table describing the geometry of the features (cities, roads,
* etc.) of each tile. */
protected static final Object[] TILE_FEATURES = new Object[] {
new Object[0], // null tile
// one must offset tile type by one when indexing into this array
new Object[] { new IntTuple(CITY, 0), }, // CITY_FOUR
@@ -344,10 +346,10 @@ public class TileUtil implements TileCodes
new Object[] { new IntTuple(CITY, 0), // CITY_TWO_ROAD
new IntTuple(GRASS, 0),
new int[] { 0, 4, 4, 0, 4, 2, 2, 4 },
new IntTuple(ROAD, 0),
new int[] { 2, 4, 4, 2 },
new IntTuple(GRASS, 0),
new int[] { 2, 4, 4, 2, 4, 4 }},
new int[] { 2, 4, 4, 2, 4, 4 },
new IntTuple(ROAD, 0),
new int[] { 2, 4, 4, 2 }},
new Object[] { new IntTuple(CITY, 0), // CITY_TWO_ACROSS
new IntTuple(GRASS, 0),