Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[improvment] Mahjong app be able to adapt to different devices( pc, i…

…pad, apad, cellphone)
  • Loading branch information...
commit a35df26e14669a9c3bff26cd437ee1562660ca88 1 parent 5051721
Li Wenbo authored August 14, 2012
129  plugins/engine2d.js
@@ -21,11 +21,12 @@ var game;
21 21
 ##register preload: opa[list(string)],opa[list(string)],( -> void) -> void
22 22
 ##args(imgIdents,audIdents,callback)
23 23
 {
24  
-   AUD_CACHE = {};
25 24
    var images = list2js(imgIdents);
26 25
    var audios = list2js(audIdents);
27  
-   var countLoaded = 0;
28  
-   var countTotal = 0; 
  26
+   var imgLoaded=0,imgError=0,imgTotal=0;
  27
+   var audLoaded=0,audError=0,audTotal=0;
  28
+
  29
+   var progressChanged = +new Date;
29 30
 	
30 31
    function incrementLoaded() {
31 32
       countLoaded++;
@@ -44,24 +45,58 @@ var game;
44 45
 
45 46
    function imgSuccessHandler() {
46 47
 	  IMG_CACHE[this.key] = this;
47  
-      incrementLoaded();
  48
+	  imgLoaded++;
  49
+	  updateProgress();
  50
+	  progressChanged = +new Date;
48 51
    }
49 52
 	
  53
+   //不知到为什么Firefox会触发两次canplay事件,
  54
+   //如果不做判断,会出现countLoaded大于countTotal的事情。
50 55
    function audSuccessHandler() {
51  
-	  //不知到为什么Firefox会触发两次canplay事件,
52  
-	  //如果不做判断,会出现countLoaded大于countTotal的事情。
53 56
 	  if(!AUD_CACHE[this.key]){
54 57
 		  AUD_CACHE[this.key] = this;
55  
-	  	  incrementLoaded();
  58
+	  	  audLoaded++;
56 59
 	  }
  60
+	  progressChanged = +new Date;
  61
+	  updateProgress();
  62
+   }
  63
+
  64
+   function imgErrorHandler() {
  65
+	  imgError++;
  66
+	  progressChanged = +new Date;
  67
+	  updateProgress();
  68
+      throw new Error('Error loading image: ' + this.src);
57 69
    }
58 70
 
59  
-   function errorHandler() {
60  
-	  incrementLoaded();
61  
-      throw new Error('Error loading ' + this.src);
  71
+   function audErrorHandler() {
  72
+	  audError++;
  73
+	  progressChanged = +new Date;
  74
+	  updateProgress();
  75
+	  throw new Error('Error loading sound: ' + this.src);
  76
+   }
  77
+
  78
+   var updateProgress = function(){
  79
+	  info = document.getElementById("loading_info");
  80
+	  if(!!info){
  81
+	  	info.innerHTML = "loading game resource... [ "+(imgLoaded+audLoaded)+" / "+(imgTotal+audTotal)+" ]"
  82
+	  }
  83
+   }
  84
+
  85
+   var statusCheck = function() {
  86
+	  var finished = ((imgLoaded + imgError >= imgTotal) && (audLoaded + audError >= audTotal));
  87
+	  if(finished){
  88
+		 return callback();
  89
+	  }	  
  90
+
  91
+	  //如果图片加载完成,声音加载超时地话,也开始游戏
  92
+	  var noProgressTime = (+new Date) - progressChanged;
  93
+	  if((imgLoaded + imgError >= imgTotal) && noProgressTime >= 3000){
  94
+		 return callback();
  95
+	  }
  96
+	  setTimeout(statusCheck,1000);
62 97
    }
63 98
 	
64  
-    for (var i=0;i<images.length;i++) {
  99
+   for (var i=0;i<images.length;i++) {
65 100
 	  var key = images[i]
66 101
 	  if (key.indexOf('png') == -1 &&
67 102
           key.indexOf('jpg') == -1 &&
@@ -70,36 +105,31 @@ var game;
70 105
       }
71 106
 	 
72 107
 	  var img = new Image();
73  
-	  countTotal++;
74  
-      img.addEventListener('load', imgSuccessHandler, true);
75  
-      img.addEventListener('error', errorHandler, true);
  108
+	  imgTotal++;
  109
+	  img.addEventListener('load', imgSuccessHandler, true);
  110
+      img.addEventListener('error',imgErrorHandler, true);
76 111
       img.src = key;
77 112
       img.key = key;
78 113
    }	
79  
-	
  114
+   
80 115
    if(window.HTMLAudioElement){
81  
-   		try{
82  
-			var audio = document.createElement("audio");
83  
-			if(audio != null && audio.canPlayType && audio.canPlayType("audio/wav")){
84  
-				for( var i=0;i<audios.length;i++){
85  
-	  				var key = audios[i]
86  
-	  				if(key.indexOf('wav') == -1) continue;
87  
-	  
88  
-					var audio = new Audio();
89  
-	  				audio.addEventListener('canplaythrough', audSuccessHandler, true);
90  
-      				audio.addEventListener('error', errorHandler, true);
91  
-      				audio.src = key;
92  
-      				audio.key = key;
93  
-	  				audio.load();
94  
-					
95  
-					countTotal++;
96  
-   				}
97  
-			}
98  
-   		}catch(e){
99  
-			alert("Error: "+e);
100  
-			window.console.error("Error"+e);
101  
-   		}
  116
+		var audio = document.createElement("audio");
  117
+		if(audio != null && !!audio.canPlayType && !!audio.canPlayType("audio/wav")){
  118
+			for( var i=0;i<audios.length;i++){
  119
+	  			var key = audios[i]
  120
+	  			if(key.indexOf('wav') == -1) continue;
  121
+	  			var audio = new Audio();
  122
+	  			audio.addEventListener('canplaythrough', audSuccessHandler, true);
  123
+      			audio.addEventListener('error', audErrorHandler, true);
  124
+      			audio.src = key;
  125
+      			audio.key = key;
  126
+	  			audio.load();
  127
+				
  128
+				audTotal++;
  129
+   			}
  130
+		}
102 131
    }
  132
+   setTimeout(statusCheck,1000);
103 133
 }
104 134
 
105 135
 ##register get: string -> Image.image
@@ -123,16 +153,16 @@ var counter;
123 153
 		return function(){
124 154
 			if(counter > 0){
125 155
 				counter = counter - 1;
126  
-				ctx.clearRect(330+28,237+28,24,24);
  156
+				ctx.clearRect(360+28,237+28,24,24);
127 157
 				ctx.restore();
128 158
 				ctx.save(ctx);
129 159
 				ctx.fillStyle = "#efea3a";
130  
-				ctx.fillRect(330+28,237+28,24,24);
  160
+				ctx.fillRect(360+28,237+28,24,24);
131 161
 				ctx.fillStyle = "red";
132 162
 				ctx.font = "normal bold 24px serif";
133 163
 				ctx.textAlign = "center";
134 164
 				ctx.textBaseline = "middle";
135  
-				ctx.fillText(counter,370,277);
  165
+				ctx.fillText(counter,400,277);
136 166
 				ctx.restore();
137 167
 
138 168
 				window.setTimeout(arguments.callee,1000);
@@ -148,8 +178,8 @@ var counter;
148 178
 	counter = 0;
149 179
 }
150 180
 
151  
-##register show_menu: int -> void
152  
-##args(opt_flag)
  181
+##register show_menu: int,bool -> void
  182
+##args(opt_flag,is_zh)
153 183
 {
154 184
 	var ctx = document.getElementById("gmcanvas").getContext("2d");
155 185
 	var arr = [8,4,2,1]
@@ -186,12 +216,12 @@ var counter;
186 216
 	}
187 217
 	loop.call(this)(); */
188 218
 	
189  
-	var img = get_img("menu_bar.png");
190  
-	for(var i=0, x=490; i<arr2.length; i++){
  219
+	var img = (!is_zh)?get_img("en_menu_bar.png"):get_img("cn_menu_bar.png");	
  220
+	for(var i=0, x=550; i<arr2.length; i++){
191 221
 		if(arr2[i]){
192  
-			ctx.drawImage(img,60*i,0,60,60,x+60*i,475,60,60);
  222
+			ctx.drawImage(img,60*i,0,60,60,x+60*i,435,60,60);
193 223
 		}else{
194  
-			ctx.drawImage(img,60*i,60,60,60,x+60*i,475,60,60);
  224
+			ctx.drawImage(img,60*i,60,60,60,x+60*i,435,60,60);
195 225
 		}
196 226
 	}
197 227
 }
@@ -214,15 +244,14 @@ var get_img = function(key){
214 244
 ##register play_sound: string -> void
215 245
 ##args(key)
216 246
 {
217  
-	var snd = AUD_CACHE[key];
218  
-	if(snd){
  247
+	if(AUD_CACHE[key]){
219 248
 		//注:如果不加snd.reload(),chrome好像无法重新播放声音,即只播放一次
220 249
 		//之后再不会播放,不知道啥原因,自从升级了chrome(18onlinux,21onwindows)
221 250
 		//就有这个问题。但不晓得这样每次播放都reload会不会带来系统负担。
222 251
 		//关注此问题!
223  
-		snd.load();
224  
-		snd.play();
225  
-	}	
  252
+		if(window.chrome) AUD_CACHE[key].load();
  253
+		AUD_CACHE[key].play();
  254
+	}
226 255
 }
227 256
 
228 257
 //两个参数,一个是cookie的名子,一个是值
BIN  resources/actions.png
BIN  resources/board.png
BIN  resources/cn_menu_bar.png
124  resources/css/game.css
... ...
@@ -0,0 +1,124 @@
  1
+* {
  2
+	margin: 0
  3
+	padding: 0
  4
+}
  5
+
  6
+body {
  7
+	background: url("/resources/page_bg.jpg") repeat wheat;
  8
+	color: #DDD;
  9
+	font-size: 13px;
  10
+	line-height: 120%;
  11
+	text-align: center;
  12
+}
  13
+
  14
+div {
  15
+	display: block;
  16
+}
  17
+
  18
+.game{
  19
+	margin: 0 auto;
  20
+}
  21
+
  22
+#container {
  23
+	position: absolute;
  24
+	width: 800px;
  25
+	height: 600px;
  26
+	left: 50%;
  27
+	top: 50%;
  28
+	margin-left: -400px;
  29
+	margin-top: -300px;
  30
+	border-color: #333333;
  31
+    border-radius: 0.3em 0.3em 0.3em 0.3em;
  32
+    border-style: solid;
  33
+    border-width: 2px;
  34
+    box-shadow: 0 0 5px blue, 0 25px 5px rgba(255, 255, 255, 0.1) inset;
  35
+}
  36
+
  37
+#chat {
  38
+	width: 225px;
  39
+	text-align: left;
  40
+	position: absolute;
  41
+	bottom: 0px;
  42
+	background: #5C5C5C;
  43
+	background: rgba(92, 92, 92, 0.5);
  44
+	border: 1px solid #444;
  45
+	border-radius: 0.3em 0.3em 0.3em 0.3em;
  46
+	text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.7);
  47
+}
  48
+
  49
+#chat_title {
  50
+	padding: 8px;
  51
+	background: #020202;
  52
+	background: rgba(2, 2, 2, 0.5);
  53
+	box-shadow: 0 17px 5px rgba(255, 255, 255, 0.1) inset;
  54
+	-moz-box-shadow: 0 17px 5px rgba(255, 255, 255, 0.1) inset;
  55
+	-webkit-box-shadow: 0 17px 5px rgba(255, 255, 255, 0.1) inset;
  56
+	border-bottom: 1px solid black;
  57
+}
  58
+
  59
+#chat ul {
  60
+	margin: 0px;
  61
+	padding: 0px;
  62
+	color: white;
  63
+	width: 100%;
  64
+	min-height: 50px;
  65
+	max-height: 200px;
  66
+	overflow: auto;
  67
+	list-style: none;
  68
+	border-top: 1px solid #666;
  69
+}
  70
