From c3f850a045847df4c16a735b7a918b8020ed590a Mon Sep 17 00:00:00 2001
From: Liming Xie <liming.xie@gmail.com>
Date: Thu, 8 Jan 2015 17:38:48 +0800
Subject: [PATCH] bugfix and improve demo ui

---
 dump.rdb             | Bin 2236 -> 3981 bytes
 lib/gamer.js         |  21 +++++++++-
 lib/holdem_game.js   |   2 -
 lib/holdem_poker.js  |  22 +++++------
 lib/jinhua_game.js   |   2 +-
 lib/jinhua_poker.js  |  14 +++----
 lib/room.js          |   1 +
 www/index.html       |   6 ++-
 www/js/en.lang.js    |  19 +++++++++
 www/js/zh.lang.js    |  21 +++++++++-
 www/main.css         |  16 +++++++-
 www/main.js          |  92 ++++++++++++++++++++++++++++---------------
 wwwsrc/index.html    |   6 ++-
 wwwsrc/js/en.lang.js |  19 +++++++++
 wwwsrc/js/zh.lang.js |  21 +++++++++-
 wwwsrc/main.css      |  12 +++++-
 wwwsrc/main.js       |  56 +++++++++++++++++++-------
 17 files changed, 251 insertions(+), 79 deletions(-)

diff --git a/dump.rdb b/dump.rdb
index 7345883ffe1937ab70301689d28a9ca19d5d70fa..10f44d018a9e2c50ff1f3a99de154b6b55c56c6d 100644
GIT binary patch
literal 3981
zcmcIn&5zSY6raQs+u4n?A0V{sE*pz&OC`P%(habxkR_mng#(mcpryeiPU2n14zaTZ
zPEoJ@C-%~+YM~bdhf0W4;n1p5Z@pVp50!dpD$y1dkv@B7cJ0IwQg?gs9OC%Ry!ZQf
zZ{9n5=KPgxHd}a!B&n*_wdKW{Wt6r0v99v2av5Jn2<2pMo<}IIm$5)!Cb0?s<x`G!
zTeao(ZXu1!nqA`ex!kU)LvpiaXv=cLa%4lV>rOv47Ye=Dw5+;aG%csnGK_9kIgL-l
zJ||>uT12R$#<2VS@n64Ni6J!Bx#?mF)nzI6<ZNdSnQlyqp?+#tFkp@Z?Cw8+4?vuG
zh=U={8pL^Wmc=p1moLs=CBH~X?-a|6`NCUWrJ`8af=>;Z%cM!F9V3RZbEl#4H0c~o
zdMoSMG+$C}-L%A)8E)LxRL3rstd{9W|HL=aNk?7NOsN-7Rpqx)b7LW%?hRCe9(-ga
zilIu}@G7y!Dls~rrRaR>1Nj`=iF_DoR&}%1Qhm}~#22BGM`Ug<BMp5?_e(a`<o1*>
zP8c)SmSS>M-Z#luW47uywdScpe;BEy<6T8nbgV=Cip=d}NNFrHT&k%KPq>*>#xq3^
z;_*=+JsSY&Bm-%9=fa8Tnl{+Zlq30_sA<IZxvo-D3~WH+dVr$(=Jw$dbEL+zBUArt
zRO)EV)|%^DbJ^Bzbutkr$##{Ratq&rlbC^%)CNw%#3H6khHBe-so35#J2?u8!Q-4_
zNHCp~^hW5s8q#@<=p2m^X_7=(9BN<DegOgr_yu(uG}eNOEc>$Va4FF@4HaBVd0%;e
zA3zzt0K1t1y8#vkKCgIUt$It%TXb((^_DenF}$VjEvC0v-qN7f#t?u_W*{z-;E5vy
z7UO<>o_JS3EIVMwIpq?*1b@uS+(KA(42b{`!X(k&EYaS6foZS(W~>yUyFwwF{pKd<
z;kK|nhZKEl_e;E@+{JexD+|P)LWs!Q#mm&wH;vg%xcoFa=5o^s!W0+3-k$cB3tT?w
z^6d*=&zK=S!}Ew&bfBvt-@WFcqcPvDP?$n3E&qyurBJ}Kk2wp-aHj#Ol3x+<dMMyW
zUO=4%TqP%z#(m~4wsP4l81rY!L;MhG`K8S5o=1pl>E%Sie<u*b+(@PQ259x;viSS8
zpVA53lHW-3D4uB4EK?I?5o>i-H)Ih_+?bMz>#CubRYxmN_2LsZ=3G22@anqis7+Bm
zkttcaX$zg_ui=DUvYMLkXPnb+Hxkd|2}4`gj2WR99|LD_iiTCy&6L|aN2$$bg^=V&
zcE}Gxb@<S$LzUHmI>E4sErFXy^^pqW^A7cqAOfJ#d&*<{7!ddd5LgHzkc&Xz=Y!(5
z5ReE0y*Q~qpy$_Lp1^+n<%vwF{GZ&Pf9lU$yk#1?sfoS*i^6p}DKSS{%;%0Cg#-@4
zLrn%;Nw5KP-~s5236uAf*0jZE0W)EK3_*?@fdB_b1ld$~!k-can3)KX$-xdEhVS;n
z0~4N4-@M@dpio1geBL5c5P^JG`CNH~AHla@4Qe1Cfxyarye$Nn2Jj)Kfxn3cUMid~
zg1v_DM_2?bkwDotr&O4IqNY@fadsys5Y{e3UNksf!|)O4chq!JlY=HZ_}O5a^yhij
z&qyEf``HzI1&|uPd$4{sboa=>-9uuX%)fWY%HEjWN22e8FwlJa8z+rzoliM)26hPW
zFeH1gO_2GKG|N369p#GEl)X2OZdPSr<eh=52yFy|NdroOT2aRzHl_k_Gzw(6$@~vj
z5vIt9L4#eJpm_ek;}x$Fi#RYemZ-sWb@RZKLUb<v?}KTWAyB7x=)^P35H^GlF=QSX
nvUSg}=;d@ctEH3CLoN)O6b9OyXja=Ca{Kpw{6)RCu<_}?IYE~*

