import greenfoot.*; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) import java.util.*; import java.awt.*; /** * InvadersWorld: A styled and fun Space Invaders clone. Special feature: multiplayer mode: two humans * can play with one keyboard, sending waves of spaceships into battle against each other. * * @author Martin Schend, Jannis Andrija Schnitzer * @version 2011-01-21 */ public class InvadersWorld extends World { /** * Magic variable altering the game's behavior. If set to true, multi-player mode is enabled! */ protected static final boolean MULTIPLAYER = true; /** * Game field width. */ protected static final int WIDTH = 800; /** * Game field height. */ protected static final int HEIGHT = 600; /** * Health bars' width. */ protected static final int BAR_WIDTH = 183; /** * Health bars' height. */ protected static final int BAR_HEIGHT = 25; /** * Distance of health bars to game field edges. */ protected static final int DISTANCE = 13; /** * Size of the zone at the upper and lower edge of the playfield where new spaceships can be * created. */ protected static final int STARTING_ZONE = 30; // constants for sides public static final int WHITE = 0; public static final int BLACK = 1; /** * Number of sides participating in the game. Usually 2, still written as a constant for clearer * code. */ public static final int N_SIDES = 2; protected SpaceshipFactory[] factory; protected CollectibleFactory[] collFactory; /** * Array[N_SIDES]: true if the corresponding side is a human player, false if they're AI. */ public boolean[] player; /** * Array[N_SIDES]: true if the corresponding side should generate & send out autonomous spaceships. * Usually true for the computer enemy or for both sides in 2-player mode. * * @see #act() */ public boolean[] player_generates_spaceships; /** * Array[N_SIDES]: Denominator for probability P for spaceship creation. I. e., chance for a * spaceship being generated each turn is 1/chance[side]. * * @see #act() */ public int[] chance; /** * Array[N_SIDES]: Denominator for probability P for collectible creation. I. e., chance for a * collectible being generated for each destroyed spaceship is 1/coll_chance[side]. * * @see #maybeCreateCollectible() * @see #createCollectible() */ public int[] coll_chance; /** * List containing objects that need destruction at the start of the next turn. * (Currently unused, if I am informed correctly.) */ protected ArrayList destroyList; protected HealthBar blackBar; protected HealthBar whiteBar; /** * if it is clear that one side wins or loses (within a Greenfoot turn), this variable is set to * the side of the winner. InvadersWorld.act() will act thereafter. * * @see #act() */ protected int next_turn_winner = -1; public InvadersWorld() { this(WIDTH, HEIGHT, 1); } /** * InvadersWorld constructor. Creates and initializes all objects that are necessary for * gameplay. The section that creates the two manually controlled spaceships might be of special * interest. They're marked in the source code. */ public InvadersWorld(int worldWidth, int worldHeight, int cellSize) { super(worldWidth, worldHeight, 1); factory = new SpaceshipFactory[N_SIDES]; factory[WHITE] = new SpaceshipFactory(WHITE, 1); // TODO: do we always start at level 1? factory[BLACK] = new SpaceshipFactory(BLACK, 1); factory[WHITE].setDirection(-1.0); factory[BLACK].setDirection(+1.0); collFactory = new CollectibleFactory[N_SIDES]; collFactory[WHITE] = new CollectibleFactory(WHITE); collFactory[BLACK] = new CollectibleFactory(BLACK); player = new boolean[N_SIDES]; player_generates_spaceships = new boolean[N_SIDES]; chance = new int[N_SIDES]; coll_chance = new int[N_SIDES]; player[WHITE] = true; player[BLACK] = MULTIPLAYER; player_generates_spaceships[WHITE] = MULTIPLAYER; player_generates_spaceships[BLACK] = true; // setting of probabilities chance[WHITE] = 100; chance[BLACK] = 100; coll_chance[WHITE] = 4; coll_chance[BLACK] = 4; setBackground("background.png"); // White manually controlled spaceship creation. The comments below are solely for readability. ManuallyControlledSpaceship white_ship = new ManuallyControlledSpaceship(1); // TODO: level, s.a. white_ship.setSide(WHITE); this.addObject(white_ship, getWidth()/2, getHeight()-65); // gun of white ship LaserGun white_gun = new LaserGun(20.0, 0.0); white_ship.addGun(0, white_gun); // white health bar whiteBar = new HealthBar(); whiteBar.setSide(WHITE); whiteBar.setMaxHealth(white_ship.getMaxHitpoints()); this.addObject(whiteBar, DISTANCE+(BAR_WIDTH/2), getHeight() - DISTANCE - BAR_HEIGHT/2); white_ship.updateHealth(); // you may only move up & down if you're in single player mode. white_ship.setMayMoveUpAndDown(player[WHITE] && !player[BLACK]); // two-player mode: creation of black spaceship if (player[BLACK]) { ManuallyControlledSpaceship black_ship = new ManuallyControlledSpaceship(1); // TODO: level black_ship.setSide(BLACK); black_ship.setRotation(180); this.addObject(black_ship, getWidth()/2, 65); // gun of black ship LaserGun black_gun = new LaserGun(-20.0, 0.0); black_ship.addGun(0, black_gun); // black health bar blackBar = new HealthBar(); blackBar.setSide(BLACK); blackBar.setMaxHealth(black_ship.getMaxHitpoints()); this.addObject(blackBar, getWidth() - ((BAR_WIDTH/2)) - DISTANCE, (BAR_HEIGHT/2)+DISTANCE); black_ship.updateHealth(); // see above black_ship.setMayMoveUpAndDown(player[BLACK] && !player[WHITE]); } destroyList = new ArrayList(); } public void setHealthBar(int side, int value) { if (side == WHITE) whiteBar.setHealth(value); else if (side == BLACK) blackBar.setHealth(value); } public void setHealthBar(int side, int value, int max_value) { if (side == WHITE) { whiteBar.setMaxHealth(max_value); whiteBar.setHealth(value); } else if (side == BLACK) { blackBar.setMaxHealth(max_value); blackBar.setHealth(value); } } /** * Actions that a world just has to perform. Main things: * 1. Create random spaceships. * 2. Delete objects in the deletion list. * 3. End the game if necessary. * * TODO: Increase difficulty (i. e. ship levels, probabilities) over time. */ public void act() { // randomly add random spaceships, huh? for (int side = 0; side < N_SIDES; side++) { if (player_generates_spaceships[side]) { if (Greenfoot.getRandomNumber(chance[side]) != 0) // no ship if the RNG doesn't want continue; int x, y; AIControlledSpaceship ship; y = 1 + Greenfoot.getRandomNumber(STARTING_ZONE); if (side == WHITE) // sorry, no abstract logic possible here anymore. y = getHeight()-y; x = 1 + Greenfoot.getRandomNumber(getWidth()-1); ship = factory[side].ship(); this.addObject(ship, x, y); } } for (int i = 0; i < destroyList.size(); i++) this.removeObject((QuantumObject) destroyList.get(i)); if (next_turn_winner > -1) gameOver(next_turn_winner); } /** * Usually called by dying spaceships, this rolls a dice and maybe creates a collectible * at that very place. */ public void maybeCreateCollectible(int x, int y, int c_side) { if (Greenfoot.getRandomNumber(coll_chance[c_side]) == 0) createCollectible(x, y, c_side); } /** * Uses the side's collectible factory to get a random collectible. * * @see CollectibleFactory */ public void createCollectible(int x, int y, int c_side) { Collectible coll; coll = collFactory[c_side].collectible(); this.addObject(coll, x, y); } public void addObjectToDestroy(QuantumObject obj) { destroyList.add(obj); } public void gameOverNextTurn(int winner) { next_turn_winner = winner; } /** * Called when the game is totally over, this method displays an end screen. * * TODO: scoring. */ public void gameOver(int winner) { ArrayList EVERYTHING = (ArrayList) this.getObjects(Actor.class); for (int i = 0; i < EVERYTHING.size(); i++) this.removeObject((Actor) EVERYTHING.get(i)); Label gameOverLabel; if (winner == WHITE) gameOverLabel = new Label("Game Over. White wins."); else gameOverLabel = new Label("Game Over. Black wins."); addObject(gameOverLabel, 550, 310); Greenfoot.stop(); } }