001 /*
002 *
003 * Created: Jun 7 2006
004 *
005 * Copyright (C) 1999-2000 Fabien Sanglard
006 *
007 * This program is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU General Public License
009 * as published by the Free Software Foundation; either version 2
010 * of the License, or (at your option) any later version.
011 *
012 * This program is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with this program; if not, write to the Free Software
019 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020 */
021
022 package rtype;
023
024 import generator.EnemyWave;
025 import generator.GeneratorSet;
026 import generator.HomingMissileGenerator;
027 import generator.IGenerator;
028 import generator.IntroGenerator;
029 import generator.LadyBirdGenerator;
030 import generator.StarGenerator;
031
032 import java.nio.ByteBuffer;
033 import java.nio.ByteOrder;
034 import java.nio.IntBuffer;
035 import java.util.ArrayList;
036 import java.util.Random;
037 import java.util.logging.Logger;
038
039 import org.lwjgl.Sys;
040 //import org.lwjgl.devil.IL;
041 //import org.lwjgl.devil.ILU;
042 import org.lwjgl.input.Keyboard;
043 import org.lwjgl.input.Mouse;
044 import org.lwjgl.opengl.Display;
045 import org.lwjgl.opengl.DisplayMode;
046 import org.lwjgl.opengl.GL11;
047 import org.lwjgl.util.Timer;
048 import org.lwjgl.util.glu.GLU;
049 import org.lwjgl.util.vector.Vector2f;
050
051 import rtype.entity.Bonus;
052 import rtype.entity.Entity;
053 import rtype.entity.Explosion;
054 import rtype.entity.HomingMissile;
055 import rtype.entity.Text;
056 import rtype.entity.IEntity;
057 import rtype.entity.LadyBird;
058 import rtype.entity.Planet;
059 import rtype.entity.PlayerShip;
060 import rtype.entity.SpaceTrash;
061 import rtype.entity.Star;
062 import rtype.entity.TextEntityCounter;
063 /* TODO
064 - Fix texture loading, DEVil is not yet usable with OS X or Linux platform
065 - Intro with musique would be great to add
066 - Add distorsion effect for magnetic orb
067 - Implement link between orb and ship
068 - Add smartBomb: I am thinking of a rain of homing missiles
069 */
070
071 public class Prototyp {
072
073 // Init parameters
074 static public final int SCREEN_WIDTH = 800;
075 static public final int SCREEN_HEIGHT = 600; //480
076 private static final boolean FULL_SCREEN = false;
077
078 //
079 boolean gameOff = false;
080
081
082 // Default values for sprites
083 public static float DEFAULT_Z = 0;
084 public static final Vector2f DEFAULT_SCROLLING_SPEED = new Vector2f(-5,0);
085
086 // This is the player's sprite
087 public static PlayerShip player1 = null;
088
089 // Set of layers, drawn in a different order ( see render method)
090 static public Layer bullets = new Layer();
091 static public Layer enemies = new Layer();
092 static public Layer fx = new Layer();
093 static public Layer bonus = new Layer();
094 static public Layer background = new Layer();
095 static public Layer frontground = new Layer();
096 static public Layer text = new Layer();
097
098 // Random object, used by every object that need randomness during the game
099 public final static Random random = new Random(System.currentTimeMillis());
100
101 static boolean useDevil = false;
102
103 // Variables used to calculate fps
104 private int frames;
105 static float deltas = 0;
106 static float fps;
107
108 // Text object used to display fps, score and entity total count
109 private Text textFPS;
110 private Text textHisCore;
111 private TextEntityCounter entitiesCount ;
112
113 static Timer timer = new Timer();
114 static float lastTime = timer.getTime();
115
116 // Measure time elapsed since last frame renderer
117 // This is the heart variable of the engine
118 public static float tick;
119
120 // Generators generate entity for the game
121 public static GeneratorSet generator = null;
122
123 // Texture loader is used to load and server textures to entities on init
124 public static ITextureLoader textureLoader = null;
125
126 // Starting point
127 public static void main(String args[]) {
128
129 if (args.length > 0)
130 useDevil = "useDevil".equals(args[0]);
131 Prototyp l8 = new Prototyp();
132 l8.run();
133 }
134
135 public Prototyp()
136 {
137 init();
138 initGL();
139 }
140
141 // Main loop
142 public void run() {
143
144
145
146 Intro intro = new Intro(this);
147 intro.play();
148
149 BonusDesc bonusDesc = new BonusDesc(this);
150 bonusDesc.play();
151
152
153 addBasicEntities();
154
155 player1.addEventListeners();
156
157 Bonus b = BonusFactory.createBonus(IEntity.BONUS_LIGHTNING_ORB);
158 b.spawn(new Vector2f(player1.position.x+100,player1.position.y), DEFAULT_SCROLLING_SPEED, bonus);
159
160
161 addControlKeys();
162
163
164 while (!gameOff)
165 {
166 heartBeat();
167 getEntries();
168 update();
169 checkCollisons();
170 render();
171
172 frames++;
173 if (frames == 50)
174 {
175 fps = frames/deltas;
176 frames = 0;
177 deltas = 0 ;
178 textFPS.setString("fps:"+(int)fps);
179 }
180 Display.update();
181 // Display.sync(10);
182 generator.generate();
183
184 }
185 Display.destroy();
186 }
187
188
189
190 private void createWindow(int screenWidth, int screenHeight, boolean fullscreen) throws Exception {
191
192
193 if (!fullscreen) // create windowed mode
194 Display.setDisplayMode(new DisplayMode(screenWidth, screenHeight));
195 else
196 {
197 Display.setFullscreen(true);
198 try
199 {
200 DisplayMode dm[] = org.lwjgl.util.Display.getAvailableDisplayModes(320, 240, -1, -1, -1, -1, 60, 85);
201 org.lwjgl.util.Display.setDisplayMode(dm, new String[] {
202 "width=" + screenWidth, "height=" + screenHeight, "freq=85",
203 "bpp=" + Display.getDisplayMode().getBitsPerPixel()
204 });
205 }
206 catch(Exception e)
207 {
208 Sys.alert("Error", "Could not start full screen, switching to windowed mode");
209 Display.setDisplayMode(new DisplayMode(screenWidth, screenHeight));
210 }
211 }
212
213 Display.create();
214 }
215
216
217
218
219 private void addBasicEntities() {
220 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)
221 player1.spawn(new Vector2f(-150,-100),new Vector2f(0,0),bullets);
222 player1.addBooster();
223
224 textFPS = new Text("");
225 textFPS.spawn(new Vector2f(SCREEN_WIDTH/2-100,SCREEN_HEIGHT/2-20),new Vector2f(0,0),text);
226
227 textHisCore = new Text("HISCORE:");
228 textHisCore.spawn(new Vector2f(-SCREEN_WIDTH/2+20,SCREEN_HEIGHT/2 - 20),new Vector2f(0,0),text);
229
230 entitiesCount = new TextEntityCounter();
231 entitiesCount.spawn(new Vector2f(-SCREEN_WIDTH/2+20,-SCREEN_HEIGHT/2 + 20),new Vector2f(0,0),text);
232
233 }
234
235
236 public static void heartBeat() {
237 Timer.tick();
238 tick = timer.getTime() - lastTime;
239 deltas+= tick;
240 lastTime = timer.getTime();
241
242 }
243
244
245 void update()
246 {
247 bullets.update();
248 enemies.update();
249 fx.update();
250 background.update();
251 bonus.update();
252 frontground.update();
253 text.update();
254 }
255
256
257
258 private void checkCollisons()
259 {
260 // Check bullets with enemies
261 ArrayList<Entity> bulletsArray = bullets.entities;
262 ArrayList<Entity> enemiesArray = enemies.entities;
263 Entity currentBullet = null;
264 Entity currentEnemy = null;
265 for (int i = 0; i < bulletsArray.size() ; i++)
266 {
267
268
269 for (int j= 0 ; j < enemiesArray.size(); j++ )
270 {
271 if (j < 0)
272 continue;
273 if (i<0)
274 break;
275 currentBullet = bulletsArray.get(i);
276 currentEnemy = enemiesArray.get(j);
277
278 if (Collision.boxBoxOverlap(currentBullet,
279 currentEnemy
280 ))
281 {
282 player1.hiscore++;
283
284 if (currentBullet.collided(currentEnemy))
285 i--;
286
287 if (currentEnemy.collided(currentBullet))
288 j--;
289 }
290
291 }
292 }
293 textHisCore.setString("HISCORE:"+player1.hiscore);
294
295 // Check players with bonuses
296 ArrayList<Entity> bonusArray = bonus.entities;
297 Entity currentBonus = null;
298 for (int j= 0 ; j < bonusArray.size(); j++ )
299 {
300 currentBonus = bonusArray.get(j);
301 if(
302 Collision.boxBoxOverlap( player1,
303 currentBonus
304 )
305
306 )
307 {
308 if (currentBonus.collided(player1))
309 j--;
310 player1.collided(currentBonus);
311 }
312 }
313 }
314
315
316 // Init engine
317 private void init()
318 {
319 try
320 {
321 Mouse.setGrabbed(false);
322
323 createWindow(SCREEN_WIDTH, SCREEN_HEIGHT, FULL_SCREEN) ;
324
325
326
327
328
329 createOffScreenBuffer();
330
331 //Display.setVSyncEnabled(true);
332
333 //TODO This is the generators
334 generator = new GeneratorSet();
335 generator.addGenerator(new StarGenerator());
336 generator.addGenerator(new IntroGenerator());
337 generator.addGenerator(new EnemyWave(100,EnemyWave.rate));
338 generator.addGenerator(new EnemyWave(150,EnemyWave.rate/2));
339
340
341 generator.addGenerator(new EnemyWave(-100,EnemyWave.rate + 10));
342 generator.addGenerator(new EnemyWave(-150,EnemyWave.rate/2+ 10));
343
344 generator.addGenerator(new EnemyWave(0,EnemyWave.rate + 20));
345 generator.addGenerator(new EnemyWave(50,EnemyWave.rate/2 + 20));
346
347 generator.addGenerator(new LadyBirdGenerator(EnemyWave.rate/2 + 30));
348
349
350 }
351 catch(Exception e)
352 {
353 e.printStackTrace(System.out);
354 }
355 }
356
357 private void initGL() {
358
359
360 GL11.glEnable(GL11.GL_TEXTURE_2D); // Enable Texture Mapping
361
362 GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background
363 GL11.glClearDepth(1.0f); // Depth Buffer Setup
364 GL11.glDisable(GL11.GL_DEPTH_TEST); // Enables Depth Testing
365 GL11.glEnable(GL11.GL_BLEND);
366 GL11.glDepthMask(false);
367 GL11.glMatrixMode(GL11.GL_PROJECTION); // Select The Projection Matrix
368 GL11.glLoadIdentity(); // Reset The Projection Matrix
369
370
371 GLU.gluOrtho2D(-(int)SCREEN_WIDTH/2,(int)SCREEN_WIDTH/2,(int)-SCREEN_HEIGHT/2,(int)SCREEN_HEIGHT/2);
372
373 GL11.glMatrixMode(GL11.GL_MODELVIEW);
374
375 if (useDevil)
376 ;//textureLoader = new DevilTextureLoader();
377 else
378 {
379 textureLoader = new WorkAroundTextureLoader();
380 }
381 textureLoader.init();
382 }
383
384
385 private static boolean P_KEY_PRESSED = false;
386
387 // LadyBirds
388 private static boolean P_KEY_F1 = false;
389 private IGenerator ladyBirdGenerator = new LadyBirdGenerator(30);
390
391 //HomingMissiles
392 private static boolean P_KEY_F2 = false;
393 private IGenerator homingGenerator = new HomingMissileGenerator();
394
395 private static Text pause = null;
396
397 private void addControlKeys()
398 {
399 KeyListener pauseKeyEvent = new KeyListener()
400 {
401 public void onKeyDown()
402 {
403 if (timer.isPaused())
404 {
405 timer.resume();
406 pause.unSpawn();
407 }
408 else
409 {
410 pause = new Text("GAME PAUSED");
411 //pause.setRatio(1.5f);
412 pause.spawn(new Vector2f(-50,0),new Vector2f(0,0),frontground);
413 timer.pause();
414 }
415 };
416 };
417 EventManager.instance().addListener(Keyboard.KEY_P, pauseKeyEvent);
418
419
420 KeyListener homingMissileKeyEvent = new KeyListener()
421 {
422 public void onKeyDown()
423 {
424 if (generator.contains(homingGenerator))
425 generator.removeGenerator(homingGenerator);
426 else
427 generator.addGenerator(homingGenerator);
428 };
429 };
430 EventManager.instance().addListener(Keyboard.KEY_F1, homingMissileKeyEvent);
431
432 KeyListener enemiesKeyEvent = new KeyListener()
433 {
434 public void onKeyDown()
435 {
436 if (generator.contains(ladyBirdGenerator))
437 generator.removeGenerator(ladyBirdGenerator);
438 else
439 generator.addGenerator(ladyBirdGenerator);
440 };
441 };
442 EventManager.instance().addListener(Keyboard.KEY_F2, enemiesKeyEvent);
443 }
444
445 private void getEntries()
446 {
447 if (exitRequested())
448 gameOff = true;
449
450 EventManager.instance(). checkEvents();
451
452 }
453
454 boolean exitRequested() {
455 return (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE) || Display.isCloseRequested()) ;
456 }
457
458
459 // This method render current scene state
460 void render()
461 {
462 GL11.glClear(GL11.GL_COLOR_BUFFER_BIT); // Clear The Screen And The Depth Buffer
463
464 background.render();
465
466 // Make backGround darker
467 {
468 fadeAlpha = 0.65f;
469 fadeScreen(false);
470 fadeAlpha = 0;
471 }
472
473 enemies.render();
474
475 bonus.render();
476
477 fx.render();
478
479 // This make a copy of the screen in a texture
480 // It is used later for deformations effects
481 saveScreen();
482
483 applyDistorsions();
484
485 bullets.render();
486
487 frontground.render();
488
489 text.render();
490
491 fadeScreen(true);
492
493 //this.drawPowerBar();
494 //this.renderScanLines();
495 }
496
497 private void saveScreen()
498 {
499 GL11.glLoadIdentity();
500 GL11.glBindTexture(GL11.GL_TEXTURE_2D,SCREEN_TEXTURE_ID);
501 GL11.glCopyTexSubImage2D(GL11.GL_TEXTURE_2D,0, 0, 0, 0, 0,(int)SCREEN_WIDTH,(int)SCREEN_HEIGHT);
502
503 }
504
505
506 // This is called when player charge power
507 /* ----------------------------------------------
508 - -
509 - -
510 - -
511 - -
512 - --- -
513 - - - -
514 - --- -
515 - -
516 - -
517 - -
518 - -
519 - -
520 ----------------------------------------------
521 */
522 private void applyDistorsions()
523 {
524
525 if (player1 == null)
526 return;
527
528 float chargeP = player1.power/PlayerShip.MAX_POWER ;
529
530
531 float R_WIDTH = 350 * chargeP;
532 float R_HEIGHT = 200 * chargeP;
533 float MIDDLE_X_ON_SCREEN = R_WIDTH/2* chargeP;
534
535 float R_POSITION_X = player1.position.x+25;
536 float R_POSITION_Y = player1.position.y-R_HEIGHT/2;
537
538 float TEXT_OUT_X_LOW = (R_POSITION_X+(SCREEN_WIDTH/2f))/1024f ;
539 float TEXT_OUT_X_HIGH = (R_POSITION_X+R_WIDTH+(SCREEN_WIDTH/2f))/1024;
540 float TEXT_OUT_Y_LOW = (R_POSITION_Y+(SCREEN_HEIGHT/2f))/1024;
541 float TEXT_OUT_Y_HIGH = (R_POSITION_Y+R_HEIGHT+(SCREEN_HEIGHT/2f))/1024;
542
543 float TEXT_IN_X_LOW = (R_POSITION_X+R_WIDTH/2+(SCREEN_WIDTH/2f))/1024f ;
544 float TEXT_IN_X_HIGH = (R_POSITION_X+R_WIDTH/2 + 1+(SCREEN_WIDTH/2f))/1024;
545 float TEXT_IN_Y_LOW = (R_POSITION_Y+R_HEIGHT/2 +(SCREEN_HEIGHT/2f))/1024f ;
546 float TEXT_IN_Y_HIGH = (R_POSITION_Y+R_HEIGHT/2 + 1+(SCREEN_HEIGHT/2f))/1024;
547
548 float SCR_OUT_X_LOW = R_POSITION_X ;
549 float SCR_OUT_X_HIGH = R_POSITION_X+R_WIDTH;
550 float SCR_OUT_Y_LOW = R_POSITION_Y;
551 float SCR_OUT_Y_HIGH = R_POSITION_Y+R_HEIGHT;
552
553 float SCR_IN_X_LOW = R_POSITION_X+MIDDLE_X_ON_SCREEN ;
554 float SCR_IN_X_HIGH = R_POSITION_X+MIDDLE_X_ON_SCREEN + 1;
555 float SCR_IN_Y_LOW = R_POSITION_Y+R_HEIGHT/2;
556 float SCR_IN_Y_HIGH = R_POSITION_Y + R_HEIGHT/2 + 1;
557
558 GL11.glBlendFunc(GL11.GL_SRC_ALPHA,GL11.GL_ONE_MINUS_SRC_ALPHA);
559 GL11.glColor4f(1,1,1,0.75f);
560 GL11.glBegin(GL11.GL_QUADS);
561 {
562 GL11.glTexCoord2f(TEXT_IN_X_HIGH,TEXT_IN_Y_HIGH);
563 GL11.glVertex2f(SCR_IN_X_HIGH, SCR_IN_Y_HIGH); // Lower right
564
565 GL11.glTexCoord2f(TEXT_OUT_X_HIGH,TEXT_OUT_Y_HIGH);
566 GL11.glVertex2f(SCR_OUT_X_HIGH,SCR_OUT_Y_HIGH); //Lower left
567
568 GL11.glTexCoord2f(TEXT_OUT_X_HIGH,TEXT_OUT_Y_LOW);
569 GL11.glVertex2f(SCR_OUT_X_HIGH,SCR_OUT_Y_LOW); //upper left
570
571 GL11.glTexCoord2f(TEXT_IN_X_HIGH,TEXT_IN_Y_LOW);
572 GL11.glVertex2f(SCR_IN_X_HIGH,SCR_IN_Y_LOW); // //upper right
573 }
574 GL11.glEnd();
575
576 GL11.glBegin(GL11.GL_QUADS);
577 {
578 GL11.glTexCoord2f(TEXT_IN_X_HIGH,TEXT_IN_Y_LOW);
579 GL11.glVertex2f(SCR_IN_X_HIGH, SCR_IN_Y_LOW); // Lower right
|