You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

1236 lines
37 KiB

/**
* Copyright 2022 Jamie Munro, All rights reserved
* CS5041 P1
* Game 1
*/
import java.util.Map;
import java.util.Iterator;
import processing.serial.*;
import ddf.minim.*;
Configuration conf;
DisplayDimensions dimensions;
Map<Integer, PImage> tileSet; //Maps tile id to related image (to avoid repeated/redundant image loads)
Map<String, PImage[]> spriteSet;
//sounds
Minim minim;
String activeSong;
Map<String, AudioPlayer> music;
Map<String, AudioPlayer> soundEffects;
Controller controller;
Serial port;
Player player;
Level level;
TileProperties props;
//viewport variables
int startCol;
int endCol;
int startRow;
int endRow;
//game state
boolean loading;
boolean titleScreen;
boolean story;
boolean pause;
boolean gameOver;
//scores
int score;
int lives;
int bombs;
int difficulty;
//timers
long startTime;
long pauseTime;
long lastShot;
long gameOverTime;
long spawnTime;
//story screen variables
int storyCounter;
float storyY;
int storyLines;
//rewards variables
int nextLife;
int nextBomb;
//projectiles
List<Projectile> playerProjectiles;
List<Projectile> enemyProjectiles;
Bomb bomb;
//enemy variables
List<Enemy> enemies;
void setup() {
fullScreen(P2D, 2);
try {
conf = new Configuration();
}
catch (Exception e) {
println("Unable to access valid configuration.properties file.");
e.printStackTrace();
exit();
}
dimensions = new DisplayDimensions(width, height, conf);
frameRate(conf.framerate);
controller = new MicrobitController(conf, false);
try {
//for (Object device : Serial.list()) {
// println(device);
//}
port = new Serial(this, Serial.list()[conf.serialDevice], conf.baudRate);
}
catch (Exception e) {
println("ERROR: could not access serial port. If your sure the device is connected and you have set the correct serialDevice in config.properties, try running as root/administrator.");
e.printStackTrace();
exit();
}
port.bufferUntil(conf.lineEnding);
//intialise game state
loading = true;
titleScreen = false;
story = false;
pause = false;
gameOver = false;
storyY = dimensions.heightOffset + (dimensions.adjustedHeight/1.75);
storyCounter = 0;
storyLines = countLines(conf.storyCrawl);
//initialise scores
score = 0;
lives = conf.initialLives;
bombs = conf.initialBombs;
difficulty = 0;
//initialise enemies
enemies = new ArrayList<Enemy>();
//initialise projectiles
playerProjectiles = new ArrayList<Projectile>();
enemyProjectiles = new ArrayList<Projectile>();
bomb = null;
//sounds
minim = new Minim(this);
//fonts
PFont mono = createFont(sketchPath() + System.getProperty("file.separator") + conf.fontPath + System.getProperty("file.separator") + "andalemo.ttf", 128);
textFont(mono);
//perform loading
thread("loadResources");
}
/**
* Called repeatedly, every 1/framerate seconds
*/
void draw() {
background(0);
if (loading) {
loadingScreen();
}
else if (titleScreen) {
titleScreen();
}
else if (story) {
storyScroll();
}
else if (pause) {
pause();
}
else if (gameOver) {
gameOver();
}
else {
game();
}
if (!loading) {
//music
if ((activeSong == null) || (!music.get(activeSong).isPlaying())) {
playRandomSong();
}
}
//println(frameRate);
}
void stop() {
//Close Minim audio classes
for (String song : music.keySet()) {
music.get(song).close();
}
for (String effect : soundEffects.keySet()) {
soundEffects.get(effect).close();
}
//Stop Minim before exiting
minim.stop();
super.stop();
}
void serialEvent(Serial p) {
String input = p.readString();
controller.decode(input.trim());
}
/**
* Main game loop
*/
void game() {
updateViewportVariables();
//draw level background
renderLevel();
//update and display projectiles
updateProjectiles();
renderProjectiles();
//handle bombs
if (bomb != null) {
boolean shouldDelete = bomb.update();
renderBomb();
detectBombCollision();
if (shouldDelete) {
bomb = null;
}
}
//update and display enemies
updateEnemies();
renderEnemies();
//update and display player
updatePosition();
renderPlayer();
//draw HUD
drawHud();
//handle button input
buttons();
//detect collisions with enemies
detectEnemyCollision();
//detect enemy projectile collisions
detectEnemyProjectileCollision();
//detect player projectile collisions
detectPlayerProjectileCollisions();
//spawn new enemies
spawnEnemy(false);
//update difficulty
long currentTime = millis();
long secondsElapsed = (currentTime - startTime) / 1000;
difficulty = (int) secondsElapsed / conf.difficultyInterval;
checkForRewards();
}
void updateViewportVariables() {
//Calculate viewport bounds based on player position and viewport dimensions
startCol = player.getX() - (conf.viewportWidth / 2);
endCol = player.getX() + (conf.viewportWidth / 2);
startRow = player.getY() - (conf.viewportHeight / 2);
endRow = player.getY() + (conf.viewportHeight / 2);
}
/**
* Renders the level for the player's current viewport
* Based on example here: https://developer.mozilla.org/en-US/docs/Games/Techniques/Tilemaps/Square_tilemaps_implementation:_Scrolling_maps
*/
void renderLevel() {
//iterate through appropriate tiles
for (int col = startCol; col <= endCol; col++) {
for (int row = startRow; row <= endRow; row++) {
Tile tile = this.level.getTile(col, row);
PImage img = this.tileSet.get(tile.getId());
//Calculate co-orindates to draw tile at based on tile position and dimensions and account for any nessessary offset due to aspect ratio
int x = dimensions.widthOffset + (col - startCol) * dimensions.tileWidth;
int y = dimensions.heightOffset + (row - startRow) * dimensions.tileHeight;
//render tile at specified location and dimenision
image(img, x, y, dimensions.tileWidth, dimensions.tileHeight);
}
}
}
/**
* Update players position based on control input
*/
void updatePosition() {
//handle Y engines
if (controller.accelerationY() < -conf.thresholdX) {
player.fireMainEngine();
stopSoundEffect("reverse");
playSoundEffect("forwards");
}
else if (controller.accelerationY() > conf.thresholdX) {
player.fireReverseEngine();
stopSoundEffect("forwards");
playSoundEffect("reverse");
}
else {
stopSoundEffect("forwards");
stopSoundEffect("reverse");
player.shutdownYEngines();
}
//handle rotation
int headingDifference = controller.currentHeading() - controller.initialHeading();
if (Math.abs(headingDifference) > conf.headingThreshold) {
playSoundEffect("rotate");
if (headingDifference < 0) {
player.fireRightEngine();
}
else {
player.fireLeftEngine();
}
}
else {
stopSoundEffect("rotate");
player.shutdownXEngines();
}
float newX = player.getX() + (player.getMomentum() * sin(radians(player.getRotation())));
float newY = player.getY() + (player.getMomentum() * -cos(radians(player.getRotation())));
if (!Boolean.parseBoolean(level.getTile(Math.round(newX), Math.round(newY)).getProperty("solid"))) {
player.setPosition((int) newX, (int) newY);
}
}
/*
* Update player position and draw to screen
*/
void renderPlayer() {
pushMatrix();
int x = dimensions.widthOffset + dimensions.adjustedWidth / 2;
int y = dimensions.heightOffset + dimensions.adjustedHeight / 2;
translate(x, y);
rotate(radians(player.getRotation()));
imageMode(CENTER);
image(player.getSprite(), 0, 0, dimensions.tileWidth * player.getScaleFactor(), dimensions.tileHeight * player.getScaleFactor());
if(player.getSheild()) {
tint(255, 126);
image(spriteSet.get("player")[9], 0, 0, dimensions.tileWidth * player.getScaleFactor(), dimensions.tileHeight * player.getScaleFactor());
noTint();
}
popMatrix();
}
/**
* Draw HUD elements
*/
void drawHud() {
//lives
imageMode(CORNER);
for (int i = 0; i < lives; i++) {
int liveX = dimensions.startLivesX + (i * dimensions.tileWidth * 9);
image(spriteSet.get("player")[0], liveX, dimensions.livesY, dimensions.tileWidth * 7, dimensions.tileHeight * 7);
}
//score
textSize(dimensions.scoreSize);
textAlign(CENTER);
text(score, dimensions.scoreX, dimensions.scoreY);
//momentum
rectMode(CORNER);
stroke(255);
strokeWeight(1);
noFill();
rect(dimensions.nMomentumX, dimensions.nMomentumY, dimensions.nMomentumWidth, dimensions.nMomentumHeight);
rect(dimensions.pMomentumX, dimensions.pMomentumY, dimensions.pMomentumWidth, dimensions.pMomentumHeight);
if (player.getMomentum() < 0) {
fill(235, 28, 28);
rect(dimensions.nMomentumX, dimensions.nMomentumY, dimensions.nMomentumWidth, (dimensions.nMomentumHeight/conf.maxRearMomentum) * Math.abs(player.getMomentum()));
}
else if (player.getMomentum() > 0) {
fill(28, 235, 38);
pushMatrix();
translate(dimensions.pMomentumX + dimensions.pMomentumWidth, dimensions.pMomentumY + dimensions.pMomentumHeight);
rotate(radians(180));
rect(0, 0, dimensions.pMomentumWidth, (dimensions.pMomentumHeight/conf.maxForwardsMomentum) * player.getMomentum());
popMatrix();
}
//back to defaults
fill(255);
strokeWeight(4);
//bombs
imageMode(CORNER);
for (int i = 0; i < bombs; i++) {
int bombX = dimensions.startBombsX + (i * dimensions.tileWidth * 9);
image(spriteSet.get("bomb")[0], bombX, dimensions.bombsY, dimensions.tileWidth * 7, dimensions.tileHeight * 7);
}
}
/**
* Respond to button presses
*/
void buttons() {
if (controller.buttonC()) {
stopAllSoundEffects();
playSoundEffect("pause");
pause = true;
pauseTime = millis();
}
if (controller.buttonA()) {
shoot();
}
if (controller.buttonB()) {
bomb();
}
}
/**
* Shoots a projectile if possible
*/
void shoot() {
if (playerProjectiles.size() - 1 <= conf.maxPlayerProjectiles) {
long currentTime = millis();
if (currentTime - lastShot >= conf.reloadTime) {
playSoundEffect("shoot");
int x = player.getX();// - Math.round((player.getScaleFactor() / 2) * sin(radians(player.getRotation())));
int y = player.getY();// + Math.round((player.getScaleFactor() / 2) * -cos(radians(player.getRotation())));
playerProjectiles.add(new Projectile(x, y, player.getRotation(), spriteSet.get("player")[10], 10, 1, 260, 2));
lastShot = currentTime;
}
}
}
/**
* Triggers a bomb if possible
*/
void bomb() {
if ((bomb == null) && (bombs > 0) && (!player.getSheild())) {
playSoundEffect("bomb");
bomb = new Bomb(player.getX(), player.getY(), conf);
bombs--;
}
}
/**
* Selects and spawns a random enemy based on difficulty level
*/
void spawnEnemy(boolean debug) {
long currentTime = millis();
if (currentTime - spawnTime > conf.spawnInterval) {
spawnTime = currentTime;
//select random starting position outside of viewport
int col = int(random(0, level.getCols()));
int row = int(random(0, level.getRows()));
//ensure x is not inside viewport
while ((col >= startCol) && (col <= endCol)) {
col = int(random(0, level.getCols()));
}
//ensure y is not inside viewport
while ((row >= startRow) && (row <= endRow)) {
row = int(random(0, level.getRows()));
}
int r = int(random(0, 101));
String name = "";
if ((r < Boss.CHANCE_OF_SPAWNING) && (difficulty >= Boss.MINIMUM_DIFFICULTY)) {
enemies.add(new Boss(col, row, spriteSet.get("boss"), spriteSet.get("boss")[2], level, player));
name = "Boss";
}
else if ((r < Saucer.CHANCE_OF_SPAWNING) && (difficulty >= Saucer.MINIMUM_DIFFICULTY)) {
enemies.add(new Saucer(col, row, spriteSet.get("saucer")[0], spriteSet.get("saucer")[1], level, player));
name = "Saucer";
}
else if ((r < GunPlatform.CHANCE_OF_SPAWNING) && (difficulty >= GunPlatform.MINIMUM_DIFFICULTY)) {
enemies.add(new GunPlatform(col, row, spriteSet.get("gunPlatform")[0], spriteSet.get("gunPlatform")[1], player));
name = "GunPlatform";
}
else if ((r < Jellyfish.CHANCE_OF_SPAWNING) && (difficulty >= Jellyfish.MINIMUM_DIFFICULTY)) {
enemies.add(new Jellyfish(col, row, spriteSet.get("jellyfish"), level, player));
name = "Jellyfish";
}
else if ((r < Asteroid2.CHANCE_OF_SPAWNING) && (difficulty >= Asteroid2.MINIMUM_DIFFICULTY)) {
enemies.add(new Asteroid2(col, row, spriteSet.get("asteroid")[2], level));
name = "Asteroid2";
}
else if ((r < Asteroid1.CHANCE_OF_SPAWNING) && (difficulty >= Asteroid1.MINIMUM_DIFFICULTY)) {
enemies.add(new Asteroid1(col, row, spriteSet.get("asteroid")[1], level));
name = "Asteroid1";
}
else if ((r < Asteroid0.CHANCE_OF_SPAWNING) && (difficulty >= Asteroid0.MINIMUM_DIFFICULTY)) {
enemies.add(new Asteroid0(col, row, spriteSet.get("asteroid")[0], level));
name = "Asteroid0";
}
if (debug) {
print(name);
print(" spawned at: ");
print(col);
print(", ");
println(row);
}
}
}
/**
* update enemy positions
*/
void updateEnemies() {
for (Enemy enemy : enemies) {
enemy.update();
if (enemy.projectile() != null) {
enemyProjectiles.add(enemy.projectile());
}
}
}
/**
* render enemies within viewport
*/
void renderEnemies() {
for (Enemy enemy : enemies) {
int col = enemy.getX();
int row = enemy.getY();
//only render if inside viewport
if ((col >= startCol) && (col <= endCol) && (row >= startRow) && (row <= endRow)) {
//Calculate co-orindates to draw tile at based on tile position and dimensions and account for any nessessary offset due to aspect ratio
int x = dimensions.widthOffset + (col - startCol) * dimensions.tileWidth;
int y = dimensions.heightOffset + (row - startRow) * dimensions.tileHeight;
pushMatrix();
translate(x,y);
rotate(radians(enemy.getRotation()));
imageMode(CENTER);
tint(enemy.getTintR(), enemy.getTintG(), enemy.getTintB());
image(enemy.getSprite(), 0, 0, dimensions.tileWidth * enemy.getScaleFactor(), dimensions.tileHeight * enemy.getScaleFactor());
popMatrix();
noTint();//return to default
}
}
}
/**
* update projectile positions
*/
void updateProjectiles() {
List<List<Projectile>> projectileLists = new ArrayList<List<Projectile>>();
projectileLists.add(playerProjectiles);
projectileLists.add(enemyProjectiles);
for(List<Projectile> projectiles: projectileLists) {
Iterator<Projectile> it = projectiles.iterator();
while(it.hasNext()) {
Projectile projectile = it.next();
boolean shouldDelete = projectile.update();
if (shouldDelete) it.remove();
}
}
}
/**
* render projectiles within viewport
*/
void renderProjectiles() {
List<List<Projectile>> projectileLists = new ArrayList<List<Projectile>>();
projectileLists.add(playerProjectiles);
projectileLists.add(enemyProjectiles);
for (List<Projectile> projectiles: projectileLists) {
for (Projectile projectile : projectiles) {
int col = projectile.getX();
int row = projectile.getY();
//only render if inside viewport
if ((col >= startCol) && (col <= endCol) && (row >= startRow) && (row <= endRow)) {
//Calculate co-orindates to draw tile at based on tile position and dimensions and account for any nessessary offset due to aspect ratio
int x = dimensions.widthOffset + (col - startCol) * dimensions.tileWidth;
int y = dimensions.heightOffset + (row - startRow) * dimensions.tileHeight;
pushMatrix();
translate(x,y);
rotate(radians(projectile.getRotation()));
imageMode(CENTER);
image(projectile.getSprite(), 0, 0, dimensions.tileWidth * projectile.getScaleFactor(), dimensions.tileHeight * projectile.getScaleFactor());
popMatrix();
}
}
}
}
/**
* detect collisions between player projectiles and enemies
*/
void detectPlayerProjectileCollisions() {
List<Enemy> newEnemies = new ArrayList<Enemy>();
Iterator<Enemy> enemyIterator = enemies.iterator();
while(enemyIterator.hasNext()) {
Enemy enemy = enemyIterator.next();
Iterator<Projectile> projectileIterator = playerProjectiles.iterator();
while(projectileIterator.hasNext()) {
Projectile projectile = projectileIterator.next();
//https://silentmatt.com/rectangle-intersection/
int eX1 = enemy.getX() - (enemy.getScaleFactor() / 2);
int eY1 = enemy.getY() - (enemy.getScaleFactor() / 2);
int eX2 = enemy.getX() + (enemy.getScaleFactor() / 2);
int eY2 = enemy.getY() + (enemy.getScaleFactor() / 2);
int pX1 = projectile.getX() - (projectile.getScaleFactor() / 2);
int pY1 = projectile.getY() - (projectile.getScaleFactor() / 2);
int pX2 = projectile.getX() + (projectile.getScaleFactor() / 2);
int pY2 = projectile.getY() + (projectile.getScaleFactor() / 2);
if ((eX1 < pX2) && (eX2 > pX1) && (eY1 < pY2) && (eY2 > pY1)) {
for (int i = 0; i < projectile.getDamage(); i++) {
if(enemy.hit()) {
//DESTROYED!
//TODO:ANIMATION
playSoundEffect("explosion");
score += enemy.getScore();
List<Enemy> spawnlings = enemy.spawnlings();
if (spawnlings != null) newEnemies.addAll(spawnlings);
enemyIterator.remove();
break;
}
}
projectileIterator.remove();
}
}
}
enemies.addAll(newEnemies);
}
/**
* detect collisions between enemy projectiles and player
*/
void detectEnemyProjectileCollision() {
//player cannot be hit whilst sheild is up
if(!player.getSheild()) {
Iterator<Projectile> projectileIterator = enemyProjectiles.iterator();
while(projectileIterator.hasNext()) {
Projectile projectile = projectileIterator.next();
//https://silentmatt.com/rectangle-intersection/
int eX1 = projectile.getX() - (projectile.getScaleFactor() / 2);
int eY1 = projectile.getY() - (projectile.getScaleFactor() / 2);
int eX2 = projectile.getX() + (projectile.getScaleFactor() / 2);
int eY2 = projectile.getY() + (projectile.getScaleFactor() / 2);
int pX1 = player.getX() - (player.getScaleFactor() / 2);
int pY1 = player.getY() - (player.getScaleFactor() / 2);
int pX2 = player.getX() + (player.getScaleFactor() / 2);
int pY2 = player.getY() + (player.getScaleFactor() / 2);
if ((eX1 < pX2) && (eX2 > pX1) && (eY1 < pY2) && (eY2 > pY1)) {
//if ((eX == player.getX()) && (eY == player.getY())) {
death();
projectileIterator.remove();
}
}
}
}
/*
* detect collisions between the player and enemies
*/
void detectEnemyCollision() {
//player cannot die whilst sheild is up
if(!player.getSheild()) {
List<Enemy> newEnemies = new ArrayList<Enemy>();
Iterator<Enemy> enemyIterator = enemies.iterator();
while(enemyIterator.hasNext()) {
Enemy enemy = enemyIterator.next();
//https://silentmatt.com/rectangle-intersection/
int eX1 = enemy.getX() - (enemy.getScaleFactor() / 2);
int eY1 = enemy.getY() - (enemy.getScaleFactor() / 2);
int eX2 = enemy.getX() + (enemy.getScaleFactor() / 2);
int eY2 = enemy.getY() + (enemy.getScaleFactor() / 2);
int pX1 = player.getX() - (player.getScaleFactor() / 2);
int pY1 = player.getY() - (player.getScaleFactor() / 2);
int pX2 = player.getX() + (player.getScaleFactor() / 2);
int pY2 = player.getY() + (player.getScaleFactor() / 2);
if ((eX1 < pX2) && (eX2 > pX1) && (eY1 < pY2) && (eY2 > pY1)) {
if(enemy.hit()) {
List<Enemy> spawnlings = enemy.spawnlings();
if (spawnlings != null) newEnemies.addAll(spawnlings);
enemyIterator.remove();
}
death();
}
}
enemies.addAll(newEnemies);
}
}
/*
* Render bomb
*/
void renderBomb() {
int col = bomb.x;
int row = bomb.y;
int x = dimensions.widthOffset + (col - startCol) * dimensions.tileWidth;
int y = dimensions.heightOffset + (row - startRow) * dimensions.tileHeight;
noFill();
strokeWeight(16);
ellipseMode(CENTER);
float diameter = bomb.getDiameter() * dimensions.tileWidth;
ellipse(x, y, diameter, diameter);
strokeWeight(4);
fill(255);
}
/*
* Destory enemies hit by bomb
*/
void detectBombCollision() {
List<Enemy> newEnemies = new ArrayList<Enemy>();
Iterator<Enemy> enemyIterator = enemies.iterator();
while(enemyIterator.hasNext()) {
Enemy enemy = enemyIterator.next();
if (sqrt((float) Math.pow((enemy.getX() - bomb.x), 2) + (float) Math.pow((enemy.getY() - bomb.y), 2)) < bomb.getDiameter()/2) {
score += enemy.getScore();
List<Enemy> spawnlings = enemy.spawnlings();
if (spawnlings != null) newEnemies.addAll(spawnlings);
enemyIterator.remove();
}
}
enemies.addAll(newEnemies);
}
/*
* Called when the player dies
*/
void death() {
player.hit();
playerProjectiles = new ArrayList<Projectile>();
enemyProjectiles = new ArrayList<Projectile>();
lives--;
if (lives == 0) {
stopAllSoundEffects();
playSoundEffect("death");
gameOverTime = millis();
gameOver = true;
}
else {
playSoundEffect("shield");
}
}
void checkForRewards() {
if (score >= nextBomb) {
nextBomb += conf.extraBombInterval;
if (bombs < conf.initialBombs) {
bombs++;
}
}
if (score >= nextLife) {
nextLife += conf.extraLifeInterval;
lives++;
}
}
/**
* Pause screen
*/
void pause() {
renderLevel();
textSize(dimensions.adjustedWidth / 11);
textAlign(CENTER);
int x = dimensions.widthOffset + dimensions.adjustedWidth / 2;
int y = dimensions.heightOffset + dimensions.adjustedHeight / 2 - (dimensions.adjustedHeight / 23);
text(conf.pauseMsg1, x, y);
textSize(dimensions.adjustedWidth / 20);
y = dimensions.heightOffset + dimensions.adjustedHeight / 2 + (dimensions.adjustedHeight / 23);
text(conf.pauseMsg2, x, y);
showControls();
if (controller.buttonA()) {
playSoundEffect("resume");
controller.resetInitialHeading();
pause = false;
//adjust timers to account for time paused
long currentTime = millis();
startTime += (currentTime - pauseTime);
}
}
/**
* Display loading screen while resources load
*/
void loadingScreen() {
textSize(dimensions.adjustedWidth / 11);
textAlign(CENTER);
int x = dimensions.widthOffset + dimensions.adjustedWidth / 2;
int y = dimensions.heightOffset + dimensions.adjustedHeight / 2;
text(conf.loadingMsg, x, y);
}
/**
* Display loading screen while resources load
*/
void gameOver() {
textSize(dimensions.adjustedWidth / 11);
textAlign(CENTER);
int x = dimensions.widthOffset + dimensions.adjustedWidth / 2;
int y = dimensions.heightOffset + dimensions.adjustedHeight / 2 - (dimensions.adjustedHeight / 23);
text(conf.gameOverMsg, x, y);
textSize(dimensions.adjustedWidth / 20);
y = dimensions.heightOffset + dimensions.adjustedHeight / 2 + (dimensions.adjustedHeight / 23);
String scoreString = "Score: " + score;
text(scoreString, x, y);
textAlign(LEFT);
textSize(dimensions.adjustedWidth / 45);
x = dimensions.widthOffset + (dimensions.adjustedWidth / 72);
y = dimensions.heightOffset + dimensions.adjustedHeight / 2 + (dimensions.adjustedHeight / 8);
text(conf.credits, x, y);
long currentTime = millis();
if (currentTime - gameOverTime > 5000) {
titleScreen = true;
gameOver = false;
story = false;
pause = false;
score = 0;
lives = conf.initialLives;
bombs = conf.initialBombs;
difficulty = 0;
enemies = new ArrayList<Enemy>();
}
}
/**
* Show title Screen
*/
void titleScreen() {
storyCounter++;
renderLevel();
textSize(dimensions.adjustedWidth / 11);
textAlign(CENTER);
int x = dimensions.widthOffset + dimensions.adjustedWidth / 2;
int y = dimensions.heightOffset + dimensions.adjustedHeight / 2 - (dimensions.adjustedHeight / 23);
text(conf.title, x, y);
textSize(dimensions.adjustedWidth / 25);
y = dimensions.heightOffset + dimensions.adjustedHeight / 2 + (dimensions.adjustedHeight / 23);
text(conf.titleMsg1, x, y);
showControls();
textSize(dimensions.adjustedWidth / 50);
x = dimensions.widthOffset + dimensions.adjustedWidth - dimensions.adjustedWidth/15;
y = dimensions.heightOffset + dimensions.adjustedHeight - dimensions.adjustedHeight/45;
text(conf.copyright, x, y);
if (storyCounter >= conf.storyWaitTime * conf.framerate) {
titleScreen = false;
story = true;
}
if ((controller.buttonA()) && (controller.buttonB())) {
playSoundEffect("shield");
titleScreen = false;
storyCounter = 0;
controller.resetInitialHeading();
nextLife = conf.extraLifeInterval;
nextBomb = conf.extraBombInterval;
player = new Player(level.getStartX(), level.getStartY(), 0, spriteSet.get("player"));
startTime = millis();
lastShot = millis();
spawnTime = millis();
}
}
/*
* Show controls
*/
void showControls() {
textAlign(LEFT);
textSize(dimensions.adjustedWidth / 45);
int x = dimensions.widthOffset + (dimensions.adjustedWidth / 72);
int y = dimensions.heightOffset + dimensions.adjustedHeight / 2 + (dimensions.adjustedHeight / 8);
text(conf.controlsMsg, x, y);
}
/*
* count the number of lines in a string
* taken from: https://stackoverflow.com/a/18816371
*/
int countLines(String str) {
if(str == null || str.isEmpty())
{
return 0;
}
int lines = 1;
int pos = 0;
while ((pos = str.indexOf("\n", pos) + 1) != 0) {
lines++;
}
return lines;
}
/*
* Star wars style scrolling text
*/
void storyScroll() {
renderLevel();
pushMatrix();
int x = dimensions.widthOffset + (dimensions.adjustedWidth / 2);
int y = dimensions.heightOffset + (dimensions.adjustedHeight/2);
translate(x, y);
fill(255,255,0);
textAlign(CENTER);
textSize(dimensions.adjustedWidth / 20);
text(conf.storyCrawl, 0, storyY);
storyY -= conf.scrollSpeed;
fill(255);
popMatrix();
//if any button is pressed or end of story is reached
if ((controller.buttonA()) || (controller.buttonB()) || (controller.buttonC()) || (storyY < (dimensions.heightOffset + (dimensions.adjustedHeight/1.75) - (storyLines * (dimensions.adjustedWidth / 10))))) {
titleScreen = true;
story = false;
storyCounter = 0;
storyY = dimensions.heightOffset + (dimensions.adjustedHeight/1.75);
}
}
/*
* Play sound effect
*/
void playSoundEffect(String name) {
if (soundEffects.containsKey(name)) {
if (!soundEffects.get(name).isPlaying()) {
soundEffects.get(name).play(0);
}
}
else {
print(name);
println(" not found!");
}
}
void stopSoundEffect(String name) {
if (soundEffects.containsKey(name)) {
soundEffects.get(name).pause();
}
else {
print(name);
println(" not found!");
}
}
void stopAllSoundEffects() {
for (String effect : soundEffects.keySet()) {
stopSoundEffect(effect);
}
}
/*
* Play random song
*/
void playRandomSong() {
List<String> keys = new ArrayList<String>(music.keySet());
int index = int(random(0, keys.size()));
String key = keys.get(index);
if (key == activeSong) {
playRandomSong();
}
activeSong = key;
music.get(activeSong).play(0);
}
/**
* Perform all intensive laod operations
* Ideally in a seperate thread
*/
void loadResources() {
try {
tileSet = generateTileSet();
props = new TileProperties(conf);
spriteSet = new HashMap<String, PImage[]>();
spriteSet.put("player", loadPlayerSprites());
spriteSet.put("bomb", loadBombSprites());
spriteSet.put("asteroid", loadAsteroidSprites());
spriteSet.put("jellyfish", loadJellyfishSprites());
spriteSet.put("gunPlatform", loadGunPlatformSprites());
spriteSet.put("saucer", loadSaucerSprites());
spriteSet.put("boss", loadBossSprites());
soundEffects = loadSoundEffects();
music = loadMusic();
}
catch (Exception e) {
e.printStackTrace();
}
level = new Level(conf, props);
level.generateRandomLevel(conf.levelWidth, conf.levelHeight, 2, 24, 1, 99);
//temp player
player = new Player(level.getStartX(), level.getStartY(), 0, spriteSet.get("player"));
titleScreen = true;
loading = false;
}
/**
* Load every image in the tile directory into an id->image map
* @return populate id->image map
* @throws IOException if tile directory cannot be accessed
*/
Map<Integer, PImage> generateTileSet() throws IOException {
Map<Integer, PImage> map = null;
File tileDirectory = new File(sketchPath() + System.getProperty("file.separator") + conf.tilePath);
//if conf.tilePath can be accessed, loop through and add each tile to the map
if ((tileDirectory.exists()) && (tileDirectory.isDirectory())) {
//Return the unknown tile (id=99999) if a tile for the requested id cannot be found
PImage unknownTile = loadImage(conf.tilePath + System.getProperty("file.separator") + conf.unknownTitle + conf.tileImageExtension);
map = new DefaultHashMap<Integer, PImage>(unknownTile);
for (File file : tileDirectory.listFiles()) {
if (file.getName().endsWith(conf.tileImageExtension)) {
int id = Integer.parseInt(file.getName().replace(conf.tileImageExtension, ""));
PImage image = loadImage(file.getAbsolutePath());
map.put(id, image);
}
}
}
else {
println("Could not find tile directory.");
exit();
}
return map;
}
/*
* Load player sprites
* @return PImage[] of player sprites
*/
PImage[] loadPlayerSprites() {
PImage[] sprites = {
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "player0.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "player1.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "player2.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "player3.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "player4.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "player5.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "player6.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "player7.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "player8.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "sheild.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "playerProjectile.png"),
};
return sprites;
}
PImage[] loadBombSprites() {
PImage[] sprites = {
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "bomb-icon.png")
};
return sprites;
}
PImage[] loadAsteroidSprites() {
PImage[] sprites = {
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "asteroid0.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "asteroid1.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "asteroid2.png")
};
return sprites;
}
PImage[] loadJellyfishSprites() {
PImage[] sprites = {
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "jellyfish0.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "jellyfish1.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "jellyfish2.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "jellyfish3.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "jellyfish4.png")
};
return sprites;
}
PImage[] loadGunPlatformSprites() {
PImage[] sprites = {
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "gunPlatform.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "gunPlatformProjectile.png")
};
return sprites;
}
PImage[] loadSaucerSprites() {
PImage[] sprites = {
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "saucer.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "saucerProjectile.png")
};
return sprites;
}
PImage[] loadBossSprites() {
PImage[] sprites = {
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "boss0.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "boss1.png"),
loadImage(sketchPath() + System.getProperty("file.separator") + conf.spritePath + System.getProperty("file.separator") + "bossProjectile.png")
};
return sprites;
}
Map<String, AudioPlayer> loadSoundEffects() {
Map<String, AudioPlayer> map = new HashMap<String, AudioPlayer>();
map.put("bomb", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.soundPath + System.getProperty("file.separator") + "bomb.mp3"));
map.put("death", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.soundPath + System.getProperty("file.separator") + "death.mp3"));
map.put("explosion", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.soundPath + System.getProperty("file.separator") + "explosion.mp3"));
map.put("forwards", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.soundPath + System.getProperty("file.separator") + "forwards.mp3"));
map.put("reverse", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.soundPath + System.getProperty("file.separator") + "reverse.mp3"));
map.put("rotate", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.soundPath + System.getProperty("file.separator") + "rotate.mp3"));
map.put("shield", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.soundPath + System.getProperty("file.separator") + "shield.mp3"));
map.put("shoot", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.soundPath + System.getProperty("file.separator") + "shoot.mp3"));
map.put("pause", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.soundPath + System.getProperty("file.separator") + "pause.mp3"));
map.put("resume", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.soundPath + System.getProperty("file.separator") + "resume.mp3"));
return map;
}
Map<String, AudioPlayer> loadMusic() {
Map<String, AudioPlayer> map = new HashMap<String, AudioPlayer>();
map.put("cold-moon", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.musicPath + System.getProperty("file.separator") + "Cold-Moon.mp3"));
map.put("cyber-rem",minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.musicPath + System.getProperty("file.separator") + "Cyber-R.E.M.mp3"));
map.put("dystopic-factory", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.musicPath + System.getProperty("file.separator") + "Dystopic-Factory.mp3"));
map.put("escape-from-the-insane-machines", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.musicPath + System.getProperty("file.separator") + "Escape-From-the-Insane-Machines.mp3"));
map.put("light-years", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.musicPath + System.getProperty("file.separator") + "Light-Years.mp3"));
map.put("retro-scifi-planet", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.musicPath + System.getProperty("file.separator") + "Retro-Sci-Fi-Planet.mp3"));
map.put("sector-off-limits", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.musicPath + System.getProperty("file.separator") + "Sector-Off-Limits.mp3"));
map.put("terraforming-begins", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.musicPath + System.getProperty("file.separator") + "Terraforming-Begins.mp3"));
map.put("trouble-on-mercury", minim.loadFile(sketchPath() + System.getProperty("file.separator") + conf.musicPath + System.getProperty("file.separator") + "Trouble-On-Mercury.mp3"));
return map;
}