+
  71
+#chat_box {
  72
+	padding: 2px;
  73
+}
  74
+
  75
+input.chat_textbox {
  76
+	font-size: 1.4em;
  77
+	margin: 0px;
  78
+}
  79
+
  80
+#gmcanvas {
  81
+	background: #258e80;
  82
+}
  83
+
  84
+#gmloader {
  85
+	background: url("/resources/loading.gif") center center no-repeat;
  86
+	position: absolute;
  87
+	margin-top: 260px;
  88
+	margin-left: 300px;
  89
+	height: 60px;
  90
+}
  91
+
  92
+#loading_info {
  93
+	position: relative;
  94
+    top: 60px;
  95
+	color: black;
  96
+}
  97
+
  98
+#chat_messages {
  99
+  clear: both;
  100
+  border: 0px;
  101
+  height: 360px;
  102
+  width: 207px;
  103
+  margin: 20px 0px 8px 10px;
  104
+  color: #ffebc7;
  105
+  overflow-y: scroll;
  106
+  text-shadow: black 1px 1px 1px;
  107
+}
  108
+
  109
+#chat_messages li {
  110
+  list-style: none;
  111
+  word-wrap: break-word;
  112
+  text-align: left;
  113
+  margin: 3px 0px;
  114
+  font-size: 13px;
  115