delta 804
zcmeB`-y=AoMNUbbMV*0xK}LaDh=qaSz)=R42UBz!PIxggFmN3>HgTi2$YB(rBM%sP
z85t($Gs-KYX~n9}o+*ZpfvY&Rs4TU}syMaqfZ*i$EUL=;QOrC5HuFCNS8`%;W?nv2
z-E$UIel+JymS#0@+>4@j-vbt2hW#Mo0EjpUA`XFw!yw`ah&T!&j)92dAmYU29#(la
zLFNZnj2k9zWK9a;;Yd%+O|?qQ$vN!c;ZVg=1q_dT1!id;1_qYojKmVQ2lH$tAtAz^
zk)M;2nrq1UfJuloD>E;nH1PqK5QEVJX4M7@1tEs{57@jJj2|!vO@6^9haR?Q4xeny
zu7a-QIEvp+Om1OUu|W&wgD6T4L4uB#yA&A4R?4M@1_nll9TqukX4wq%#fr(F*;6Lp
z<Wv+A<;qDcE{V^{PtVNbc_7Vr?(pPZ4rTNx#1<`+V>tH*^FYkc&&$coOV!l{8m$Dx
zzy!kzwviodA~?-}1&rAru=+xR>_4*8j1N03b=b<X6{vUB<nLTLMgr<Ati{RsMXBr`
znV3^63RpifvF4<frRGSme_}#$GR(ZmqC9H+Xkjqfi${YWJ#-s+o{69ZCnV|q;+2vF
k#y%{S|7YMR%FoXQCAxzr7$&dei{kvx`BH#=)jgIk0D&j*Jpcdz

diff --git a/lib/gamer.js b/lib/gamer.js
index da584a5..a94d41a 100644
--- a/lib/gamer.js
+++ b/lib/gamer.js
@@ -93,7 +93,16 @@ Gamer.prototype.getName = function() {
 
 Gamer.prototype.saveData = function( reply ){
 	var gamer = this;
-	var db = gamer.server.db;
+	if(typeof reply !== 'function') reply = function(err,ret){};
+
+	var db = null;
+	if(gamer.server) db = gamer.server.db;
+	else if(gamer.room) db = gamer.room.db;
+	else {
+		reply(500, 'db err');
+		return;
+	}
+
 	var p = gamer.profile;
 	
 	var uid_key = 'user:#' + gamer.uid;
@@ -112,7 +121,15 @@ Gamer.prototype.saveData = function( reply ){
 
 Gamer.prototype.refresh = function( reply ){
 	var gamer = this;
-	var db = gamer.server.db;
+	if(typeof reply !== 'function') reply = function(err,ret){};
+	
+	var db = null;
+	if(gamer.server) db = gamer.server.db;
+	else if(gamer.room) db = gamer.room.db;
+	else {
+		reply(500, 'db err');
+		return;
+	}
 
 	var uid_key = 'user:#' + gamer.uid;
 	db.hgetall(uid_key, function(err,userinfo){
diff --git a/lib/holdem_game.js b/lib/holdem_game.js
index 7946299..5e7b384 100644
--- a/lib/holdem_game.js
+++ b/lib/holdem_game.js
@@ -431,8 +431,6 @@ HoldemGame.prototype.gamerMoveTurn = function(move) {
 	
 	if(move) room.moveTurnToNext();
 	
-	console.log( room.state, room.no_raise_counter + ' / ' + room.ingamers_count );
-	
 	var deal_card = false;
 	if(room.no_raise_counter === room.ingamers_count) {
 		room.state ++;
diff --git a/lib/holdem_poker.js b/lib/holdem_poker.js
index c8b7708..e780d84 100644
--- a/lib/holdem_poker.js
+++ b/lib/holdem_poker.js
@@ -14,17 +14,17 @@ var HIGH_CARD		= 1, // 高牌, AQ953
 	ROYAL_FLUSH		= 10; // 皇家同花顺, AKQJ10
 
 var HOLDEM_PATTERNS = {
-	0: 'Invalid',		// 错误
-	1: 'High Card',		// 高牌
-	2: 'One Pair',		// 一对
-	3: 'Two Pair',		// 两对
-	4: 'Three of a Kind', // 三条
-	5: 'Straight', 		// 顺子
-	6: 'Flush', 		//  同花
-	7: 'Fullhouse', 	// 葫芦
-	8: 'Four of a Kind', // 四条
-	9: 'Straight Flush', // 同花顺
-	10: 'Royal Flush' 	// 皇家同花顺
+	0: 'invalid',		// 错误
+	1: 'high card',		// 高牌
+	2: 'one pair',		// 一对
+	3: 'two pair',		// 两对
+	4: 'three of a kind', // 三条
+	5: 'straight', 		// 顺子
+	6: 'flush', 		//  同花
+	7: 'fullhouse', 	// 葫芦
+	8: 'four of a kind', // 四条
+	9: 'straight flush', // 同花顺
+	10: 'royal flush' 	// 皇家同花顺
 };
 
 var Holdem = {
diff --git a/lib/jinhua_game.js b/lib/jinhua_game.js
index 3646b7a..8fccc92 100644
--- a/lib/jinhua_game.js
+++ b/lib/jinhua_game.js
@@ -152,9 +152,9 @@ JinhuaGame.prototype.gameStart = function() {
 		seat = gamer.seat;
 		in_seats.push( seat );
 		
+		gamer.is_ready = false;
 		room.ready_gamers --;
 		
-		gamer.is_ready = false;
 		gamer.is_ingame = true;
 		
 		gamer.profile.coins -= ante;
diff --git a/lib/jinhua_poker.js b/lib/jinhua_poker.js
index b0af27d..c213d32 100644
--- a/lib/jinhua_poker.js
+++ b/lib/jinhua_poker.js
@@ -10,13 +10,13 @@ var HIGH_CARD		= 1, // 单张
 	THREE			= 6; // 豹子
 
 var JINHUA_PATTERNS = {
-	0: '错误',
-	1: '单张',
-	2: '对子',
-	3: '顺子',
-	4: '同花',
-	5: '同花顺',
-	6: '豹子'
+	0: 'invalid',
+	1: 'danzhang',
+	2: 'duizi',
+	3: 'shunzi',
+	4: 'tonghua',
+	5: 'tonghuashun',
+	6: 'baozi'
 };
 
 var Jinhua = {
diff --git a/lib/room.js b/lib/room.js
index ebe096b..e7d609c 100644
--- a/lib/room.js
+++ b/lib/room.js
@@ -238,6 +238,7 @@ Room.prototype.onGamer_enter = function(req, reply) {
 		if(! ret) { reply(404, 'user ' + uid + ' not found'); return; }
 		
 		var gamer = new Gamer().setProfile(ret);
+		gamer.room = room;
 		gamer.seat = -1;
 		
 		room.gamers[ uid ] = gamer;
diff --git a/www/index.html b/www/index.html
index 8010a83..ed2dcf7 100644
--- a/www/index.html
+++ b/www/index.html
@@ -14,10 +14,12 @@
 <body>
 	<div id='env'>
 		<h3 id="roomname">not in room</h3>
-		<ul id="list"></ul>
+		<div id='roomdesc'></div>
 		<h3 id='sharedcards'></h3>
 		<h3 id='pot'></h3>
 		<h3 id='countdown'></h3>
+		<ul id="seats"></ul>
+		<h3 id='mycards'></h3>
 	</div>
 	<div id='logs'>
 	<ul id="messages" class="list"></ul>
@@ -28,6 +30,6 @@ <h3 id='countdown'></h3>
 		<button id='send'>Send</button>
 	</form>
  -->	
-	<div id='cmds'></div>
+	<div id='cmds' class='inactive'></div>
 </body>
 </html>
\ No newline at end of file
diff --git a/www/js/en.lang.js b/www/js/en.lang.js
index 994bf06..b16b7e3 100644
--- a/www/js/en.lang.js
+++ b/www/js/en.lang.js
@@ -27,6 +27,7 @@ hotjs.i18n.put('en', {
 	'pot': 'Pot',
 	'shared cards': 'Shared cards',
 	'private cards': 'Private cards',
+	'my cards': 'My cards',
 	'bet': 'Bet',
 	'ready': 'Ready',
 	'seecard': 'See Card',
@@ -34,6 +35,7 @@ hotjs.i18n.put('en', {
 	'call': 'Call',
 	'raise': 'Raise',
 	'fold': 'Fold/Give Up',
+	'check': 'Check',
 	'pk': 'PK',
 	'result': 'Result',
 	'win': 'Win',
@@ -54,4 +56,21 @@ hotjs.i18n.put('en', {
 	'email': 'Email',
 	'phone': 'Phone',
 	'uuid': 'UUID',
+	'invalid' : 'Invalid',
+	'danzhang': 'High Card',
+	'duizi': 'Pair',
+	'shunzi': 'Straight',
+	'tonghua': 'Flush',
+	'tonghuashun': 'Straight Flush',
+	'baozi': 'Three of a Kind',
+	'high card': 'High Card',
+	'one pair': 'One Pair',
+	'two pair': 'Two Pair',
+	'three of a kind': 'Three of a Kind',
+	'straight': 'Straight',
+	'flush': 'Flush',
+	'fullhouse': 'Fullhouse',
+	'four of a kind': 'Four of a Kind',
+	'straight flush': 'Straight Flush',
+	'royal flush': 'Royal Flush',
 });
diff --git a/www/js/zh.lang.js b/www/js/zh.lang.js
index 8773f68..51cd1d4 100644
--- a/www/js/zh.lang.js
+++ b/www/js/zh.lang.js
@@ -27,7 +27,8 @@ hotjs.i18n.put('zh', {
 	'seconds': '秒',
 	'pot': '彩池',
 	'shared cards': '公牌',
-	'private cards': '私牌',
+	'private cards': '手牌',
+	'my cards': '我的牌',
 	'bet': '下注',
 	'ready': '准备',
 	'seecard': '看牌',
@@ -35,6 +36,7 @@ hotjs.i18n.put('zh', {
 	'call': '跟注',
 	'raise': '加注',
 	'fold': '盖牌/弃牌',
+	'check': '看牌',
 	'pk': '比牌',
 	'result': '结果',
 	'win': '胜',
@@ -55,4 +57,21 @@ hotjs.i18n.put('zh', {
 	'email': 'Email',
 	'phone': '电话号码',
 	'uuid': 'UUID',
+	'invalid' : '错误',
+	'danzhang': '单张',
+	'duizi': '对子',
+	'shunzi': '顺子',
+	'tonghua': '同花',
+	'tonghuashun': '同花顺',
+	'baozi': '豹子',
+	'high card': '高牌',
+	'one pair': '一对',
+	'two pair': '两对',
+	'three of a kind': '三条',
+	'straight': '顺子',
+	'flush': '同花',
+	'fullhouse': '葫芦',
+	'four of a kind': '四条',
+	'straight flush': '同花顺',
+	'royal flush': '皇家同花顺',
 });
diff --git a/www/main.css b/www/main.css
index 8173252..e5cc36d 100644
--- a/www/main.css
+++ b/www/main.css
@@ -30,7 +30,6 @@ div#cmds {
   width: 100%;
   position: fixed;
   bottom: 0;
-  background-color: gray;
 }
 
 form {
@@ -109,6 +108,19 @@ input.cmd {
   background: #eee;
 }
 
-li.active {
+li.active,
+div.active {
   background: yellow;
+}
+
+li.active,
+div.active {
+  background: yellow;
+  border: 1px solid black;
+}
+
+li.inactive,
+div.inactive {
+  background: gray;
+  border: 1px solid black;
 }
\ No newline at end of file
diff --git a/www/main.js b/www/main.js
index b659fe8..7d73666 100644
--- a/www/main.js
+++ b/www/main.js
@@ -7,7 +7,19 @@ var Client = require('../lib/client'),
 
 var client = null;
 
-//hotjs.i18n.setLang('zh');
+hotjs.i18n.setLang('zh');
+
+Poker.toHTML = function(cards) {
+	var html = '';
+	for(var i=0; i<cards.length; i++) {
+		var card = cards[i];
+		var color = card >> 4;
+		var number = card & 0xf;
+		var png = color + '_' + number + '.png';
+		html += "<img src='img/" + png + "'/>";
+	}
+	return html;
+};
 
 $(document).ready(function(){
 	var socket = io();
@@ -78,7 +90,7 @@ $(document).ready(function(){
 	});
 	
 	client.on('gamestart', function(ret){
-		addMsg(_('game start'));
+		addMsg(_T('game start'));
 		
 		if(ret.room) {
 			client.room = ret.room;
@@ -121,6 +133,14 @@ $(document).ready(function(){
 		$('li.seat').removeClass('active');
 		$('li#seat'+seat).addClass('active');
 		
+		if(ret.uid === client.uid) {
+			$('#cmds').removeClass('inactive');
+			$('#cmds').addClass('active');
+		} else {
+			$('#cmds').removeClass('active');
+			$('#cmds').addClass('inactive');
+		}
+		
 		addMsg(_T('now:') + seat + ', ' + ret.uid);
 	});
 	
@@ -172,7 +192,7 @@ $(document).ready(function(){
 	});
 
 	client.on('pk', function(ret){
-		addMsg( ret.uid + _T('at seat') + ret.seat +  _T('pk') + ret.pk_uid + _T('at seat') + ret.pk_seat + ', ' + _T('result') + ': ' + (ret.win?_T('win'):_T('fail')));
+		addMsg( ret.uid + _T_('at seat') + ret.seat +  _T('pk') + ret.pk_uid + _T_('at seat') + ret.pk_seat + ', ' + _T('result') + ': ' + (ret.win?_T('win'):_T('fail')));
 		
 		var gamers = client.room.gamers;
 		if(ret.uid in gamers) {
@@ -217,11 +237,11 @@ $(document).ready(function(){
 			var pattern = '';
 			if(mycards.length === 3) {
 				pattern = Jinhua.patternString(mycards);
-				addMsg( '#' + gamer.seat + ', ' + uid + ': ' + n + ', ' + pattern );
+				addMsg( '#' + gamer.seat + ', ' + uid + ': ' + n + ', ' + _T_(pattern) );
 			} else {
 				var maxFive = Holdem.sort( Holdem.maxFive(mycards, shared_cards) );
 				pattern = Holdem.patternString( maxFive );
-				addMsg( '#' + gamer.seat + ', ' + uid + ': ' + n + ', ' + pattern + ' (' + Poker.visualize(maxFive) + ')' );
+				addMsg( '#' + gamer.seat + ', ' + uid + ': ' + n + ', ' + _T_(pattern) + ' (' + Poker.visualize(maxFive) + ')' );
 			}
 			
 			cards[ gamer.seat ] = gamer.cards;
@@ -374,7 +394,8 @@ function updateCmds( cmds ){
 			$('#cmds').append(div);
 			for(var i=0; i<v.length; i++) {
 				var arg = v[i];
-				btn = $('<button>').text(_T(k)+' '+ _T(arg)).attr('id', k).attr('arg', arg).addClass('cmd');
+				var t_arg = (typeof arg === 'string') ? _T(arg) : arg;
+				btn = $('<button>').text(_T(k)+' '+ t_arg).attr('id', k).attr('arg', arg).addClass('cmd');
 				div.append(btn);
 				btn.on('click', onBtnClicked);
 			}
@@ -483,7 +504,7 @@ function list_games(){
 		if(err) echo(ret);
 		else {
 			$('#roomname').text(_T('available games'));
-			var list = $('#list');
+			var list = $('#seats');
 			list.empty();
 			for(var i=0; i<ret.length; i++) {
 				var game = ret[i];
@@ -498,7 +519,7 @@ function list_rooms( gameid ) {
 	client.rpc('rooms', gameid, function(err, ret){
 		if(err) echo(ret);
 		else {
-			var list = $('#list');
+			var list = $('#seats');
 			list.empty();
 			for(var i=0; i<ret.length; i++) {
 				var room = ret[i];
@@ -537,11 +558,13 @@ function parseReply(err, ret) {
 }
 
 function showRoom(room) {
-	$('#roomname').text(_T('not in room'));
-	$('#list').empty();
+	$('#roomname').empty();
+	$('#roomdesc').empty();
 	$('#sharedcards').empty();
 	$('#pot').empty();
 	$('#countdown').empty();
+	$('#seats').empty();
+	$('#mycards').empty();
 	if(! room) return;
 	
 	$('#roomname').text( _T('room number') + ': ' + room.id + ' (' + room.name + ')');
@@ -550,7 +573,7 @@ function showRoom(room) {
 	var seats = room.seats;
 	var cards = room.cards;
 	var chips = room.chips;
-	$('#list').append($('<li>').text(_T('gamers in room') + ': ' + Object.keys(gamers).join(', ')));
+	$('#roomdesc').text(_T('gamers in room') + ': ' + Object.keys(gamers).join(', '));
 	for(var i=0, len=seats.length; i<len; i++) {
 		var uid = seats[i];
 		var g = uid ? gamers[ uid ] : null;
@@ -559,6 +582,10 @@ function showRoom(room) {
 			str += g.uid + ' (' + g.name + ') [' + g.coins + ', ' + g.score + ', ' + g.exp + ', ' + g.level + ']';
 			if(cards && cards[i]) {
 				str += _T_('private cards') + '[ ' + Poker.visualize( cards[i] ) + ' ]';
+				
+				if(g.uid === client.uid) {
+					$('#mycards').html( client.uid + ', ' + _T('my cards') + ': <br/>' + Poker.toHTML(cards[i]) );
+				}
 			}
 			if(chips && chips[i]) {
 				str += _T_('bet') + '[ ' + chips[i] + ' ]';
@@ -567,16 +594,17 @@ function showRoom(room) {
 		} else {
 			str += '(' + _T('empty') + ')';
 		}
-		$('#list').append($('<li>').text(str).attr('id', 'seat'+i).addClass('seat'));
+		$('#seats').append($('<li>').text(str).attr('id', 'seat'+i).addClass('seat'));
 	}
 	
 	if(room.shared_cards) {
-		$('#sharedcards').text( _T('shared cards') + ': ' + Poker.visualize(room.shared_cards) );
+		$('#sharedcards').html( _T('shared cards') + ': <br/>' + Poker.toHTML(room.shared_cards) );
 	}
 	
 	if(room.pot) {
 		$('#pot').text( _T('pot') + ': ' + room.pot );
 	}
+	
 }
 
 function execCmd() {
@@ -588,7 +616,7 @@ function execCmd() {
 	var words = cmd.split(' ');
 	switch(words[0]) {
 	case 'clear':
-		$('#list').empty();
+		$('#seats').empty();
 		$('#messages').empty();
 		break;
 	case 'fastsignup':
@@ -922,17 +950,17 @@ var HIGH_CARD		= 1, // 高牌, AQ953
 	ROYAL_FLUSH		= 10; // 皇家同花顺, AKQJ10
 
 var HOLDEM_PATTERNS = {
-	0: 'Invalid',		// 错误
-	1: 'High Card',		// 高牌
-	2: 'One Pair',		// 一对
-	3: 'Two Pair',		// 两对
-	4: 'Three of a Kind', // 三条
-	5: 'Straight', 		// 顺子
-	6: 'Flush', 		//  同花
-	7: 'Fullhouse', 	// 葫芦
-	8: 'Four of a Kind', // 四条
-	9: 'Straight Flush', // 同花顺
-	10: 'Royal Flush' 	// 皇家同花顺
+	0: 'invalid',		// 错误
+	1: 'high card',		// 高牌
+	2: 'one pair',		// 一对
+	3: 'two pair',		// 两对
+	4: 'three of a kind', // 三条
+	5: 'straight', 		// 顺子
+	6: 'flush', 		//  同花
+	7: 'fullhouse', 	// 葫芦
+	8: 'four of a kind', // 四条
+	9: 'straight flush', // 同花顺
+	10: 'royal flush' 	// 皇家同花顺
 };
 
 var Holdem = {
@@ -1150,13 +1178,13 @@ var HIGH_CARD		= 1, // 单张
 	THREE			= 6; // 豹子
 
 var JINHUA_PATTERNS = {
-	0: '错误',
-	1: '单张',
-	2: '对子',
-	3: '顺子',
-	4: '同花',
-	5: '同花顺',
-	6: '豹子'
+	0: 'invalid',
+	1: 'danzhang',
+	2: 'duizi',
+	3: 'shunzi',
+	4: 'tonghua',
+	5: 'tonghuashun',
+	6: 'baozi'
 };
 
 var Jinhua = {
diff --git a/wwwsrc/index.html b/wwwsrc/index.html
index 8010a83..ed2dcf7 100644
--- a/wwwsrc/index.html
+++ b/wwwsrc/index.html
@@ -14,10 +14,12 @@
 <body>
 	<div id='env'>
 		<h3 id="roomname">not in room</h3>
-		<ul id="list"></ul>
+		<div id='roomdesc'></div>
 		<h3 id='sharedcards'></h3>
 		<h3 id='pot'></h3>
 		<h3 id='countdown'></h3>
+		<ul id="seats"></ul>
+		<h3 id='mycards'></h3>
 	</div>
 	<div id='logs'>
 	<ul id="messages" class="list"></ul>
@@ -28,6 +30,6 @@ <h3 id='countdown'></h3>
 		<button id='send'>Send</button>
 	</form>
  -->	
-	<div id='cmds'></div>
+	<div id='cmds' class='inactive'></div>
 </body>
 </html>
\ No newline at end of file
diff --git a/wwwsrc/js/en.lang.js b/wwwsrc/js/en.lang.js
index 994bf06..b16b7e3 100644
--- a/wwwsrc/js/en.lang.js
+++ b/wwwsrc/js/en.lang.js
@@ -27,6 +27,7 @@ hotjs.i18n.put('en', {
 	'pot': 'Pot',
 	'shared cards': 'Shared cards',
 	'private cards': 'Private cards',
+	'my cards': 'My cards',
 	'bet': 'Bet',
 	'ready': 'Ready',
 	'seecard': 'See Card',
@@ -34,6 +35,7 @@ hotjs.i18n.put('en', {
 	'call': 'Call',
 	'raise': 'Raise',
 	'fold': 'Fold/Give Up',
+	'check': 'Check',
 	'pk': 'PK',
 	'result': 'Result',
 	'win': 'Win',
@@ -54,4 +56,21 @@ hotjs.i18n.put('en', {
 	'email': 'Email',
 	'phone': 'Phone',
 	'uuid': 'UUID',
+	'invalid' : 'Invalid',
+	'danzhang': 'High Card',
+	'duizi': 'Pair',
+	'shunzi': 'Straight',
+	'tonghua': 'Flush',
+	'tonghuashun': 'Straight Flush',
+	'baozi': 'Three of a Kind',
+	'high card': 'High Card',
+	'one pair': 'One Pair',
+	'two pair': 'Two Pair',
+	'three of a kind': 'Three of a Kind',
+	'straight': 'Straight',
+	'flush': 'Flush',
+	'fullhouse': 'Fullhouse',
+	'four of a kind': 'Four of a Kind',
+	'straight flush': 'Straight Flush',
+	'royal flush': 'Royal Flush',
 });
diff --git a/wwwsrc/js/zh.lang.js b/wwwsrc/js/zh.lang.js
index 8773f68..51cd1d4 100644
--- a/wwwsrc/js/zh.lang.js
+++ b/wwwsrc/js/zh.lang.js
@@ -27,7 +27,8 @@ hotjs.i18n.put('zh', {
 	'seconds': '秒',
 	'pot': '彩池',
 	'shared cards': '公牌',
-	'private cards': '私牌',
+	'private cards': '手牌',
+	'my cards': '我的牌',
 	'bet': '下注',
 	'ready': '准备',
 	'seecard': '看牌',
@@ -35,6 +36,7 @@ hotjs.i18n.put('zh', {
 	'call': '跟注',
 	'raise': '加注',
 	'fold': '盖牌/弃牌',
+	'check': '看牌',
 	'pk': '比牌',
 	'result': '结果',
 	'win': '胜',
@@ -55,4 +57,21 @@ hotjs.i18n.put('zh', {
 	'email': 'Email',
 	'phone': '电话号码',
 	'uuid': 'UUID',
+	'invalid' : '错误',
+	'danzhang': '单张',
+	'duizi': '对子',
+	'shunzi': '顺子',
+	'tonghua': '同花',
+	'tonghuashun': '同花顺',
+	'baozi': '豹子',
+	'high card': '高牌',
+	'one pair': '一对',
+	'two pair': '两对',
+	'three of a kind': '三条',
+	'straight': '顺子',
+	'flush': '同花',
+	'fullhouse': '葫芦',
+	'four of a kind': '四条',
+	'straight flush': '同花顺',
+	'royal flush': '皇家同花顺',
 });
diff --git a/wwwsrc/main.css b/wwwsrc/main.css
index 846ad4c..15aabb4 100644
--- a/wwwsrc/main.css
+++ b/wwwsrc/main.css
@@ -30,7 +30,6 @@ div#cmds {
 	width: 100%;
 	position: fixed;
 	bottom: 0;
-	background-color: gray;
 }
 
 form {
@@ -109,7 +108,16 @@ input.cmd {
 	background: #eee;
 }
 
-li.active {
+li.active, div.active {
 	background: yellow;	
 }
 
+li.active, div.active {
+	background: yellow;	
+	border: 1px solid black;
+}
+
+li.inactive, div.inactive {
+	background: gray;	
+	border: 1px solid black;
+}
diff --git a/wwwsrc/main.js b/wwwsrc/main.js
index bfbb645..e4f7cb9 100644
--- a/wwwsrc/main.js
+++ b/wwwsrc/main.js
@@ -6,7 +6,19 @@ var Client = require('../lib/client'),
 
 var client = null;
 
-//hotjs.i18n.setLang('zh');
+hotjs.i18n.setLang('zh');
+
+Poker.toHTML = function(cards) {
+	var html = '';
+	for(var i=0; i<cards.length; i++) {
+		var card = cards[i];
+		var color = card >> 4;
+		var number = card & 0xf;
+		var png = color + '_' + number + '.png';
+		html += "<img src='img/" + png + "'/>";
+	}
+	return html;
+};
 
 $(document).ready(function(){
 	var socket = io();
@@ -77,7 +89,7 @@ $(document).ready(function(){
 	});
 	
 	client.on('gamestart', function(ret){
-		addMsg(_('game start'));
+		addMsg(_T('game start'));
 		
 		if(ret.room) {
 			client.room = ret.room;
@@ -120,6 +132,14 @@ $(document).ready(function(){
 		$('li.seat').removeClass('active');
 		$('li#seat'+seat).addClass('active');
 		
+		if(ret.uid === client.uid) {
+			$('#cmds').removeClass('inactive');
+			$('#cmds').addClass('active');
+		} else {
+			$('#cmds').removeClass('active');
+			$('#cmds').addClass('inactive');
+		}
+		
 		addMsg(_T('now:') + seat + ', ' + ret.uid);
 	});
 	
@@ -171,7 +191,7 @@ $(document).ready(function(){
 	});
 
 	client.on('pk', function(ret){
-		addMsg( ret.uid + _T('at seat') + ret.seat +  _T('pk') + ret.pk_uid + _T('at seat') + ret.pk_seat + ', ' + _T('result') + ': ' + (ret.win?_T('win'):_T('fail')));
+		addMsg( ret.uid + _T_('at seat') + ret.seat +  _T('pk') + ret.pk_uid + _T_('at seat') + ret.pk_seat + ', ' + _T('result') + ': ' + (ret.win?_T('win'):_T('fail')));
 		
 		var gamers = client.room.gamers;
 		if(ret.uid in gamers) {
@@ -216,11 +236,11 @@ $(document).ready(function(){
 			var pattern = '';
 			if(mycards.length === 3) {
 				pattern = Jinhua.patternString(mycards);
-				addMsg( '#' + gamer.seat + ', ' + uid + ': ' + n + ', ' + pattern );
+				addMsg( '#' + gamer.seat + ', ' + uid + ': ' + n + ', ' + _T_(pattern) );
 			} else {
 				var maxFive = Holdem.sort( Holdem.maxFive(mycards, shared_cards) );
 				pattern = Holdem.patternString( maxFive );
-				addMsg( '#' + gamer.seat + ', ' + uid + ': ' + n + ', ' + pattern + ' (' + Poker.visualize(maxFive) + ')' );
+				addMsg( '#' + gamer.seat + ', ' + uid + ': ' + n + ', ' + _T_(pattern) + ' (' + Poker.visualize(maxFive) + ')' );
 			}
 			
 			cards[ gamer.seat ] = gamer.cards;
@@ -373,7 +393,8 @@ function updateCmds( cmds ){
 			$('#cmds').append(div);
 			for(var i=0; i<v.length; i++) {
 				var arg = v[i];
-				btn = $('<button>').text(_T(k)+' '+ _T(arg)).attr('id', k).attr('arg', arg).addClass('cmd');
+				var t_arg = (typeof arg === 'string') ? _T(arg) : arg;
+				btn = $('<button>').text(_T(k)+' '+ t_arg).attr('id', k).attr('arg', arg).addClass('cmd');
 				div.append(btn);
 				btn.on('click', onBtnClicked);
 			}
@@ -482,7 +503,7 @@ function list_games(){
 		if(err) echo(ret);
 		else {
 			$('#roomname').text(_T('available games'));
-			var list = $('#list');
+			var list = $('#seats');
 			list.empty();
 			for(var i=0; i<ret.length; i++) {
 				var game = ret[i];
@@ -497,7 +518,7 @@ function list_rooms( gameid ) {
 	client.rpc('rooms', gameid, function(err, ret){
 		if(err) echo(ret);
 		else {
-			var list = $('#list');
+			var list = $('#seats');
 			list.empty();
 			for(var i=0; i<ret.length; i++) {
 				var room = ret[i];
@@ -536,11 +557,13 @@ function parseReply(err, ret) {
 }
 
 function showRoom(room) {
-	$('#roomname').text(_T('not in room'));
-	$('#list').empty();
+	$('#roomname').empty();
+	$('#roomdesc').empty();
 	$('#sharedcards').empty();
 	$('#pot').empty();
 	$('#countdown').empty();
+	$('#seats').empty();
+	$('#mycards').empty();
 	if(! room) return;
 	
 	$('#roomname').text( _T('room number') + ': ' + room.id + ' (' + room.name + ')');
@@ -549,7 +572,7 @@ function showRoom(room) {
 	var seats = room.seats;
 	var cards = room.cards;
 	var chips = room.chips;
-	$('#list').append($('<li>').text(_T('gamers in room') + ': ' + Object.keys(gamers).join(', ')));
+	$('#roomdesc').text(_T('gamers in room') + ': ' + Object.keys(gamers).join(', '));
 	for(var i=0, len=seats.length; i<len; i++) {
 		var uid = seats[i];
 		var g = uid ? gamers[ uid ] : null;
@@ -558,6 +581,10 @@ function showRoom(room) {
 			str += g.uid + ' (' + g.name + ') [' + g.coins + ', ' + g.score + ', ' + g.exp + ', ' + g.level + ']';
 			if(cards && cards[i]) {
 				str += _T_('private cards') + '[ ' + Poker.visualize( cards[i] ) + ' ]';
+				
+				if(g.uid === client.uid) {
+					$('#mycards').html( client.uid + ', ' + _T('my cards') + ': <br/>' + Poker.toHTML(cards[i]) );
+				}
 			}
 			if(chips && chips[i]) {
 				str += _T_('bet') + '[ ' + chips[i] + ' ]';
@@ -566,16 +593,17 @@ function showRoom(room) {
 		} else {
 			str += '(' + _T('empty') + ')';
 		}
-		$('#list').append($('<li>').text(str).attr('id', 'seat'+i).addClass('seat'));
+		$('#seats').append($('<li>').text(str).attr('id', 'seat'+i).addClass('seat'));
 	}
 	
 	if(room.shared_cards) {
-		$('#sharedcards').text( _T('shared cards') + ': ' + Poker.visualize(room.shared_cards) );
+		$('#sharedcards').html( _T('shared cards') + ': <br/>' + Poker.toHTML(room.shared_cards) );
 	}
 	
 	if(room.pot) {
 		$('#pot').text( _T('pot') + ': ' + room.pot );
 	}
+	
 }
 
 function execCmd() {
@@ -587,7 +615,7 @@ function execCmd() {
 	var words = cmd.split(' ');
 	switch(words[0]) {
 	case 'clear':
-		$('#list').empty();
+		$('#seats').empty();
 		$('#messages').empty();
 		break;
 	case 'fastsignup':