1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package tsukuba_bunko.peko;
20
21 import java.awt.Point;
22
23 import java.awt.event.WindowEvent;
24 import java.awt.event.WindowAdapter;
25
26 import java.io.BufferedReader;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.InputStreamReader;
30
31 import java.net.URL;
32
33 import java.util.List;
34
35 import javax.swing.Icon;
36 import javax.swing.ImageIcon;
37 import javax.swing.JFrame;
38 import javax.swing.JOptionPane;
39
40 import tsukuba_bunko.peko.canvas.CanvasManager;
41
42 import tsukuba_bunko.peko.resource.ResourceManager;
43
44 import tsukuba_bunko.peko.scenario.ScenarioProcessor;
45
46 import tsukuba_bunko.peko.session.Session;
47 import tsukuba_bunko.peko.session.SessionManager;
48
49
50 /***
51 * "Peko" Visual Novel System のメインクラスです。
52 * @author $Author: ppoi $
53 * @version $Revision: 1.5 $
54 */
55 public class PekoSystem {
56
57 /***
58 * 唯一の <code>PekoSystem</code> のインスタンス
59 */
60 private static PekoSystem _instance = null;
61
62
63 /***
64 * <code>PekoSysmtem</code> のバージョン情報
65 */
66 private Object[] _versionInfo = null;
67
68 /***
69 * メインウィンドウ
70 */
71 private JFrame _mainWindow = null;
72
73 /***
74 * CanvasManager
75 */
76 private CanvasManager _canvasManager = null;
77
78 /***
79 * ScenrioProcessor
80 */
81 private ScenarioProcessor _scenarioProcessor = null;
82
83 /***
84 * SessionManager
85 */
86 private SessionManager _sessionManager = null;
87
88 /***
89 * ActionControler
90 */
91 private ActionControler _actionControler = null;
92
93 /***
94 * スタートフラグ
95 */
96 private boolean _started = false;
97
98
99 /***
100 * <code>PekoSystem</code> のインスタンスを作成します。
101 */
102 protected PekoSystem()
103 {
104 super();
105 }
106
107
108 /***
109 * PVNS を開始します。
110 */
111 public void start()
112 {
113 if( _started ) {
114 return;
115 }
116
117 Point location = (Point)_sessionManager.getSystemSaveData().getEntry( "windowLocation" );
118 if( location != null ) {
119 _mainWindow.setLocation( location );
120 }
121 else {
122 _mainWindow.setLocationRelativeTo( null );
123 }
124
125 _mainWindow.setVisible( true );
126 synchronized( _mainWindow ) {
127 try {
128 if( !_mainWindow.isShowing() ) {
129 _mainWindow.wait();
130 Logger.debug( "[system] window opened." );
131 }
132 }
133 catch( InterruptedException ie ) {
134 Logger.debug( "[system] interrupted in waiting for opening window." );
135 }
136 }
137 _actionControler.returnTitle( true );
138 }
139
140 /***
141 * PVNS を終了します。
142 */
143 public void exit()
144 {
145 boolean last = _actionControler.isActive();
146 _actionControler.setActive( false );
147 ResourceManager resources = ResourceManager.getInstance();
148 String title = (String)resources.getResource( "peko.dialog.exit.title" );
149 String message = (String)resources.getResource( "peko.dialog.exit.message" );
150 if( JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(_mainWindow, message, title, JOptionPane.OK_CANCEL_OPTION) ) {
151 _actionControler.setActive( last );
152 return;
153 }
154 _mainWindow.dispose();
155
156 try {
157 _sessionManager.getSystemSaveData().addEntry( "windowLocation", _mainWindow.getLocation() );
158 _sessionManager.saveSystemSaveData();
159 }
160 catch( Exception e ) {
161 Logger.error( MessageIDs.SYS6001E );
162 }
163
164 Logger.info( "Bye!" );
165 System.exit( 0 );
166 }
167
168 /***
169 * タイトル画面画像を表示します。
170 */
171 public void showTitle()
172 {
173 try {
174 if( _started ) {
175 _scenarioProcessor.exit();
176 _canvasManager.clearAll();
177 }
178 else {
179 _canvasManager.getStageCanvas().setUsingEffect( false );
180 _canvasManager.clearAll();
181 _canvasManager.getStageCanvas().setUsingEffect( true );
182 _started = true;
183 }
184 gc();
185
186 _mainWindow.setTitle( (String)ResourceManager.getInstance().getResource("game-info.title") );
187 boolean first = true;
188 while( true ) {
189 String id = _canvasManager.showTitle( first );
190 if( id == null ) {
191 Logger.debug( "[system] canceled." );
192 break;
193 }
194 else if( "start".equals(id) ) {
195 ResourceManager resources = ResourceManager.getInstance();
196 String startPage = (String)resources.getResource( "peko.system.start-scene" );
197 if( startPage == null ) {
198 Logger.fatal( "[system] not specified scenario.start-scene." );
199 throw new InitializationError( "[system] not specified scenario.start-scene." );
200 }
201 else {
202 _canvasManager.clearAll();
203 _sessionManager.initializeSession();
204 gc();
205 _scenarioProcessor.playScenario( startPage, _sessionManager.getSession() );
206 }
207 break;
208 }
209 else if( "resume".equals(id) ) {
210 if( load() ) {
211 break;
212 }
213 }
214 else if( "exit".equals(id) ) {
215 exit();
216 }
217 first = false;
218 }
219 }
220 catch( Exception e ) {
221 Logger.fatal( "[system] fatal error occured during saving states.", e );
222 JOptionPane.showMessageDialog( _mainWindow, "ERROR!", "Error!", JOptionPane.ERROR_MESSAGE );
223 }
224 }
225
226 /***
227 * セーブします。
228 */
229 public void save()
230 {
231 try {
232 Session session = _sessionManager.getSession();
233 _canvasManager.saveState( session );
234 _sessionManager.saveCurrentSession();
235 }
236 catch( Exception e ) {
237 Logger.fatal( "[system] fatal error occured during saving states.", e );
238 JOptionPane.showMessageDialog( _mainWindow, "[system] fatal error occured during saving states.", "Error!", JOptionPane.ERROR_MESSAGE );
239 return;
240 }
241
242 try {
243 _sessionManager.saveSystemSaveData();
244 }
245 catch( Exception e ) {
246 Logger.fatal( "[system] fatal error occured during saving states.", e );
247 PekoSystem.showErrorDialog( "A fatal error occured during saving states", e, true );
248 }
249 }
250
251 /***
252 * ロードします。
253 */
254 public boolean load()
255 {
256 boolean result = false;
257 try {
258 _actionControler.setPlayModeToNormal();
259 if( _sessionManager.load() ) {
260 _scenarioProcessor.exit();
261 _canvasManager.clearAll();
262 gc();
263
264 Session session = _sessionManager.getSession();
265 _mainWindow.setTitle( session.getSceneContext().getSceneTitle() + " - " + ResourceManager.getInstance().getResource("game-info.title") );
266 _canvasManager.resume( session );
267 gc();
268 _scenarioProcessor.playScenario( session.getSceneContext().getSceneName(), session );
269 result = true;
270 }
271 else {
272 result = false;
273 }
274 }
275 catch( Exception e ) {
276 Logger.fatal( "[system] fatal error occured during saving states.", e );
277 JOptionPane.showMessageDialog( _mainWindow, "[system] fatal error occured during saving states.", "Error!", JOptionPane.ERROR_MESSAGE );
278 }
279 return result;
280 }
281
282 /***
283 * システムのバージョン情報ダイアログを表示します。
284 */
285 public void showSystemVersionInfo()
286 {
287 ImageIcon icon = null;
288 URL iconURL = getClass().getClassLoader().getResource( "pvns-logo.gif" );
289 if( iconURL != null ) {
290 icon = new ImageIcon( iconURL, "PVNS Logo" );
291 }
292 JOptionPane.showMessageDialog( _mainWindow, _versionInfo, (String)_versionInfo[0], JOptionPane.INFORMATION_MESSAGE, icon );
293 }
294
295 /***
296 * ゲームのバージョン情報ダイアログを表示します。
297 */
298 public void showGameVersionInfo()
299 {
300 ResourceManager resources = ResourceManager.getInstance();
301
302 Icon icon = (Icon)resources.getResource( "game-info.logo", true );
303 List texts = new java.util.ArrayList();
304 String[] props = { "game-info.title", "game-info.version", "game-info.publisher", "game-info.copyright" };
305 for( int i = 0; i < props.length; ++i ) {
306 String var = (String)resources.getResource( props[i], true );
307 if( var != null ) {
308 texts.add( var );
309 }
310 }
311
312 List additionalInfo = (List)resources.getResource( "game-info.additional-info", true );
313 if( (additionalInfo != null) && !additionalInfo.isEmpty() ) {
314 if( !texts.isEmpty() ) {
315 texts.add( " " );
316 }
317 texts.addAll( additionalInfo );
318 }
319
320 if( !texts.isEmpty() ) {
321 JOptionPane.showMessageDialog( _mainWindow, texts.toArray(), (String)resources.getResource("game-info.title"), JOptionPane.INFORMATION_MESSAGE, icon );
322 }
323 }
324
325 /***
326 * PVNS のバージョン情報を取得します.
327 * @return バージョン情報
328 */
329 public Object[] getPekoSystemVersion()
330 {
331 return _versionInfo;
332 }
333
334 /***
335 * メインウィンドウを取得します。
336 * @return メインウィンドウ
337 */
338 public JFrame getMainWindow()
339 {
340 return _mainWindow;
341 }
342
343 /***
344 * CanvasManager を取得します。
345 * @return CanvasManager
346 */
347 public CanvasManager getCanvasManager()
348 {
349 return _canvasManager;
350 }
351
352 /***
353 * ActionControler を取得します。
354 * @return ActionControler
355 */
356 public ActionControler getActionControler()
357 {
358 return _actionControler;
359 }
360
361
362 /***
363 * "Peko" のバージョン情報を準備します。
364 */
365 private void prepareVersionInfo()
366 {
367 try {
368 InputStream is = PekoSystem.class.getResourceAsStream( "version.txt" );
369 if( is != null ) {
370 BufferedReader reader = new BufferedReader( new InputStreamReader(is, "Shift_JIS") );
371 String line = reader.readLine();
372 List lines = new java.util.ArrayList();
373 while( line != null ) {
374 lines.add( line );
375 line = reader.readLine();
376 }
377 reader.close();
378 is.close();
379 _versionInfo = lines.toArray();
380 }
381 else {
382 Logger.error( "[system] missing version.txt." );
383 Logger.debug( "[system] using embeded version info." );
384 _versionInfo = new Object[]{ "\"Peko\" Visual Novel System", "version 1.0", "All Rights Reserved.", "(c)Copyright by Tsukuba Bunko." };
385 }
386 }
387 catch( IOException ioe ) {
388 Logger.error( "[system] fail to read version info.", ioe );
389 Logger.debug( "[system] using embeded version info." );
390 _versionInfo = new Object[]{ "\"Peko\" Visual Novel System", "version 1.0", "All Rights Reserved.", "(c)Copyright by Tsukuba Bunko." };
391 }
392 }
393
394 /***
395 * メインウィンドウを準備します。
396 */
397 private void prepareMainWindow()
398 {
399 JFrame window = new JFrame( (String)_versionInfo[0] );
400 _mainWindow = window;
401
402 window.setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE );
403 window.addWindowListener( new WindowAdapter() {
404 public void windowOpened( WindowEvent ev )
405 {
406 synchronized( _mainWindow ) {
407 _mainWindow.notify();
408 }
409 }
410 public void windowClosing( WindowEvent ev )
411 {
412 exit();
413 }
414 });
415 }
416
417 /***
418 * ResourceManager を初期化します.
419 * @throws InitializationError 初期化に失敗した場合
420 */
421 private void prepareResources()
422 {
423 try {
424 ResourceManager.getInstance();
425 }
426 catch( Exception e ) {
427 throw new InitializationError();
428 }
429 }
430
431 /***
432 * CanvasManager を初期化します。
433 */
434 private void prepareCanvasManager()
435 {
436 _canvasManager = new CanvasManager();
437 _canvasManager.initialize();
438 }
439
440 /***
441 * ScenarioProcessor を準備します。
442 */
443 private void prepareScenarioProcessor()
444 {
445 _scenarioProcessor = new ScenarioProcessor();
446 }
447
448 /***
449 * SessionManager を準備します。
450 */
451 private void prepareSessionManager()
452 {
453 _sessionManager = new SessionManager();
454 }
455
456 /***
457 * ActionControler を準備します。
458 */
459 private void prepareActionControler()
460 {
461 ActionControler controler = new ActionControler();
462 _canvasManager.getTextCanvas().addMouseListener( controler );
463 _canvasManager.getStageCanvas().addMouseListener( controler );
464 _mainWindow.addKeyListener( controler );
465 _actionControler = controler;
466 }
467
468 /***
469 * 強制的にガベッジコレクタを起動します。
470 */
471 private void gc()
472 {
473 Runtime runtime = Runtime.getRuntime();
474 long before = runtime.freeMemory();
475
476 System.runFinalization();
477 System.gc();
478
479 long after = runtime.freeMemory();
480 Logger.debug( "[system] run gc. before=" + before + ", after=" + after );
481 }
482
483 /***
484 * 唯一の <code>PekoSystem</code> のインスタンスを取得します.
485 * @return 唯一の <code>PekoSystem</code> のインスタンス
486 */
487 public static PekoSystem getInstance()
488 {
489 if( _instance == null ) {
490 synchronized( PekoSystem.class ) {
491 if( _instance == null ) {
492 _instance = new PekoSystem();
493 _instance.prepareVersionInfo();
494 _instance.prepareResources();
495 _instance.prepareSessionManager();
496 _instance.prepareMainWindow();
497 _instance.prepareCanvasManager();
498 _instance.prepareActionControler();
499 _instance.prepareScenarioProcessor();
500 }
501 }
502 }
503 return _instance;
504 }
505
506 /***
507 * エラーダイアログを表示します。
508 * @param message エラーメッセージ
509 * @param e 例外オブジェクト
510 * @param exit 強制終了する場合 <code>true</code>、しない場合 <code>false</code>
511 */
512 public static void showErrorDialog( String message, Throwable e, boolean exit )
513 {
514 if( message == null ) {
515 message = e.getMessage();
516 }
517
518 StackTraceElement[] stackTrace = e.getStackTrace();
519 Object[] messages = new Object[ stackTrace.length + 2 ];
520 for( int i = 0; i < stackTrace.length; ++i ) {
521 messages[i + 1] = stackTrace[i];
522 }
523 messages[0] = "Fatal Error :" + message;
524 messages[1] = e.getClass().getName() + " : " + e.getMessage();
525 JOptionPane.showMessageDialog( null, messages, "FATAL ERROR ! -\"Peko\" Visual Novel System", JOptionPane.ERROR_MESSAGE );
526 if( exit ) {
527 System.exit( -1 );
528 }
529 }
530
531
532 /***
533 * "Peko" Visual Novel System を起動します。
534 */
535 public static void main( String[] args )
536 throws Exception
537 {
538 Logger.prepare();
539 try {
540 PekoSystem system = PekoSystem.getInstance();
541 Object[] versionInfo = system.getPekoSystemVersion();
542 Logger.info( (String)versionInfo[0] );
543 Logger.info( (String)versionInfo[1] );
544
545 system.start();
546 }
547 catch( Throwable e ) {
548 showErrorDialog( null, e, true );
549 }
550 }
551 }