OSDN Git Service

BreadItemWidgetClass.js
[h58pcdgame/GameScriptCoreLibrary.git] / www / corelib / core.js
1 /*
2 ****PCD-2013 GameScriptCoreLibrary****
3 Tokyo Gakugei University Senior High School.
4
5 <クライアント要件>
6 Mac OSX: Safari, Chrome
7 Windows: Chrome, IE(9 or later)
8
9 以下の技術が実行可能である必要があります:
10 HTML5
11         Canvas
12         FormData
13
14 <Usage>
15 HTMLソース側に、
16 <div id="MainArea">
17 </div>
18 <div id="Resources">
19 </div>
20 および
21 mainManager = new GameManager();
22 が必要。
23 */
24
25 //
26 //定数
27 //
28
29 var loc = document.location.href;
30
31 var URL_PCD_Root = loc.slice(0, loc.lastIndexOf("/") + 1);
32
33 var URL_PCD_Auth = URL_PCD_Root + "auth.php";
34 var URL_PCD_Audio = URL_PCD_Root + "audio/";
35 var URL_PCD_Stage = URL_PCD_Root + "stage/";
36
37 // ゲームを呼び出す関数       適切なdiv要素で呼び出すとゲームを初期化できる。
38 // つまり、自作ゲームをしたい場合
39 // audio, corelib, images, stageの各フォルダをコピーしたdirにhtmlを置き、<div>.InitGameManager() と実行するスクリプトを書く
40 // stageName == nullだとステージを開始しない。指定する時は.jsおよびパスを省く
41
42 //
43 var CollideBody = 16;
44 var CollideTop = 8;
45 var CollideBottom = 4;
46 var CollideLeft = 2;
47 var CollideRight = 1;
48
49 HTMLDivElement.prototype.InitGameManager = function(stageName)
50 {
51         if(this instanceof HTMLDivElement)
52         {
53                 var man = new GameManager(this, null);
54                 if(stageName) man.loadStageFromNetwork.apply(man, [stageName]);
55                 return man;
56         }else
57         {
58                 throw new TypeError("InitGameManager はdiv要素にしか実行できません");
59         }
60 };
61
62 //
63 //ゲームマネージャー
64 //
65
66 function GameManager(parent, debugTextName){
67         
68         //引数チェック
69         if(debugTextName == undefined) debugTextName = "DebugText";
70         if(parent == undefined) parent = document.getElementById("MainArea");
71         
72         //parentの初期設定
73         if(parent.style.position != 'absolute') parent.style.position = 'relative';
74         
75         this.userID = 0;
76         //サブマネージャーの設定
77         this.networkManager = new NetworkManager(this);
78         this.UIManager = new UIManager(this);
79         this.userManager = new UserManager(this);
80         this.mainArea = parent;
81         //必要最低限のCanvasとコンテキストの設定
82         this.mainCanvas = createCanvas("MainCanvas", 640, 480, 0, 0, 1, parent);
83         this.mainCanvas.style.border = "solid 1px";
84         this.mainContext = this.mainCanvas.getContext('2d');
85         this.debugText = document.getElementById(debugTextName);        //要素が存在しないとnullになり、デバッグが無効になる
86         if(!this.debugText) this.debugText = null;
87         
88         //ブラウザチェック
89         this.isIE = false;
90         if(!this.isAvailableBrowser()){
91                 return null;
92         }
93         //描画コンテキストの初期設定
94         this.mainContext.fillStyle = "rgba(200,255,200,0.5)";
95         this.mainContext.strokeStyle = "rgba(0, 0, 0, 0.5)";
96         this.mainContext.font = "normal 20px sans-serif";
97         //実行中のGameStageオブジェクトを格納
98         this.runningStage = null;
99         this.runningStageName = null;
100         //現在存在しているWidghetのリストを格納
101         this.runningWidgets = [];
102         
103         //タイマーカウントを初期化
104         this.tickCount = 0;
105         //タイマーカウントの秒あたりの回数を設定
106         this.tickPerSecond = 60;
107         // pauseStage()が呼ばれたときにnullじゃなくなる
108         this.stagePausedFunction = null;
109         
110         this.backgroundMusic = null;
111
112         //**イベントリスナー設定**
113         //コールバックを行うために、イベントリスナーのmanagerプロパティにGameManagerのインスタンスを代入する。
114         //timerTick
115         timerTickEventListener.manager = this;
116         window.setInterval(timerTickEventListener, 1000/this.tickPerSecond);
117         
118         //各種コールバック(使用元のスクリプトで使う用)
119         this.stageStartedEvent = null;          //ステージが開始されたときに呼ばれる。引数: stage
120         this.stageStoppedEvent = null;          //ステージが終了されたときに呼ばれる。引数: stage
121 }
122 GameManager.prototype = {
123         timerTick: function(){
124                 //各オブジェクトの単位時間ごとの動作と再描画を行う
125                 //単位時間ごとの動作
126                 this.tickCount++;
127                 if(this.stagePausedFunction == null){
128                         //ポーズしていなければ更新処理
129                         if(this.runningStage){
130                                 //ステージ
131                                 this.runningStage.timerTick();
132                         }
133                 }
134                 // runningStage.timerTick() 内でpauseStage()された時、ここで再度判定しないとWidghetのtickが実行されてしまう
135                 if(this.stagePausedFunction == null){
136                         //ウィジェット
137                         for(var i = 0; i < this.runningWidgets.length; i++){
138                                 var w = this.runningWidgets[i];
139                                 if(!w.tick()){
140                                         // Widghetのtick()からfalseで帰ってきたらWidghetを開放
141                                         this.removeWidget(w);
142                                         i--;
143                                 }
144                         }
145                 } else{
146                         //ポーズしているならば処理関数を実行
147                         this.stagePausedFunction();
148                 }
149                 
150                 
151                 //描画処理
152                 if(this.runningStage){
153                         //ステージ
154                         this.runningStage.draw();
155                 }
156                 //ウィジェット
157                 for(var i = 0; i < this.runningWidgets.length; i++){
158                         var w = this.runningWidgets[i];
159                         w.draw();
160                 }
161                 
162                 //各オブジェクトの走査が終わってから、死亡判定およびステージ再読み込みを行う。
163                 if(this.runningStage && this.runningStage.userControlledCharacter){
164                         if(this.runningStage.userControlledCharacter.HP == 0){
165                                 this.runningStage.userControlledCharacter.HP = this.runningStage.userControlledCharacter.max_HP;
166                                 this.addWidget(new MessageWidgetClass(this, ["死んでしまった……\n", null, function(w){
167                                         w.manager.UIManager.clearInput();
168                                         w.manager.runningStage.userControlledCharacter.HP = w.manager.runningStage.userControlledCharacter.max_HP;
169                                         if(w.manager.runningStageName){
170                                                 w.manager.loadStageFromNetwork(w.manager.runningStageName);
171                                         } else{
172                                                 //ローカルモード時は動作を停止させるだけ
173                                                 w.manager.stopStage();
174                                         }
175                                 }]));
176                                 
177                         }
178                 }
179         },
180         addWidget: function(w){
181                 w.attach();
182                 this.runningWidgets.push(w);
183         },
184         removeWidget: function(w){
185                 if(removeObjectFromArray(this.runningWidgets, w))
186                 {
187                         w.detach();
188                 }
189         },
190         runStage: function(stage){
191                 //新たなステージを開始する
192                 //実行中のステージがあれば終了処理を行わせる。
193                 if(this.runningStage){
194                         this.stopStage();
195                 }
196                 //新たに開始するステージの初期化
197                 //GameManager側の情報をGameStageに渡す。
198                 stage.manager = this;
199                 stage.mainCanvas = this.mainCanvas
200                 stage.debugCanvas = this.debugCanvas
201                 stage.mainContext = this.mainContext
202                 stage.debugContext = this.debugContext
203                 //GameStage側の初期化処理を行わせる。
204                 stage.runStage();
205                 //ネットワーク同期初期化
206                 this.networkManager.joinStage(stage);
207                 //runningStageに登録することで、イベントの通知が開始され、GameStageは実行状態に入る。
208                 this.runningStage = stage;
209                 
210                 this.addWidget(new UserStateWidgetClass(this));
211                 this.addWidget(new PickedItemWidgetClass(this));
212                 
213                 if(this.stageStartedEvent)
214                 {
215                         this.stageStartedEvent.apply(window, [stage]);
216                 }
217                 
218         },
219         pauseStage: function(func){
220                 //ステージの実行を一時停止する。一時停止中、funcに指定された関数が毎tick毎に呼ばれる
221                 if(this.stagePausedFunction == null){
222                         this.stagePausedFunction = func;
223                         return true;
224                 } else{
225                         //ステージが一時停止中のfunc()の中から二重にpauseStage()を呼んではいけない
226                         return false;
227                 }
228         },
229         resumeStage: function(){
230                 //ステージの実行を再開する
231                 if(this.stagePausedFunction != null) {
232                         //必ずpauseStage()の引数に指定したfunc()の中から呼ばれる・・・はず。
233                         this.stagePausedFunction = null;
234                         return true;
235                 } else{
236                         return false;
237                 }
238         },
239         stopStage: function(){
240                 //現在実行中のステージを終了する
241                 if(this.runningStage){
242                         //runningStageから設定解除することで、イベントの通知は行われなくなる。
243                         var aGameStage = this.runningStage;
244                         this.runningStage = null;
245                         //GameStage側の終了処理を行わせる。
246                         aGameStage.stopStage();
247                         //GameStageインスタンスからGameManagerの情報を削除する。
248                         aGameStage.manager = null;
249                         aGameStage.mainCanvas = null;
250                         aGameStage.debugCanvas = null;
251                         aGameStage.mainContext = null;
252                         aGameStage.debugContext = null;
253                         
254                         //画面上に表示されたすべてのWidgetを解放する
255                         for(;this.runningWidgets.length>0;)
256                         {
257                                 this.removeWidget(this.runningWidgets[0]);
258                         }
259                         
260                         if(this.stageStoppedEvent)
261                         {
262                                 this.stageStoppedEvent.apply(window, [aGameStage]);
263                         }
264                 }
265         },
266         loadStageFromLocal: function(code){
267                 //各種パスをローカル用に変更
268                 URL_PCD_Root = "./";
269                 URL_PCD_Auth = URL_PCD_Root + "auth.php";
270                 URL_PCD_Audio = URL_PCD_Root + "audio/";
271                 URL_PCD_Stage = URL_PCD_Root + "stage/";
272                 
273                 var stage = eval(code);
274                 mainManager.runStage(stage);
275         },
276         loadStageFromNetwork: function(name){
277                 //URL_PCD_Stage/name.jsを利用してステージを作成する。
278                 var request = this.networkManager.CreateRequestObject();
279                 //同期モード
280                 request.open('GET', URL_PCD_Stage + name + ".js", false);
281                 this.networkManager.RequestObjectDisableCache(request);
282                 request.send(null);
283                 
284                 if(request.status == 0){
285                         alert("ネットワークにアクセスできません。" + request.status + ":" + request.statusText);
286                 }else if((200 <= request.status && request.status < 300) || (request.status == 304)){
287                         if(this.userID != 0){
288                                 var rq2 = this.networkManager.CreateRequestObject();
289                                 //同期モード
290                                 rq2.open('GET', URL_PCD_Auth + "?action=chstg&name=" + name + "&id=" + this.userID);
291                                 this.networkManager.RequestObjectDisableCache(rq2);
292                                 rq2.send(null);
293                         }
294                         var stage = eval(request.responseText);
295                         this.runStage(stage);
296                         this.runningStageName = name;
297                 }else{
298                         alert("サーバーがエラーを返しました。" + request.status + ":" + request.statusText);
299                 }
300         },
301         debugOut: function(str){
302                 if(this.debugText != null)
303                 {
304                         if(this.isIE)
305                         {
306                                 //CRLF
307                                 this.debugText.value = str.replace(/\n/g,"\r\n") + this.debugText.value;
308                         } else{
309                                 //LF
310                                 this.debugText.innerHTML = str + this.debugText.value;
311                         }
312                 }
313         },
314         isAvailableBrowser: function(){
315                 //ブラウザの判別を行う。実行不可能な場合はfalseを返す。
316                 //http://d.hatena.ne.jp/Naotsugu/20110927/1317140891
317                 var userAgent = window.navigator.userAgent.toLowerCase();
318                 var appVersion = window.navigator.appVersion.toLowerCase();
319                 
320                 if (userAgent.indexOf('opera') != -1) {
321                         //opera
322                         this.debugOut("Browser:Opera\n");
323                 } else if (userAgent.indexOf('msie') != -1) {
324                         if (appVersion.indexOf("msie 9.") != -1) {
325                                 //ie9
326                                 this.debugOut("Browser:IE9\n");
327                         } else if (appVersion.indexOf("msie 8.") != -1) {
328                                 //ie8
329                                 this.debugOut("Browser:IE8\n");
330                         } else if (appVersion.indexOf("msie 7.") != -1) {
331                                 //ie7
332                                 this.debugOut("Browser:IE7\n");
333                         } else if (appVersion.indexOf("msie 6.") != -1) {
334                                 //ie6
335                                 this.debugOut("Browser:IE6\n");
336                         } else{
337                                 this.debugOut("Browser:IE?\n");
338                         }
339                         this.isIE = true;
340                 } else if (userAgent.indexOf('chrome') != -1) {
341                         //chrome
342                         this.debugOut("Browser:Chrome\n");
343                 } else if (userAgent.indexOf('safari') != -1) {
344                         //safari
345                         this.debugOut("Browser:Safari\n");
346                 } else if (userAgent.indexOf('gecko') != -1) {
347                         //gecko
348                         this.debugOut("Browser:Gecko\n");
349                 } else {
350                         //unknown
351                         this.debugOut("Browser:Unknown\n");
352                 }
353                 //描画コンテキストからHTML5対応チェック
354                         if(!this.mainCanvas || !this.mainCanvas.getContext){
355                                 //HTML5未対応の場合
356                                 alert("このゲームを遊ぶためには、HTML5に対応しているブラウザ(Google Chrome等)でアクセスしてください...。");
357                         return false;
358                 }
359                 return true;
360         },
361         setBackgroundMusic: function(name){
362                 if(this.backgroundMusic){
363                         //再生していたら止める
364                         this.backgroundMusic.pause();
365                         this.backgroundMusic = null;
366                 }
367                 if(name){
368                         //引数がnullでなければaudioオブジェクトを取得
369                         this.backgroundMusic = createAudio(name);
370                         if(this.backgroundMusic){
371                                 //ループを設定して再生開始
372                                 this.backgroundMusic.loop = true;
373                                 this.backgroundMusic.play();
374                         }
375                 }
376         },
377 };
378
379 //
380 //イベントリスナー
381 //
382 //イベントリスナーにおけるthisは、イベントリスナーを登録したオブジェクトまたはwindowになり、通常とは異なるので注意。
383
384 function timerTickEventListener(event)
385 {
386         timerTickEventListener.manager.timerTick(event);
387 }