+}
  116
+
  117
+#chat_messages .author {
  118
+	clear: both;
  119
+    float: left;
  120
+    font-size: 12px;
  121
+    font-weight: bold;
  122
+    margin-right: 5px;
  123
+}
  124
+
58  resources/css/game_small.css
... ...
@@ -0,0 +1,58 @@
  1
+* {
  2
+	margin: 0;
  3
+	padding: 0;
  4
+	-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  5
+}
  6
+
  7
+body {
  8
+	background: wheat;
  9
+	color: #DDD;
  10
+	font-size: 13px;
  11
+	line-height: 120%;
  12
+	text-align: center;
  13
+}
  14
+
  15
+div {
  16
+	display: block;
  17
+}
  18
+
  19
+.game {
  20
+	margin: 0 auto;
  21
+}
  22
+
  23
+#container {
  24
+	position: absolute;
  25
+	width: 800px;
  26
+	height: 600px;
  27
+	left: 50%;
  28
+	top: 50%;
  29
+	margin-left: -400px;
  30
+	margin-top: -320px;
  31
+}
  32
+
  33
+#chat {
  34
+	display: none;
  35
+}
  36
+
  37
+#gmcanvas {
  38
+	background: #258e80;
  39
+}
  40
+
  41
+#gmloader {
  42
+	background: url("/resources/loading.gif") center center no-repeat;
  43
+	position: absolute;
  44
+	width: 300px;
  45
+	height: 80px;
  46
+	left: 50%;
  47
+	top: 50%;
  48
+	margin-top: -40px;
  49
+	margin-left: -150px;
  50
+}
  51
+
  52
+#loading_info {
  53
+	position: relative;
  54
+	margin-top: 60px;
  55
+	color: black;
  56
+}
  57
+
  58
+
0  resources/main.css → resources/css/main.css
File renamed without changes
87  resources/css/main_small.css
... ...
@@ -0,0 +1,87 @@
  1
+* {margin:0;padding:0;} 
  2
+
  3
+/* must declare 0 margins on everything, also for main layout components use padding, not 
  4
+vertical margins (top and bottom) to add spacing, else those margins get added to total height 
  5
+and your footer gets pushed down a bit more, creating vertical scroll bars in the browser */
  6
+
  7
+html, body {height: 100%;}
  8
+
  9
+#container {
  10
+	height: 100%;
  11
+	min-height: 400px;
  12
+}
  13
+
  14
+#content {
  15
+	overflow:auto;
  16
+	padding-bottom: 60px;
  17
+	height: 400px;
  18
+}  /* must be same height as the footer */
  19
+
  20
+
  21
+#footer {
  22
+    position: relative;
  23
+	margin-top: -60px; /* negative value of footer height */
  24
+	height: 60px;
  25
+	clear:both;
  26
+	text-align: center;
  27
+	background-color: #AAA;
  28
+	width: 100%;
  29
+	padding-top: 10px;
  30
+} 
  31
+
  32
+/*Opera Fix*/
  33
+body:before {/* thanks to Maleika (Kohoutec)*/
  34
+	content:"";
  35
+	height:100%;
  36
+	float:left;
  37
+	width:0;
  38
+	margin-top:-32767px;/* thank you Erik J - negate effect of float*/
  39
+}
  40
+
  41
+div {
  42
+	display: block;
  43
+}
  44
+
  45
+body,html {
  46
+	margin: 0;
  47
+	padding: 0;
  48
+	font: 12px/1.5 arial;
  49
+	height:100%;
  50
+}
  51
+
  52
+body {
  53
+	background:  #0D1F29;
  54
+	color: #DDD;
  55
+	font-size: 13px;
  56
+	line-height: 120%;
  57
+	min-height: 580px0;
  58
+	display: block;
  59
+}
  60
+
  61
