Mercurial > hg > ristipolku
comparison src/Engine.java @ 161:fb33d3796942
Rename source directory.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 21 Jun 2016 12:53:53 +0300 |
parents | game/Engine.java@1ba6f56203f2 |
children |
comparison
equal
deleted
inserted
replaced
160:d5fbad999841 | 161:fb33d3796942 |
---|---|
1 /* | |
2 * Ristipolku Game Engine | |
3 * (C) Copyright 2011 Matti 'ccr' Hämäläinen <ccr@tnsp.org> | |
4 */ | |
5 package game; | |
6 | |
7 import java.awt.*; | |
8 import java.awt.geom.*; | |
9 import java.awt.event.*; | |
10 import java.awt.image.*; | |
11 import java.awt.event.*; | |
12 import java.awt.font.*; | |
13 import javax.imageio.*; | |
14 import javax.swing.*; | |
15 import java.util.*; | |
16 import java.util.concurrent.locks.Lock; | |
17 import java.util.concurrent.locks.ReentrantReadWriteLock; | |
18 import java.io.*; | |
19 import game.*; | |
20 import javax.sound.sampled.*; | |
21 | |
22 | |
23 class AboutBox extends IDMWidget | |
24 { | |
25 BufferedImage aboutImg; | |
26 boolean aboutSecret; | |
27 | |
28 IDMPoint currPos, currOffs; | |
29 Paint textPaint; | |
30 Font textFont; | |
31 FontMetrics textMetrics; | |
32 | |
33 | |
34 public AboutBox() | |
35 { | |
36 try { | |
37 ResourceLoader res = new ResourceLoader("graphics/girl.jpg"); | |
38 aboutImg = ImageIO.read(res.getStream()); | |
39 } | |
40 catch (IOException e) | |
41 { | |
42 } | |
43 | |
44 aboutSecret = false; | |
45 | |
46 setPos(150f, 150f); | |
47 setSize(675f, 420f); | |
48 } | |
49 | |
50 public void setTextFont(Font font, FontMetrics metrics) | |
51 { | |
52 textFont = font; | |
53 textMetrics = metrics; | |
54 } | |
55 | |
56 public void setTextPaint(Paint paint) | |
57 { | |
58 textPaint = paint; | |
59 } | |
60 | |
61 public void setCurrPos(IDMPoint npos) | |
62 { | |
63 currPos = npos; | |
64 currOffs = new IDMPoint(0, 0); | |
65 } | |
66 | |
67 public void setCurrPos(float x, float y) | |
68 { | |
69 setCurrPos(new IDMPoint(x, y)); | |
70 } | |
71 | |
72 public void drawString(Graphics2D g, String text) | |
73 { | |
74 Paint savePaint = g.getPaint(); | |
75 g.setPaint(textPaint); | |
76 g.setFont(textFont); | |
77 | |
78 int i = 0; | |
79 while (i < text.length()) | |
80 { | |
81 int p = text.indexOf("\n", i); | |
82 boolean linefeed; | |
83 String str; | |
84 if (p >= i) | |
85 { | |
86 str = text.substring(i, p); | |
87 i = p + 1; | |
88 linefeed = true; | |
89 } | |
90 else | |
91 { | |
92 str = text.substring(i); | |
93 i += str.length(); | |
94 linefeed = false; | |
95 } | |
96 | |
97 g.drawString(str, currPos.x + currOffs.x, currPos.y + currOffs.y); | |
98 | |
99 if (linefeed) | |
100 { | |
101 currOffs.x = 0; | |
102 currOffs.y += textMetrics.getHeight(); | |
103 } | |
104 else | |
105 { | |
106 currOffs.x += textMetrics.stringWidth(str); | |
107 } | |
108 } | |
109 | |
110 g.setPaint(savePaint); | |
111 } | |
112 | |
113 public void paint(Graphics2D g) | |
114 { | |
115 int x = getScaledX(), y = getScaledY(), | |
116 w = getScaledWidth(), h = getScaledHeight(); | |
117 | |
118 g.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.7f)); | |
119 g.fill(new RoundRectangle2D.Float(x, y, w, h, 10, 10)); | |
120 | |
121 setTextFont(G.fonts[3], G.metrics[3]); | |
122 setTextPaint(Color.white); | |
123 | |
124 setCurrPos(x + 20, y + 30); | |
125 drawString(g, "RistiPolku (CrossPaths) v"+ G.version +"\n"); | |
126 | |
127 setTextFont(G.fonts[1], G.metrics[1]); | |
128 if (aboutSecret) | |
129 { | |
130 g.drawImage(aboutImg, x + 20, y + 55, | |
131 aboutImg.getWidth(), aboutImg.getHeight(), null); | |
132 | |
133 setCurrPos(x + 225, y + 75); | |
134 drawString(g, | |
135 "Dedicated to my\n" + | |
136 "favorite woman\n" + | |
137 "in the world."); | |
138 | |
139 setCurrPos(x + 370, y + 175); | |
140 drawString(g, "- Matti"); | |
141 } | |
142 else | |
143 { | |
144 setTextPaint(Color.yellow); | |
145 drawString(g, "(c) Copyright 2011 Tecnic Software productions (TNSP)\n"); | |
146 | |
147 setTextPaint(Color.white); | |
148 drawString(g, "Programming, graphics and design by " + | |
149 "Matti 'ccr' Hämäläinen.\n" + | |
150 "Audio from archive.org, used non-commercially.\n \n"); | |
151 | |
152 setTextPaint(Color.red); | |
153 drawString(g, "Controls:\n"); | |
154 | |
155 IDMPoint old = currOffs.copy(); | |
156 | |
157 setTextPaint(Color.white); | |
158 drawString(g, | |
159 "Arrow keys / mouse wheel\n"+ | |
160 "Enter / mouse click\n"+ | |
161 "Space bar\n"); | |
162 | |
163 currPos.x += 330; | |
164 currOffs.y = old.y; | |
165 drawString(g, | |
166 "- Rotate piece\n" + | |
167 "- Place/lock piece\n" + | |
168 "- Swap piece\n"); | |
169 | |
170 currPos.x -= 330; | |
171 setTextPaint(Color.green); | |
172 drawString(g, | |
173 "\nObjective: Create a path long as possible by rotating\n"+ | |
174 "and placing pieces. More points will be awarded for\n"+ | |
175 "advancing the path by several segments per turn." | |
176 ); | |
177 } | |
178 } | |
179 | |
180 public boolean keyPressed(KeyEvent e) | |
181 { | |
182 if (e.getKeyCode() == KeyEvent.VK_L) | |
183 { | |
184 aboutSecret = true; | |
185 } | |
186 else | |
187 { | |
188 clicked(); | |
189 aboutSecret = false; | |
190 } | |
191 return true; | |
192 } | |
193 | |
194 public void clicked() | |
195 { | |
196 parent.remove(this); | |
197 } | |
198 } | |
199 | |
200 | |
201 class GameBoard extends IDMWidget | |
202 { | |
203 static final int boardSize = 9; | |
204 static final int boardMiddle = 4; | |
205 Piece[][] board; | |
206 float pscale, ptime; | |
207 | |
208 public boolean flagGameOver, flagBoardIsDirty; | |
209 int gameScore; | |
210 | |
211 Piece currPiece, nextPiece; | |
212 int currX, currY, currPoint; | |
213 | |
214 Sound sndPlaced; | |
215 | |
216 private final ReentrantReadWriteLock pointLock = new ReentrantReadWriteLock(); | |
217 private ArrayList<AnimatedPointElement> pointElems; | |
218 | |
219 public GameBoard(IDMPoint pos, float ps) | |
220 { | |
221 super(pos); | |
222 pscale = ps; | |
223 | |
224 // sndPlaced = G.smgr.getSound("sounds/placed.wav"); | |
225 | |
226 pointElems = new ArrayList<AnimatedPointElement>(); | |
227 | |
228 startNewGame(); | |
229 } | |
230 | |
231 public void startNewGame() | |
232 { | |
233 board = new Piece[boardSize][boardSize]; | |
234 board[boardMiddle][boardMiddle] = new Piece(PieceType.START); | |
235 | |
236 currX = boardMiddle; | |
237 currY = boardMiddle; | |
238 currPoint = 0; | |
239 | |
240 currPiece = null; | |
241 nextPiece = new Piece(PieceType.ACTIVE); | |
242 | |
243 flagGameOver = false; | |
244 flagBoardIsDirty = true; | |
245 pieceFinishTurn(); | |
246 gameScore = 0; | |
247 } | |
248 | |
249 public void paintBackPlate(Graphics2D g) | |
250 { | |
251 g.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.2f)); | |
252 g.setStroke(new BasicStroke(5.0f)); | |
253 g.draw(new RoundRectangle2D.Float(getScaledX(), getScaledY(), | |
254 boardSize * pscale, boardSize * pscale, | |
255 pscale / 5, pscale / 5)); | |
256 } | |
257 | |
258 public void paintBoard(Graphics2D g, boolean drawCurrent) | |
259 { | |
260 for (int y = 0; y < boardSize; y++) | |
261 for (int x = 0; x < boardSize; x++) | |
262 if (board[x][y] != null) | |
263 { | |
264 if ((drawCurrent && board[x][y] == currPiece) || | |
265 (!drawCurrent && board[x][y] != currPiece)) | |
266 { | |
267 board[x][y].paint(g, | |
268 getScaledX() + (x * pscale), | |
269 getScaledY() + (y * pscale), | |
270 pscale - pscale / 10); | |
271 } | |
272 } | |
273 } | |
274 | |
275 public void paint(Graphics2D g) | |
276 { | |
277 paintBoard(g, true); | |
278 | |
279 Lock read = pointLock.readLock(); | |
280 read.lock(); | |
281 try | |
282 { | |
283 for (AnimatedPointElement elem : pointElems) | |
284 { | |
285 elem.paint(g); | |
286 } | |
287 } | |
288 finally | |
289 { | |
290 read.unlock(); | |
291 } | |
292 | |
293 | |
294 if (!flagGameOver) | |
295 { | |
296 if (nextPiece != null) | |
297 { | |
298 // Draw next piece | |
299 AffineTransform save = g.getTransform(); | |
300 nextPiece.paint(g, G.screenDim.width * 0.85f - 90.0f/2.0f, G.screenDim.height * 0.43f, 90.0f); | |
301 g.setTransform(save); | |
302 } | |
303 } | |
304 else | |
305 { | |
306 // Game over text | |
307 String text = "Game Over!"; | |
308 int textWidth = G.metrics[2].stringWidth(text); | |
309 g.setFont(G.fonts[2]); | |
310 | |
311 g.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.5f)); | |
312 g.drawString(text, (G.screenDim.width - textWidth) / 2 + 5, G.screenDim.height / 2 + 5); | |
313 | |
314 double f = Math.sin(ptime * 0.1) * 4.0; | |
315 g.setPaint(Color.white); | |
316 g.drawString(text, (G.screenDim.width - textWidth) / 2 + (float) f, G.screenDim.height / 2 + (float) f); | |
317 } | |
318 | |
319 // Score | |
320 String text = ""+ String.format("%05d", gameScore); | |
321 int textWidth = G.metrics[2].stringWidth(text); | |
322 g.setFont(G.fonts[2]); | |
323 g.setPaint(Color.white); | |
324 g.drawString(text, (G.screenDim.width * 0.85f) - textWidth / 2, G.screenDim.height * 0.3f); | |
325 } | |
326 | |
327 public boolean contains(float x, float y) | |
328 { | |
329 return (x >= getScaledX() && | |
330 y >= getScaledY() && | |
331 x < getScaledX() + boardSize * pscale && | |
332 y < getScaledY() + boardSize * pscale); | |
333 } | |
334 | |
335 public boolean isBoardDirty() | |
336 { | |
337 if (flagBoardIsDirty) | |
338 { | |
339 flagBoardIsDirty = false; | |
340 return true; | |
341 } | |
342 else | |
343 return false; | |
344 } | |
345 | |
346 public void animate(float time) | |
347 { | |
348 if (nextPiece != null) | |
349 { | |
350 nextPiece.animate(time); | |
351 } | |
352 | |
353 ptime = time; | |
354 for (int y = 0; y < boardSize; y++) | |
355 for (int x = 0; x < boardSize; x++) | |
356 if (board[x][y] != null) | |
357 { | |
358 board[x][y].animate(time); | |
359 if (board[x][y] != currPiece && board[x][y].active) | |
360 flagBoardIsDirty = true; | |
361 } | |
362 | |
363 Lock write = pointLock.writeLock(); | |
364 write.lock(); | |
365 try | |
366 { | |
367 ArrayList<AnimatedPointElement> tmp = new ArrayList<AnimatedPointElement>(); | |
368 | |
369 for (AnimatedPointElement elem : pointElems) | |
370 { | |
371 elem.animate(time); | |
372 if (elem.active) | |
373 tmp.add(elem); | |
374 } | |
375 | |
376 pointElems = tmp; | |
377 | |
378 if (time % 32 == 1) | |
379 { | |
380 Random rnd = new Random(); | |
381 pointElems.add(new AnimatedPointElement( | |
382 new IDMPoint(10 + rnd.nextInt(400), 10 + rnd.nextInt(100)), ".")); | |
383 } | |
384 } | |
385 finally | |
386 { | |
387 write.unlock(); | |
388 } | |
389 } | |
390 | |
391 public void pieceRotate(Piece.RotateDir dir) | |
392 { | |
393 if (currPiece != null && !flagGameOver) | |
394 { | |
395 currPiece.rotate(dir); | |
396 } | |
397 } | |
398 | |
399 // Change coordinates based on the "outgoing" | |
400 // piece connection point. | |
401 private void pieceMoveTo(int point) | |
402 { | |
403 switch (point) | |
404 { | |
405 case 0: currY--; break; | |
406 case 1: currY--; break; | |
407 | |
408 case 2: currX++; break; | |
409 case 3: currX++; break; | |
410 | |
411 case 4: currY++; break; | |
412 case 5: currY++; break; | |
413 | |
414 case 6: currX--; break; | |
415 case 7: currX--; break; | |
416 } | |
417 } | |
418 | |
419 public void pieceCreateNew() | |
420 { | |
421 currPiece = nextPiece; | |
422 currPiece.changed(); | |
423 nextPiece = new Piece(PieceType.ACTIVE); | |
424 flagBoardIsDirty = true; | |
425 } | |
426 | |
427 public void pieceSwapCurrent() | |
428 { | |
429 if (!flagGameOver) | |
430 { | |
431 Piece tmp = currPiece; | |
432 currPiece = nextPiece; | |
433 nextPiece = tmp; | |
434 board[currX][currY] = currPiece; | |
435 currPiece.changed(); | |
436 nextPiece.changed(); | |
437 flagBoardIsDirty = true; | |
438 } | |
439 } | |
440 | |
441 // Check one piece, set connections, find the new placement | |
442 // based on piece rotations etc. | |
443 private boolean pieceCheck(Piece piece) | |
444 { | |
445 if (piece == null) | |
446 { | |
447 // Create new piece | |
448 pieceCreateNew(); | |
449 board[currX][currY] = currPiece; | |
450 return true; | |
451 } | |
452 else | |
453 if (piece.getType() == PieceType.START) | |
454 { | |
455 if (currPiece != null) | |
456 { | |
457 // Hit center starting piece, game over | |
458 flagGameOver = true; | |
459 return true; | |
460 } | |
461 else | |
462 { | |
463 // Start piece as first piece means game is starting | |
464 pieceMoveTo(currPoint); | |
465 pieceCreateNew(); | |
466 board[currX][currY] = currPiece; | |
467 return true; | |
468 } | |
469 } | |
470 | |
471 // Mark the current piece as locked | |
472 piece.setType(PieceType.LOCKED); | |
473 | |
474 // Solve connection (with rotations) through the piece | |
475 currPoint = piece.getRotatedPoint(piece.getMatchingPoint(currPoint)); | |
476 | |
477 // Mark connection as active | |
478 piece.setConnectionState(currPoint, true); | |
479 | |
480 // Solve exit point (with rotations) | |
481 currPoint = piece.getAntiRotatedPoint(piece.getConnection(currPoint)); | |
482 | |
483 // Move to next position accordingly | |
484 pieceMoveTo(currPoint); | |
485 return false; | |
486 } | |
487 | |
488 // Finish one move/turn of the game, resolve path and find placement | |
489 // of the next piece, or set "game over" state if required. | |
490 public void pieceFinishTurn() | |
491 { | |
492 boolean finished = false; | |
493 int connections = 0; | |
494 | |
495 if (currPiece != null) | |
496 { | |
497 G.smgr.play(sndPlaced); | |
498 } | |
499 | |
500 while (!finished) | |
501 { | |
502 if (currX >= 0 && currX < boardSize && currY >= 0 && currY < boardSize) | |
503 { | |
504 int oldX = currX, oldY = currY; | |
505 connections++; | |
506 finished = pieceCheck(board[currX][currY]); | |
507 | |
508 if (!finished) | |
509 { | |
510 Lock write = pointLock.writeLock(); | |
511 write.lock(); | |
512 try | |
513 { | |
514 pointElems.add(new AnimatedPointElement( | |
515 new IDMPoint( | |
516 getScaledX() + ((oldX + 0.5f) * pscale), | |
517 getScaledY() + ((oldY + 0.5f) * pscale)), | |
518 "" + connections)); | |
519 } | |
520 finally | |
521 { | |
522 write.unlock(); | |
523 } | |
524 } | |
525 | |
526 } | |
527 else | |
528 { | |
529 // Outside of the board, game over | |
530 finished = true; | |
531 flagGameOver = true; | |
532 } | |
533 } | |
534 | |
535 // Compute and add score | |
536 gameScore += connections * connections; | |
537 | |
538 // If game over, clear the game | |
539 if (flagGameOver) | |
540 { | |
541 currPiece = null; | |
542 } | |
543 | |
544 flagBoardIsDirty = true; | |
545 } | |
546 | |
547 public boolean mouseWheelMoved(MouseWheelEvent e) | |
548 { | |
549 int notches = e.getWheelRotation(); | |
550 | |
551 if (notches < 0) | |
552 pieceRotate(Piece.RotateDir.LEFT); | |
553 else | |
554 pieceRotate(Piece.RotateDir.RIGHT); | |
555 | |
556 return true; | |
557 } | |
558 | |
559 public void clicked() | |
560 { | |
561 if (!flagGameOver) | |
562 pieceFinishTurn(); | |
563 } | |
564 | |
565 public boolean keyPressed(KeyEvent e) | |
566 { | |
567 if (flagGameOver) | |
568 return false; | |
569 | |
570 switch (e.getKeyCode()) | |
571 { | |
572 case KeyEvent.VK_LEFT: | |
573 case KeyEvent.VK_UP: | |
574 pieceRotate(Piece.RotateDir.LEFT); | |
575 return true; | |
576 | |
577 case KeyEvent.VK_RIGHT: | |
578 case KeyEvent.VK_DOWN: | |
579 pieceRotate(Piece.RotateDir.RIGHT); | |
580 return true; | |
581 | |
582 case KeyEvent.VK_ENTER: | |
583 pieceFinishTurn(); | |
584 return true; | |
585 } | |
586 return false; | |
587 } | |
588 } | |
589 | |
590 | |
591 public class Engine extends JPanel | |
592 implements Runnable, KeyListener, | |
593 MouseListener, MouseWheelListener | |
594 { | |
595 long startTime; | |
596 float gameUpdates, gameFrames; | |
597 | |
598 Thread animThread; | |
599 boolean animEnable = false; | |
600 | |
601 GameBoard lauta = null; | |
602 InputStream musa; | |
603 IDMContainer widgets; | |
604 AboutBox aboutBox; | |
605 | |
606 public void dbg(String msg) | |
607 { | |
608 System.out.print("Engine: " + msg); | |
609 } | |
610 | |
611 public Engine() | |
612 { | |
613 // Initialize globals | |
614 System.out.print("Engine() constructor\n"); | |
615 | |
616 // Sound system | |
617 G.smgr = new SoundManager(new AudioFormat(22050, 16, 1, true, false), 1); | |
618 | |
619 // Load resources | |
620 try | |
621 { | |
622 ResourceLoader res = new ResourceLoader("graphics/board.jpg"); | |
623 G.lautaBG = ImageIO.read(res.getStream()); | |
624 | |
625 try { | |
626 res = new ResourceLoader("graphics/font.ttf"); | |
627 | |
628 G.fonts = new Font[G.numFonts]; | |
629 G.fonts[0] = Font.createFont(Font.TRUETYPE_FONT, res.getStream()); | |
630 G.fonts[1] = G.fonts[0].deriveFont(24f); | |
631 G.fonts[2] = G.fonts[0].deriveFont(64f); | |
632 G.fonts[3] = G.fonts[0].deriveFont(32f); | |
633 } | |
634 catch (FontFormatException e) | |
635 { | |
636 dbg("Could not initialize fonts.\n"); | |
637 } | |
638 | |
639 res = new ResourceLoader("sounds/gamemusic.wav"); | |
640 musa = res.getStream(); | |
641 } | |
642 catch (IOException e) | |
643 { | |
644 JOptionPane.showMessageDialog(null, | |
645 e.getMessage(), | |
646 "Initialization error", | |
647 JOptionPane.ERROR_MESSAGE); | |
648 | |
649 dbg(e.getMessage()); | |
650 } | |
651 | |
652 // Create IDM GUI widgets | |
653 widgets = new IDMContainer(); | |
654 | |
655 lauta = new GameBoard(new IDMPoint(95, 130), 63); | |
656 widgets.add(lauta); | |
657 | |
658 widgets.add(new BtnSwapPiece(767f, 450f)); | |
659 widgets.add(new BtnAbout (767f, 550f)); | |
660 widgets.add(new BtnNewGame (767f, 630f)); | |
661 | |
662 aboutBox = new AboutBox(); | |
663 | |
664 // Game | |
665 startNewGame(); | |
666 | |
667 // Initialize event listeners | |
668 addKeyListener(this); | |
669 addMouseListener(this); | |
670 addMouseWheelListener(this); | |
671 | |
672 // Start playing background music | |
673 G.smgr.play(musa); | |
674 | |
675 // Get initial focus | |
676 if (!hasFocus()) | |
677 { | |
678 dbg("Requesting focus.\n"); | |
679 requestFocus(); | |
680 } | |
681 | |
682 gameUpdates = 0; | |
683 } | |
684 | |
685 public void startNewGame() | |
686 { | |
687 gameFrames = 0; | |
688 startTime = new Date().getTime(); | |
689 lauta.startNewGame(); | |
690 } | |
691 | |
692 public void paintComponent(Graphics g) | |
693 { | |
694 Graphics2D g2 = (Graphics2D) g; | |
695 boolean scaleChanged = false, | |
696 updateBoard = lauta.isBoardDirty(); | |
697 | |
698 // Rescale if parent component size has changed | |
699 Dimension dim = getSize(); | |
700 if (G.screenDim == null || !dim.equals(G.screenDim)) | |
701 { | |
702 float dw = dim.width / 1024.0f, | |
703 dh = dim.height / 768.0f; | |
704 | |
705 // Rescale IDM GUI widgets | |
706 widgets.setScale(dw, dh); | |
707 G.screenDim = dim; | |
708 | |
709 // Rescale background image | |
710 // Rescale fonts | |
711 G.fonts[1] = G.fonts[0].deriveFont(24f * dw); | |
712 G.fonts[2] = G.fonts[0].deriveFont(64f * dw); | |
713 G.fonts[3] = G.fonts[0].deriveFont(32f * dw); | |
714 | |
715 dbg("Scale changed.\n"); | |
716 scaleChanged = true; | |
717 updateBoard = true; | |
718 } | |
719 | |
720 if (updateBoard) | |
721 { | |
722 // dbg("updateBoard()\n"); | |
723 G.lautaBGScaled = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_RGB); | |
724 Graphics2D gimg = G.lautaBGScaled.createGraphics(); | |
725 gimg.setRenderingHint(RenderingHints.KEY_INTERPOLATION, | |
726 RenderingHints.VALUE_INTERPOLATION_BICUBIC); | |
727 | |
728 gimg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, | |
729 RenderingHints.VALUE_ANTIALIAS_ON); | |
730 | |
731 gimg.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, | |
732 RenderingHints.VALUE_TEXT_ANTIALIAS_ON); | |
733 | |
734 gimg.drawImage(G.lautaBG, 0, 0, dim.width, dim.height, null); | |
735 lauta.paintBackPlate(gimg); | |
736 lauta.paintBoard(gimg, false); | |
737 } | |
738 | |
739 // Get font metrics against current Graphics2D context | |
740 if (G.metrics == null || scaleChanged) | |
741 { | |
742 G.metrics = new FontMetrics[G.numFonts]; | |
743 for (int i = 0; i < G.numFonts; i++) | |
744 G.metrics[i] = g2.getFontMetrics(G.fonts[i]); | |
745 } | |
746 | |
747 // Draw background image, pieces, widgets | |
748 g2.drawImage(G.lautaBGScaled, 0, 0, null); | |
749 | |
750 // Use antialiasing when rendering the game elements | |
751 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, | |
752 RenderingHints.VALUE_ANTIALIAS_ON); | |
753 | |
754 g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, | |
755 RenderingHints.VALUE_TEXT_ANTIALIAS_ON); | |
756 | |
757 widgets.paint(g2); | |
758 | |
759 | |
760 // Frames per second counter | |
761 /* | |
762 g2.setFont(G.fonts[1]); | |
763 long currTime = new Date().getTime(); | |
764 g2.drawString("fps = "+ ((gameFrames * 1000) / (currTime - startTime)), G.screenDim.width - 120, 20); | |
765 */ | |
766 gameFrames++; | |
767 } | |
768 | |
769 public void startThreads() | |
770 { | |
771 dbg("startThreads()\n"); | |
772 if (animThread == null) | |
773 { | |
774 animThread = new Thread(this); | |
775 animEnable = true; | |
776 animThread.start(); | |
777 } | |
778 } | |
779 | |
780 public void stopThreads() | |
781 { | |
782 dbg("stopThreads()\n"); | |
783 | |
784 // Stop animations | |
785 if (animThread != null) | |
786 { | |
787 animThread.interrupt(); | |
788 animEnable = false; | |
789 animThread = null; | |
790 } | |
791 | |
792 // Shut down sound manager | |
793 G.smgr.close(); | |
794 } | |
795 | |
796 public void mouseEntered(MouseEvent e) | |
797 { | |
798 widgets.mouseEntered(e); | |
799 } | |
800 public void mouseExited(MouseEvent e) | |
801 { | |
802 widgets.mouseExited(e); | |
803 } | |
804 | |
805 public void mousePressed(MouseEvent e) | |
806 { | |
807 if (widgets.containsObject(aboutBox)) | |
808 aboutBox.mousePressed(e); | |
809 else | |
810 widgets.mousePressed(e); | |
811 } | |
812 | |
813 public void mouseReleased(MouseEvent e) | |
814 { | |
815 if (widgets.containsObject(aboutBox)) | |
816 aboutBox.mouseReleased(e); | |
817 else | |
818 widgets.mouseReleased(e); | |
819 } | |
820 | |
821 public void mouseClicked(MouseEvent e) | |
822 { | |
823 if (!hasFocus()) | |
824 { | |
825 dbg("Requesting focus.\n"); | |
826 requestFocus(); | |
827 } | |
828 } | |
829 | |
830 public void mouseWheelMoved(MouseWheelEvent e) | |
831 { | |
832 lauta.mouseWheelMoved(e); | |
833 } | |
834 | |
835 public void keyTyped(KeyEvent e) { } | |
836 public void keyReleased(KeyEvent e) { } | |
837 | |
838 public void keyPressed(KeyEvent e) | |
839 { | |
840 // Handle keyboard input | |
841 if (widgets.containsObject(aboutBox)) | |
842 aboutBox.keyPressed(e); | |
843 else | |
844 widgets.keyPressed(e); | |
845 } | |
846 | |
847 public void run() | |
848 { | |
849 while (animEnable) | |
850 { | |
851 // Progress game animation clock | |
852 gameUpdates++; | |
853 | |
854 // Animate components | |
855 lauta.animate(gameUpdates); | |
856 | |
857 // Repaint with a frame limiter | |
858 if (gameUpdates % 4 == 1) | |
859 repaint(); | |
860 | |
861 // Sleep for a moment | |
862 try { | |
863 Thread.sleep(10); | |
864 } | |
865 catch (InterruptedException x) { | |
866 } | |
867 } | |
868 } | |
869 | |
870 class BtnNewGame extends IDMButton | |
871 { | |
872 public BtnNewGame(float x, float y) | |
873 { | |
874 super(x, y, KeyEvent.VK_ESCAPE, G.fonts[1], "New Game"); | |
875 } | |
876 | |
877 public void clicked() | |
878 { | |
879 startNewGame(); | |
880 } | |
881 } | |
882 | |
883 class BtnSwapPiece extends IDMButton | |
884 { | |
885 public BtnSwapPiece(float x, float y) | |
886 { | |
887 super(x, y, KeyEvent.VK_SPACE, G.fonts[1], "Swap"); | |
888 } | |
889 | |
890 public void clicked() | |
891 { | |
892 lauta.pieceSwapCurrent(); | |
893 } | |
894 } | |
895 | |
896 class BtnAbout extends IDMButton | |
897 { | |
898 public BtnAbout(float x, float y) | |
899 { | |
900 super(x, y, KeyEvent.VK_A, G.fonts[1], "About"); | |
901 } | |
902 | |
903 public void clicked() | |
904 { | |
905 if (!widgets.containsObject(aboutBox)) | |
906 widgets.add(aboutBox); | |
907 } | |
908 } | |
909 } |