View Javadoc

1   /*
2    * All Rights Reserved.
3    * Copyright (C) 1999-2005 Tsukuba Bunko.
4    *
5    * Licensed under the BSD License ("the License"); you may not use
6    * this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *       http://www.tsukuba-bunko.org/licenses/LICENSE.txt
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   *
17   * $Id: PekoSystem.java,v 1.5 2005/11/23 05:41:43 ppoi Exp $
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 }