+#content {
  62
+	padding: 10px;
  63
+	background: url("/resources/dragon_small.jpg") no-repeat scroll center top #0C1E28;
  64
+}
  65
+
  66
+#login_box {
  67
+	position: relative;
  68
+	margin: 0 auto;
  69
+	width: 300px;
  70
+	height: 240px;
  71
+	top: 135px;
  72
+	color: #EBEBEB;
  73
+	font: 12px Arial, Helvetica, sans-serif;
  74
+}
  75
+
  76
+.txt_user{
  77
+	margin-top: 8px;
  78
+	width: 96%;
  79
+}
  80
+.btn_play{
  81
+	margin: 0px;
  82
+	width: 100%;	
  83
+}
  84
+
  85
+#btn_group{
  86
+	display: none;
  87
+}
0  resources/page.css → resources/css/page.css
File renamed without changes
46  resources/css/page_small.css
... ...
@@ -0,0 +1,46 @@
  1
+* {
  2
+	margin: 0px;
  3
+	padding: 0px;
  4
+}
  5
+
  6
+body {
  7
+	background:	url("/resources/page_bg.jpg") repeat #0c1e28;
  8
+}
  9
+
  10
+.dragon_bg {
  11
+	background: url("/resources/dragon_small.jpg") no-repeat scroll center top #0C1E28;
  12
+	margin: 0px auto;
  13
+}
  14
+
  15
+#game_list {
  16
+	background: #0C1E28;
  17
+	margin: 10px auto;
  18
+	width: 92%;
  19
+	border: 3px solid red;
  20
+	padding: 5px;
  21
+}
  22
+
  23
+.quick-start {
  24
+	clear: both;
  25
+	float: right;
  26
+	margin: 5px 0px;
  27
+}
  28
+
  29
+.title {
  30
+	color: WHITE;
  31
+	float: right;	
  32
+}
  33
+
  34
+.tb_game_list {
  35
+	background: WHITE;
  36
+	width: 100%;
  37
+	border: 1px solid RED;
  38
+}
  39
+
  40
