/*
*
* Created: Jun 7 2006
*
* Copyright (C) 1999-2000 Fabien Sanglard
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package rtype;
import generator.EnemyWave;
import generator.GeneratorSet;
import generator.HomingMissileGenerator;
import generator.IGenerator;
import generator.IntroGenerator;
import generator.LadyBirdGenerator;
import generator.StarGenerator;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Random;
import java.util.logging.Logger;
import org.lwjgl.Sys;
//import org.lwjgl.devil.IL;
//import org.lwjgl.devil.ILU;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.Timer;
import org.lwjgl.util.glu.GLU;
import org.lwjgl.util.vector.Vector2f;
import rtype.entity.Bonus;
import rtype.entity.Entity;
import rtype.entity.Explosion;
import rtype.entity.HomingMissile;
import rtype.entity.Text;
import rtype.entity.IEntity;
import rtype.entity.LadyBird;
import rtype.entity.Planet;
import rtype.entity.PlayerShip;
import rtype.entity.SpaceTrash;
import rtype.entity.Star;
import rtype.entity.TextEntityCounter;
/* TODO
- Fix texture loading, DEVil is not yet usable with OS X or Linux platform
- Intro with musique would be great to add
- Add distorsion effect for magnetic orb
- Implement link between orb and ship
- Add smartBomb: I am thinking of a rain of homing missiles
*/
public class Prototyp {
// Init parameters
static public final int SCREEN_WIDTH = 800;
static public final int SCREEN_HEIGHT = 600; //480
private static final boolean FULL_SCREEN = false;
//
boolean gameOff = false;
// Default values for sprites
public static float DEFAULT_Z = 0;
public static final Vector2f DEFAULT_SCROLLING_SPEED = new Vector2f(-5,0);
// This is the player's sprite
public static PlayerShip player1 = null;
// Set of layers, drawn in a different order ( see render method)
static public Layer bullets = new Layer();
static public Layer enemies = new Layer();
static public Layer fx = new Layer();
static public Layer bonus = new Layer();
static public Layer background = new Layer();
static public Layer frontground = new Layer();
static public Layer text = new Layer();
// Random object, used by every object that need randomness during the game
public final static Random random = new Random(System.currentTimeMillis());
static boolean useDevil = false;
// Variables used to calculate fps
private int frames;
static float deltas = 0;
static float fps;
// Text object used to display fps, score and entity total count
private Text textFPS;
private Text textHisCore;
private TextEntityCounter entitiesCount ;
static Timer timer = new Timer();
static float lastTime = timer.getTime();
// Measure time elapsed since last frame renderer
// This is the heart variable of the engine
public static float tick;
// Generators generate entity for the game
public static GeneratorSet generator = null;
// Texture loader is used to load and server textures to entities on init
public static ITextureLoader textureLoader = null;
// Starting point
public static void main(String args[]) {
if (args.length > 0)
useDevil = "useDevil".equals(args[0]);
Prototyp l8 = new Prototyp();
l8.run();
}
public Prototyp()
{
init();
initGL();
}
// Main loop
public void run() {
Intro intro = new Intro(this);
intro.play();
BonusDesc bonusDesc = new BonusDesc(this);
bonusDesc.play();
addBasicEntities();
player1.addEventListeners();
Bonus b = BonusFactory.createBonus(IEntity.BONUS_LIGHTNING_ORB);
b.spawn(new Vector2f(player1.position.x+100,player1.position.y), DEFAULT_SCROLLING_SPEED, bonus);
addControlKeys();
while (!gameOff)
{
heartBeat();
getEntries();
update();
checkCollisons();
render();
frames++;
if (frames == 50)
{
fps = frames/deltas;
frames = 0;
deltas = 0 ;
textFPS.setString("fps:"+(int)fps);
}
Display.update();
// Display.sync(10);
generator.generate();
}
Display.destroy();
}
private void createWindow(int screenWidth, int screenHeight, boolean fullscreen) throws Exception {
if (!fullscreen) // create windowed mode
Display.setDisplayMode(new DisplayMode(screenWidth, screenHeight));
else
{
Display.setFullscreen(true);
try
{
DisplayMode dm[] = org.lwjgl.util.Display.getAvailableDisplayModes(320, 240, -1, -1, -1, -1, 60, 85);
org.lwjgl.util.Display.setDisplayMode(dm, new String[] {
"width=" + screenWidth, "height=" + screenHeight, "freq=85",
"bpp=" + Display.getDisplayMode().getBitsPerPixel()
});
}
catch(Exception e)
{
Sys.alert("Error", "Could not start full screen, switching to windowed mode");
Display.setDisplayMode(new DisplayMode(screenWidth, screenHeight));
}
}
Display.create();
}
private void addBasicEntities() {
player1 = new PlayerShip(); // Calling default constructor, if you want to chanage them: PlayerShip(Use Keyboard.KEY_UP,Keyboard.KEY_DOWN,Keyboard.KEY_LEFT,Keyboard.KEY_RIGHT,Keyboard.KEY_SPACE,Keyboard.KEY_X)
player1.spawn(new Vector2f(-150,-100),new Vector2f(0,0),bullets);
player1.addBooster();
textFPS = new Text("");
textFPS.spawn(new Vector2f(SCREEN_WIDTH/2-100,SCREEN_HEIGHT/2-20),new Vector2f(0,0),text);
textHisCore = new Text("HISCORE:");
textHisCore.spawn(new Vector2f(-SCREEN_WIDTH/2+20,SCREEN_HEIGHT/2 - 20),new Vector2f(0,0),text);
entitiesCount = new TextEntityCounter();
entitiesCount.spawn(new Vector2f(-SCREEN_WIDTH/2+20,-SCREEN_HEIGHT/2 + 20),new Vector2f(0,0),text);
}
public static void heartBeat() {
Timer.tick();
tick = timer.getTime() - lastTime;
deltas+= tick;
lastTime = timer.getTime();
}
void update()
{
bullets.update();
enemies.update();
fx.update();
background.update();
bonus.update();
frontground.update();
text.update();
}
private void checkCollisons()
{
// Check bullets with enemies
ArrayList<Entity> bulletsArray = bullets.entities;
ArrayList<Entity> enemiesArray = enemies.entities;
Entity currentBullet = null;
Entity currentEnemy = null;
for (int i = 0; i < bulletsArray.size() ; i++)
{
for (int j= 0 ; j < enemiesArray.size(); j++ )
{
if (j < 0)
continue;
if (i<0)
break;
currentBullet = bulletsArray.get(i);
currentEnemy = enemiesArray.get(j);
if (Collision.boxBoxOverlap(currentBullet,
currentEnemy
))
{
player1.hiscore++;
if (currentBullet.collided(currentEnemy))
i--;
if (currentEnemy.collided(currentBullet))
j--;
}
}
}
textHisCore.setString("HISCORE:"+player1.hiscore);
// Check players with bonuses
ArrayList<Entity> bonusArray = bonus.entities;
Entity currentBonus = null;
for (int j= 0 ; j < bonusArray.size(); j++ )
{
currentBonus = bonusArray.get(j);
if(
Collision.boxBoxOverlap( player1,
currentBonus
)
)
{
if (currentBonus.collided(player1))
j--;
player1.collided(currentBonus);
}
}
}
// Init engine
private void init()
{
try
{
Mouse.setGrabbed(false);
createWindow(SCREEN_WIDTH, SCREEN_HEIGHT, FULL_SCREEN) ;
createOffScreenBuffer();
//Display.setVSyncEnabled(true);
//TODO This is the generators
generator = new GeneratorSet();
generator.addGenerator(new StarGenerator());
generator.addGenerator(new IntroGenerator());
generator.addGenerator(new EnemyWave(100,EnemyWave.rate));
generator.addGenerator(new EnemyWave(150,EnemyWave.rate/2));
generator.addGenerator(new EnemyWave(-100,EnemyWave.rate + 10));
generator.addGenerator(new EnemyWave(-150,EnemyWave.rate/2+ 10));
generator.addGenerator(new EnemyWave(0,EnemyWave.rate + 20));
generator.addGenerator(new EnemyWave(50,EnemyWave.rate/2 + 20));
generator.addGenerator(new LadyBirdGenerator(EnemyWave.rate/2 + 30));
}
catch(Exception e)
{
e.printStackTrace(System.out);
}
}
private void initGL() {
GL11.glEnable(GL11.GL_TEXTURE_2D); // Enable Texture Mapping
GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background
GL11.glClearDepth(1.0f); // Depth Buffer Setup
GL11.glDisable(GL11.GL_DEPTH_TEST); // Enables Depth Testing
GL11.glEnable(GL11.GL_BLEND);
GL11.glDepthMask(false);
GL11.glMatrixMode(GL11.GL_PROJECTION); // Select The Projection Matrix
GL11.glLoadIdentity(); // Reset The Projection Matrix
GLU.gluOrtho2D(-(int)SCREEN_WIDTH/2,(int)SCREEN_WIDTH/2,(int)-SCREEN_HEIGHT/2,(int)SCREEN_HEIGHT/2);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
if (useDevil)
;//textureLoader = new DevilTextureLoader();
else
{
textureLoader = new WorkAroundTextureLoader();
}
textureLoader.init();
}
private static boolean P_KEY_PRESSED = false;
// LadyBirds
private static boolean P_KEY_F1 = false;
private IGenerator ladyBirdGenerator = new LadyBirdGenerator(30);
//HomingMissiles
private static boolean P_KEY_F2 = false;
private IGenerator homingGenerator = new HomingMissileGenerator();
private static Text pause = null;
private void addControlKeys()
{
KeyListener pauseKeyEvent = new KeyListener()
{
public void onKeyDown()
{
if (timer.isPaused())
{
timer.resume();
pause.unSpawn();
}
else
{
pause = new Text("GAME PAUSED");
//pause.setRatio(1.5f);
pause.spawn(new Vector2f(-50,0),new Vector2f(0,0),frontground);
timer.pause();
}
};
};
EventManager.instance().addListener(Keyboard.KEY_P, pauseKeyEvent);
KeyListener homingMissileKeyEvent = new KeyListener()
{
public void onKeyDown()
{
if (generator.contains(homingGenerator))
generator.removeGenerator(homingGenerator);
else
generator.addGenerator(homingGenerator);
};
};
EventManager.instance().addListener(Keyboard.KEY_F1, homingMissileKeyEvent);
KeyListener enemiesKeyEvent = new KeyListener()
{
public void onKeyDown()
{
if (generator.contains(ladyBirdGenerator))
generator.removeGenerator(ladyBirdGenerator);
else
generator.addGenerator(ladyBirdGenerator);
};
};
EventManager.instance().addListener(Keyboard.KEY_F2, enemiesKeyEvent);
}
private void getEntries()
{
if (exitRequested())
gameOff = true;
EventManager.instance(). checkEvents();
}
boolean exitRequested() {
return (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE) || Display.isCloseRequested()) ;
}
// This method render current scene state
void render()
{
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT); // Clear The Screen And The Depth Buffer
background.render();
// Make backGround darker
{
fadeAlpha = 0.65f;
fadeScreen(false);
fadeAlpha = 0;
}
enemies.render();
bonus.render();
fx.render();
// This make a copy of the screen in a texture
// It is used later for deformations effects
saveScreen();
applyDistorsions();
bullets.render();
frontground.render();
text.render();
fadeScreen(true);
//this.drawPowerBar();
//this.renderScanLines();
}
private void saveScreen()
{
GL11.glLoadIdentity();
GL11.glBindTexture(GL11.GL_TEXTURE_2D,SCREEN_TEXTURE_ID);
GL11.glCopyTexSubImage2D(GL11.GL_TEXTURE_2D,0, 0, 0, 0, 0,(int)SCREEN_WIDTH,(int)SCREEN_HEIGHT);
}
// This is called when player charge power
/* ----------------------------------------------
- -
- -
- -
- -
- --- -
- - - -
- --- -
- -
- -
- -
- -
- -
----------------------------------------------
*/
private void applyDistorsions()
{
if (player1 == null)
return;
float chargeP = player1.power/PlayerShip.MAX_POWER ;
float R_WIDTH = 350 * chargeP;
float R_HEIGHT = 200 * chargeP;
float MIDDLE_X_ON_SCREEN = R_WIDTH/2* chargeP;
float R_POSITION_X = player1.position.x+25;
float R_POSITION_Y = player1.position.y-R_HEIGHT/2;
float TEXT_OUT_X_LOW = (R_POSITION_X+(SCREEN_WIDTH/2f))/1024f ;
float TEXT_OUT_X_HIGH = (R_POSITION_X+R_WIDTH+(SCREEN_WIDTH/2f))/1024;
float TEXT_OUT_Y_LOW = (R_POSITION_Y+(SCREEN_HEIGHT/2f))/1024;
float TEXT_OUT_Y_HIGH = (R_POSITION_Y+R_HEIGHT+(SCREEN_HEIGHT/2f))/1024;
float TEXT_IN_X_LOW = (R_POSITION_X+R_WIDTH/2+(SCREEN_WIDTH/2f))/1024f ;
float TEXT_IN_X_HIGH = (R_POSITION_X+R_WIDTH/2 + 1+(SCREEN_WIDTH/2f))/1024;
float TEXT_IN_Y_LOW = (R_POSITION_Y+R_HEIGHT/2 +(SCREEN_HEIGHT/2f))/1024f ;
float TEXT_IN_Y_HIGH = (R_POSITION_Y+R_HEIGHT/2 + 1+(SCREEN_HEIGHT/2f))/1024;
float SCR_OUT_X_LOW = R_POSITION_X ;
float SCR_OUT_X_HIGH = R_POSITION_X+R_WIDTH;
float SCR_OUT_Y_LOW = R_POSITION_Y;
float SCR_OUT_Y_HIGH = R_POSITION_Y+R_HEIGHT;
float SCR_IN_X_LOW = R_POSITION_X+MIDDLE_X_ON_SCREEN ;
float SCR_IN_X_HIGH = R_POSITION_X+MIDDLE_X_ON_SCREEN + 1;
float SCR_IN_Y_LOW = R_POSITION_Y+R_HEIGHT/2;
float SCR_IN_Y_HIGH = R_POSITION_Y + R_HEIGHT/2 + 1;
GL11.glBlendFunc(GL11.GL_SRC_ALPHA,GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glColor4f(1,1,1,0.75f);
GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2f(TEXT_IN_X_HIGH,TEXT_IN_Y_HIGH);
GL11.glVertex2f(SCR_IN_X_HIGH, SCR_IN_Y_HIGH); // Lower right
GL11.glTexCoord2f(TEXT_OUT_X_HIGH,TEXT_OUT_Y_HIGH);
GL11.glVertex2f(SCR_OUT_X_HIGH,SCR_OUT_Y_HIGH); //Lower left
GL11.glTexCoord2f(TEXT_OUT_X_HIGH,TEXT_OUT_Y_LOW);
GL11.glVertex2f(SCR_OUT_X_HIGH,SCR_OUT_Y_LOW); //upper left
GL11.glTexCoord2f(TEXT_IN_X_HIGH,TEXT_IN_Y_LOW);
GL11.glVertex2f(SCR_IN_X_HIGH,SCR_IN_Y_LOW); // //upper right
}
GL11.glEnd();
GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2f(TEXT_IN_X_HIGH,TEXT_IN_Y_LOW);
GL11.glVertex2f(SCR_IN_X_HIGH, SCR_IN_Y_LOW); // Lower right
GL11.glTexCoord2f(TEXT_OUT_X_HIGH,TEXT_OUT_Y_LOW);
GL11.glVertex2f(SCR_OUT_X_HIGH,SCR_OUT_Y_LOW); //Lower left
GL11.glTexCoord2f(TEXT_OUT_X_LOW,TEXT_OUT_Y_LOW);
GL11.glVertex2f(SCR_OUT_X_LOW,SCR_OUT_Y_LOW); //upper left
GL11.glTexCoord2f(TEXT_IN_X_LOW,TEXT_IN_Y_LOW);
GL11.glVertex2f(SCR_IN_X_LOW,SCR_IN_Y_LOW); // //upper right
}
GL11.glEnd();
GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2f(TEXT_IN_X_LOW,TEXT_IN_Y_LOW);
GL11.glVertex2f(SCR_IN_X_LOW, SCR_IN_Y_LOW); // Lower right
GL11.glTexCoord2f(TEXT_OUT_X_LOW,TEXT_OUT_Y_HIGH);
GL11.glVertex2f(SCR_OUT_X_LOW,SCR_OUT_Y_HIGH); //Lower left
GL11.glTexCoord2f(TEXT_OUT_X_HIGH,TEXT_OUT_Y_HIGH);
GL11.glVertex2f(SCR_OUT_X_HIGH,SCR_OUT_Y_HIGH); //upper left
GL11.glTexCoord2f(TEXT_IN_X_HIGH,TEXT_IN_Y_HIGH);
GL11.glVertex2f(SCR_IN_X_HIGH,SCR_IN_Y_HIGH); // //upper right
}
GL11.glEnd();
GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2f(TEXT_IN_X_LOW,TEXT_IN_Y_LOW);
GL11.glVertex2f(SCR_IN_X_LOW, SCR_IN_Y_LOW); // Lower right
GL11.glTexCoord2f(TEXT_OUT_X_LOW,TEXT_OUT_Y_LOW);
GL11.glVertex2f(SCR_OUT_X_LOW,SCR_OUT_Y_LOW); //Lower left
GL11.glTexCoord2f(TEXT_OUT_X_LOW,TEXT_OUT_Y_HIGH);
GL11.glVertex2f(SCR_OUT_X_LOW,SCR_OUT_Y_HIGH); //upper left
GL11.glTexCoord2f(TEXT_IN_X_LOW,TEXT_IN_Y_HIGH);
GL11.glVertex2f(SCR_IN_X_LOW,SCR_IN_Y_HIGH); // //upper right
}
GL11.glEnd();
//GL11.glBlendFunc(GL11.GL_SRC_ALPHA,GL11.GL_ONE);
GL11.glColor4f(1,1,1,1);
}
public static float fadeAlpha = 0;
public void fadeScreen(boolean draw_fb) // draw_fb is a quick and dirty fix, fb should be in a specificl layer "postFadeEffect" or something
{
if (fadeAlpha > 0.1)
{
GL11.glLoadIdentity();
GL11.glTranslatef(0,0,Prototyp.DEFAULT_Z);
GL11.glColor4f(0,0,0,fadeAlpha/1.2f);
GL11.glDisable(GL11.GL_TEXTURE_2D);
//GL11.glColor4f(0.5f,0.5f,0.5f,0.5f);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA,GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glBegin(GL11.GL_QUADS);
{
GL11.glVertex2f(Prototyp.SCREEN_WIDTH/2, -Prototyp.SCREEN_HEIGHT/2);
GL11.glVertex2f(-Prototyp.SCREEN_WIDTH/2, -Prototyp.SCREEN_HEIGHT/2);
GL11.glVertex2f(-Prototyp.SCREEN_WIDTH/2,Prototyp.SCREEN_HEIGHT/2);
GL11.glVertex2f(Prototyp.SCREEN_WIDTH/2,Prototyp.SCREEN_HEIGHT/2);
}
GL11.glEnd();
GL11.glColor4f(1,1,1,1);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA,GL11.GL_ONE);
}
if (player1 != null && player1.orb != null && player1.orb.fb!= null && player1.orb.fb.displayAnimation && draw_fb)
{
player1.orb.fb.updateTick();
player1.orb.fb.draw(fadeAlpha*2);
}
}
// Not used, would need more work
private void drawPowerBar()
{
GL11.glLoadIdentity();
GL11.glTranslatef(0,0,Prototyp.DEFAULT_Z);
GL11.glColor4f(0,0,1,1f);
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glBegin(GL11.GL_QUADS);
GL11.glVertex2f(0,-0 );
GL11.glVertex2f(0,-0 -100);
GL11.glVertex2f(0 + player1.power*10/4 ,0 );
GL11.glVertex2f(0 + player1.power*10/4 ,0 -100 );
GL11.glEnd();
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glColor4f(1,1,1,1f);
}
// Not used, didn't like the effect
private void renderScanLines()
{
GL11.glLoadIdentity();
GL11.glTranslatef(0,0,Prototyp.DEFAULT_Z);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureLoader.getTexture(IEntity.SCANLINE).getTextureId());
GL11.glColor4f(1,1,1,1);
GL11.glBegin(GL11.GL_QUADS);
GL11.glTexCoord2f(20,0); //Upper right
GL11.glVertex2f(SCREEN_WIDTH/4, -SCREEN_HEIGHT/4);
GL11.glTexCoord2f(0,0); //Upper left
GL11.glVertex2f(-SCREEN_WIDTH/4, -SCREEN_HEIGHT/4);
GL11.glTexCoord2f(0,20); //Lower left
GL11.glVertex2f(-SCREEN_WIDTH/4,SCREEN_HEIGHT/4);
GL11.glTexCoord2f(20,20); // Lower right
GL11.glVertex2f(SCREEN_WIDTH/4,SCREEN_HEIGHT/4 );
GL11.glEnd();
}
// This is the address where screen copy is stored
public static int SCREEN_TEXTURE_ID = 0;
private void createOffScreenBuffer()
{
int bytesPerPixel = 3;
ByteBuffer scratch = ByteBuffer.allocateDirect(1024* 1024 * bytesPerPixel);
IntBuffer buf = ByteBuffer.allocateDirect(12).order(ByteOrder.nativeOrder()).asIntBuffer();
GL11.glGenTextures(buf); // Create Texture In OpenGL
GL11.glBindTexture(GL11.GL_TEXTURE_2D, buf.get(0));
SCREEN_TEXTURE_ID = buf.get(0);
int glType = GL11.GL_RGB;
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0,glType,1024,1024, 0,glType, GL11.GL_UNSIGNED_BYTE, scratch);
}
}