home *** CD-ROM | disk | FTP | other *** search
Java Source | 2000-12-02 | 19.5 KB | 754 lines |
- //
- // Wabburami.java
- //
-
- import waba.ui.*;
- import waba.fx.*;
- import waba.sys.*;
-
- import extra.ui.*;
-
- import wabadma.ui.*;
- import wabadma.util.*;
-
- /**
- * The classic game of Hammurabi for the JavaOne 1999 KVM.
- *<P>
- * Play the part of the ruler of ancient Babylon with this
- * great-great-great-granddaddy of empire building games.
- *<P>
- * The foundation of this Spotlet is Sun's Rumor game that was
- * distributed at the 1999 JavaOne developer conference; all the event
- * and text handling comes directly from that code. Everything else
- * was converted from the C version of the game (see below). I won't
- * claim that the coding is incredibly clean or well-implemented, but this
- * is more a demonstration that an ordinary Java engineer can write a
- * functional KVM application than anything else (although there's no
- * data access of any kind).
- *<P>
- * Based on C code from the Open License Software Supplement (Skunkware)
- * that I found at ftp.sco.com/skunkware/ and seemed to me to be close enough
- * to the original version (in BASIC?) to use as the basis for this code. : )
- *<P>
- * TODO:
- *<LI> Add saving of high scores and perhaps game state in a .pdb file.
- *<P>
- *
- * @author Jim Lesko -- Jim@MartianWind.com * @version 0.9
- *
- * version 0.9w: 07 Nov 2000
- * modified into waba by dmarcher@pobox.com
- *
- */
-
-
- public class Wabburami extends ExtraMainWindow {
- static String TITLE="Wabburami";
- static String VERSION="0.9w(b10)";
- static String VERSIONDATE="2000 Nov 07";
-
- static DLWRandom r;
- // UI objects:
- MenuBar mb;
- //static Button newButton; // unused
- static Button exitButton; // exit the game
- static Button doneButton; // done with turn
- static Button buyButton; // buy acres
- static Button sellButton; // sell acres
- static Button plantMaxButton; // plant Max. acres
- static Button feedNeededButton; // feed Min. Needed bushels
- static Button plantRbButton; // plant using Remaining bushels
- static Button feedRbButton; // feed Remaining bushels
- static Button bsPlusButton; // increase buy/sell #
- static Button bsMinusButton; // decrease buy/sell #
- static Button feedAcresButton; // feed min. bushels to tend acres
- static Button plantAvgButton; // plant min. so avg. yields feeds all
- static NumericEdit buysellEdit; // # of acres to buy or sell
- static NumericEdit feedEdit; // # of bushels to feed people
- static NumericEdit plantEdit; // # of acres to plant
- static OutputBox out;
- static Label statusLabel;
-
- // UI state:
- static int lineAscent;
- static int lineHeight;
- FontMetrics fm;
-
- // Game variables:
- static boolean playing;
- static boolean hard; // hard difficulty (otherwise it's easy)
-
- static int year; // Turn number
- static int acres; // Acres owned
- static int bushels; // Bushels in storage
- static int pop; // Population of city
- static int immigrants; // How many folks immigrated
- static int starved; // How many died of starvation
- static int rats; // How many bushels rats destroyed
- static int harvest; // Size of last year's harvest
- static int acreCost; // Cost of an acre in bushels
-
- static boolean plague; // Plagues struck this turn.
- static int plagueCount; // How many died of plague
-
-
- // Game finals: (some of these will probably change to vars. when
- // easy/hard difficulty is added.)
- private final static int PLANT_MAX_PER_PERSON = 10;
- private final static int PLANT_ACRES_PER_BUSHEL = 2;
- private final static int BUSHELS_NEEDED_PER_PERSON = 20;
- private final static int MAX_RAT_PCT = 60;
- private final static int PLAGUE_PCT = 10;
- private final static int MAX_IMMIGRANTS = 50;
- private final static int AVG_YIELD = 3;
-
- // finals locating ui objects:
- private final static int W=160; // Width
- private final static int H=160; // Height
- private final static int L=0; // Left edge
- private final static int R=159; // Right edge
- private final static int T=0; // Top edge
- private final static int B=159; // Bottom edge
- private final static int G_H=15;// generic height used by many objects
- private final static int YEAR_X=120;
- private final static int YEAR_Y=2;
- private final static int YEAR_W=W-YEAR_X+1;
-
- private final static int OUT_H=78;
-
- private final static int[] LINES_A = {94,95};
-
- private final static int OUT_Y=16;
- private final static int STATUS_Y=98;
- private final static int BUYSELL_Y=112;
- private final static int FEED_Y=128;
- private final static int PLANT_Y=144;
- private final static int EXIT_Y=128;
- private final static int DONE_Y=144;
-
- private final static int[][] LABEL_LOCS =
- {
- {0, BUYSELL_Y, 70, G_H},
- {0, FEED_Y, 55, G_H},
- {0, PLANT_Y, 55, G_H}
- };
-
- private final static String[] LABEL_TXTS =
- {
- "Trade Acres:",
- "Feed Bushels:",
- "Plant Acres:"
- };
-
- private final static int E_X=55; // x of *Edit
- private final static int E_W=25; // w of *Edit
- private final static int ED_B_W=29; // w of Exit & Done Buttons
- private final static int BS_B_W=22; // w of Buy & Sell Buttons
-
- /*
- * X Layouts: (button widths: Buy&Sell 24, Exit&Done 32, others 15)
- *
- * Buy/Sell Acres:
- * [Label 0..69] [Edit 55..79] [- 83] [+ 99] [Buy 115] [Sell 138]
- *
- * Feed:
- * [Label 0..54] [Edit 55..79] [ 83] [R 99] [! 115] [Exit 131]
- *
- * Plant:
- * [Label 0..54] [Edit 55..79] [ 83] [R 99] [> 115] [Done 131]
- *
- */
-
- private final static int BS_M_X=83; // x of Minus Button
- private final static int BS_P_X=99; // x of Plus Button
- private final static int BUY_X=115; // x of Buy Button
- private final static int SELL_X=138; // x of Sell Button
-
- private final static int PF_A_X=83; // x of A Buttons
- private final static int RB_X=99; // x of *RbButton
-
- private final static int PLANT_MAX_X=115; // x of Plant Max Button
- private final static int FEED_NEEDED_X=115;// x of Feed Needed Button
- private final static int ED_B_X=131; // x of Edit & Done Buttons
-
-
- private final static String aboutText =
-
- TITLE + "\n" + VERSION + "\n" + VERSIONDATE;
-
- //"Greetings, Wabburami.";
-
- private final static String helpText1 =
-
- "You have"+
- " inherited control of Babylon and are charged with"+
- " growing it into a great nation. You must utilize careful"+
- " land management, distribute food to your subjects, and permit"+
- " those subjects to tend to your crops. Rats, starvation, and"+
- " plague are the enemies which you must fight.";
-
- //"against every step of the way.";
-
- private final static String helpText2 =
-
- "Each year you must decide whether to buy or sell land, how much grain"+
- " to feed the people, and how many acres to plant. Once you've"+
- " allocated your resources by entering the appropriate amounts and"+
- " tapping the 'Done' button, you will be told of the"+
- " results of your decisions.";
-
- //"The game ends only when you sell all"+
- //" your land or tap the 'Exit' button.";
-
- //"Go now -- Babylon awaits your enlightened rule.";
-
- private final static String historyText =
-
- "Wabburami is a Waba version of the classic Hammurabi game. Its"+
- " implementation is based on the following versions:\n"+
-
- "Focal version by ? DEC programmer\n"+
- "Basic version by David H. Ahl\n"+
- "C version by ? SCO contributer\n"+
- "Java KVM version by Jim Lesko\n"+ //JavaOne 1999
- "Waba (this) version by Dave Archer";
-
-
- private final static String keyText1 =
-
- "To save time writing digits in the 3 numeric fields, you may also"+
- " use the following buttons as shortcuts.\n"+
- "\nTrading Buttons:\n"+
- " [-] decreases acres to trade\n"+
- " [+] increases acres to trade\n"+
- " [Buy] buys # of acres displayed\n"+
- " [Sell] sells # of acres displayed";
-
- private final static String keyText2 =
- "Feed Buttons:\n"+
- " [A] feed enough people to tend crops\n"+
- " [R] feed with remaining bushels\n"+
- " [!] feed enough for all people\n"+
- "Plant Buttons:\n"+
- " [A] plant enough for average yield\n"+
- " [R] plant with remaining bushels\n"+
- " [>] plant as many acres as possible";
-
-
- /**
- * App starts here. Need to check for saved game and/or prompt
- * for new game.
- *
- * Currently just starts a new game.
- */
- public Wabburami() {
- super(TITLE); // adding ,true double-buffers but is slow
- r=new DLWRandom();
- fm=getFontMetrics(defaultFont);
- lineAscent=fm.getAscent();
- lineHeight=fm.getHeight();
-
- String[] mls1={"File","New","Quit"};
- String[] mls2={"Options","Help1","Help2","Key1","Key2","History","About"};
- mb = new MenuBar();
- mb.add(new Menu(mls1));
- mb.add(new Menu(mls2));
- setMenuBar(mb);
-
- InitNewGame(false); // init easy game
- StartGame();
- }
-
-
- /**
- * Initialize variables for a new game.
- *
- */
- private void InitNewGame(boolean hard) {
- this.hard = hard;
- year = 1;
- acres = 1000;
- bushels = 2800;
- pop = 100;
- starved = 0;
- rats = 200;
- harvest = 3000;
- acreCost= 0;
- immigrants = 5;
- plague = false;
- plagueCount=0;
- }
-
-
- /**
- * Game (new or saved) starts here. Create UI structures,
- * display beginning info, and begin allowing UI events.
- */
- private void StartGame() {
- CreateUI();
- // show first (or last) turn
- showReport();
-
- playing = true;
- this.repaint();
- }
-
-
- /**
- * Generic Button setup.
- *
- */
- private Button addNewButton(String s, int x, int y, int w, int h) {
- Button b = new Button(s);
- b.setRect(x,y,w,h);
- add(b);
- return b;
- }
-
-
- /**
- * Create UI structures.
- *
- */
- private void CreateUI() {
- // add buttons
- exitButton = addNewButton("Exit", ED_B_X, EXIT_Y, ED_B_W, G_H);
- doneButton = addNewButton("Done", ED_B_X, DONE_Y, ED_B_W, G_H);
- buyButton = addNewButton("Buy", BUY_X, BUYSELL_Y, BS_B_W, G_H);
- sellButton = addNewButton("Sell", SELL_X, BUYSELL_Y, BS_B_W, G_H);
- feedNeededButton = addNewButton("!", FEED_NEEDED_X, FEED_Y, G_H, G_H);
- plantMaxButton = addNewButton(">", PLANT_MAX_X, PLANT_Y, G_H, G_H);
- feedRbButton = addNewButton("R", RB_X, FEED_Y, G_H, G_H);
- plantRbButton = addNewButton("R", RB_X, PLANT_Y, G_H, G_H);
- bsMinusButton = addNewButton("-", BS_M_X, BUYSELL_Y, G_H, G_H);
- bsPlusButton = addNewButton("+", BS_P_X, BUYSELL_Y, G_H, G_H);
- feedAcresButton = addNewButton("A", PF_A_X, FEED_Y, G_H, G_H);
- plantAvgButton = addNewButton("A", PF_A_X, PLANT_Y, G_H, G_H);
-
-
-
- // create a place to output text
- out = new OutputBox(0,OUT_Y,W,OUT_H);
- add(out);
-
- // add static labels
- for(int i=0;i<LABEL_TXTS.length;i++) {
- //System.err.println("Label "+LABEL_TXTS[i]);
- Label l = new Label(LABEL_TXTS[i]);
- l.setRect(LABEL_LOCS[i][0],LABEL_LOCS[i][1],LABEL_LOCS[i][2],LABEL_LOCS[i][3]);
- add(l);
- }
-
- // add status label
- statusLabel = new Label("");
- statusLabel.setRect(0, STATUS_Y, W, lineHeight);
- add(statusLabel);
-
-
- // add edit controls
- buysellEdit = new NumericEdit();
- buysellEdit.setRect(E_X, BUYSELL_Y, E_W, G_H);
- buysellEdit.setInt(5);
- add(buysellEdit);
-
- feedEdit = new NumericEdit();
- feedEdit.setRect(E_X,FEED_Y,E_W,G_H);
- feedEdit.setInt(2000);
- add(feedEdit);
-
- plantEdit = new NumericEdit();
- plantEdit.setRect(E_X,PLANT_Y,E_W,G_H);
- plantEdit.setInt(1000);
- add(plantEdit);
- }
-
-
- public void clearRect(Graphics g, int x, int y, int w, int h) {
- g.setColor(255,255,255);
- g.fillRect(x,y,w,h);
- g.setColor(0,0,0);
- }
-
-
- public void onPaint(Graphics g) {
- //System.err.println("onPaint");
-
- // Year display is next to Title, not in out box
- clearRect(g,YEAR_X,YEAR_Y,YEAR_W,lineAscent);
- g.drawText("Year "+year,YEAR_X,YEAR_Y);
-
- // Draw lines as per LINES_A
- for(int i=0;i<LINES_A.length;i++)
- g.drawLine(T,LINES_A[i],R,LINES_A[i]);
- }
-
-
- /**
- * Update info. in status line.
- */
- private void updateStatus() {
- int surplus = calcSurplus();
- statusLabel.setText("B: "+bushels+" P: "+pop+" A: "+acres+" Surplus: "+surplus);
- statusLabel.repaint();
- }
-
-
- /**
- * Refresh screen with this turn's info.
- */
- private void showReport() {
-
- // draw the border between the top text and the input area
- //g.drawLine(0,14,159,14);
-
- out.clear();
-
- // Tell user how this turn went
- if (plague) {
- plagueCount = pop;
- pop /= 2;
- plagueCount -= pop;
- out.println("Plague! "+plagueCount+ ((plagueCount == 1) ?
- " person" : " people") +" died, "+starved+" starved,");
- } else
- out.println("Last year "+starved+((starved == 1) ?
- " person" : " people") + " starved");
-
- out.println("and "+immigrants+((immigrants == 1) ?
- " person" : " people") + " entered the city.");
-
- out.println("The population is now "+pop+".");
-
- out.println("We harvested "+harvest+" bushels at "+
- acreCost+" BPA.");
-
- out.println("Rats ate "+rats+" bushels, leaving "+
- bushels+".");
-
- out.println("The city owns "+acres+" acres of land.");
-
- acreCost = r.random(6) + 22;
-
- out.println("Land is worth "+acreCost+" an acre.");
-
- updateStatus();
- this.repaint();
- }
-
- /**
- * Events only checked while playing.
- * (Presumably none of these events will get hit when not
- * playing because the objects won't exist, but this also
- * makes onEvent look a bit neater.)
- *
- */
- private void playingEvent(Event e) {
- if (e.type == ControlEvent.PRESSED) {
- if (e.target instanceof Button) {
- // pressed buttons
- if (e.target == exitButton)
- endGame();
- else if (e.target == doneButton)
- processTurn();
- else if (e.target == buyButton)
- buyAcres();
- else if (e.target == sellButton)
- sellAcres();
- else if (e.target == plantMaxButton)
- plantMax();
- else if (e.target == feedNeededButton)
- feedNeeded();
- else if (e.target == plantRbButton)
- plantRb();
- else if (e.target == feedRbButton)
- feedRb();
- else if (e.target == bsMinusButton)
- modifyBs(-1);
- else if (e.target == bsPlusButton)
- modifyBs(1);
- else if (e.target == feedAcresButton)
- feedAcres();
- else if (e.target == plantAvgButton)
- plantAvg();
-
- }
- }
- }
-
- /**
- * Checks for events. Responsible for updating state of the
- * game, exiting, acknowledging errors, etc.
- */
- public void onEvent(Event e) {
- if (playing)
- playingEvent(e); // check playing events
-
- if (e.type == ControlEvent.PRESSED) {
- // check menu items being selected
- if (e.target instanceof Menu) {
- String sel=((Menu)e.target).getSelected();
- if (sel.equals("Quit")) {
- endGame();
- } else if (sel.equals("About")) {
- Dialog id = new Dialog("About",0,80,160,80);
- id.out.printlnCentered(aboutText);
- add(id);
- return;
- } else if (sel.equals("History")) {
- Dialog h = new Dialog("History",0,16,160,height-16,historyText);
- add(h);
- return;
- } else if (sel.equals("Help1")) {
- Dialog h = new Dialog("Help1",0,16,160,height-16,helpText1);
- add(h);
- return;
- } else if (sel.equals("Help2")) {
- Dialog h = new Dialog("Help2",0,16,160,height-16,helpText2);
- add(h);
- return;
- } else if (sel.equals("Key1"))
- add(new Dialog("Key1",0,16,160,height-16,keyText1));
- else if (sel.equals("Key2"))
- add(new Dialog("Key2",0,16,160,height-16,keyText2));
-
- }
- }
- }
-
-
- /**
- * modify buy/sell acres by i
- *
- */
- private void modifyBs(int i) {
- int v = buysellEdit.getInt();
-
- v += i;
- if (v < 0)
- v = 0;
-
- buysellEdit.setInt(v);
- }
-
-
- /**
- * buy some acres
- *
- */
- private void buyAcres() {
- int value = buysellEdit.getInt();
- if ((value*acreCost) > bushels)
- showError();
- else {
- bushels -= value*acreCost;
- acres += value;
- }
- updateStatus();
- }
-
-
- /**
- * sell some acres
- *
- */
- private void sellAcres() {
- int value = buysellEdit.getInt();
- if (value > acres)
- showError();
- else {
- bushels += value*acreCost;
- acres -= value;
- }
- // be nice and reduce acres being planted if needed
-
- if (acres < plantEdit.getInt())
- plantEdit.setInt(acres);
- updateStatus();
- }
-
-
- /**
- * plant maximum possible acres
- *
- */
- private void plantMax() {
- int m;
-
- m=pop * PLANT_MAX_PER_PERSON;
-
- if (m>acres)
- m=acres;
-
- plantEdit.setInt(m);
- plantEdit.repaint();
- updateStatus();
- }
-
-
- /**
- * try to feed everybody just enough
- *
- */
- private void feedNeeded() {
- int m;
-
- m = pop * BUSHELS_NEEDED_PER_PERSON;
-
- if (m > bushels)
- m = bushels;
-
- feedEdit.setInt(m);
- feedEdit.repaint();
- updateStatus();
- }
-
-
- /**
- * plant remaining bushels (after bushels used feeding people)
- *
- */
- private void plantRb() {
- int m = ( bushels-feedEdit.getInt() ) * PLANT_ACRES_PER_BUSHEL;
- if (m < 0 )
- m = 0;
- plantEdit.setInt(m);
- plantEdit.repaint();
- updateStatus();
- }
-
-
- /**
- * feed remaining bushels (after bushels used for planting)
- *
- */
- private void feedRb() {
- int m = bushels-(plantEdit.getInt()/PLANT_ACRES_PER_BUSHEL);
-
- if (m < 0 )
- m = 0;
- feedEdit.setInt(m);
- feedEdit.repaint();
- updateStatus();
- }
-
-
- /**
- * feed just enough people to tend all fields
- *
- */
- private void feedAcres() {
- feedEdit.setInt(acres/PLANT_MAX_PER_PERSON*BUSHELS_NEEDED_PER_PERSON);
- feedEdit.repaint();
- updateStatus();
- }
-
-
- /**
- * plant just enough to feed everyone with an avg. yield
- *
- */
- private void plantAvg() {
- plantEdit.setInt((pop*BUSHELS_NEEDED_PER_PERSON)/AVG_YIELD);
- plantEdit.repaint();
- updateStatus();
- }
-
-
- private int calcSurplus() {
- int feed = feedEdit.getInt();
- int plant = plantEdit.getInt();
-
- return bushels-feed-(plant/PLANT_ACRES_PER_BUSHEL);
- }
-
- private void processTurn() {
- int feed = feedEdit.getInt();
- int plant = plantEdit.getInt();
-
- if (feed > bushels) {
- // trying to feed more bushels than we have
- showError();
- return;
- }
-
- if (plant > acres ||
- plant / PLANT_ACRES_PER_BUSHEL > (bushels-feed) ||
- plant > pop * PLANT_MAX_PER_PERSON) {
- // trying to plant more acres than we have, or
- // trying to plant more bushels than we have, or
- // trying to plant more than the population can
- showError();
- return;
- }
-
-
- bushels -= feed;
- starved = pop - feed / BUSHELS_NEEDED_PER_PERSON;
- immigrants = 0;
-
- if (starved < 0) {
- immigrants = -1 * (starved / 2);
- starved = 0;
- }
-
- // Essentially, the turn's over; update state &
- // figure out random events (plague):
- bushels -= plant / PLANT_ACRES_PER_BUSHEL;
- acreCost = r.random(5) + 1;
- harvest = acreCost * plant;
- int p = r.random(MAX_RAT_PCT*10);
- //ratPop = r.random(600);
- //rats = (ratPop * (bushels + harvest)) / 1000;
- if (hard)
- rats = p * (bushels + harvest) / 1000;
- else
- rats = p * bushels / 1000;
- bushels = bushels - rats + harvest;
-
- //plague = (r.random(10) == 0);
- plague = (r.random(100) < PLAGUE_PCT);
- immigrants += (5 - acreCost) * bushels / 600 + 1;
- immigrants = (immigrants > MAX_IMMIGRANTS) ? MAX_IMMIGRANTS : immigrants;
- immigrants = (immigrants < 0) ? 0 : immigrants;
- pop += immigrants - starved;
-
- year ++;
- showReport();
- this.repaint();
- }
-
-
- /**
- * Game has ended either by user request or naturally.
- *
- * This is a good place to add final score, saving stats,
- * that sort of thing. (But not a saved game.)
- */
- public void endGame () {
- playing = false;
- exit(0);
- }
-
- /**
- * Exit caught by VM. This is called after exit(0) in endGame,
- * so when we get here, we should check the value of playing to
- * see if we need to save the game.
- *
- */
- public void onExit() {
- if (playing)
- ; // save game
- else
- ; // remove old saved game
- }
-
- /**
- * Let the player know that something isn't right.
- */
- private void showError() {
- add(new Dialog("Wabburami, Think Again",0,80,160,80,
- "You do not have enough bushels, acres, or people to do "+
- "as you request."));
- }
- }
-