+.tb_game_list th, .tb_game_list td {
  41
+    border-right: 1px solid black;
  42
+	border-bottom: 1px solid black;
  43
+    padding: 2px;
  44
+    text-align: center;
  45
+    vertical-align: middle;
  46
+}
BIN  resources/dialog.png
BIN  resources/dragon_small.jpg
0  resources/menu_bar.png → resources/en_menu_bar.png
File renamed without changes
BIN  resources/exit.png
BIN  resources/loading.bak.gif
BIN  resources/loading.gif
BIN  resources/mahjong-48.png
BIN  resources/setting.png
153  resources/style.css
... ...
@@ -1,153 +0,0 @@
1  
-* {
2  
-	margin: 0
3  
-	padding: 0
4  
-}
5  
-
6  
-body {
7  
-	background: url("/resources/page_bg.jpg") repeat scroll center top #0c1e28;
8  
-	color: #DDD;
9  
-	font-size: 13px;
10  
-	line-height: 120%;
11  
-	text-align: center;
12  
-	height: 100%;
13  
-	min-height: 640px;
14  
-}
15  
-
16  
-
17  
-.text {
18  
-  /** background: url("/resources/input_field_left.png") top left no-repeat;
19  
-  padding-left: 6px;
20  
-  display: block;
21  
-  height: 50px; */
22  
-
23  
-}
24  
-
25  
-#gmloader {
26  
-	background: url("/resources/loading.gif") center center no-repeat;
27  
-	position: absolute;
28  
-	height: 620px;
29  
-	width: 740px;
30  
-}
31  
-
32  
-#loading_info {
33  
-	position: relative;
34  
-    top: 350px;	
35  
-}
36  
-
37  
-.game {
38  
-	background: url("/resources/game_bg.png") top left no-repeat;
39  
-	margin: 5px auto;
40  
-	width: 990px;
41  
-	height: 645px;
42  
-	border-color: #333333;
43  
-    border-radius: 0.3em 0.3em 0.3em 0.3em;
44  
-    border-style: solid;
45  
-    border-width: 2px;
46  
-    box-shadow: 0 0 5px black, 0 25px 5px rgba(255, 255, 255, 0.1) inset;
47  
-}
48  
-
49  
-.game .canvas {
50  
-	margin: 10px;
51  
-	float: left;
52  
-}
53  
-
54  
-.game #gameinfo{
55  
-	width: 230px;
56  
-	height: 100%;
57  
-	float: right;
58  
-}
59  
-
60  
-.game #players {
61  
-	margin-left: 8px;
62  
-	width: 210px;
63  
-	height: 100px;
64  
-}
65  
-
66  
-.game #tb_players td {
67  
-	max-width: 160px;
68  
-	overflow: hidden;
69  
-	height: 23px;
70  
-	text-align: left;
71  
-}
72  
-
73  
-.game #tb_players .self_row {
74  
-	border: 1px;
75  
-	background-color: red;
76  
-	font-size: 14px;
77  
-}
78  
-
79  
-.game .chat{
80  
-	height: 410px
81  
-}
82  
-
83  
-#chat_messages {
84  
-  clear: both;
85  
-  border: 0px;
86  
-  height: 360px;
87  
-  width: 207px;
88  
-  margin: 20px 0px 8px 10px;
89  
-  color: #ffebc7;
90  
-  overflow-y: scroll;
91  
-  text-shadow: black 1px 1px 1px;
92  
-}
93  
-
94  
-#chat_messages li {
95  
-  list-style: none;
96  
-  word-wrap: break-word;
97  
-  text-align: left;
98  
-  margin: 3px 0px;
99  
-  font-size: 13px;
100  
-}
101  
-
102  
-#chat_messages .author {
103  
-	clear: both;
104  
-    float: left;
105  
-    font-size: 12px;
106  
-    font-weight: bold;
107  
-    margin-right: 5px;
108  
-}
109  
-
110  
-
111  
-.game .chat .input {
112  
-	width: 93%;
113  
-	height: 40px;
114  
-	margin: 0 5px;
115  
-}
116  
-
117  
-.game .chat #entry {
118  
-	float: left;
119  
-	width: 150px;
120  
-}
121  
-
122  
-.game .chat #post {
123  
-	float: right;
124  
-}
125  
-
126  
-#panel {
127  
-	height: 40px;
128  
-    margin-top: 3px;
129  
-    text-align: left;
130  
-}
131  
-
132  
-#scores {
133  
-	height: 68px;
134  
-    padding: 5px 10px;
135  
-}
136  
-
137  
-.score_left {
138  
-	float: left;
139  
-	max-width: 105px;
140  
-	overflow: hidden;
141  
-	height: 35px;
142  
-}
143  
-
144  
-.score_middle {
145  
-	float: left;
146  
-    padding-left: 3px;
147  
-    padding-top: 14px;
148  
-}
149  
-
150  
-.score_right {
151  
-	float: right;
152  
-	margin-right: 3px;
153  
-}
1  src/bot.opa
@@ -72,6 +72,7 @@ server module Bot {
72 72
 	* 从手牌中选择一张弃掉
73 73
 	**/
74 74
 	function play(game,idx){
  75
+		Log.debug("Bot","gameid = {game.id}, game.status = {game.status}");
75 76
 		deck = LowLevelArray.get(game.board.decks,idx);
76 77
 		//如果能胡,胡之
77 78
 		if(Board.can_hoo_self(deck)) Mahjong.request_action(game.id,idx,{hoo}) else {
310  src/game.opa
@@ -152,7 +152,7 @@ module Game {
152 152
 		Scheduler.timer(2000,function(){
153 153
 			timestamp = Date.in_milliseconds(Date.now());
154 154
 			Map.iter(function(_,game){
155  
-				if(timestamp - game.last_act >= 12000){
  155
+				if(game.last_act != 0 && timestamp - game.last_act >= 12000){
156 156
 					match(game.status){
157 157
 						case {select_action}: Mahjong.default_action(game);
158 158
 						case {wait_for_resp}: Mahjong.do_action(game);
@@ -379,32 +379,46 @@ module Game {
379 379
 			}
380 380
 		}
381 381
 	}
  382
+	
  383
+	// 获得鼠标点击事件在画布上的坐标 
  384
+	client function get_pos(event){
  385
+		canvas_pos = Dom.get_position(#container);
  386
+		mouse_pos = event.mouse_position_on_page;
  387
+		s = Render.g_scale.get();
  388
+		x = Float.to_int(Float.of_int(mouse_pos.x_px - canvas_pos.x_px) / s);
  389
+		y = Float.to_int(Float.of_int(mouse_pos.y_px - canvas_pos.y_px) / s);
  390
+		if(Render.g_rotate.get()){
  391
+			x = x + 300; y = y + 400;
  392
+			{x:y, y: Render.SRN_HEIGHT - x}
  393
+		}else{
  394
+			x = x + 400; y = y + 300;
  395
+			~{x,y}
  396
+		}
  397
+	}
382 398
 
383 399
 	/**
384 400
 	* 处理鼠标点击的事件 
385 401
 	*/
386  
-	client function process(event){
387  
-		game = get_game();
388  
-		if(game.status == {prepare} || game.status == {wait_for_resp} 
389  
-			|| game.status == {show_result} || game.curr_turn == game.idx){
390  
-			// 获得鼠标点击事件在画布上的坐标 
391  
-			canvas_pos = Dom.get_position(#gmcanvas);
392  
-			mouse_pos = event.mouse_position_on_page;	
393  
-			x = mouse_pos.x_px - canvas_pos.x_px;
394  
-			y = mouse_pos.y_px - canvas_pos.y_px;
395  
-			x = x * 2;
396  
-			y = y * 2;
397  
-			pos = ~{x,y}
398  
-			action = Mahjong.get_action(pos,game);
399  
-			match(action){
400  
-				case {none}: void
401  
-				default: {
402  
-					Render.refresh();
403  
-					Mahjong.request_action(game.id,game.idx,action);
  402
+	client function mouse_down(event){
  403
+		pos = get_pos(event);
  404
+		if(Button.is_pressed(pos,Render.btn_exit)){
  405
+			//退出游戏
  406
+			Client.goto("/hall");
  407
+		}else{
  408
+			game = get_game();
  409
+			if(game.status == {prepare} || game.status == {wait_for_resp} 
  410
+				|| game.status == {show_result} || game.curr_turn == game.idx){
  411
+				action = Mahjong.get_action(pos,game);
  412
+				match(action){
  413
+					case {none}: void
  414
+					default: {
  415
+						Render.refresh();
  416
+						Mahjong.request_action(game.id,game.idx,action);
  417
+					}
404 418
 				}
405 419
 			}
406  
-		} 
407  
-	}	
  420
+		}
  421
+	}
408 422
 
409 423
 	/**
410 424
 	* 收到游戏消息后的处理函数
@@ -430,12 +444,10 @@ module Game {
430 444
 			case {GAME_RESTART: game_msg}:{
431 445
 				Render.update(game_msg);
432 446
 				Render.refresh();
433  
-				refresh_players(game_msg.pls);
434 447
 			}
435 448
 			case {PLAYER_CHANGE: game_msg}:{
436 449
 				Render.update(game_msg);
437 450
 				Render.refresh();
438  
-				refresh_players(game_msg.pls);
439 451
 			}
440 452
 			case {DISCARD_CARD: msg}:{  //玩家弃牌消息
441 453
 				play_sound("da.wav");
@@ -485,16 +497,15 @@ module Game {
485 497
 					case {none}: {
486 498
 						set_game(game);
487 499
 						Render.refresh();
488  
-						Render.show_draw_play(game,195,75);
  500
+						Render.show_draw_play(game,225,75);
489 501
 					}
490 502
 					case ~{some}: {
491 503
 						players = Mahjong.update_scores(game.players,some);
492 504
 						set_game({game with ~players}); 
493  
-						refresh_players(players);
494 505
 						
495 506
 						play_sound("countfan.wav");
496 507
 						Render.refresh();
497  
-						Render.show_result(game,some,195,75);
  508
+						Render.show_result(game,some,225,75);
498 509
 					}
499 510
 				}
500 511
 			}
@@ -512,7 +523,6 @@ module Game {
512 523
 				});
513 524
 				set_game({game with ~players});
514 525
 				Render.refresh();
515  
-				refresh_players(players);
516 526
 			}
517 527
 			case {PLAYER_READY: ready_flags}:{
518 528
 				game = {get_game() with ~ready_flags};
@@ -532,53 +542,6 @@ module Game {
532 542
 		Dom.scroll_to_bottom(#chat_messages)
533 543
 	}
534 544
 	
535  
-	client function game_ready(game,player){
536  
-		//加载资源
537  
-		imgs = ["table_bg.png","board.png","result.png","arrow.png","win.png","menu_bar.png","ting.png","dialog.png",
538  
-				"tiles.png","tiles_small.png","numbers.png","start.png","offline.png","player_frame_h.png","player_frame_v.png",
539  
-				"portrait.jpg","eswn.png","btn_tutor.png"];
540  
-		auds = ["start.wav","da.wav","pung.wav","countfan.wav","win.wav"];
541  
-		preload(imgs,auds,function(){
542  
-			Dom.set_value(#loading_info,"prepare game...");
543  
-			game_obs = Network.observe(game_msg_received,game.game_channel);
544  
-			chat_obs = Network.observe(user_update,game.chat_channel);
545  
-			
546  
-			ck_player = get_cookie("player");
547  
-			ck_coins = get_cookie("coins");
548  
-			coins = if(ck_player != player.name || String.is_empty(ck_coins)) DEFAULT_COINS else string_to_int(ck_coins);
549  
-			player = {player with coins: coins};
550  
-
551  
-			game = update_player(game,player);
552  
-			set_game(game_obj(game,player));
553  
-			refresh_players(game.players);
554  
-			
555  
-			//离开页面的提示(对Opera无效)
556  
-			Dom.bind_beforeunload_confirmation(function(_){
557  
-				{some: "Are you sure to quit?"}
558  
-			});
559  
-			Dom.bind_unload_confirmation(function(_){
560  
-				Mahjong.quit(game.id,player.idx);
561  
-				Network.unobserve(game_obs);
562  
-				Network.unobserve(chat_obs);
563  
-				{none}
564  
-			});
565  
-				
566  
-			//广播游戏信息
567  
-			Network.broadcast({PLAYER_CHANGE: game_msg(game)},game.game_channel);
568  
-			
569  
-			//去掉#gamecanvas的loading样式
570  
-			Render.refresh();
571  
-			Dom.remove(#gmloader);			
572  
-		});
573  
-
574  
-		if(AUTO_READY || player.is_bot) Mahjong.set_ready(game,player.idx);
575  
-
576  
-		//缩放Canvas到合适比例
577  
-		canvas = Canvas.get(#gmcanvas);
578  
-		ctx = Option.get(Canvas.get_context_2d(Option.get(canvas)));
579  
-		Canvas.scale(ctx,0.5,0.5);
580  
-	}
581  
-	
582 545
 	/**
583 546
 	* 获得空的座位 
584 547
 	*/
@@ -628,77 +591,114 @@ module Game {
628 591
 			}}
629 592
 		},game.players,{none});		
630 593
 	}
  594
+
  595
+	client function load_page(game,idx){
  596
+		lang = I18n.lang();
  597
+		if(String.has_prefix(lang,"zh") || String.has_prefix(lang,"jp")
  598
+		   || String.has_prefix(lang,"tw") || String.has_prefix(lang,"hk")){
  599
+				Render.g_zh.set({true})
  600
+		}else Render.g_zh.set({false})
  601
+
  602
+		//加载资源
  603
+		imgs = ["actions.png","table_bg.png","board.png","result.png","arrow.png","win.png","en_menu_bar.png","cn_menu_bar.png",
  604
+				"ting.png","dialog.png","tiles.png","tiles_small.png","numbers.png","start.png","offline.png","player_frame_h.png",
  605
+				"player_frame_v.png","portrait.jpg","eswn.png","btn_tutor.png","exit.png","setting.png"];
  606
+		auds = ["start.wav","da.wav","pung.wav","countfan.wav","win.wav"];
  607
+
  608
+		player = Option.get(LowLevelArray.get(game.players,idx));
  609
+		preload(imgs,auds,function(){
  610
+			Dom.set_value(#loading_info,"prepare game...");
  611
+			game_obs = Network.observe(game_msg_received,game.game_channel);
  612
+			chat_obs = Network.observe(user_update,game.chat_channel);
  613
+			
  614
+			ck_player = get_cookie("player");
  615
+			ck_coins = get_cookie("coins");
  616
+			coins = if(ck_player != player.name || String.is_empty(ck_coins)) DEFAULT_COINS else string_to_int(ck_coins);
  617
+			player = {player with coins: coins};
  618
+
  619
+			game = update_player(game,player);
  620
+			set_game(game_obj(game,player));
  621
+			
  622
+			//离开页面的提示(对Opera无效)
  623
+			Dom.bind_beforeunload_confirmation(function(_){
  624
+				{some: "Are you sure to quit?"}
  625
+			});
  626
+			Dom.bind_unload_confirmation(function(_){
  627
+				Mahjong.quit(game.id,player.idx);
  628
+				Network.unobserve(game_obs);
  629
+				Network.unobserve(chat_obs);
  630
+				{none}
  631
+			});
  632
+				
  633
+			//广播游戏信息
  634
+			Network.broadcast({PLAYER_CHANGE: game_msg(game)},game.game_channel);
  635
+			
  636
+			if(Render.g_zh.get()) Render.g_show_classic_tile.set({true});
  637
+
  638
+			//去掉#gamecanvas的loading样式
  639
+			Render.adjust();
  640
+			Render.refresh();
  641
+			Dom.remove(#gmloader);		
  642
+		});
  643
+		
  644
+		if(AUTO_READY || player.is_bot) Mahjong.set_ready(game,player.idx);
  645
+	}
  646
+
  647
+	function game_ready(game,idx){
  648
+		load_page(game,idx);
  649
+
  650
+		_ = ClientEvent.set_on_disconnect_client(function(ctx){
  651
+			//如果ctx和game的第idx个client一致,说明这个玩家处于死链接状态,去除之。
  652
+			with_game(game.id,function(game){
  653
+				match(LowLevelArray.get(game.clients,idx)){
  654
+					case {none}: void
  655
+					case {some:c}:{
  656
+						if(c.client == ctx.client && c.page == ctx.page){
  657
+							Mahjong.quit(game.id,idx);
  658
+						}
  659
+					}
  660
+				}
  661
+			});
  662
+		});
  663
+
  664
+		void;
  665
+	}
631 666
 	
632 667
 	/** 
633 668
 	* 游戏视图
634 669
 	*/
635 670
 	function game_view(game,idx){
636  
-		//更新第idx个玩家的client_ctx
637  
-		player = Option.get(LowLevelArray.get(game.players,idx));			
638  
-		Resource.styled_page("Mahjong",["/resources/style.css"],
  671
+		player = Option.get(LowLevelArray.get(game.players,idx));
  672
+		Resource.full_page("Mahjong",
639 673
 			<>
640  
-			<div class="game" onready={function(_){
641  
-				_ = ClientEvent.set_on_disconnect_client(function(ctx){
642  
-					//如果ctx和game的第idx个client一致,说明这个玩家处于死链接状态,去除之。
643  
-					with_game(game.id,function(game){
644  
-						match(LowLevelArray.get(game.clients,idx)){
645  
-							case {none}: void
646  
-							case {some:c}:{
647  
-								if(c.client == ctx.client && c.page == ctx.page){
648  
-									Mahjong.quit(game.id,idx);
649  
-								}
650  
-							}
651  
-						}
652  
-					});
653  
-				});
654  
-				game_ready(game,player);
655  
-			  }}>	
656  
-			  <div class="canvas">
  674
+			<div class="game" onready={function(_){game_ready(game,idx)}}>
  675
+			  <div id=#container>
657 676
 			  	<div id=#gmloader >
658 677
 					<p id=#loading_info>loading</p>
659 678
 				</div>
660  
-			  	<canvas id=#gmcanvas width="740" height="625"
661  
-					onmousedown={function(event){ process(event) }}>
  679
+			  	<canvas id=#gmcanvas width="800" height="600" 
  680
+					onmousedown={function(event){mouse_down(event)}}>
662 681
 					"Your browser does not support html5 canvas element."
663 682
 				</canvas>
664 683
 			  </div>
665  
-			  <div id=#gameinfo>
666  
-				<div id=#panel>
667  
-					<div style="float:left;text-align:left;width:45%">
668  
-						<div>
669  
-							<input type="checkbox" style="margin:0px" onclick={function(_){Render.show_or_hide_number()}}/>
670  
-							<span>show number</span>
671  
-						</div>
672  
-						<div>
673  
-							<input type="checkbox" style="margin:0px" onclick={function(_){Render.change_tile_style()}}/>
674  
-							<span>classic tile</span>
675  
-						</div>
676  
-					</div>
677  
-					<div style="float:right;margin:4px">
678  
-						<input type="button" class="btn btn-info" value="Back" onclick={function(_){ Client.goto("/hall")} }/>
679  
-					</div>
680  
-				</div>
681  
-				<div id=#scores>
682  
-					<div class="score_left"><h2>{player.name}</h2></div>
683  
-					<div class="score_middle">[{Board.idx_to_place(player.idx)}]</div>
684  
-					<div class="score_right"><h2 id=#txt_score>{player.coins}</h2></div>
685  
-				</div>
686  
-				<div id=#players>
687  
-					<table id=#tb_players></table>
688  
-				</div>
689  
-				<div class="chat">
690  
-					<ul id="chat_messages"></ul>
691  
-					<div class="input">
692  
-						<input type="text" id=#entry class="input-large"
693  
-								onnewline={function(_){post_chat_msg(player.name,game.chat_channel)}} placeholder="Your Message Here"/>
694  
-						<input id=#post type="button" class="btn btn-primary" value="post"
695  
-								onclick={function(_){post_chat_msg(player.name,game.chat_channel)}}/>
696  
-					</div>
  684
+			  <div id="chat">
  685
+			  	<div id="chat_title">Chat</div>
  686
+				<div id="chat_box">
  687
+					<ul id=#chat_messages></ul>
  688
+					<input id=#entry type="text" class="chat_textbox" 
  689
+					onnewline={function(_){post_chat_msg(player.name,game.chat_channel)}}></input>
697 690
 				</div>
698 691
 			  </div>
699  
-			</div>
700  
-			</>
701  
-		);
  692
+			</div>		
  693
+			</>,
  694
+			<>
  695
+			<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  696
+			<link rel="stylesheet" type="text/css" href="/resources/css/game.css" media="only screen and (min-width:800px)">
  697
+			<link rel="stylesheet" type="text/css" href="/resources/css/game_small.css"
  698
+				  media="only screen and (min-width:240px) and (max-width:800px)">
  699
+			</>,
  700
+			{success},[]
  701
+		); 
702 702
 	}
703 703
 
704 704
 	@async function post_chat_msg(author,channel){
@@ -709,35 +709,6 @@ module Game {
709 709
 		}
710 710
 	}
711 711
 
712  
-	client function refresh_players(players){
713  
-		Dom.remove_content(#tb_players);
714  
-		LowLevelArray.iteri(function(i,player){
715  
-			match(player){
716  
-				case {none}: void
717  
-				case ~{some}: {
718  
-					self_idx = get_game().idx;
719  
-					table_row = if(self_idx == i) {
720  
-						//更新得分
721  
-						set_cookie("player",some.name);
722  
-						set_cookie("coins",int_to_string(some.coins));
723  
-						#txt_player = "{some.name}"
724  
-						#txt_score = "{some.coins}";
725  
-						<tr class="self_row">
726  
-							<td width="160px"> {some.name} </td>
727  
-							<td width="80px"> {some.coins} </td>
728  
-						</tr>
729  
-					}else {
730  
-						<tr>
731  
-							<td width="160px"> {some.name} </td>
732  
-							<td width="80px"> {some.coins} </td>
733  
-						</tr>
734  
-					}
735  
-					#tb_players =+ table_row
736  
-				}
737  
-			}
738  
-		},players);
739  
-	}
740  
-
741 712
 	/** 开始游戏 */
742 713
 	function start(game){
743 714
 		idx = mod(game.dealer / 10000,4);
@@ -787,6 +758,25 @@ module Game {
787 758
 		 ok_flags:0, change_flag:{true}} |> Mahjong.reset_actions(_);
788 759
 	}
789 760
 
  761
+	function reset(game){
  762
+		{game with 
  763
+			status:			 {prepare},
  764
+			winners:         [],
  765
+			curr_turn:       0,
  766
+			last_act:        0,
  767
+			ready_flags: 	 0,
  768
+			ok_flags:		 0,
  769
+			round:			 1,
  770
+			dealer:			 1,
  771
+			change_flag:     {false},
  772
+			curr_card:       {none},
  773
+			players:         LowLevelArray.create(4,{none}),
  774
+			clients:         LowLevelArray.create(4,{none}),
  775
+			actions:         LowLevelArray.create(4,{none}),
  776
+			board:           Board.create()
  777
+		}		
  778
+	}
  779
+
790 780
 	/** 获取某个玩家的deck */
791 781
 	exposed function get_player_deck(game_id,player){
792 782
 		match(get(game_id)){
56  src/login.opa
@@ -60,12 +60,6 @@ function is_logged_in(){
60 60
 	}
61 61
 }
62 62
 
63  
-A = function(user_compat){
64  
-	"U"
65  
-}
66  
-
67  
-
68  
-
69 63
 module Login {
70 64
 	
71 65
 	function get_agent(user_compat){
@@ -107,50 +101,21 @@ module Login {
107 101
 		if(not(String.is_empty(login_name))){
108 102
 			Dom.set_value(#username,login_name);
109 103
 		}
110  
-		jlog("width = {Client.width()}, height = {Client.height()}");
111 104
 	}
112 105
 
113 106
 	function login_view(){
114  
-		/** r = Resource.styled_page("China Mahjong", ["/resources/main.css"],
115  
-			<>
116  
-			<div id="container" onready={function(_){page_ready()}}>
117  
-				<div id="content">
118  
-					<div id=#login_box>
119  
-						<h1> Login </h1>
120  
-						<input id=#username type="text" class="input-xlarge" placeholder="Enter a nickname"
121  
-						 	   onnewline={function(_){attempt_login()}}/>
122  
-						<button class="btn btn-primary btn-large" style="margin-top:10px" onclick={function(_){attempt_login()}}>Play</button>
123  
-						<hr style="margin-bottom:5px">
124  
-						<p>Note: This game need HTML5 canvas support, please use IE9+, Firefox4+, Chrome10+, Opera11+, Safari5+</p>
125  
-						<div style="padding:0px 5px">
126  
-							<button class="btn btn-info" style="float:left;width:60px;"
127  
-							onclick={function(_){Tutor.show_tutor()}}>Tutorial</button>
128  
-							<button class="btn btn-info" style="width:120px">Add to Chrome</button>
129  
-							<a class="btn btn-info" style="float:right;width:105px;" 
130  
-								href="https://github.com/winbomb/mahjong" target="_blank">Fork on Github</a>
131  
-						</div>
132  
-    	    		</div>
133  
-				</div>
134  
-				<div id="footer">
135  
-					<p>Build with <a target="_blank" href="http://www.opalang.org">Opa</a></p>
136  
-					<p>Any Feedback, please mail to: <a href="mailto:li.wenbo@whu.edu.cn">li.wenbo@whu.edu.cn</a></p>
137  
-				</div>
138  
-			</div>			
139  
-			</>
140  
-        ); */
141  
-		
142 107
 		Resource.full_page("China Mahjong",
143 108
 			<>
144 109
 			<div id="container" onready={function(_){page_ready()}}>
145 110
 				<div id="content">
146 111
 					<div id=#login_box>
147 112
 						<h1> Login </h1>
148  
-						<input id=#username type="text" class="input-xlarge" placeholder="Enter a nickname"
  113
+						<input id=#username type="text" class="input-xlarge txt_user" placeholder="Enter a nickname"
149 114