├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── account.sql
├── common
├── config.go
├── generate.go
├── model.go
├── poker.go
├── protocol.go
├── rule.go
└── rule.json
├── conf
└── app.conf
├── controllers
├── Index.go
├── Login.go
├── Logout.go
└── Register.go
├── db
└── landlord.db
├── go.mod
├── go.sum
├── main.exe
├── main
├── config.go
├── init.go
├── main.go
└── rule.json
├── router
└── router.go
├── rule.json
├── service
├── client.go
├── request.go
├── robot.go
├── room.go
└── table.go
├── static
├── 1.png
├── 2.png
├── 3.png
├── audio
│ ├── bg_game.ogg
│ ├── bg_room.mp3
│ ├── deal.mp3
│ ├── end_lose.mp3
│ ├── end_win.mp3
│ ├── f_score_0.mp3
│ ├── f_score_1.mp3
│ ├── f_score_2.mp3
│ ├── f_score_3.mp3
│ ├── m_score_0.mp3
│ ├── m_score_1.mp3
│ ├── m_score_2.mp3
│ └── m_score_3.mp3
├── generator.html
├── i
│ ├── bg.png
│ ├── btn.json
│ ├── btn.png
│ ├── btn
│ │ ├── alarm.png
│ │ ├── btn.tps
│ │ ├── exit.png
│ │ ├── hint.png
│ │ ├── icon_default.png
│ │ ├── icon_farmer.png
│ │ ├── icon_landlord.png
│ │ ├── pass.png
│ │ ├── quick.png
│ │ ├── register.png
│ │ ├── score_0.png
│ │ ├── score_1.png
│ │ ├── score_2.png
│ │ ├── score_3.png
│ │ ├── setting.png
│ │ ├── shot.png
│ │ ├── start.png
│ │ └── table.png
│ ├── poker.png
│ └── preload.png
├── js
│ ├── boot.js
│ ├── game.js
│ ├── generator.js
│ ├── net.js
│ ├── phaser-input.js
│ ├── phaser-input.min.js
│ ├── phaser.min.js
│ ├── player.js
│ ├── player_net.js
│ └── rule.js
├── rule.json
└── s
│ └── bg3.ogg
└── templates
└── poker.html
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.js linguist-language=go
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | *.exe
3 | *.exe~
4 | *.log
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016-2018 GaoYC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 斗地主
2 | 斗地主, golang 1.12, sqlite3, go module
3 |
4 | 说明:go modules管理依赖包,执行编译自动下载依赖;使用sqlite数据库,无需配置;自带无脑出牌AI;服务端经过压力测试,客户端代码来自https://github.com/mailgyc/doudizhu 。
5 |
6 | 烦请请各位老爷点个star。。。
7 |
8 |
9 | 运行步骤:
10 |
11 | 1.下载源码:
12 |
13 | git clone https://github.com/dwg255/landlord.git
14 |
15 | 2.编译:
16 |
17 | cd landlord/main
18 | go build -o ../main.exe
19 | cd ..
20 | main.exe
21 |
22 | 3.启动:
23 | 浏览器访问 http://localhost
24 |
25 | 4.效果展示:
26 | http://blzz.shop:8080
27 |
28 |
29 | ---
30 |
31 | License
32 |
33 | This project is released under the terms of the MIT license. See [LICENSE](LICENSE) for more
34 | information or see https://opensource.org/licenses/MIT.
35 |
36 |
37 | ---
38 |
39 | 
40 | 
41 | 
42 |
--------------------------------------------------------------------------------
/account.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE IF NOT EXISTS landlord.account (
2 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
3 | email VARCHAR(20) NOT NULL UNIQUE,
4 | username VARCHAR(10) NOT NULL,
5 | password VARCHAR(100) NOT NULL,
6 | coin INT default 4000,
7 | created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
8 | updated_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
9 | );
--------------------------------------------------------------------------------
/common/config.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "database/sql"
5 | )
6 |
7 | var GameConfInfo GameConf
8 |
9 | type GameConf struct {
10 | HttpPort int
11 | LogPath string
12 | LogLevel string
13 | AppSecret string
14 | DbPath string
15 | Db *sql.DB
16 | }
17 |
--------------------------------------------------------------------------------
/common/generate.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "os"
5 | "github.com/astaxie/beego/logs"
6 | "strconv"
7 | "encoding/json"
8 | )
9 |
10 | func write() {
11 | path := "rule.json"
12 | _, err := os.Stat(path)
13 | if err != nil {
14 | if os.IsNotExist(err) {
15 | generate()
16 | } else {
17 | logs.Error("rule.json err:%v", err)
18 | return
19 | }
20 | }
21 | }
22 |
23 | //生成连续num个的单牌的顺子
24 | func generateSeq(num int, seq []string) (res []string) {
25 | for i, _ := range seq {
26 | if i+num > 12 {
27 | break
28 | }
29 | var sec string
30 | for j := i; j < i+num; j++ {
31 | sec += seq[j]
32 | }
33 | res = append(res, sec)
34 | }
35 | return
36 | }
37 |
38 | //生成num个不同单的组合
39 | func combination(seq []string, num int) (comb []string) {
40 | if num == 0 {
41 | panic("generate err , combination count can not be 0")
42 | }
43 | if len(seq) < num {
44 | logs.Error("seq: %v,num:%d",seq,num)
45 | return
46 | //panic("generate err , seq length less than num")
47 | }
48 | if num == 1 {
49 | return seq
50 | }
51 | if len(seq) == num {
52 | allSingle := ""
53 | for _,single := range seq{
54 | allSingle += single
55 | }
56 | return []string{allSingle}
57 | }
58 | noFirst := combination(seq[1:],num)
59 | hasFirst := []string(nil)
60 | for _,comb := range combination(seq[1:],num-1) {
61 | hasFirst = append(hasFirst, string(seq[0])+comb)
62 | }
63 | comb = append(comb, noFirst...)
64 | comb = append(comb, hasFirst...)
65 | return
66 | }
67 |
68 | func generate() {
69 | CARDS := "34567890JQKA2"
70 | RULE := map[string][]string{}
71 | RULE["single"] = []string{}
72 | RULE["pair"] = []string{}
73 | RULE["trio"] = []string{}
74 | RULE["bomb"] = []string{}
75 | for _, c := range CARDS {
76 | card := string(c)
77 | RULE["single"] = append(RULE["single"], card)
78 | RULE["pair"] = append(RULE["pair"], card+card)
79 | RULE["trio"] = append(RULE["trio"], card+card+card)
80 | RULE["bomb"] = append(RULE["bomb"], card+card+card+card)
81 | }
82 | for _, num := range []int{5, 6, 7, 8, 9, 10, 11, 12} {
83 | RULE["seq_single"+strconv.Itoa(num)] = generateSeq(num, RULE["single"])
84 | }
85 | for _, num := range []int{3, 4, 5, 6, 7, 8, 9, 10} {
86 | RULE["seq_pair"+strconv.Itoa(num)] = generateSeq(num, RULE["pair"])
87 | }
88 | for _, num := range []int{2, 3, 4, 5, 6} {
89 | RULE["seq_trio"+strconv.Itoa(num)] = generateSeq(num, RULE["trio"])
90 | }
91 | RULE["single"] = append(RULE["single"], "w")
92 | RULE["single"] = append(RULE["single"], "W")
93 | RULE["rocket"] = append(RULE["rocket"], "Ww")
94 |
95 | RULE["trio_single"] = make([]string, 0)
96 | RULE["trio_pair"] = make([]string, 0)
97 |
98 | for _, t := range RULE["trio"] {
99 | for _, s := range RULE["single"] {
100 | if s[0] != t[0] {
101 | RULE["trio_single"] = append(RULE["trio_single"], t+s)
102 | }
103 | }
104 | for _, p := range RULE["pair"] {
105 | if p[0] != t[0] {
106 | RULE["trio_pair"] = append(RULE["trio_pair"], t+p)
107 | }
108 | }
109 | }
110 | for _, num := range []int{2, 3, 4, 5} {
111 | seqTrioSingle := []string(nil)
112 | seqTrioPair := []string(nil)
113 | for _, seqTrio := range RULE["seq_trio"+strconv.Itoa(num)] {
114 | seq := make([]string, len(RULE["single"]))
115 | copy(seq, RULE["single"])
116 | for i := 0; i < len(seqTrio); i = i + 3 {
117 | for k, v := range seq {
118 | if v[0] == seqTrio[i] {
119 | copy(seq[k:], seq[k+1:])
120 | seq = seq[:len(seq)-1 ]
121 | break
122 | }
123 | }
124 | }
125 | for _, singleCombination := range combination(seq, len(seqTrio)/3) {
126 | seqTrioSingle = append(seqTrioSingle, seqTrio+singleCombination)
127 | var hasJoker bool
128 | for _, single := range singleCombination {
129 | if single == 'w' || single == 'W' {
130 | hasJoker = true
131 | }
132 | }
133 | if !hasJoker {
134 | seqTrioPair = append(seqTrioPair, seqTrio+singleCombination+singleCombination)
135 | }
136 | }
137 | }
138 | RULE["seq_trio_single"+strconv.Itoa(num)] = seqTrioSingle
139 | RULE["seq_trio_pair"+strconv.Itoa(num)] = seqTrioPair
140 | }
141 |
142 | RULE["bomb_single"] = []string(nil)
143 | RULE["bomb_pair"] = []string(nil)
144 | for _, b := range RULE["bomb"] {
145 | seq := make([]string, len(RULE["single"]))
146 | copy(seq, RULE["single"])
147 | for i, single := range seq {
148 | if single[0] == b[0] {
149 | copy(seq[i:], seq[i+1:])
150 | seq = seq[:len(seq)-1]
151 | }
152 | }
153 | for _,comb := range combination(seq,2){
154 | RULE["bomb_single"] = append(RULE["bomb_single"],b+comb)
155 | if comb[0] != 'w' && comb[0] != 'W' && comb[1] != 'w' && comb[1] != 'W' {
156 | RULE["bomb_pair"] = append(RULE["bomb_pair"],b+comb+comb)
157 | }
158 | }
159 | }
160 |
161 | res,err := json.Marshal(RULE)
162 | if err != nil {
163 | panic("json marsha1 RULE err :" + err.Error())
164 | }
165 | file, err := os.Create("rule.json")
166 | defer func(){
167 | err = file.Close()
168 | if err != nil {
169 | logs.Error("generate err: %v",err)
170 | }
171 | }()
172 | if err != nil {
173 | panic("create rule.json err:" + err.Error())
174 | }
175 | _,err = file.Write(res)
176 | if err != nil {
177 | panic("create rule.json err:" + err.Error())
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/common/model.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | type Account struct {
4 | Id int `json:"id" db:"id"`
5 | Email string `json:"email" db:"email"`
6 | Username string `json:"username" db:"username"`
7 | Password string `json:"password" db:"password"`
8 | Coin int `json:"coin" db:"coin"`
9 | CreatedDate string `json:"created_date" db:"created_date"`
10 | UpdateDate string `json:"updated_date" db:"updated_date"`
11 | }
12 |
--------------------------------------------------------------------------------
/common/poker.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "os"
5 | "io"
6 | "encoding/json"
7 | "fmt"
8 | )
9 |
10 | var (
11 | Pokers = make(map[string]*Combination, 16384)
12 | TypeToPokers = make(map[string][]*Combination, 38)
13 | )
14 |
15 | type Combination struct {
16 | Type string
17 | Score int
18 | Poker string
19 | }
20 |
21 | func init() {
22 | path := "rule.json"
23 | _, err := os.Stat(path)
24 | if os.IsNotExist(err) {
25 | write()
26 | }
27 | file, err := os.Open(path)
28 | if err != nil {
29 | panic(err)
30 | }
31 | defer file.Close()
32 |
33 | var jsonStrByte []byte
34 | for {
35 | buf := make([]byte, 1024)
36 | readNum, err := file.Read(buf)
37 | if err != nil && err != io.EOF {
38 | panic(err)
39 | }
40 | for i := 0; i < readNum; i++ {
41 | jsonStrByte = append(jsonStrByte, buf[i])
42 | }
43 | if 0 == readNum {
44 | break
45 | }
46 | }
47 | var rule = make(map[string][]string)
48 | err = json.Unmarshal(jsonStrByte, &rule)
49 | if err != nil {
50 | fmt.Printf("json unmarsha1 err:%v \n", err)
51 | return
52 | }
53 | for pokerType, pokers := range rule {
54 | for score, poker := range pokers {
55 | cards := SortStr(poker)
56 | p := &Combination{
57 | Type: pokerType,
58 | Score: score,
59 | Poker: cards,
60 | }
61 | Pokers[cards] = p
62 | TypeToPokers[pokerType] = append(TypeToPokers[pokerType], p)
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/common/protocol.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | const (
4 | ReqCheat = 1
5 | ResCheat = 2
6 |
7 | ReqLogin = 11
8 | ResLogin = 12
9 |
10 | ReqRoomList = 13
11 | ResRoomList = 14
12 |
13 | ReqTableList = 15
14 | ResTableList = 16
15 |
16 | ReqJoinRoom = 17
17 | ResJoinRoom = 18
18 |
19 | ReqJoinTable = 19
20 | ResJoinTable = 20
21 |
22 | ReqNewTable = 21
23 | ResNewTable = 22
24 |
25 | ReqDealPoker = 31
26 | ResDealPoker = 32
27 |
28 | ReqCallScore = 33
29 | ResCallScore = 34
30 |
31 | ReqShowPoker = 35
32 | ResShowPoker = 36
33 |
34 | ReqShotPoker = 37
35 | ResShotPoker = 38
36 |
37 | ReqGameOver = 41
38 | ResGameOver = 42
39 |
40 | ReqChat = 43
41 | ResChat = 44
42 |
43 | ReqRestart = 45
44 | ResRestart = 46
45 | )
46 |
--------------------------------------------------------------------------------
/common/rule.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "sort"
5 | "github.com/astaxie/beego/logs"
6 | )
7 |
8 | func SortStr(pokers string) (sortPokers string) {
9 | runeArr := make([]int, 0)
10 | for _, s := range pokers {
11 | runeArr = append(runeArr, int(s))
12 | }
13 | sort.Ints(runeArr)
14 | res := make([]byte, 0)
15 | for _, v := range runeArr {
16 | res = append(res, byte(v))
17 | }
18 | return string(res)
19 | }
20 |
21 | // 出的牌是否在手牌中存在
22 | func IsContains(parent, child string) (result bool) {
23 | for _, childCard := range child {
24 | inHand := false
25 | for i, parentCard := range parent {
26 | if childCard == parentCard {
27 | inHand = true
28 | tmp := []byte(parent)
29 | copy(tmp[i:], tmp[i+1:])
30 | tmp = tmp[:len(tmp)-1]
31 | parent = string(tmp)
32 | break
33 | }
34 | }
35 | if !inHand {
36 | return
37 | }
38 | }
39 | return true
40 | }
41 |
42 | //将牌编号转换为扑克牌
43 | func ToPokers(num []int) (string) {
44 | totalCards := "A234567890JQK"
45 | res := make([]byte, 0)
46 | for _, poker := range num {
47 | if poker == 52 {
48 | res = append(res, 'W')
49 | } else if poker == 53 {
50 | res = append(res, 'w')
51 | } else {
52 | res = append(res, totalCards[poker%13])
53 | }
54 | }
55 | return string(res)
56 | }
57 |
58 | //将牌转换为编号
59 | func ToPoker(card byte) (poker []int) {
60 | if card == 'W' {
61 | return []int{52}
62 | }
63 | if card == 'w' {
64 | return []int{53}
65 | }
66 | cards := "A234567890JQK"
67 | for i, c := range []byte(cards) {
68 | if c == card {
69 | return []int{i, i + 13, i + 13*2, i + 13*3}
70 | }
71 | }
72 | return []int{54}
73 | }
74 |
75 | //将机器人要出的牌转换为编号
76 | func pokersInHand(num []int, findPokers string) (pokers []int) {
77 | var isInResPokers = func(poker int) bool {
78 | for _, p := range pokers {
79 | if p == poker {
80 | return true
81 | }
82 | }
83 | return false
84 | }
85 |
86 | for _, poker := range findPokers {
87 | poker := ToPoker(byte(poker))
88 | out:
89 | for _,pItem := range poker {
90 | for _, n := range num {
91 | if pItem == n && !isInResPokers(n) {
92 | pokers = append(pokers, pItem)
93 | break out
94 | }
95 | }
96 | }
97 | }
98 | return
99 | }
100 |
101 | //获得牌型和大小
102 | func pokersValue(pokers string) (cardType string, score int) {
103 | if combination, ok := Pokers[SortStr(pokers)]; ok {
104 | cardType = combination.Type
105 | score = combination.Score
106 | }
107 | return
108 | }
109 |
110 | //比较牌大小,并返回是否翻倍
111 | func ComparePoker(baseNum, comparedNum []int) (int, bool) {
112 | logs.Debug("comparedNum %v %v", baseNum, comparedNum)
113 | if len(baseNum) == 0 || len(comparedNum) == 0 {
114 | if len(baseNum) == 0 && len(comparedNum) == 0 {
115 | return 0, false
116 | } else {
117 | if len(baseNum) != 0 {
118 | return -1, false
119 | } else {
120 | comparedType, _ := pokersValue(ToPokers(comparedNum))
121 | if comparedType == "rocket" || comparedType == "bomb" {
122 | return 1, true
123 | }
124 | return 1, false
125 | }
126 | }
127 | }
128 | baseType, baseScore := pokersValue(ToPokers(baseNum))
129 | comparedType, comparedScore := pokersValue(ToPokers(comparedNum))
130 | logs.Debug("compare poker %v, %v, %v, %v", baseType, baseScore, comparedType, comparedScore)
131 | if baseType == comparedType {
132 | return comparedScore - baseScore, false
133 | }
134 | if comparedType == "rocket" {
135 | return 1, true
136 | }
137 | if baseType == "rocket" {
138 | return -1, false
139 | }
140 | if comparedType == "bomb" {
141 | return 1, true
142 | }
143 | return 0, false
144 | }
145 |
146 | //查找手牌中是否有比被比较牌型大的牌
147 | func CardsAbove(handsNum, lastShotNum []int) (aboveNum []int) {
148 | handCards := ToPokers(handsNum)
149 | turnCards := ToPokers(lastShotNum)
150 | cardType, cardScore := pokersValue(turnCards)
151 | logs.Debug("CardsAbove handsNum %v ,lastShotNum %v, handCards %v,cardType %v,turnCards %v",
152 | handsNum, lastShotNum, handCards, cardType, turnCards)
153 | if len(cardType) == 0 {
154 | return
155 | }
156 | for _, combination := range TypeToPokers[cardType] {
157 | if combination.Score > cardScore && IsContains(handCards, combination.Poker) {
158 | aboveNum = pokersInHand(handsNum, combination.Poker)
159 | return
160 | }
161 | }
162 | if cardType != "boom" && cardType != "rocket" {
163 | for _, combination := range TypeToPokers["boom"] {
164 | if IsContains(handCards, combination.Poker) {
165 | aboveNum = pokersInHand(handsNum, combination.Poker)
166 | return
167 | }
168 | }
169 | } else if IsContains(handCards, "Ww") {
170 | aboveNum = pokersInHand(handsNum, "Ww")
171 | return
172 | }
173 | return
174 | }
175 |
--------------------------------------------------------------------------------
/conf/app.conf:
--------------------------------------------------------------------------------
1 | [dev]
2 |
3 | http_port=80
4 |
5 | ;日志相关配置
6 | log_path=./logs/game.log
7 | log_level=debug
8 |
9 | ;sqlite配置
10 | db_path=./db/landlord.db
11 |
12 |
13 | [testing]
14 |
15 | http_port=80
16 |
17 | ;日志相关配置
18 | log_path=./logs/game.log
19 | log_level=warn
20 |
21 | ;sqlite配置
22 | db_path=./db/landlord.db
23 |
24 | [product]
25 |
26 | http_port=80
27 |
28 | ;日志相关配置
29 | log_path=./logs/game.log
30 | log_level=error
31 |
32 | ;sqlite配置
33 | db_path=./db/landlord.db
--------------------------------------------------------------------------------
/controllers/Index.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/astaxie/beego/logs"
6 | "html/template"
7 | "landlord/common"
8 | "net/http"
9 | "strconv"
10 | )
11 |
12 | func Index(w http.ResponseWriter, r *http.Request) {
13 | ret := make(map[string]interface{})
14 | t, err := template.ParseFiles("templates/poker.html")
15 | if err != nil {
16 | logs.Error("user request Index - can't find template file %s", "templates/poker.html")
17 | return
18 | }
19 | userId := ""
20 | cookie, err := r.Cookie("userid")
21 | if err != nil {
22 | logs.Error("user request Index - get cookie err: %v", err)
23 | } else {
24 | userId = cookie.Value
25 | }
26 | username := ""
27 | cookie, err = r.Cookie("username")
28 | if err != nil {
29 | logs.Error("user request Index - get cookie err: %v", err)
30 | } else {
31 | username = cookie.Value
32 | }
33 | logs.Debug("user request Index - userId [%v]", userId)
34 | logs.Debug("user request Index - username [%v]", username)
35 | user := make(map[string]interface{})
36 | user["uid"] = userId
37 | user["username"] = username
38 | res, err := json.Marshal(user)
39 | if err != nil {
40 | logs.Error("user request Index - json marsha1 user %v ,err:%v", user, err)
41 | return
42 | }
43 | ret["user"] = string(res)
44 | ret["port"] = strconv.Itoa(common.GameConfInfo.HttpPort)
45 | err = t.Execute(w, ret)
46 | if err != nil {
47 | logs.Error("user request Index - template execute err : %v", err)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/controllers/Login.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "net/http"
5 | "github.com/astaxie/beego/logs"
6 | "fmt"
7 | "crypto/md5"
8 | "landlord/common"
9 | )
10 |
11 | func Login(w http.ResponseWriter, r *http.Request) {
12 | defer func() {
13 | if r:= recover(); r != nil {
14 | logs.Error("user request Login - Login panic:%v ",r)
15 | }
16 | }()
17 | var ret = []byte{'1'}
18 | defer func() {
19 | w.Header().Set("Access-Control-Allow-Origin", "*")
20 | _,err := w.Write(ret)
21 | if err != nil {
22 | logs.Error("")
23 | }
24 | }()
25 | email := r.FormValue("email")
26 | if len(email) == 0 {
27 | email = r.PostFormValue("email")
28 | if email == "" {
29 | logs.Error("user request Login - err: email is empty")
30 | return
31 | }
32 | }
33 | password := r.FormValue("password")
34 | if len(password) == 0 {
35 | password = r.PostFormValue("password")
36 | if password == "" {
37 | logs.Error("user request Login - err: password is empty")
38 | return
39 | }
40 | }
41 | md5Password := fmt.Sprintf("%x", md5.Sum([]byte(password)))
42 | var account = common.Account{}
43 | //err := common.GameConfInfo.MysqlConf.Pool.Get(&account, "select * from account where username=? and password", email,md5Password)
44 | row := common.GameConfInfo.Db.QueryRow("SELECT * FROM `account` WHERE username=? AND password=?",email,md5Password)
45 | if row != nil {
46 | err := row.Scan(&account.Id,&account.Email,&account.Username,&account.Password,&account.Coin,&account.CreatedDate,&account.UpdateDate)
47 | if err != nil {
48 | cookie := http.Cookie{Name: "userid", Value: string(account.Id), Path: "/", MaxAge: 86400}
49 | http.SetCookie(w, &cookie)
50 | cookie = http.Cookie{Name: "username", Value: account.Username, Path: "/", MaxAge: 86400}
51 | http.SetCookie(w, &cookie)
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/controllers/Logout.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "net/http"
5 | "github.com/astaxie/beego/logs"
6 | )
7 |
8 | func LoginOut(w http.ResponseWriter, r *http.Request) {
9 | defer func() {
10 | if r := recover(); r != nil {
11 | logs.Error("LoginOut panic:%v ", r)
12 | }
13 | }()
14 | cookie := http.Cookie{Name: "user", Path: "/", MaxAge: -1}
15 | http.SetCookie(w, &cookie)
16 |
17 | _,err := w.Write([]byte{'1'})
18 | if err != nil {
19 | logs.Error("LoginOut err: %v",err)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/controllers/Register.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/astaxie/beego/logs"
8 | "landlord/common"
9 | "net/http"
10 | "strconv"
11 | "time"
12 | )
13 |
14 | func Register(w http.ResponseWriter, r *http.Request) {
15 | defer func() {
16 | if r := recover(); r != nil {
17 | logs.Error("Register panic:%v ", r)
18 | }
19 | }()
20 | var ret = []byte{'1'}
21 | defer func() {
22 | w.Header().Set("Access-Control-Allow-Origin", "*")
23 | _, err := w.Write(ret)
24 | if err != nil {
25 | logs.Error("user request Register - err : %v", err)
26 | }
27 | }()
28 | username := r.FormValue("username")
29 | if len(username) == 0 {
30 | username = r.PostFormValue("username")
31 | if username == "" {
32 | logs.Error("register err: username is empty")
33 | return
34 | }
35 | }
36 | password := r.FormValue("password")
37 | if len(password) == 0 {
38 | password = r.PostFormValue("password")
39 | if password == "" {
40 | logs.Error("register err: password is empty")
41 | return
42 | }
43 | }
44 | logs.Debug("user request register : username=%v, password=%v ", username, password)
45 |
46 | var account = common.Account{}
47 | row := common.GameConfInfo.Db.QueryRow("select * from account where username=?", username)
48 | err := row.Scan(&account.Id, &account.Email, &account.Username, &account.Password, &account.Coin, &account.CreatedDate, &account.UpdateDate)
49 | if err != nil {
50 | now := time.Now().Format("2006-01-02 15:04:05")
51 | md5Password := fmt.Sprintf("%x", md5.Sum([]byte(password)))
52 | stmt, err := common.GameConfInfo.Db.Prepare(`insert into account(email,username,password,coin,created_date,updated_date) values(?,?,?,?,?,?) `)
53 | if err != nil {
54 | logs.Error("insert new account [%v] err : %v", username, err)
55 | return
56 | }
57 | result, err := stmt.Exec(username, username, md5Password, 10000, now, now)
58 | if err != nil {
59 | logs.Error("insert new account [%v] err : %v", username, err)
60 | return
61 | }
62 | lastInsertId, err := result.LastInsertId()
63 | if err != nil {
64 | logs.Error("insert new account [%v] get last insert id err : %v", username, err)
65 | return
66 | }
67 | ret, err = json.Marshal(map[string]interface{}{"uid": lastInsertId, "username": username})
68 | if err != nil {
69 | logs.Error("json marsha1 failed err:%v", err)
70 | return
71 | }
72 | cookie := http.Cookie{Name: "userid", Value: strconv.Itoa(int(lastInsertId)), Path: "/", MaxAge: 86400}
73 | http.SetCookie(w, &cookie)
74 | cookie = http.Cookie{Name: "username", Value: username, Path: "/", MaxAge: 86400}
75 | http.SetCookie(w, &cookie)
76 | } else {
77 | logs.Debug("user [%v] request register err: user already exists", username)
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/db/landlord.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/db/landlord.db
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module landlord
2 |
3 | go 1.12
4 |
5 | replace (
6 | golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 => github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85
7 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a => github.com/golang/net v0.0.0-20181114220301-adae6a3d119a
8 | golang.org/x/sys v0.3.0 => github.com/golang/sys v0.3.0
9 | golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0
10 | )
11 |
12 | require (
13 | github.com/astaxie/beego v1.11.1
14 | github.com/go-sql-driver/mysql v1.4.1
15 | github.com/gorilla/websocket v1.4.0
16 | github.com/jmoiron/sqlx v1.2.0
17 | github.com/mattn/go-sqlite3 v1.10.0
18 | )
19 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
2 | github.com/astaxie/beego v1.11.1 h1:6DESefxW5oMcRLFRKi53/6exzup/IR6N4EzzS1n6CnQ=
3 | github.com/astaxie/beego v1.11.1/go.mod h1:i69hVzgauOPSw5qeyF4GVZhn7Od0yG5bbCGzmhbWxgQ=
4 | github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
5 | github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
6 | github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff/go.mod h1:PhH1ZhyCzHKt4uAasyx+ljRCgoezetRNf59CUtwUkqY=
7 | github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
8 | github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
9 | github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
10 | github.com/couchbase/go-couchbase v0.0.0-20181122212707-3e9b6e1258bb/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
11 | github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
12 | github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
13 | github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
14 | github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
15 | github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
16 | github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
17 | github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
18 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
19 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
20 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
21 | github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85 h1:B7ZbAFz7NOmvpUE5RGtu3u0WIizy5GdvbNpEf4RPnWs=
22 | github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:uZvAcrsnNaCxlh1HorK5dUQHGmEKPh2H/Rl1kehswPo=
23 | github.com/golang/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:98y8FxUyMjTdJ5eOj/8vzuiVO14/dkJ98NYhEPG8QGY=
24 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
25 | github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
26 | github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
27 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
28 | github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
29 | github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
30 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
31 | github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
32 | github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
33 | github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
34 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
35 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
36 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
37 | github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
38 | github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
39 | github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
40 | github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
41 | github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
42 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
43 | gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
44 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
45 |
--------------------------------------------------------------------------------
/main.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/main.exe
--------------------------------------------------------------------------------
/main/config.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/astaxie/beego/config"
5 | "github.com/astaxie/beego/logs"
6 | "landlord/common"
7 | "os"
8 | )
9 |
10 | var (
11 | gameConf = &common.GameConfInfo
12 | )
13 |
14 | func initConf() (err error) {
15 | environment := os.Getenv("ENV")
16 | if environment != "dev" && environment != "testing" && environment != "product" {
17 | environment = "product"
18 | }
19 | logs.Info("the running environment is : %s", environment)
20 | conf, err := config.NewConfig("ini", "conf/app.conf")
21 | if err != nil {
22 | logs.Error("new conf failed ,err : %v", err)
23 | return
24 | }
25 |
26 | environment += "::"
27 | gameConf.HttpPort, err = conf.Int(environment + "http_port")
28 | if err != nil {
29 | logs.Error("init http_port failed,err: %v", err)
30 | return
31 | }
32 |
33 | logs.Debug("read conf succ , http port : %v", gameConf.HttpPort)
34 |
35 | //todo 日志配置
36 | gameConf.LogPath = conf.String(environment + "log_path")
37 | if len(gameConf.LogPath) == 0 {
38 | gameConf.LogPath = "./logs/game.log"
39 | }
40 |
41 | logs.Debug("read conf succ , LogPath : %v", gameConf.LogPath)
42 | gameConf.LogLevel = conf.String(environment + "log_level")
43 | if len(gameConf.LogLevel) == 0 {
44 | gameConf.LogLevel = "debug"
45 | }
46 | logs.Debug("read conf succ , LogLevel : %v", gameConf.LogLevel)
47 |
48 | //todo sqlite配置
49 | gameConf.DbPath = conf.String(environment + "db_path")
50 | if len(gameConf.DbPath) == 0 {
51 | gameConf.DbPath = "./db/landlord.db"
52 | }
53 | logs.Debug("read conf succ , DbPath : %v", gameConf.DbPath)
54 | return
55 | }
56 |
--------------------------------------------------------------------------------
/main/init.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "database/sql"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/astaxie/beego/logs"
8 | _ "github.com/mattn/go-sqlite3"
9 | )
10 |
11 | func conversionLogLevel(logLevel string) int {
12 | switch logLevel {
13 | case "debug":
14 | return logs.LevelDebug
15 | case "warn":
16 | return logs.LevelWarn
17 | case "info":
18 | return logs.LevelInfo
19 | case "trace":
20 | return logs.LevelTrace
21 | }
22 | return logs.LevelDebug
23 | }
24 |
25 | func initLogger() (err error) {
26 | config := make(map[string]interface{})
27 | config["filename"] = gameConf.LogPath
28 | config["level"] = conversionLogLevel(gameConf.LogLevel)
29 |
30 | configStr, err := json.Marshal(config)
31 | if err != nil {
32 | fmt.Println("marsha1 faild,err", err)
33 | return
34 | }
35 | err = logs.SetLogger(logs.AdapterFile, string(configStr))
36 | if err != nil {
37 | logs.Error("init logger :%v",err)
38 | }
39 | return
40 | }
41 |
42 | func initSqlite() (err error) {
43 | gameConf.Db, err = sql.Open("sqlite3", gameConf.DbPath)
44 | if err != nil {
45 | logs.Error("initSqlite err : %v", err)
46 | return
47 | }
48 | var stmt *sql.Stmt
49 | stmt, err = gameConf.Db.Prepare(`CREATE TABLE IF NOT EXISTS "account" ("id" INTEGER NOT NULL,"email" text(32),"username" TEXT(16),"password" TEXT(32),"coin" integer,"created_date" TEXT(32),"updated_date" TEXT(32),PRIMARY KEY ("id"))`)
50 | if err != nil {
51 | logs.Error("initSqlite err : %v", err)
52 | return
53 | }
54 | _, err = stmt.Exec()
55 | if err != nil {
56 | logs.Error("create table err:", err)
57 | return
58 | }
59 | return
60 | }
61 |
62 | func initSec() (err error) {
63 | err = initLogger()
64 | if err != nil {
65 | logs.Error("init logger failed,err:%v", err)
66 | return
67 | }
68 | err = initSqlite()
69 | if err != nil {
70 | logs.Error("init sqlite failed,err:%v", err)
71 | return
72 | }
73 | logs.Info("init sec succ")
74 | return
75 | }
76 |
--------------------------------------------------------------------------------
/main/main.go:
--------------------------------------------------------------------------------
1 | package main // import "landlord"
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "github.com/astaxie/beego/logs"
7 | "io"
8 | _ "landlord/router"
9 | "net/http"
10 | "os"
11 | "strconv"
12 | )
13 |
14 | func main() {
15 | err := initConf()
16 | if err != nil {
17 | logs.Error("init conf err:%v", err)
18 | return
19 | }
20 | defer func() {
21 | if gameConf.Db != nil {
22 | err = gameConf.Db.Close()
23 | if err != nil {
24 | logs.Error("main close sqllite db err :%v", err)
25 | }
26 | }
27 | }()
28 | err = initSec()
29 | if err != nil {
30 | logs.Error("init sec err:%v", err)
31 | return
32 | }
33 |
34 | var addr = flag.String("addr", fmt.Sprintf(":%d", gameConf.HttpPort), "http service address")
35 | err = http.ListenAndServe(*addr, nil)
36 | if err != nil {
37 | logs.Error("ListenAndServe: err:%v", err)
38 | }
39 | }
40 |
41 | func init() { //生成pid文件,保存pid
42 | pidFileName := "pid"
43 | fileInfo, err := os.Stat(pidFileName)
44 | if err != nil {
45 | if os.IsNotExist(err) {
46 | err = os.Mkdir(pidFileName, os.ModePerm)
47 | fileInfo, _ = os.Stat(pidFileName)
48 | }
49 | }
50 | if fileInfo.IsDir() {
51 | pid := os.Getpid()
52 | pidFile, err := os.OpenFile(pidFileName+"/landlord.pid", os.O_RDWR|os.O_CREATE, 0766)
53 | if err != nil {
54 | logs.Error("open pidFile [%s] error :%v", pidFileName, err)
55 | return
56 | }
57 | err = pidFile.Truncate(0) //清空数据
58 |
59 | _, err = io.WriteString(pidFile, strconv.Itoa(pid))
60 | if err != nil {
61 | logs.Error("write pid error :%v", err)
62 | }
63 |
64 | err = pidFile.Close()
65 | if err != nil {
66 | logs.Error("close pid file err: %v", err)
67 | }
68 | } else {
69 | logs.Error("pidFile [%s] is exists and not dir", pidFileName)
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/router/router.go:
--------------------------------------------------------------------------------
1 | package router
2 |
3 | import (
4 | "net/http"
5 | "landlord/service"
6 | "landlord/controllers"
7 | )
8 |
9 | func init() {
10 | http.HandleFunc("/", controllers.Index)
11 | http.HandleFunc("/login", controllers.Login)
12 | http.HandleFunc("/loginOut", controllers.LoginOut)
13 | http.HandleFunc("/reg", controllers.Register)
14 |
15 | http.HandleFunc("/ws", service.ServeWs)
16 |
17 | // 设置静态目录
18 | static := http.FileServer(http.Dir("./static"))
19 | http.Handle("/static/", http.StripPrefix("/static/", static))
20 | }
21 |
--------------------------------------------------------------------------------
/service/client.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "bytes"
5 | "net/http"
6 | "time"
7 | "github.com/gorilla/websocket"
8 | "github.com/astaxie/beego/logs"
9 | "encoding/json"
10 | "landlord/common"
11 | "strconv"
12 | )
13 |
14 | const (
15 | writeWait = 1 * time.Second
16 | pongWait = 60 * time.Second
17 | pingPeriod = (pongWait * 9) / 10
18 | maxMessageSize = 512
19 |
20 | RoleFarmer = 0
21 | RoleLandlord = 1
22 | )
23 |
24 | var (
25 | newline = []byte{'\n'}
26 | space = []byte{' '}
27 | upGrader = websocket.Upgrader{
28 | ReadBufferSize: 1024,
29 | WriteBufferSize: 1024,
30 | CheckOrigin: func(r *http.Request) bool { return true },
31 | }//不验证origin
32 | )
33 |
34 | type UserId int
35 |
36 | type UserInfo struct {
37 | UserId UserId `json:"user_id"`
38 | Username string `json:"username"`
39 | Coin int `json:"coin"`
40 | Role int
41 | }
42 |
43 | type Client struct {
44 | conn *websocket.Conn
45 | UserInfo *UserInfo
46 | Room *Room
47 | Table *Table
48 | HandPokers []int
49 | Ready bool
50 | IsCalled bool //是否叫完分
51 | Next *Client //链表
52 | IsRobot bool
53 | toRobot chan []interface{} //发送给robot的消息
54 | toServer chan []interface{} //robot发送给服务器
55 | }
56 |
57 | //重置状态
58 | func (c *Client) reset() {
59 | c.UserInfo.Role = 1
60 | c.HandPokers = make([]int, 0, 21)
61 | c.Ready = false
62 | c.IsCalled = false
63 | }
64 |
65 | //发送房间内已有的牌桌信息
66 | func (c *Client) sendRoomTables() {
67 | res := make([][2]int, 0)
68 | for _, table := range c.Room.Tables {
69 | if len(table.TableClients) < 3 {
70 | res = append(res, [2]int{int(table.TableId), len(table.TableClients)})
71 | }
72 | }
73 | c.sendMsg([]interface{}{common.ResTableList, res})
74 | }
75 |
76 | func (c *Client) sendMsg(msg []interface{}) {
77 | if c.IsRobot {
78 | c.toRobot <- msg
79 | return
80 | }
81 | msgByte, err := json.Marshal(msg)
82 | if err != nil {
83 | logs.Error("send msg [%v] marsha1 err:%v", string(msgByte), err)
84 | return
85 | }
86 | err = c.conn.SetWriteDeadline(time.Now().Add(writeWait))
87 | if err != nil {
88 | logs.Error("send msg SetWriteDeadline [%v] err:%v", string(msgByte), err)
89 | return
90 | }
91 | w, err := c.conn.NextWriter(websocket.TextMessage)
92 | if err != nil {
93 | err = c.conn.Close()
94 | if err != nil {
95 | logs.Error("close client err: %v",err)
96 | }
97 | }
98 | _,err = w.Write(msgByte)
99 | if err != nil {
100 | logs.Error("Write msg [%v] err: %v",string(msgByte),err)
101 | }
102 | if err := w.Close(); err != nil {
103 | err = c.conn.Close()
104 | if err != nil {
105 | logs.Error("close err: %v",err)
106 | }
107 | }
108 | }
109 |
110 | //光比客户端
111 | func (c *Client) close() {
112 | if c.Table != nil {
113 | for _, client := range c.Table.TableClients {
114 | if c.Table.Creator == c && c != client {
115 | c.Table.Creator = client
116 | }
117 | if c == client.Next {
118 | client.Next = nil
119 | }
120 | }
121 | if len(c.Table.TableClients) != 1 {
122 | for _, client := range c.Table.TableClients {
123 | if client != client.Table.Creator {
124 | client.Table.Creator.Next = client
125 | }
126 | }
127 | }
128 | if len(c.Table.TableClients) == 1 {
129 | c.Table.Creator = nil
130 | delete(c.Room.Tables, c.Table.TableId)
131 | return
132 | }
133 | delete(c.Table.TableClients, c.UserInfo.UserId)
134 | if c.Table.State == GamePlaying {
135 | c.Table.syncUser()
136 | //c.Table.reset()
137 | }
138 | if c.IsRobot {
139 | close(c.toRobot)
140 | close(c.toServer)
141 | }
142 | }
143 | }
144 |
145 | //可能是因为版本问题,导致有些未处理的error
146 | func (c *Client) readPump() {
147 | defer func() {
148 | //logs.Debug("readPump exit")
149 | c.conn.Close()
150 | c.close()
151 | if c.Room.AllowRobot {
152 | if c.Table != nil {
153 | for _, client := range c.Table.TableClients {
154 | client.close()
155 | }
156 | }
157 | }
158 | }()
159 | c.conn.SetReadLimit(maxMessageSize)
160 | c.conn.SetReadDeadline(time.Now().Add(pongWait))
161 | c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
162 | for {
163 | _, message, err := c.conn.ReadMessage()
164 | if err != nil {
165 | if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
166 | logs.Error("websocket user_id[%d] unexpected close error: %v", c.UserInfo.UserId, err)
167 | }
168 | return
169 | }
170 | message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
171 | var data []interface{}
172 | err = json.Unmarshal(message, &data)
173 | if err != nil {
174 | logs.Error("message unmarsha1 err, user_id[%d] err:%v", c.UserInfo.UserId, err)
175 | } else {
176 | wsRequest(data, c)
177 | }
178 | }
179 | }
180 |
181 | //心跳
182 | func (c *Client) Ping() {
183 | ticker := time.NewTicker(pingPeriod)
184 | defer func() {
185 | ticker.Stop()
186 | }()
187 | for {
188 | select {
189 | case <-ticker.C:
190 | c.conn.SetWriteDeadline(time.Now().Add(writeWait))
191 | if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
192 | return
193 | }
194 | }
195 | }
196 | }
197 |
198 | func ServeWs(w http.ResponseWriter, r *http.Request) {
199 | conn, err := upGrader.Upgrade(w, r, nil)
200 | if err != nil {
201 | logs.Error("upgrader err:%v", err)
202 | return
203 | }
204 | client := &Client{conn: conn, HandPokers: make([]int, 0, 21), UserInfo: &UserInfo{}}
205 | var userId int
206 | var username string
207 | cookie, err := r.Cookie("userid")
208 |
209 | if err != nil {
210 | logs.Error("get cookie err: %v", err)
211 | } else {
212 | userIdStr := cookie.Value
213 | userId, err = strconv.Atoi(userIdStr)
214 | }
215 | cookie, err = r.Cookie("username")
216 |
217 | if err != nil {
218 | logs.Error("get cookie err: %v", err)
219 | } else {
220 | username = cookie.Value
221 | }
222 |
223 | if userId != 0 && username != "" {
224 | client.UserInfo.UserId = UserId(userId)
225 | client.UserInfo.Username = username
226 | go client.readPump()
227 | go client.Ping()
228 | return
229 | }
230 | logs.Error("user need login first")
231 | client.conn.Close()
232 | }
233 |
--------------------------------------------------------------------------------
/service/request.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "github.com/astaxie/beego/logs"
5 | "landlord/common"
6 | )
7 |
8 | //处理websocket请求
9 | func wsRequest(data []interface{}, client *Client) {
10 | defer func() {
11 | if r := recover(); r != nil {
12 | logs.Error("wsRequest panic:%v ", r)
13 | }
14 | }()
15 | if len(data) < 1 {
16 | return
17 | }
18 | var req int
19 | if r, ok := data[0].(float64); ok {
20 | req = int(r)
21 | }
22 | switch req {
23 | case common.ReqCheat:
24 | if len(data) < 2 {
25 | logs.Error("user [%d] request ReqCheat ,but missing user id", client.UserInfo.UserId)
26 | return
27 | }
28 |
29 | case common.ReqLogin:
30 | client.sendMsg([]interface{}{common.ResLogin, client.UserInfo.UserId, client.UserInfo.Username})
31 |
32 | case common.ReqRoomList:
33 | client.sendMsg([]interface{}{common.ResRoomList})
34 |
35 | case common.ReqTableList:
36 | client.sendRoomTables()
37 |
38 | case common.ReqJoinRoom:
39 | if len(data) < 2 {
40 | logs.Error("user [%d] request join room ,but missing room id", client.UserInfo.UserId)
41 | return
42 | }
43 | var roomId int
44 | if id, ok := data[1].(float64); ok {
45 | roomId = int(id)
46 | }
47 | roomManager.Lock.RLock()
48 | defer roomManager.Lock.RUnlock()
49 | if room, ok := roomManager.Rooms[roomId]; ok {
50 | client.Room = room
51 | res := make([][2]int, 0)
52 | for _, table := range client.Room.Tables {
53 | if len(table.TableClients) < 3 {
54 | res = append(res, [2]int{int(table.TableId), len(table.TableClients)})
55 | }
56 | }
57 | client.sendMsg([]interface{}{common.ResJoinRoom, res})
58 | }
59 |
60 | case common.ReqNewTable:
61 | table := client.Room.newTable(client)
62 | table.joinTable(client)
63 |
64 | case common.ReqJoinTable:
65 | if len(data) < 2 {
66 | return
67 | }
68 | var tableId TableId
69 | if id, ok := data[1].(float64); ok {
70 | tableId = TableId(id)
71 | }
72 | if client.Room == nil {
73 | return
74 | }
75 | client.Room.Lock.RLock()
76 | defer client.Room.Lock.RUnlock()
77 |
78 | if table, ok := client.Room.Tables[tableId]; ok {
79 | table.joinTable(client)
80 | }
81 | client.sendRoomTables()
82 | case common.ReqDealPoker:
83 | if client.Table.State == GameEnd {
84 | client.Ready = true
85 | }
86 | case common.ReqCallScore:
87 | logs.Debug("[%v] ReqCallScore %v", client.UserInfo.Username, data)
88 | client.Table.Lock.Lock()
89 | defer client.Table.Lock.Unlock()
90 |
91 | if client.Table.State != GameCallScore {
92 | logs.Debug("game call score at run time ,%v", client.Table.State)
93 | return
94 | }
95 | if client.Table.GameManage.Turn == client || client.Table.GameManage.FirstCallScore == client {
96 | client.Table.GameManage.Turn = client.Next
97 | } else {
98 | logs.Debug("user [%v] call score turn err ")
99 | }
100 | if len(data) < 2 {
101 | return
102 | }
103 | var score int
104 | if s, ok := data[1].(float64); ok {
105 | score = int(s)
106 | }
107 |
108 | if score > 0 && score < client.Table.GameManage.MaxCallScore || score > 3 {
109 | logs.Error("player[%d] call score[%d] cheat", client.UserInfo.UserId, score)
110 | return
111 | }
112 | if score > client.Table.GameManage.MaxCallScore {
113 | client.Table.GameManage.MaxCallScore = score
114 | client.Table.GameManage.MaxCallScoreTurn = client
115 | }
116 | client.IsCalled = true
117 | callEnd := score == 3 || client.Table.allCalled()
118 | userCall := []interface{}{common.ResCallScore, client.UserInfo.UserId, score, callEnd}
119 | for _, c := range client.Table.TableClients {
120 | c.sendMsg(userCall)
121 | }
122 | if callEnd {
123 | logs.Debug("call score end")
124 | client.Table.callEnd()
125 | }
126 | case common.ReqShotPoker:
127 | logs.Debug("user [%v] ReqShotPoker %v", client.UserInfo.Username, data)
128 | client.Table.Lock.Lock()
129 | defer func() {
130 | client.Table.GameManage.Turn = client.Next
131 | client.Table.Lock.Unlock()
132 | }()
133 |
134 | if client.Table.GameManage.Turn != client {
135 | logs.Error("shot poker err,not your [%d] turn .[%d]", client.UserInfo.UserId, client.Table.GameManage.Turn.UserInfo.UserId)
136 | return
137 | }
138 | if len(data) > 1 {
139 | if pokers, ok := data[1].([]interface{}); ok {
140 | shotPokers := make([]int, 0, len(pokers))
141 | for _, item := range pokers {
142 | if i, ok := item.(float64); ok {
143 | poker := int(i)
144 | inHand := false
145 | for _, handPoker := range client.HandPokers {
146 | if handPoker == poker {
147 | inHand = true
148 | break
149 | }
150 | }
151 | if inHand {
152 | shotPokers = append(shotPokers, poker)
153 | } else {
154 | logs.Warn("player[%d] play non-exist poker", client.UserInfo.UserId)
155 | res := []interface{}{common.ResShotPoker, client.UserInfo.UserId, []int{}}
156 | for _, c := range client.Table.TableClients {
157 | c.sendMsg(res)
158 | }
159 | return
160 | }
161 | }
162 | }
163 | if len(shotPokers) > 0 {
164 | compareRes, isMulti := common.ComparePoker(client.Table.GameManage.LastShotPoker, shotPokers)
165 | if client.Table.GameManage.LastShotClient != client && compareRes < 1 {
166 | logs.Warn("player[%d] shot poker %v small than last shot poker %v ", client.UserInfo.UserId, shotPokers, client.Table.GameManage.LastShotPoker)
167 | res := []interface{}{common.ResShotPoker, client.UserInfo.UserId, []int{}}
168 | for _, c := range client.Table.TableClients {
169 | c.sendMsg(res)
170 | }
171 | return
172 | }
173 | if isMulti {
174 | client.Table.GameManage.Multiple *= 2
175 | }
176 | client.Table.GameManage.LastShotClient = client
177 | client.Table.GameManage.LastShotPoker = shotPokers
178 | for _, shotPoker := range shotPokers {
179 | for i, poker := range client.HandPokers {
180 | if shotPoker == poker {
181 | copy(client.HandPokers[i:], client.HandPokers[i+1:])
182 | client.HandPokers = client.HandPokers[:len(client.HandPokers)-1]
183 | break
184 | }
185 | }
186 | }
187 | }
188 | res := []interface{}{common.ResShotPoker, client.UserInfo.UserId, shotPokers}
189 | for _, c := range client.Table.TableClients {
190 | c.sendMsg(res)
191 | }
192 | if len(client.HandPokers) == 0 {
193 | client.Table.gameOver(client)
194 | }
195 | }
196 | }
197 |
198 | //case common.ReqGameOver:
199 | case common.ReqChat:
200 | if len(data) > 1 {
201 | switch data[1].(type) {
202 | case string:
203 | client.Table.chat(client, data[1].(string))
204 | }
205 | }
206 | case common.ReqRestart:
207 | client.Table.Lock.Lock()
208 | defer client.Table.Lock.Unlock()
209 |
210 | if client.Table.State == GameEnd {
211 | client.Ready = true
212 | for _, c := range client.Table.TableClients {
213 | if c.Ready == false {
214 | return
215 | }
216 | }
217 | logs.Debug("restart")
218 | client.Table.reset()
219 | }
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/service/robot.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "github.com/astaxie/beego/logs"
5 | "landlord/common"
6 | "time"
7 | )
8 |
9 | func (c *Client) runRobot() {
10 | for {
11 | select {
12 | case msg, ok := <-c.toServer:
13 | if !ok {
14 | return
15 | }
16 | wsRequest(msg, c)
17 | case msg, ok := <-c.toRobot:
18 | if !ok {
19 | return
20 | }
21 | logs.Debug("robot [%v] receive message %v ", c.UserInfo.Username, msg)
22 | if len(msg) < 1 {
23 | logs.Error("send to robot [%v],message err ,%v", c.UserInfo.Username, msg)
24 | return
25 | }
26 | if act, ok := msg[0].(int); ok {
27 | protocolCode := int(act)
28 | switch protocolCode {
29 | case common.ResDealPoker:
30 | time.Sleep(time.Second)
31 | c.Table.Lock.RLock()
32 | if c.Table.GameManage.FirstCallScore == c {
33 | c.autoCallScore()
34 | }
35 | c.Table.Lock.RUnlock()
36 |
37 | case common.ResCallScore:
38 | if len(msg) < 4 {
39 | logs.Error("ResCallScore msg err:%v",msg)
40 | return
41 | }
42 | time.Sleep(time.Second)
43 | c.Table.Lock.RLock()
44 | if c.Table.GameManage.Turn == c && !c.IsCalled {
45 | var callEnd bool
46 | logs.Debug("ResCallScore %t",msg[3])
47 | if res, ok := msg[3].(bool); ok {
48 | callEnd = bool(res)
49 | }
50 | if !callEnd {
51 | c.autoCallScore()
52 | }
53 | }
54 | c.Table.Lock.RUnlock()
55 |
56 | case common.ResShotPoker:
57 | time.Sleep(time.Second)
58 | c.Table.Lock.RLock()
59 | if c.Table.GameManage.Turn == c {
60 | c.autoShotPoker()
61 | }
62 | c.Table.Lock.RUnlock()
63 |
64 | case common.ResShowPoker:
65 | time.Sleep(time.Second)
66 | //logs.Debug("robot [%v] role [%v] receive message ResShowPoker turn :%v", c.UserInfo.Username, c.UserInfo.Role, c.Table.GameManage.Turn.UserInfo.Username)
67 | c.Table.Lock.RLock()
68 | if c.Table.GameManage.Turn == c || (c.Table.GameManage.Turn == nil && c.UserInfo.Role == RoleLandlord) {
69 | c.autoShotPoker()
70 | }
71 | c.Table.Lock.RUnlock()
72 | case common.ResGameOver:
73 | c.Ready = true
74 | }
75 | }
76 | }
77 | }
78 | }
79 |
80 | //自动出牌
81 | func (c *Client) autoShotPoker() {
82 | //因为机器人休眠一秒后才出牌,有可能因用户退出而关闭chan
83 | defer func() {
84 | err := recover()
85 | if err != nil {
86 | logs.Warn("autoShotPoker err : %v",err)
87 | }
88 | }()
89 | logs.Debug("robot [%v] auto-shot poker", c.UserInfo.Username)
90 | shotPokers := make([]int, 0)
91 | if len(c.Table.GameManage.LastShotPoker) == 0 || c.Table.GameManage.LastShotClient == c {
92 | shotPokers = append(shotPokers, c.HandPokers[0])
93 | } else {
94 | shotPokers = common.CardsAbove(c.HandPokers, c.Table.GameManage.LastShotPoker)
95 | }
96 | float64Pokers := make([]interface{}, 0)
97 | for _, poker := range shotPokers {
98 | float64Pokers = append(float64Pokers, float64(poker))
99 | }
100 | req := []interface{}{float64(common.ReqShotPoker)}
101 | req = append(req, float64Pokers)
102 | logs.Debug("robot [%v] autoShotPoker %v", c.UserInfo.Username, float64Pokers)
103 | c.toServer <- req
104 | }
105 |
106 | //自动叫分
107 | func (c *Client) autoCallScore() {
108 | defer func() {
109 | err := recover()
110 | if err != nil {
111 | logs.Warn("autoCallScore err : %v",err)
112 | }
113 | }()
114 | logs.Debug("robot [%v] autoCallScore", c.UserInfo.Username)
115 | c.toServer <- []interface{}{float64(common.ReqCallScore), float64(3)}
116 | }
117 |
--------------------------------------------------------------------------------
/service/room.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "sync"
5 | "github.com/astaxie/beego/logs"
6 | )
7 |
8 | var (
9 | roomManager = RoomManager{
10 | Rooms: map[int]*Room{
11 | 1: {
12 | RoomId: 1,
13 | AllowRobot: true,
14 | EntranceFee: 200,
15 | Tables: make(map[TableId]*Table),
16 | },
17 | 2: {
18 | RoomId: 2,
19 | AllowRobot: false,
20 | EntranceFee: 200,
21 | Tables: make(map[TableId]*Table),
22 | },
23 | },
24 | }
25 | )
26 |
27 | type RoomId int
28 |
29 | type RoomManager struct {
30 | Lock sync.RWMutex
31 | Rooms map[int]*Room
32 | TableIdInc TableId
33 | }
34 |
35 | type Room struct {
36 | RoomId RoomId
37 | Lock sync.RWMutex
38 | AllowRobot bool
39 | Tables map[TableId]*Table
40 | EntranceFee int
41 | }
42 |
43 | //新建牌桌
44 | func (r *Room) newTable(client *Client) (table *Table) {
45 | roomManager.Lock.Lock()
46 | defer roomManager.Lock.Unlock()
47 |
48 | r.Lock.Lock()
49 | defer r.Lock.Unlock()
50 | roomManager.TableIdInc = roomManager.TableIdInc + 1
51 | table = &Table{
52 | TableId: roomManager.TableIdInc,
53 | Creator: client,
54 | TableClients: make(map[UserId]*Client, 3),
55 | GameManage: &GameManage{
56 | FirstCallScore: client,
57 | Multiple: 1,
58 | LastShotPoker: make([]int, 0),
59 | Pokers: make([]int, 0, 54),
60 | },
61 | }
62 | r.Tables[table.TableId] = table
63 | logs.Debug("create new table ok! allow robot :%v", r.AllowRobot)
64 | return
65 | }
66 |
67 | //func init() {
68 | // go func() { //压测
69 | // time.Sleep(time.Second * 3)
70 | // for i:=0;i<1;i++{
71 | // client := &Client{
72 | // Room: roomManager.Rooms[1],
73 | // HandPokers: make([]int, 0, 21),
74 | // UserInfo: &UserInfo{
75 | // UserId: UserId(rand.Intn(10000)),
76 | // Username: "ROBOT-0",
77 | // Coin: 10000,
78 | // },
79 | // IsRobot: true,
80 | // toRobot: make(chan []interface{}, 3),
81 | // toServer: make(chan []interface{}, 3),
82 | // }
83 | // go client.runRobot()
84 | // table := client.Room.newTable(client)
85 | // table.joinTable(client)
86 | // }
87 | // }()
88 | //}
--------------------------------------------------------------------------------
/service/table.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "fmt"
5 | "github.com/astaxie/beego/logs"
6 | "landlord/common"
7 | "math/rand"
8 | "sort"
9 | "sync"
10 | "time"
11 | )
12 |
13 | type TableId int
14 |
15 | const (
16 | GameWaitting = iota
17 | GameCallScore
18 | GamePlaying
19 | GameEnd
20 | )
21 |
22 | type Table struct {
23 | Lock sync.RWMutex
24 | TableId TableId
25 | State int
26 | Creator *Client
27 | TableClients map[UserId]*Client
28 | GameManage *GameManage
29 | }
30 |
31 | type GameManage struct {
32 | Turn *Client
33 | FirstCallScore *Client //每局轮转
34 | MaxCallScore int //最大叫分
35 | MaxCallScoreTurn *Client
36 | LastShotClient *Client
37 | Pokers []int
38 | LastShotPoker []int
39 | Multiple int //加倍
40 | }
41 |
42 | func (table *Table) allCalled() bool {
43 | for _, client := range table.TableClients {
44 | if !client.IsCalled {
45 | return false
46 | }
47 | }
48 | return true
49 | }
50 |
51 | //一局结束
52 | func (table *Table) gameOver(client *Client) {
53 | coin := table.Creator.Room.EntranceFee * table.GameManage.MaxCallScore * table.GameManage.Multiple
54 | table.State = GameEnd
55 | for _, c := range table.TableClients {
56 | res := []interface{}{common.ResGameOver, client.UserInfo.UserId}
57 | if client == c {
58 | res = append(res, coin*2-100)
59 | } else {
60 | res = append(res, coin)
61 | }
62 | for _, cc := range table.TableClients {
63 | if cc != c {
64 | userPokers := make([]int, 0, len(cc.HandPokers)+1)
65 | userPokers = append(append(userPokers, int(cc.UserInfo.UserId)), cc.HandPokers...)
66 | res = append(res, userPokers)
67 | }
68 | }
69 | c.sendMsg(res)
70 | }
71 | logs.Debug("table[%d] game over", table.TableId)
72 | }
73 |
74 | //叫分阶段结束
75 | func (table *Table) callEnd() {
76 | table.State = GamePlaying
77 | table.GameManage.FirstCallScore = table.GameManage.FirstCallScore.Next
78 | if table.GameManage.MaxCallScoreTurn == nil || table.GameManage.MaxCallScore == 0 {
79 | table.GameManage.MaxCallScoreTurn = table.Creator
80 | table.GameManage.MaxCallScore = 1
81 | //return
82 | }
83 | landLord := table.GameManage.MaxCallScoreTurn
84 | landLord.UserInfo.Role = RoleLandlord
85 | table.GameManage.Turn = landLord
86 | for _, poker := range table.GameManage.Pokers {
87 | landLord.HandPokers = append(landLord.HandPokers, poker)
88 | }
89 | res := []interface{}{common.ResShowPoker, landLord.UserInfo.UserId, table.GameManage.Pokers}
90 | for _, c := range table.TableClients {
91 | c.sendMsg(res)
92 | }
93 | }
94 |
95 | //客户端加入牌桌
96 | func (table *Table) joinTable(c *Client) {
97 | table.Lock.Lock()
98 | defer table.Lock.Unlock()
99 | if len(table.TableClients) > 2 {
100 | logs.Error("Player[%d] JOIN Table[%d] FULL", c.UserInfo.UserId, table.TableId)
101 | return
102 | }
103 | logs.Debug("[%v] user [%v] request join table", c.UserInfo.UserId, c.UserInfo.Username)
104 | if _, ok := table.TableClients[c.UserInfo.UserId]; ok {
105 | logs.Error("[%v] user [%v] already in this table", c.UserInfo.UserId, c.UserInfo.Username)
106 | return
107 | }
108 |
109 | c.Table = table
110 | c.Ready = true
111 | for _, client := range table.TableClients {
112 | if client.Next == nil {
113 | client.Next = c
114 | break
115 | }
116 | }
117 | table.TableClients[c.UserInfo.UserId] = c
118 | table.syncUser()
119 | if len(table.TableClients) == 3 {
120 | c.Next = table.Creator
121 | table.State = GameCallScore
122 | table.dealPoker()
123 | } else if c.Room.AllowRobot {
124 | go table.addRobot(c.Room)
125 | logs.Debug("robot join ok")
126 | }
127 | }
128 |
129 | //加入机器人
130 | func (table *Table) addRobot(room *Room) {
131 | logs.Debug("robot [%v] join table", fmt.Sprintf("ROBOT-%d", len(table.TableClients)))
132 | if len(table.TableClients) < 3 {
133 | client := &Client{
134 | Room: room,
135 | HandPokers: make([]int, 0, 21),
136 | UserInfo: &UserInfo{
137 | UserId: table.getRobotID() ,
138 | Username: fmt.Sprintf("ROBOT-%d", len(table.TableClients)),
139 | Coin: 10000,
140 | },
141 | IsRobot: true,
142 | toRobot: make(chan []interface{}, 3),
143 | toServer: make(chan []interface{}, 3),
144 | }
145 | go client.runRobot()
146 | table.joinTable(client)
147 | }
148 | }
149 |
150 | //生成随机robotID
151 | func (table *Table)getRobotID() (robot UserId) {
152 | time.Sleep(time.Microsecond * 10)
153 | rand.Seed(time.Now().UnixNano())
154 | robot = UserId(rand.Intn(10000))
155 | table.Lock.RLock()
156 | defer table.Lock.RUnlock()
157 | if _,ok := table.TableClients[robot];ok {
158 | return table.getRobotID()
159 | }
160 | return
161 | }
162 |
163 | //发牌
164 | func (table *Table) dealPoker() {
165 | logs.Debug("deal poker")
166 | table.GameManage.Pokers = make([]int, 0)
167 | for i := 0; i < 54; i++ {
168 | table.GameManage.Pokers = append(table.GameManage.Pokers, i)
169 | }
170 | table.ShufflePokers()
171 | for i := 0; i < 17; i++ {
172 | for _, client := range table.TableClients {
173 | client.HandPokers = append(client.HandPokers, table.GameManage.Pokers[len(table.GameManage.Pokers)-1])
174 | table.GameManage.Pokers = table.GameManage.Pokers[:len(table.GameManage.Pokers)-1]
175 | }
176 | }
177 | response := make([]interface{}, 0, 3)
178 | response = append(append(append(response, common.ResDealPoker), table.GameManage.FirstCallScore.UserInfo.UserId), nil)
179 | for _, client := range table.TableClients {
180 | sort.Ints(client.HandPokers)
181 | response[len(response)-1] = client.HandPokers
182 | client.sendMsg(response)
183 | }
184 | }
185 |
186 | func (table *Table) chat(client *Client, msg string) {
187 | res := []interface{}{common.ResChat, client.UserInfo.UserId, msg}
188 | for _, c := range table.TableClients {
189 | c.sendMsg(res)
190 | }
191 | }
192 |
193 | func (table *Table) reset() {
194 | table.GameManage = &GameManage{
195 | FirstCallScore: table.GameManage.FirstCallScore,
196 | Turn: nil,
197 | MaxCallScore: 0,
198 | MaxCallScoreTurn: nil,
199 | LastShotClient: nil,
200 | Pokers: table.GameManage.Pokers[:0],
201 | LastShotPoker: table.GameManage.LastShotPoker[:0],
202 | Multiple: 1,
203 | }
204 | table.State = GameCallScore
205 | if table.Creator != nil {
206 | table.Creator.sendMsg([]interface{}{common.ResRestart})
207 | }
208 | for _, c := range table.TableClients {
209 | c.reset()
210 | }
211 | if len(table.TableClients) == 3 {
212 | table.dealPoker()
213 | }
214 | }
215 |
216 | //洗牌
217 | func (table *Table) ShufflePokers() {
218 | logs.Debug("ShufflePokers")
219 | r := rand.New(rand.NewSource(time.Now().Unix()))
220 | i := len(table.GameManage.Pokers)
221 | for i > 0 {
222 | randIndex := r.Intn(i)
223 | table.GameManage.Pokers[i-1], table.GameManage.Pokers[randIndex] = table.GameManage.Pokers[randIndex], table.GameManage.Pokers[i-1]
224 | i--
225 | }
226 | }
227 |
228 | //同步用户信息
229 | func (table *Table) syncUser() () {
230 | logs.Debug("sync user")
231 | response := make([]interface{}, 0, 3)
232 | response = append(append(response, common.ResJoinTable), table.TableId)
233 | tableUsers := make([][2]interface{}, 0, 2)
234 | current := table.Creator
235 | for i := 0; i < len(table.TableClients); i++ {
236 | tableUsers = append(tableUsers, [2]interface{}{current.UserInfo.UserId, current.UserInfo.Username})
237 | current = current.Next
238 | }
239 | response = append(response, tableUsers)
240 | for _, client := range table.TableClients {
241 | client.sendMsg(response)
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/static/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/1.png
--------------------------------------------------------------------------------
/static/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/2.png
--------------------------------------------------------------------------------
/static/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/3.png
--------------------------------------------------------------------------------
/static/audio/bg_game.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/bg_game.ogg
--------------------------------------------------------------------------------
/static/audio/bg_room.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/bg_room.mp3
--------------------------------------------------------------------------------
/static/audio/deal.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/deal.mp3
--------------------------------------------------------------------------------
/static/audio/end_lose.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/end_lose.mp3
--------------------------------------------------------------------------------
/static/audio/end_win.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/end_win.mp3
--------------------------------------------------------------------------------
/static/audio/f_score_0.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/f_score_0.mp3
--------------------------------------------------------------------------------
/static/audio/f_score_1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/f_score_1.mp3
--------------------------------------------------------------------------------
/static/audio/f_score_2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/f_score_2.mp3
--------------------------------------------------------------------------------
/static/audio/f_score_3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/f_score_3.mp3
--------------------------------------------------------------------------------
/static/audio/m_score_0.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/m_score_0.mp3
--------------------------------------------------------------------------------
/static/audio/m_score_1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/m_score_1.mp3
--------------------------------------------------------------------------------
/static/audio/m_score_2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/m_score_2.mp3
--------------------------------------------------------------------------------
/static/audio/m_score_3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/audio/m_score_3.mp3
--------------------------------------------------------------------------------
/static/generator.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Poker
6 |
7 |
8 | Code: Click card to get code.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
71 |
72 |
--------------------------------------------------------------------------------
/static/i/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/bg.png
--------------------------------------------------------------------------------
/static/i/btn.json:
--------------------------------------------------------------------------------
1 | {"frames": [
2 |
3 | {
4 | "filename": "alarm.png",
5 | "frame": {"x":251,"y":0,"w":84,"h":84},
6 | "rotated": false,
7 | "trimmed": false,
8 | "spriteSourceSize": {"x":0,"y":0,"w":84,"h":84},
9 | "sourceSize": {"w":84,"h":84}
10 | },
11 | {
12 | "filename": "exit.png",
13 | "frame": {"x":128,"y":180,"w":160,"h":60},
14 | "rotated": false,
15 | "trimmed": false,
16 | "spriteSourceSize": {"x":0,"y":0,"w":160,"h":60},
17 | "sourceSize": {"w":160,"h":60}
18 | },
19 | {
20 | "filename": "hint.png",
21 | "frame": {"x":0,"y":132,"w":128,"h":48},
22 | "rotated": false,
23 | "trimmed": false,
24 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":48},
25 | "sourceSize": {"w":128,"h":48}
26 | },
27 | {
28 | "filename": "icon_default.png",
29 | "frame": {"x":335,"y":0,"w":84,"h":84},
30 | "rotated": false,
31 | "trimmed": false,
32 | "spriteSourceSize": {"x":0,"y":0,"w":84,"h":84},
33 | "sourceSize": {"w":84,"h":84}
34 | },
35 | {
36 | "filename": "icon_farmer.png",
37 | "frame": {"x":167,"y":0,"w":84,"h":84},
38 | "rotated": false,
39 | "trimmed": false,
40 | "spriteSourceSize": {"x":0,"y":0,"w":84,"h":84},
41 | "sourceSize": {"w":84,"h":84}
42 | },
43 | {
44 | "filename": "icon_landlord.png",
45 | "frame": {"x":83,"y":0,"w":84,"h":84},
46 | "rotated": false,
47 | "trimmed": false,
48 | "spriteSourceSize": {"x":0,"y":0,"w":84,"h":84},
49 | "sourceSize": {"w":84,"h":84}
50 | },
51 | {
52 | "filename": "pass.png",
53 | "frame": {"x":256,"y":132,"w":128,"h":48},
54 | "rotated": false,
55 | "trimmed": false,
56 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":48},
57 | "sourceSize": {"w":128,"h":48}
58 | },
59 | {
60 | "filename": "quick.png",
61 | "frame": {"x":160,"y":240,"w":160,"h":60},
62 | "rotated": false,
63 | "trimmed": false,
64 | "spriteSourceSize": {"x":0,"y":0,"w":160,"h":60},
65 | "sourceSize": {"w":160,"h":60}
66 | },
67 | {
68 | "filename": "register.png",
69 | "frame": {"x":320,"y":240,"w":160,"h":60},
70 | "rotated": false,
71 | "trimmed": false,
72 | "spriteSourceSize": {"x":0,"y":0,"w":160,"h":60},
73 | "sourceSize": {"w":160,"h":60}
74 | },
75 | {
76 | "filename": "score_0.png",
77 | "frame": {"x":0,"y":180,"w":128,"h":48},
78 | "rotated": false,
79 | "trimmed": false,
80 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":48},
81 | "sourceSize": {"w":128,"h":48}
82 | },
83 | {
84 | "filename": "score_1.png",
85 | "frame": {"x":128,"y":132,"w":128,"h":48},
86 | "rotated": false,
87 | "trimmed": false,
88 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":48},
89 | "sourceSize": {"w":128,"h":48}
90 | },
91 | {
92 | "filename": "score_2.png",
93 | "frame": {"x":0,"y":84,"w":128,"h":48},
94 | "rotated": false,
95 | "trimmed": false,
96 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":48},
97 | "sourceSize": {"w":128,"h":48}
98 | },
99 | {
100 | "filename": "score_3.png",
101 | "frame": {"x":128,"y":84,"w":128,"h":48},
102 | "rotated": false,
103 | "trimmed": false,
104 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":48},
105 | "sourceSize": {"w":128,"h":48}
106 | },
107 | {
108 | "filename": "setting.png",
109 | "frame": {"x":0,"y":240,"w":160,"h":60},
110 | "rotated": false,
111 | "trimmed": false,
112 | "spriteSourceSize": {"x":0,"y":0,"w":160,"h":60},
113 | "sourceSize": {"w":160,"h":60}
114 | },
115 | {
116 | "filename": "shot.png",
117 | "frame": {"x":256,"y":84,"w":128,"h":48},
118 | "rotated": false,
119 | "trimmed": false,
120 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":48},
121 | "sourceSize": {"w":128,"h":48}
122 | },
123 | {
124 | "filename": "start.png",
125 | "frame": {"x":288,"y":180,"w":160,"h":60},
126 | "rotated": false,
127 | "trimmed": false,
128 | "spriteSourceSize": {"x":0,"y":0,"w":160,"h":60},
129 | "sourceSize": {"w":160,"h":60}
130 | },
131 | {
132 | "filename": "table.png",
133 | "frame": {"x":0,"y":0,"w":83,"h":64},
134 | "rotated": false,
135 | "trimmed": false,
136 | "spriteSourceSize": {"x":0,"y":0,"w":83,"h":64},
137 | "sourceSize": {"w":83,"h":64}
138 | }],
139 | "meta": {
140 | "app": "http://www.codeandweb.com/texturepacker",
141 | "version": "1.0",
142 | "image": "btn.png",
143 | "format": "RGBA8888",
144 | "size": {"w":480,"h":300},
145 | "scale": "1",
146 | "smartupdate": "$TexturePacker:SmartUpdate:69ff5bba555b3f26130bf3e3671006cb:e8e25170cdaffd298411d399512029d0:382bbd42eff5a2b763aa915dc00d7597$"
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/static/i/btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn.png
--------------------------------------------------------------------------------
/static/i/btn/alarm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/alarm.png
--------------------------------------------------------------------------------
/static/i/btn/btn.tps:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | fileFormatVersion
5 | 4
6 | texturePackerVersion
7 | 4.4.0
8 | fileName
9 | /Users/nv/workspace/pycharm/doudizhu/src/static/i/btn/btn.tps
10 | autoSDSettings
11 |
12 |
13 | scale
14 | 1
15 | extension
16 |
17 | spriteFilter
18 |
19 | acceptFractionalValues
20 |
21 | maxTextureSize
22 |
23 | width
24 | -1
25 | height
26 | -1
27 |
28 |
29 |
30 | allowRotation
31 |
32 | shapeDebug
33 |
34 | dpi
35 | 72
36 | dataFormat
37 | phaser-json-array
38 | textureFileName
39 |
40 | flipPVR
41 |
42 | pvrCompressionQuality
43 | PVR_QUALITY_NORMAL
44 | atfCompressData
45 |
46 | mipMapMinSize
47 | 32768
48 | etc1CompressionQuality
49 | ETC1_QUALITY_LOW_PERCEPTUAL
50 | etc2CompressionQuality
51 | ETC2_QUALITY_LOW_PERCEPTUAL
52 | dxtCompressionMode
53 | DXT_PERCEPTUAL
54 | jxrColorFormat
55 | JXR_YUV444
56 | jxrTrimFlexBits
57 | 0
58 | jxrCompressionLevel
59 | 0
60 | ditherType
61 | NearestNeighbour
62 | backgroundColor
63 | 0
64 | libGdx
65 |
66 | filtering
67 |
68 | x
69 | Linear
70 | y
71 | Linear
72 |
73 |
74 | shapePadding
75 | 0
76 | jpgQuality
77 | 80
78 | pngOptimizationLevel
79 | 0
80 | webpQualityLevel
81 | 101
82 | textureSubPath
83 |
84 | atfFormats
85 |
86 | textureFormat
87 | png
88 | borderPadding
89 | 0
90 | maxTextureSize
91 |
92 | width
93 | 2048
94 | height
95 | 2048
96 |
97 | fixedTextureSize
98 |
99 | width
100 | -1
101 | height
102 | -1
103 |
104 | algorithmSettings
105 |
106 | algorithm
107 | Basic
108 | freeSizeMode
109 | Best
110 | sizeConstraints
111 | AnySize
112 | forceSquared
113 |
114 | maxRects
115 |
116 | heuristic
117 | Best
118 |
119 | basic
120 |
121 | sortBy
122 | Best
123 | order
124 | Ascending
125 |
126 | polygon
127 |
128 | alignToGrid
129 | 1
130 |
131 |
132 | andEngine
133 |
134 | minFilter
135 | Linear
136 | packageName
137 | Texture
138 | wrap
139 |
140 | s
141 | Clamp
142 | t
143 | Clamp
144 |
145 | magFilter
146 | MagLinear
147 |
148 | dataFileNames
149 |
156 | multiPack
157 |
158 | forceIdenticalLayout
159 |
160 | outputFormat
161 | RGBA8888
162 | alphaHandling
163 | ClearTransparentPixels
164 | contentProtection
165 |
166 | key
167 |
168 |
169 | autoAliasEnabled
170 |
171 | trimSpriteNames
172 |
173 | prependSmartFolderName
174 |
175 | autodetectAnimations
176 |
177 | globalSpriteSettings
178 |
179 | scale
180 | 1
181 | scaleMode
182 | Smooth
183 | extrude
184 | 0
185 | trimThreshold
186 | 1
187 | trimMargin
188 | 1
189 | trimMode
190 | None
191 | tracerTolerance
192 | 200
193 | heuristicMask
194 |
195 | defaultPivotPoint
196 | 0.5,0.5
197 | writePivotPoints
198 |
199 |
200 | individualSpriteSettings
201 |
268 | fileList
269 |
270 | .
271 |
272 | ignoreFileList
273 |
274 | replaceList
275 |
276 | ignoredWarnings
277 |
278 | commonDivisorX
279 | 1
280 | commonDivisorY
281 | 1
282 | packNormalMaps
283 |
284 | autodetectNormalMaps
285 |
286 | normalMapFilter
287 |
288 | normalMapSuffix
289 |
290 | normalMapSheetFileName
291 |
292 | exporterProperties
293 |
294 |
295 |
296 |
--------------------------------------------------------------------------------
/static/i/btn/exit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/exit.png
--------------------------------------------------------------------------------
/static/i/btn/hint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/hint.png
--------------------------------------------------------------------------------
/static/i/btn/icon_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/icon_default.png
--------------------------------------------------------------------------------
/static/i/btn/icon_farmer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/icon_farmer.png
--------------------------------------------------------------------------------
/static/i/btn/icon_landlord.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/icon_landlord.png
--------------------------------------------------------------------------------
/static/i/btn/pass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/pass.png
--------------------------------------------------------------------------------
/static/i/btn/quick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/quick.png
--------------------------------------------------------------------------------
/static/i/btn/register.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/register.png
--------------------------------------------------------------------------------
/static/i/btn/score_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/score_0.png
--------------------------------------------------------------------------------
/static/i/btn/score_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/score_1.png
--------------------------------------------------------------------------------
/static/i/btn/score_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/score_2.png
--------------------------------------------------------------------------------
/static/i/btn/score_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/score_3.png
--------------------------------------------------------------------------------
/static/i/btn/setting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/setting.png
--------------------------------------------------------------------------------
/static/i/btn/shot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/shot.png
--------------------------------------------------------------------------------
/static/i/btn/start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/start.png
--------------------------------------------------------------------------------
/static/i/btn/table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/btn/table.png
--------------------------------------------------------------------------------
/static/i/poker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/poker.png
--------------------------------------------------------------------------------
/static/i/preload.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/i/preload.png
--------------------------------------------------------------------------------
/static/js/boot.js:
--------------------------------------------------------------------------------
1 | PG = {
2 | music: null,
3 | playerInfo: {},
4 | orientated: false
5 | };
6 |
7 | PG.getCookie = function (name) {
8 | var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
9 | return r ? r[1] : undefined;
10 | };
11 |
12 | PG.PW = 90;
13 | PG.PH = 120;
14 |
15 | PG.Boot = {
16 | preload: function () {
17 | this.load.image('preloaderBar', 'static/i/preload.png');
18 | },
19 | create: function () {
20 | this.input.maxPointers = 1;
21 | this.stage.disableVisibilityChange = true;
22 | this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
23 | this.scale.enterIncorrectOrientation.add(this.enterIncorrectOrientation, this);
24 | this.scale.leaveIncorrectOrientation.add(this.leaveIncorrectOrientation, this);
25 | this.onSizeChange();
26 | this.state.start('Preloader');
27 | },
28 | onSizeChange: function () {
29 | this.scale.minWidth = 480;
30 | this.scale.minHeight = 270;
31 | var device = this.game.device;
32 | if (device.android || device.iOS) {
33 | this.scale.maxWidth = window.innerWidth;
34 | this.scale.maxHeight = window.innerHeight;
35 | } else {
36 | this.scale.maxWidth = 960;
37 | this.scale.maxHeight = 540;
38 | }
39 | this.scale.pageAlignHorizontally = true;
40 | this.scale.pageAlignVertically = true;
41 | this.scale.forceOrientation(true);
42 | },
43 | enterIncorrectOrientation: function () {
44 | PG.orientated = false;
45 | document.getElementById('orientation').style.display = 'block';
46 | },
47 | leaveIncorrectOrientation: function () {
48 | PG.orientated = true;
49 | document.getElementById('orientation').style.display = 'none';
50 | }
51 | };
52 |
53 | PG.Preloader = {
54 |
55 | preload: function () {
56 | this.preloadBar = this.game.add.sprite(120, 200, 'preloaderBar');
57 | this.load.setPreloadSprite(this.preloadBar);
58 |
59 | this.load.audio('music_room', 'static/audio/bg_room.mp3');
60 | this.load.audio('music_game', 'static/audio/bg_game.ogg');
61 | this.load.audio('music_deal', 'static/audio/deal.mp3');
62 | this.load.audio('music_win', 'static/audio/end_win.mp3');
63 | this.load.audio('music_lose', 'static/audio/end_lose.mp3');
64 | this.load.audio('f_score_0', 'static/audio/f_score_0.mp3');
65 | this.load.audio('f_score_1', 'static/audio/f_score_1.mp3');
66 | this.load.audio('f_score_2', 'static/audio/f_score_2.mp3');
67 | this.load.audio('f_score_3', 'static/audio/f_score_3.mp3');
68 | this.load.atlas('btn', 'static/i/btn.png', 'static/i/btn.json');
69 | this.load.image('bg', 'static/i/bg.png');
70 | this.load.spritesheet('poker', 'static/i/poker.png', 90, 120);
71 | this.load.json('rule', 'static/rule.json');
72 | },
73 |
74 | create: function () {
75 | PG.RuleList = this.cache.getJSON('rule');
76 | var jsonVal = document.getElementById("user").value;
77 | if (jsonVal) {
78 | PG.playerInfo = JSON.parse(jsonVal);
79 | if (PG.playerInfo['uid']) {
80 | this.state.start('MainMenu');
81 | } else {
82 | this.state.start('Login');
83 | }
84 | } else {
85 | this.state.start('Login');
86 | }
87 | PG.music = this.game.add.audio('music_room');
88 | PG.music.loop = true;
89 | PG.music.loopFull();
90 | PG.music.play();
91 | }
92 | };
93 |
94 | PG.MainMenu = {
95 | create: function () {
96 | this.stage.backgroundColor = '#182d3b';
97 | var bg = this.game.add.sprite(this.game.width / 2, 0, 'bg');
98 | bg.anchor.set(0.5, 0);
99 |
100 | var aiRoom = this.game.add.button(this.game.world.width / 2, this.game.world.height / 4, 'btn', this.gotoAiRoom, this, 'quick.png', 'quick.png', 'quick.png');
101 | aiRoom.anchor.set(0.5);
102 | this.game.world.add(aiRoom);
103 |
104 | var humanRoom = this.game.add.button(this.game.world.width / 2, this.game.world.height / 2, 'btn', this.gotoRoom, this, 'start.png', 'start.png', 'start.png');
105 | humanRoom.anchor.set(0.5);
106 | this.game.world.add(humanRoom);
107 |
108 | var setting = this.game.add.button(this.game.world.width / 2, this.game.world.height * 3 / 4, 'btn', this.gotoSetting, this, 'setting.png', 'setting.png', 'setting.png');
109 | setting.anchor.set(0.5);
110 | this.game.world.add(setting);
111 |
112 | var style = {font: "28px Arial", fill: "#fff", align: "right"};
113 | var text = this.game.add.text(this.game.world.width - 4, 4, "欢迎回来 " + PG.playerInfo.username, style);
114 | text.addColor('#cc00cc', 4);
115 | text.anchor.set(1, 0);
116 | },
117 |
118 | gotoAiRoom: function () {
119 | // start(key, clearWorld, clearCache, parameter)
120 | this.state.start('Game', true, false, 1);
121 | // this.music.stop();
122 | },
123 |
124 | gotoRoom: function () {
125 | this.state.start('Game', true, false, 2);
126 | },
127 |
128 | gotoSetting: function () {
129 | var style = {font: "22px Arial", fill: "#fff", align: "center"};
130 | var text = this.game.add.text(0, 0, "hei hei hei hei", style);
131 | var tween = this.game.add.tween(text).to({x: 600, y: 450}, 2000, "Linear", true);
132 | tween.onComplete.add(Phaser.Text.prototype.destroy, text);
133 | }
134 | };
135 |
136 | PG.Login = {
137 | create: function () {
138 | this.stage.backgroundColor = '#182d3b';
139 | var bg = this.game.add.sprite(this.game.width / 2, 0, 'bg');
140 | bg.anchor.set(0.5, 0);
141 |
142 | var style = {
143 | font: '24px Arial', fill: '#000', width: 300, padding: 12,
144 | borderWidth: 1, borderColor: '#c8c8c8', borderRadius: 2,
145 | textAlign: 'center', placeHolder: '姓名'
146 | // type: PhaserInput.InputType.password
147 | };
148 | this.game.add.plugin(PhaserInput.Plugin);
149 |
150 | this.username = this.game.add.inputField((this.game.world.width - 300) / 2, this.game.world.centerY - 160, style);
151 |
152 | style.placeHolder = '密码';
153 | this.password = this.game.add.inputField((this.game.world.width - 300) / 2, this.game.world.centerY - 90, style);
154 |
155 | style.placeHolder = '再次输入密码';
156 | this.passwordAgain = this.game.add.inputField((this.game.world.width - 300) / 2, this.game.world.centerY - 15, style);
157 |
158 | var style = {font: "22px Arial", fill: "#f00", align: "center"};
159 | this.errorText = this.game.add.text(this.game.world.centerX, this.game.world.centerY + 45, '', style);
160 | this.errorText.anchor.set(0.5, 0);
161 |
162 | var login = this.game.add.button(this.game.world.centerX, this.game.world.centerY + 100, 'btn', this.onLogin, this, 'register.png', 'register.png', 'register.png');
163 | login.anchor.set(0.5);
164 | },
165 |
166 | onLogin: function () {
167 | if (!this.username.value) {
168 | this.username.startFocus();
169 | this.errorText.text = '请输入用户名';
170 | return;
171 | }
172 | if (!this.password.value) {
173 | this.password.startFocus();
174 | this.errorText.text = '请输入密码';
175 | return;
176 | }
177 | if (!this.passwordAgain.value) {
178 | this.passwordAgain.startFocus();
179 | this.errorText.text = '请再次输入密码';
180 | return;
181 | }
182 | if (this.password.value != this.passwordAgain.value) {
183 | this.errorText.text = "两次输入的密码不一致";
184 | return;
185 | }
186 |
187 | var httpRequest = new XMLHttpRequest();
188 | var that = this;
189 | httpRequest.onreadystatechange = function () {
190 | if (httpRequest.readyState === XMLHttpRequest.DONE) {
191 | if (httpRequest.status === 200) {
192 | if (httpRequest.responseText == '1') {
193 | that.errorText.text = '该用户名已经被占用';
194 | } else {
195 | PG.playerInfo = JSON.parse(httpRequest.responseText);
196 | that.state.start('MainMenu');
197 | }
198 | } else {
199 | console.log('Error:' + httpRequest.status);
200 | that.errorText.text = httpRequest.responseText;
201 | }
202 | }
203 | };
204 | httpRequest.open('POST', '/reg', true);
205 | httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
206 | httpRequest.setRequestHeader('X-Csrftoken', PG.getCookie("_xsrf"));
207 |
208 | var req = 'username=' + encodeURIComponent(this.username.value) + '&password=' + encodeURIComponent(this.password.value);
209 | httpRequest.send(req);
210 | }
211 | };
212 |
--------------------------------------------------------------------------------
/static/js/game.js:
--------------------------------------------------------------------------------
1 |
2 | PG.Game = function(game) {
3 |
4 | this.roomId = 1;
5 | this.players = [];
6 |
7 | this.titleBar = null;
8 | this.tableId = 0;
9 | this.shotLayer = null;
10 |
11 | this.tablePoker = [];
12 | this.tablePokerPic = {};
13 |
14 | this.lastShotPlayer = null;
15 |
16 | this.whoseTurn = 0;
17 |
18 | };
19 |
20 | PG.Game.prototype = {
21 |
22 | init: function(roomId) {
23 | this.roomId = roomId;
24 | },
25 |
26 | debug_log(obj) {
27 | console.log('*******');
28 | console.log(obj);
29 | console.log('********');
30 | },
31 |
32 | create: function () {
33 | this.stage.backgroundColor = '#182d3b';
34 |
35 | this.players.push(PG.createPlay(0, this));
36 | this.players.push(PG.createPlay(1, this));
37 | this.players.push(PG.createPlay(2, this));
38 | this.players[0].updateInfo(PG.playerInfo.uid, PG.playerInfo.username);
39 | PG.Socket.connect(this.onopen.bind(this), this.onmessage.bind(this), this.onerror.bind(this));
40 |
41 | this.createTitleBar();
42 | },
43 |
44 | onopen: function() {
45 | console.log('socket onopen');
46 | PG.Socket.send([PG.Protocol.REQ_JOIN_ROOM, this.roomId]);
47 | },
48 |
49 | onerror: function() {
50 | console.log('socket connect onerror');
51 | },
52 |
53 | send_message: function(request) {
54 | PG.Socket.send(request);
55 | },
56 |
57 | onmessage: function(packet) {
58 | var opcode = packet[0];
59 | switch(opcode) {
60 | case PG.Protocol.RSP_JOIN_ROOM:
61 | if (this.roomId == 1) {
62 | PG.Socket.send([PG.Protocol.REQ_JOIN_TABLE, -1]);
63 | } else {
64 | this.createTableLayer(packet[1]);
65 | }
66 | break;
67 | case PG.Protocol.RSP_TABLE_LIST:
68 | this.createTableLayer(packet[1]);
69 | break;
70 | case PG.Protocol.RSP_NEW_TABLE:
71 | this.tableId = packet[1];
72 | this.titleBar.text = '房间:' + this.tableId;
73 | break;
74 | case PG.Protocol.RSP_JOIN_TABLE:
75 | this.tableId = packet[1];
76 | this.titleBar.text = '房间:' + this.tableId;
77 | var playerIds = packet[2];
78 | for (var i = 0; i < playerIds.length; i++) {
79 | if (playerIds[i][0] == this.players[0].uid) {
80 | var info_1 = playerIds[(i+1)%3];
81 | var info_2 = playerIds[(i+2)%3];
82 | this.players[1].updateInfo(info_1[0], info_1[1]);
83 | this.players[2].updateInfo(info_2[0], info_2[1]);
84 | break;
85 | }
86 | }
87 | break;
88 | case PG.Protocol.RSP_DEAL_POKER:
89 | var playerId = packet[1];
90 | var pokers = packet[2];
91 | console.log(pokers);
92 | this.dealPoker(pokers);
93 | this.whoseTurn = this.uidToSeat(playerId);
94 | this.startCallScore(0);
95 | break;
96 | case PG.Protocol.RSP_CALL_SCORE:
97 | var playerId = packet[1];
98 | var score = packet[2];
99 | var callend = packet[3];
100 | this.debug_log(callend);
101 | this.whoseTurn = this.uidToSeat(playerId);
102 | //this.debug_log(playerId);
103 |
104 | var hanzi = ['不叫', "一分", "两分", "三分"];
105 | this.players[this.whoseTurn].say(hanzi[score]);
106 | if (!callend) {
107 | this.whoseTurn = (this.whoseTurn + 1) % 3;
108 | this.startCallScore(score);
109 | }
110 | break;
111 | case PG.Protocol.RSP_SHOW_POKER:
112 | this.whoseTurn = this.uidToSeat(packet[1]);
113 | this.tablePoker[0] = packet[2][0];
114 | this.tablePoker[1] = packet[2][1];
115 | this.tablePoker[2] = packet[2][2];
116 | this.players[this.whoseTurn].setLandlord();
117 | this.showLastThreePoker();
118 | break;
119 | case PG.Protocol.RSP_SHOT_POKER:
120 | this.handleShotPoker(packet);
121 | break;
122 | case PG.Protocol.RSP_GAME_OVER:
123 | var winner = packet[1];
124 | var coin = packet[2];
125 |
126 | var loserASeat = this.uidToSeat(packet[3][0]);
127 | this.players[loserASeat].replacePoker(packet[3], 1);
128 | this.players[loserASeat].reDealPoker();
129 |
130 | var loserBSeat = this.uidToSeat(packet[4][0]);
131 | this.players[loserBSeat].replacePoker(packet[4], 1);
132 | this.players[loserBSeat].reDealPoker();
133 | // this.players[loserBSeat].removeAllPoker();
134 | // this.players[loserASeat].pokerInHand = [];
135 |
136 | this.whoseTurn = this.uidToSeat(winner);
137 |
138 | function gameOver() {
139 | alert(this.players[this.whoseTurn].isLandlord ? "地主赢" : "农民赢");
140 | PG.Socket.send([PG.Protocol.REQ_RESTART]);
141 | this.cleanWorld();
142 | }
143 | this.game.time.events.add(3000, gameOver, this);
144 | break;
145 | case PG.Protocol.RSP_CHEAT:
146 | var seat = this.uidToSeat(packet[1]);
147 | this.players[seat].replacePoker(packet[2], 0);
148 | this.players[seat].reDealPoker();
149 | break;
150 | case PG.Protocol.RSP_RESTART:
151 | this.restart();
152 | default:
153 | console.log("UNKNOWN PACKET:", packet)
154 | }
155 | },
156 |
157 | cleanWorld: function () {
158 | for (i =0; i < 3; i ++) {
159 | this.players[i].cleanPokers();
160 | try {
161 | this.players[i].uiLeftPoker.kill();
162 | }
163 | catch (err) {
164 | }
165 | this.players[i].uiHead.frameName = 'icon_farmer.png';
166 | }
167 |
168 | for (var i = 0; i < this.tablePoker.length; i++) {
169 | var p = this.tablePokerPic[this.tablePoker[i]];
170 | // p.kill();
171 | p.destroy();
172 | }
173 | },
174 |
175 | restart: function () {
176 | this.players = [];
177 | this.shotLayer = null;
178 |
179 | this.tablePoker = [];
180 | this.tablePokerPic = {};
181 |
182 | this.lastShotPlayer = null;
183 |
184 | this.whoseTurn = 0;
185 |
186 | this.stage.backgroundColor = '#182d3b';
187 | this.players.push(PG.createPlay(0, this));
188 | this.players.push(PG.createPlay(1, this));
189 | this.players.push(PG.createPlay(2, this));
190 | player_id = [1, 11, 12];
191 | for (var i = 0; i < 3; i++) {
192 | //this.players[i].uiHead.kill();
193 | this.players[i].updateInfo(player_id[i], ' ');
194 | }
195 |
196 | // this.send_message([PG.Protocol.REQ_DEAL_POKEER, -1]);
197 | // PG.Socket.send([PG.Protocol.REQ_JOIN_TABLE, this.tableId]);
198 | },
199 |
200 | update: function () {
201 | },
202 |
203 | uidToSeat: function (uid) {
204 | for (var i = 0; i < 3; i++) {
205 | // this.debug_log(this.players[i].uid);
206 | if (uid == this.players[i].uid)
207 | return i;
208 | }
209 | console.log('ERROR uidToSeat:' + uid);
210 | return -1;
211 | },
212 |
213 | dealPoker: function(pokers) {
214 |
215 | for (var i = 0; i < 3; i++) {
216 | var p = new PG.Poker(this, 54, 54);
217 | this.game.world.add(p);
218 | this.tablePoker[i] = p.id;
219 | this.tablePoker[i + 3] = p;
220 | }
221 |
222 | for (var i = 0; i < 17; i++) {
223 | this.players[2].pokerInHand.push(54);
224 | this.players[1].pokerInHand.push(54);
225 | this.players[0].pokerInHand.push(pokers.pop());
226 | }
227 |
228 | this.players[0].dealPoker();
229 | this.players[1].dealPoker();
230 | this.players[2].dealPoker();
231 | //this.game.time.events.add(1000, function() {
232 | // this.send_message([PG.Protocol.REQ_CHEAT, this.players[1].uid]);
233 | // this.send_message([PG.Protocol.REQ_CHEAT, this.players[2].uid]);
234 | //}, this);
235 | },
236 |
237 | showLastThreePoker: function() {
238 | for (var i = 0; i < 3; i++) {
239 | var pokerId = this.tablePoker[i];
240 | var p = this.tablePoker[i + 3];
241 | p.id = pokerId;
242 | p.frame = pokerId;
243 | this.game.add.tween(p).to({ x: this.game.world.width/2 + (i - 1) * 60}, 600, Phaser.Easing.Default, true);
244 | }
245 | this.game.time.events.add(1500, this.dealLastThreePoker, this);
246 | },
247 |
248 | dealLastThreePoker: function() {
249 | var turnPlayer = this.players[this.whoseTurn];
250 |
251 | for (var i = 0; i < 3; i++) {
252 | var pid = this.tablePoker[i];
253 | var poker = this.tablePoker[i + 3];
254 | turnPlayer.pokerInHand.push(pid);
255 | turnPlayer.pushAPoker(poker);
256 | }
257 | turnPlayer.sortPoker();
258 | if (this.whoseTurn == 0) {
259 | turnPlayer.arrangePoker();
260 | for (var i = 0; i < 3; i++) {
261 | var p = this.tablePoker[i + 3];
262 | var tween = this.game.add.tween(p).to({y: this.game.world.height - PG.PH * 0.8 }, 400, Phaser.Easing.Default, true);
263 | function adjust(p) {
264 | this.game.add.tween(p).to({y: this.game.world.height - PG.PH /2}, 400, Phaser.Easing.Default, true, 400);
265 | };
266 | tween.onComplete.add(adjust, this, p);
267 | }
268 | } else {
269 | var first = turnPlayer.findAPoker(54);
270 | for (var i = 0; i < 3; i++) {
271 | var p = this.tablePoker[i + 3];
272 | p.frame = 54;
273 | p.frame = 54;
274 | this.game.add.tween(p).to({ x: first.x, y: first.y}, 200, Phaser.Easing.Default, true);
275 | }
276 | }
277 |
278 | this.tablePoker = [];
279 | this.lastShotPlayer = turnPlayer;
280 | if (this.whoseTurn == 0) {
281 | this.startPlay();
282 | }
283 | },
284 |
285 | handleShotPoker: function(packet) {
286 | this.whoseTurn = this.uidToSeat(packet[1]);
287 | var turnPlayer = this.players[this.whoseTurn];
288 | var pokers = packet[2];
289 | if (pokers.length == 0) {
290 | this.players[this.whoseTurn].say("不出");
291 | } else {
292 | var pokersPic = {};
293 | pokers.sort(PG.Poker.comparePoker);
294 | var count= pokers.length;
295 | var gap = Math.min((this.game.world.width - PG.PW * 2) / count, PG.PW * 0.36);
296 | for (var i = 0; i < count; i++) {
297 | var p = turnPlayer.findAPoker(pokers[i]);
298 | p.id = pokers[i];
299 | p.frame = pokers[i];
300 | p.bringToTop();
301 | this.game.add.tween(p).to({ x: this.game.world.width/2 + (i - count/2) * gap, y: this.game.world.height * 0.4}, 500, Phaser.Easing.Default, true);
302 |
303 | turnPlayer.removeAPoker(pokers[i]);
304 | pokersPic[p.id] = p;
305 | }
306 |
307 | for (var i = 0; i < this.tablePoker.length; i++) {
308 | var p = this.tablePokerPic[this.tablePoker[i]];
309 | // p.kill();
310 | p.destroy();
311 | }
312 | this.tablePoker = pokers;
313 | this.tablePokerPic = pokersPic;
314 | this.lastShotPlayer = turnPlayer;
315 | turnPlayer.arrangePoker();
316 | }
317 | if (turnPlayer.pokerInHand.length > 0) {
318 | this.whoseTurn = (this.whoseTurn + 1) % 3;
319 | if (this.whoseTurn == 0) {
320 | this.game.time.events.add(1000, this.startPlay, this);
321 | }
322 | }
323 | },
324 |
325 | startCallScore: function(minscore) {
326 | function btnTouch(btn) {
327 | this.send_message([PG.Protocol.REQ_CALL_SCORE, btn.score]);
328 | btn.parent.destroy();
329 | var audio = this.game.add.audio('f_score_' + btn.score);
330 | audio.play();
331 | };
332 |
333 | if (this.whoseTurn == 0) {
334 | var step = this.game.world.width/6;
335 | var ss = [1.5, 1, 0.5, 0];
336 | var sx = this.game.world.width/2 - step * ss[minscore];
337 | var sy = this.game.world.height * 0.6;
338 | var group = this.game.add.group();
339 | var pass = this.game.make.button(sx, sy, "btn", btnTouch, this, 'score_0.png', 'score_0.png', 'score_0.png');
340 | pass.anchor.set(0.5, 0);
341 | pass.score = 0;
342 | group.add(pass);
343 | sx += step;
344 |
345 | for (var i = minscore + 1; i <= 3; i++) {
346 | var tn = 'score_' + i + '.png';
347 | var call = this.game.make.button(sx, sy, "btn", btnTouch, this, tn, tn, tn);
348 | call.anchor.set(0.5, 0);
349 | call.score = i;
350 | group.add(call);
351 | sx += step;
352 | }
353 | } else {
354 | // TODO show clock on player
355 | }
356 |
357 | },
358 |
359 | startPlay: function() {
360 | if (this.isLastShotPlayer()) {
361 | this.players[0].playPoker([]);
362 | } else {
363 | this.players[0].playPoker(this.tablePoker);
364 | }
365 | },
366 |
367 | finishPlay: function(pokers) {
368 | this.send_message([PG.Protocol.REQ_SHOT_POKER, pokers]);
369 | },
370 |
371 | isLastShotPlayer: function() {
372 | return this.players[this.whoseTurn] == this.lastShotPlayer;
373 | },
374 |
375 | createTableLayer: function (tables) {
376 | tables.push([-1, 0]);
377 |
378 | var group = this.game.add.group();
379 | this.game.world.bringToTop(group);
380 | var gc = this.game.make.graphics(0, 0);
381 | gc.beginFill(0x00000080);
382 | gc.endFill();
383 | group.add(gc);
384 | var style = {font: "22px Arial", fill: "#fff", align: "center"};
385 |
386 | for (var i = 0; i < tables.length; i++) {
387 | var sx = this.game.world.width * (i%6 + 1)/(6 + 1);
388 | var sy = this.game.world.height * (Math.floor(i/6) + 1)/(4 + 1);
389 |
390 | var table = this.game.make.button(sx, sy, 'btn', this.onJoin, this, 'table.png', 'table.png', 'table.png');
391 | table.anchor.set(0.5, 1);
392 | table.tableId = tables[i][0];
393 | group.add(table);
394 |
395 | var text = this.game.make.text(sx, sy, '房间:' + tables[i][0] + '人数:' + tables[i][1], style);
396 | text.anchor.set(0.5, 0);
397 | group.add(text);
398 |
399 | if (i == tables.length - 1) {
400 | text.text = '新建房间';
401 | }
402 | }
403 | },
404 |
405 | quitGame: function () {
406 | this.state.start('MainMenu');
407 | },
408 |
409 | createTitleBar: function() {
410 | var style = {font: "22px Arial", fill: "#fff", align: "center"};
411 | this.titleBar = this.game.add.text(this.game.world.centerX, 0, '房间:', style);
412 | },
413 |
414 | onJoin: function (btn) {
415 | if (btn.tableId == -1) {
416 | this.send_message([PG.Protocol.REQ_NEW_TABLE]);
417 | } else {
418 | this.send_message([PG.Protocol.REQ_JOIN_TABLE, btn.tableId]);
419 | }
420 | btn.parent.destroy();
421 | }
422 | };
423 |
424 |
425 |
426 |
427 |
428 |
429 |
--------------------------------------------------------------------------------
/static/js/generator.js:
--------------------------------------------------------------------------------
1 | var symbolPath = {
2 | 'a': 'M6,200V183H23L58,0H78L117,183H131V200H85V183H97L92,156H46L42,183H54V200H6zM88,135L68,37L49,135H88z',
3 | '2': 'M10,200L11,187C15,149,23,136,70,97C93,78,100,68,101,57C104,31,81,23,65,23C46,22,23,34,35,62L12,68C8,43,12,18,33,8C61,-6,96,-1,115,21C127,36,129,56,123,72C104,113,39,131,35,179H105V152H127V200L10,200z',
4 | '3': 'M2,156L18,145C31,167,47,181,70,178C104,176,119,140,112,113C105,89,76,77,53,90C47,93,43,96,41,96C39,96,33,85,34,82C50,59,87,21,87,21H28V47H6V0H120V16C120,16,90,48,80,64C104,65,125,81,132,105C136,118,135,148,129,160C119,182,94,199,71,200C33,202,12,176,2,156L2,156z',
5 | '4': 'M70,200L70,183L86,183L86,153L5,153L5,133L93,0L107,0L107,133L132,133L132,153L107,153L107,183L120,183L120,200zM86,49L30,133L86,133z',
6 | '5': 'M4,148L24,148C28,160,37,173,48,176C80,183,101,166,108,144C116,120,107,84,85,71C67,61,40,70,27,92L13,83L20,0H112V20H37L37,55C52,44,77,44,93,52C123,66,137,98,131,137C123,175,105,197,64,200C20,201,4,170,4,148L4,148z',
7 | '6': 'M8,139C6,122,6,78,8,65C15,26,30,7,55,2C81,-4,116,3,124,35L103,36C91,14,60,15,46,29C34,37,28,68,30,70C30,70,50,55,73,55C120,55,132,94,130,127C129,167,116,198,73,200C31,198,12,177,8,139zM110,128C111,101,98,80,73,77C50,76,26,99,27,127C29,155,40,179,69,179C101,179,110,147,110,128z',
8 | '7': 'M37,200C50,131,65,79,102,22H26V46H6V0H117L131,22C91,64,54,202,61,200H37z',
9 | '8': 'M2,142C3,115,13,105,32,90C17,79,10,63,12,50C15,17,41,0,69,0C98,1,123,24,125,48C127,69,120,79,105,90C123,105,135,115,135,141C134,168,111,199,71,200C31,201,1,168,2,142L2,142zM113,142C115,117,93,101,69,101C45,101,23,121,23,143C23,166,51,178,69,178C91,178,112,163,113,142L113,142zM105,55C106,34,87,20,67,21C50,21,31,34,31,51C31,72,52,83,70,83C86,84,105,71,105,55L105,55z',
10 | '9': 'MM11,161L30,156C37,174,52,180,67,178C94,176,102,146,104,120C94,131,78,137,64,136C21,134,10,100,10,65C9,35,21,13,43,3C55,-1,81,-1,92,4C118,18,128,42,126,98C126,144,117,198,66,200C36,204,14,181,11,161L11,161zM85,111C94,105,98,100,102,92C106,86,106,83,106,69C103,36,86,17,60,21C44,23,36,31,33,46C24,73,35,105,55,112C63,116,78,115,85,111L85,111z',
11 | //10
12 | '0': 'M6,200V0H26V200H6M85,0C66,0,50,17,50,39V162C50,183,66,200,85,200H96C115,200,130,183,130,162V39C130,17,115,0,96,0H85M90,19C102,19,110,28,110,38V163C110,174,102,183,90,183C79,183,70,174,70,163V38C70,28,79,19,90,19L90,19z',
13 | 'j': 'M68,0V21H88C88,21,89,41,89,84C89,126,90,146,88,158C81,185,40,185,32,166C27,155,28,146,28,134H6C6,134,6,140,6,147C6,178,17,193,41,198C65,204,95,194,105,174C111,162,111,161,111,89C111,41,111,21,111,21H130V0H68z',
14 | 'q': 'M24,134L6,134L6,112L24,112C24,112,24,60,24,40C24,18,40,0,66,0C92,0,110,18,110,40C110,62,111,148,110,155C110,168,108,170,108,171C110,176,130,178,130,177L130,199C115,201,109,199,96,190C88,198,65,205,46,196C32,190,24,174,24,134zM81,174C73,162,58,145,44,140C44,156,46,165,51,171C59,181,71,183,81,174zM66,22C50,22,44,30,44,70C44,94,44,116,44,116C67,123,90,150,90,150L90,70C90,30,82,22,66,22z',
15 | 'k': 'M76,180L96,180L64,106L40,142L40,180L56,180L56,200L0,200L0,180L20,180L20,20L0,20L0,0L56,0L56,20L40,20L40,100L92.0636,19.841L76,20L76,0L136,0L136,20L120,20L76,88L116,180L136,180L136,200L76,200z',
16 | //jOker,jo
17 | 'o': 'M141,0L181,0C168,55,161,150,129,183C91,219,15,198,21,141L60,137C58,157,62,166,81,166C102,165,110,143,115,118M6,378C6,306,53,256,119,256C197,256,213,346,187,398C164,438,130,458,88,459C39,458,7,422,6,378M47,377C49,406,67,425,93,425C168,423,182,292,115,290C69,294,47,338,47,377M0,714L42,518L84,518L66,601L159,518L215,518L124,595L191,714L144,714L94,621L55,654L42,714M8,973L50,777L200,777L193,809L85,809L75,854L180,854L173,887L68,887L56,940L173,940L166,973M43,1231L1,1231L44,1035L133,1035C170,1037,197,1051,198,1087C195,1127,169,1143,136,1148C158,1171,171,1206,182,1231L137,1231C116,1182,112,1150,60,1150M67,1121C96,1121,155,1126,156,1087C151,1061,100,1068,78,1068z',
18 | //Hearts
19 | 'h': 'M100,30C60,7,0,7,0,76C0,131,100,190,100,190C100,190,200,131,200,76C200,7,140,7,100,30z',
20 | //Diamonds
21 | 'd': 'M184,100C152,120,120,160,100,200C80,160,48,120,16,100C48,80,80,40,100,0C120,40,152,80,184,100z',
22 | //Spades
23 | 's': 'M200,120C200,168,144,176,116,156C116,180,116,188,128,200C112,196,88,196,72,200C84,188,84,180,84,156C56,176,0,168,0,120C0,72,60,36,100,0C140,36,200,72,200,120z',
24 | //Clubs
25 | 'c': 'M80,200C92,184,92,160,92,136C76,180,0,176,0,124C0,80,40,76,68,88C80,92,80,88,72,84C44,64,40,0,100,0C160,0,156,64,128,84C120,88,120,92,132,88C160,76,200,80,200,124C200,176,124,180,108,136C108,160,108,184,120,200C100,196,100,196,80,200z',
26 | //cRown,cr
27 | 'r': 'M44,60,C45,56,-3,33,0,70,C2,107,39,146,48,150,C57,154,12,107,12,77,C12,45,43,65,44,60,M37,65,C31,64,20,60,19,81,C19,100,63,158,65,149,C65,139,33,102,37,65,M86,56,C87,52,38,28,40,66,C43,103,69,141,78,148,C86,155,54,102,54,71,C54,39,86,60,86,56,M82,65,C77,64,59,54,59,74,C60,95,82,146,84,138,C86,132,78,102,82,65,M154,60,C153,56,203,33,200,70,C197,107,159,146,151,150,C142,154,187,107,187,77,C187,45,155,65,154,60,M161,65,C167,64,179,60,180,81,C181,100,137,158,135,149,C134,139,165,102,161,65,M113,56,C112,52,161,28,158,66,C155,103,130,141,122,148,C114,155,145,102,145,71,C145,39,114,60,113,56,M117,65,C123,64,141,54,141,74,C140,95,118,146,116,138,C114,132,121,102,117,65z'
28 | //Nine - for 99, remove remark for below line and add a comma to upper line
29 | //'n': 'M157,89C159,188,80,211,16,196L23,160C62,172,100,167,107,119C93,127,83,133,62,132C28,133,0,113,0,70C0,25,37,0,78,0C137,0,157,41,157,89M105,56C100,42,92,34,77,33C59,33,49,49,49,69C52,101,83,104,107,95C107,82,108,66,105,56z'
30 | }, fixSuit = function(suit) {
31 | //hearts, diamonds, spades, clubs
32 | return (suit || 'h').substr(0, 1).toLowerCase();
33 | }, fixSymbol = function(symbol) {
34 | //A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, JOKER, NINE, CROWN
35 | symbol = (symbol || 'o').toString().toLowerCase();
36 | return symbol.substr((symbol.match(/jo|10|cr/)) ? 1 : 0, 1);
37 | };
38 |
39 | /**
40 | * Draw card number side
41 | * @summary canvas.drawPokerCard (x, y, size, suit, point)
42 | * @param {number} [x=0] - The x coordinate of top left corner of card in canvas.
43 | * @param {number} [y=0] - The y coordinate of top left corner of card in canvas.
44 | * @param {number} [size=200] - Height pixel of card. The ratio of card width and height is fixed to 3:4.
45 | * @param {string} [suit='h'] - Poker suit. The value is case insensitive and it should be one of these value in []:
46 | * ['h', 'hearts', 'd', 'diamonds', 's', 'spades', 'c', 'clubs']
47 | * 'h', 'd', 's', 'c' are abbreviation
48 | * When card point is 'O', 'h' or 'd' means big joker, 's' or 'c' means little joker.
49 | * @param {string} [point='O'] - Card point. The value is case insensitive and it should be one of these value in []:
50 | * ['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'O', 'JOKER']
51 | * 'O'(letter O) is abbreviation of 'JOKER'
52 | * @example
53 | * canvas.drawPokerCard (0, 400, 100, 'hearts', 'O');
54 | * canvas.drawPokerCard (0, 200, 100, 'd', 'Q');
55 | */
56 | CanvasRenderingContext2D.prototype.drawPokerCard = function(x, y, size, suit, point) {
57 | var ax = function(n) {
58 | return x + n * size / 200;
59 | }, ay = function(n) {
60 | return y + n * size / 200;
61 | }, as = function(n) {
62 | return n * size / 200;
63 | };
64 |
65 | suit = fixSuit(suit);
66 | point = fixSymbol(point);
67 |
68 | this.drawEmptyCard(ax(0), ay(0), as(200));
69 | this.fillStyle = (suit === 'h' || suit === 'd') ? '#a22' : '#000';
70 | if (size >= 100) {
71 | if (point !== 'o') {
72 | this.fillPokerSymbol(ax(40), ay(65), as(70), suit);
73 | this.fillPokerSymbol(ax(10), ay(10), as(40), point);
74 | this.fillPokerSymbol(ax(11), ay(55), as(25), suit);
75 | this.fillPokerSymbol(ax(140), ay(190), as(-40), point);
76 | this.fillPokerSymbol(ax(139), ay(145), as(-25), suit);
77 | } else {
78 | this.fillPokerSymbol(ax(11), ay(10), as(18), 'o');
79 | this.fillPokerSymbol(ax(139), ay(190), as(-18), 'o');
80 | if (suit === 'h' || suit === 'd') {
81 | this.drawPokerCrown(ax(38), ay(63), as(74), '#b55', '#a22');
82 | this.drawPokerCrown(ax(40), ay(65), as(70), '#fdf98b', '#e7bd4f', '#a22');
83 | } else {
84 | this.drawPokerCrown(ax(38), ay(63), as(74), '#000', '#000');
85 | this.drawPokerCrown(ax(40), ay(65), as(70), '#eee', '#888', '#333');
86 | }
87 | }
88 | } else {
89 | if (point !== 'o') {
90 | this.fillPokerSymbol(ax(30), ay(75), as(100), suit);
91 | this.fillPokerSymbol(ax(15), ay(15), as(50), point);
92 | } else {
93 | this.fillPokerSymbol(ax(11), ay(10), as(22), 'o');
94 | if (suit === 'h' || suit === 'd') {
95 | this.drawPokerCrown(ax(45), ay(73), as(89), '#b55', '#a22');
96 | this.drawPokerCrown(ax(47), ay(75), as(85), '#fdf98b', '#e7bd4f', '#a22');
97 | } else {
98 | this.drawPokerCrown(ax(45), ay(73), as(89), '#000', '#000');
99 | this.drawPokerCrown(ax(47), ay(75), as(85), '#eee', '#888', '#333');
100 | }
101 | }
102 | }
103 | };
104 |
105 | /**
106 | * Draw card back side
107 | * @summary canvas.drawPokerBack (x, y, size[, foregroundColor, backgroundColor])
108 | * @param {number} [x=0] - The x coordinate of top left corner of card in canvas.
109 | * @param {number} [y=0] - The y coordinate of top left corner of card in canvas.
110 | * @param {number} [size=200] - Height pixel of card. The ratio of card width and height is fixed to 3:4.
111 | * @param {string} [foregroundColor='#BB5555'] - Foreground color.
112 | * @param {string} [backgroundColor='#AA2222'] - Background color.
113 | * @example
114 | * canvas.drawPokerBack (10, 10, 300, '#a22', '#b55')
115 | * canvas.drawPokerBack (375, 400, 100, '#2E319C', '#7A7BB8');
116 | */
117 | CanvasRenderingContext2D.prototype.drawPokerBack = function(x, y, size, foregroundColor, backgroundColor) {
118 | var ax = function(n) {
119 | return x + n * size / 200;
120 | }, ay = function(n) {
121 | return y + n * size / 200;
122 | }, as = function(n) {
123 | return n * size / 200;
124 | };
125 |
126 | foregroundColor = foregroundColor || '#b55';
127 | backgroundColor = backgroundColor || '#a22';
128 |
129 | this.drawEmptyCard(x, y, size);
130 |
131 | this.fillStyle = backgroundColor;
132 | this.fillRoundRect(ax(10), ay(10), as(130), as(180), as(8));
133 | this.strokeStyle = foregroundColor;
134 | this.strokeRoundRect(ax(18), ay(18), as(114), as(164), as(4));
135 | this.fillStyle = foregroundColor;
136 | this.fillRoundRect(ax(26), ay(26), as(96), as(148), as(24), true);
137 |
138 | this.fillPokerSymbol(ax(24), ay(24), as(20), 's');
139 | this.fillPokerSymbol(ax(106), ay(24), as(20), 's');
140 | this.fillPokerSymbol(ax(44), ay(176), as(-20), 's');
141 | this.fillPokerSymbol(ax(126), ay(176), as(-20), 's');
142 | this.fillStyle = backgroundColor;
143 | this.fillRoundRect(ax(50), ay(40), as(50), as(120), as(24));
144 | this.fillPokerSymbol(ax(32), ay(54), as(86), 's');
145 | this.fillPokerSymbol(ax(30), ay(60), as(16), 's');
146 | this.fillPokerSymbol(ax(104), ay(60), as(16), 's');
147 | this.fillPokerSymbol(ax(30), ay(128), as(16), 's');
148 | this.fillPokerSymbol(ax(104), ay(128), as(16), 's');
149 | this.strokePokerSymbol(ax(31), ay(53), as(88), 's');
150 | this.fillStyle = foregroundColor;
151 |
152 | this.fillPokerSymbol(ax(50), ay(75), as(50), 'c');
153 | //for 99, replace the upper line to below 2 lines.
154 | //this.fillPokerSymbol(ax(47), ay(80), as(35), 'n');
155 | //this.fillPokerSymbol(ax(77), ay(80), as(35), 'n');
156 | };
157 |
158 | /**
159 | * Draw round corner rectangle
160 | * @summary canvas.roundRect (x, y[, width, height[, radius[, direction]]])
161 | * @param {number} [x=0] - The x coordinate of top left corner of rectangle in canvas.
162 | * @param {number} [y=0] - The y coordinate of top left corner of rectangle in canvas.
163 | * @param {number} [width=200] - Width of the rectangle.
164 | * @param {number} [height=200] - Height of the rectangle.
165 | * @param {number} [radius=20] - Radius of corner round.
166 | * @param {boolean} [direction=false] - Direction of corner round. The true or false means inward or outward.
167 | * @example
168 | * canvas.roundRect (0, 0, 200, 200, 30);
169 | * canvas.roundRect (50, 50, 100, 100, 30, true);
170 | */
171 | CanvasRenderingContext2D.prototype.roundRect = function(x, y, width, height, radius, direction) {
172 | width = width || 200;
173 | height = height || 200;
174 | radius = radius || 20;
175 |
176 | this.beginPath();
177 | if (!direction) {
178 | this.moveTo(x + radius, y);
179 | this.lineTo(x + width - radius, y);
180 | this.arc(x + width - radius, y + radius, radius, (Math.PI / 180) * 270, 0);
181 | this.lineTo(x + width, y + height - radius);
182 | this.arc(x + width - radius, y + height - radius, radius, 0, (Math.PI / 2));
183 | this.lineTo(x + radius, y + height);
184 | this.arc(x + radius, y + height - radius, radius, (Math.PI / 2), Math.PI);
185 | this.lineTo(x, y + radius);
186 | this.arc(x + radius, y + radius, radius, Math.PI, (Math.PI / 180) * 270);
187 | } else {
188 | this.moveTo(x, y + radius);
189 | this.lineTo(x, y + height - radius);
190 | this.arc(x, y + height, radius, (Math.PI / 180) * 270, 0);
191 | this.lineTo(x + width - radius, y + height);
192 | this.arc(x + width, y + height, radius, Math.PI, (Math.PI / 180) * 270);
193 | this.lineTo(x + width, y + radius);
194 | this.arc(x + width, y, radius, Math.PI / 2, Math.PI);
195 | this.lineTo(x + radius, y);
196 | this.arc(x, y, radius, 0, Math.PI / 2);
197 | }
198 | this.closePath();
199 | };
200 |
201 | /**
202 | * Stroke round corner rectangle
203 | * @summary canvas.strokeRoundRect (x, y[, width, height[, radius[, direction]]])
204 | * @param {number} [x=0] - The x coordinate of top left corner of rectangle in canvas.
205 | * @param {number} [y=0] - The y coordinate of top left corner of rectangle in canvas.
206 | * @param {number} [width=200] - Width of the rectangle.
207 | * @param {number} [height=200] - Height of the rectangle.
208 | * @param {number} [radius=20] - Radius of corner round.
209 | * @param {boolean} [direction=false] - Direction of corner round. The true or false means inward or outward.
210 | * @example
211 | * canvas.strokeRoundRect (0, 0, 200, 200, 30);
212 | * canvas.strokeRoundRect (50, 50, 100, 100, 30, true);
213 | */
214 | CanvasRenderingContext2D.prototype.strokeRoundRect = function(x, y, width, height, radius, direction) {
215 | this.roundRect(x + 0.5, y + 0.5, width - 1, height - 1, radius, direction);
216 | this.stroke();
217 | };
218 |
219 | /**
220 | * Fill round corner rectangle
221 | * @summary canvas.fillRoundRect (x, y[, width, height[, radius[, direction]]])
222 | * @param {number} [x=0] - The x coordinate of top left corner of rectangle in canvas.
223 | * @param {number} [y=0] - The y coordinate of top left corner of rectangle in canvas.
224 | * @param {number} [width=200] - Width of the rectangle.
225 | * @param {number} [height=200] - Height of the rectangle.
226 | * @param {number} [radius=20] - Radius of corner round.
227 | * @param {boolean} [direction=false] - Direction of corner round. The true or false means inward or outward.
228 | * @example
229 | * canvas.fillRoundRect (0, 0, 200, 200, 30);
230 | * canvas.fillRoundRect (50, 50, 100, 100, 30, true);
231 | */
232 | CanvasRenderingContext2D.prototype.fillRoundRect = function(x, y, width, height, radius, direction) {
233 | this.roundRect(x, y, width, height, radius, direction);
234 | this.fill();
235 | };
236 |
237 | /**
238 | * Draw SVG curve
239 | * @summary canvas.svgCurve (x, y, size, svgPath)
240 | * @param {number} [x=0] - The x coordinate of top left corner of card in canvas.
241 | * @param {number} [y=0] - The y coordinate of top left corner of card in canvas.
242 | * @param {number} size - The pixel size of the curve.
243 | * @param {string} svgPath - Value of property 'd' of SVG 'path' method.
244 | * When create the curve by svg software, please move the origin of coordinate be 0,0.
245 | * And keep the bigger size of height and width to 200px.
246 | * Don't use AQ or T method in svg software, browser canvas have not relative method to render it.
247 | * @example
248 | * draw a heart symbol:
249 | * canvas.svgCurve ('M100,30C60,7 0,7 0,76C0,131 100,190 100,190C100,190 200,131 200,76C200,7 140,7 100,30z', 0, 0, 200));
250 | */
251 | CanvasRenderingContext2D.prototype.svgCurve = function(x, y, size, svgPath) {
252 | var relativeX, relativeY, pathNumber, pathArray, svgPathArray, ax = function(n) {
253 | return ( relativeX = x + n * size / 200);
254 | }, ay = function(n) {
255 | return ( relativeY = y + n * size / 200);
256 | };
257 | svgPathArray = svgPath.replace(/ *([MZLHVCSQTA]) */gi, '|$1,').replace(/^\||\|[Z],/gi, '').split(/\|/);
258 |
259 | this.beginPath();
260 | for (pathNumber in svgPathArray) {
261 | pathArray = svgPathArray[pathNumber].split(/[, ]/);
262 | if (pathArray[0] === 'M') {
263 | this.moveTo(ax(pathArray[1]), ay(pathArray[2]));
264 | } else if (pathArray[0] === 'L') {
265 | this.lineTo(ax(pathArray[1]), ay(pathArray[2]));
266 | } else if (pathArray[0] === 'H') {
267 | this.lineTo(ax(pathArray[1]), relativeY);
268 | } else if (pathArray[0] === 'V') {
269 | this.lineTo(relativeX, ay(pathArray[1]));
270 | } else if (pathArray[0] === 'C') {
271 | this.bezierCurveTo(ax(pathArray[1]), ay(pathArray[2]), ax(pathArray[3]), ay(pathArray[4]), ax(pathArray[5]), ay(pathArray[6]));
272 | } else if (pathArray[0] === 'Q') {
273 | this.quadraticCurveTo(ax(pathArray[1]), ay(pathArray[2]), ax(pathArray[3]), ay(pathArray[4]));
274 | }
275 | }
276 | this.closePath();
277 | };
278 |
279 | /**
280 | * Draw poker symbol
281 | * @summary canvas.drawPokerSymbol (x, y, size[, symbol])
282 | * @param {number} [x=0] - The x coordinate of top left corner of card in canvas.
283 | * @param {number} [y=0] - The y coordinate of top left corner of card in canvas.
284 | * @param {number} [size=200] - Height pixel of card. The ratio of card width and height is fixed to 3:4.
285 | * @param {string} [symbol='O'] - The name of symbol. Value is case insensitive and should be one of below:
286 | * ['h', 'hearts', 'd', 'diamonds', 's', 'spades', 'c', 'clubs']
287 | * 'h', 'd', 's', 'c' are abbreviation
288 | * ['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'O', 'JOKER']
289 | * 'O'(letter O) is abbreviation of 'JOKER'
290 | * ['R', 'CROWN'] // crown, a part of crown, to jointing a crown of JOKER card
291 | * ['N', 'NINE'] // Nine, bold '9' for jointing '99' pattern
292 | * @example
293 | * canvas.drawPokerSymbol (0, 0, 200, 'hearts');
294 | */
295 | CanvasRenderingContext2D.prototype.drawPokerSymbol = function(x, y, size, symbol) {
296 | symbol = fixSymbol(symbol);
297 | if (symbolPath[symbol]) {
298 | this.svgCurve(x, y, size, symbolPath[symbol]);
299 | }
300 | };
301 |
302 | /**
303 | * Stroke poker symbol
304 | * @summary canvas.strokePokerSymbol (x, y, size[, symbol])
305 | * @param {number} [x=0] - The x coordinate of top left corner of card in canvas.
306 | * @param {number} [y=0] - The y coordinate of top left corner of card in canvas.
307 | * @param {number} [size=200] - Height pixel of card. The ratio of card width and height is fixed to 3:4.
308 | * @param {string} [symbol='O'] - The name of symbol. Value is case insensitive and should be one of below:
309 | * ['h', 'hearts', 'd', 'diamonds', 's', 'spades', 'c', 'clubs']
310 | * 'h', 'd', 's', 'c' are abbreviation
311 | * ['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'O', 'JOKER']
312 | * 'O'(letter O) is abbreviation of 'JOKER'
313 | * ['R', 'CROWN'] // crown, a part of crown, to jointing a crown of JOKER card
314 | * ['N', 'NINE'] // Nine, bold '9' for jointing '99' pattern
315 | * @example
316 | * canvas.strokePokerSymbol (0, 0, 200, 'hearts');
317 | */
318 | CanvasRenderingContext2D.prototype.strokePokerSymbol = function(x, y, size, symbol) {
319 | this.drawPokerSymbol(x + 0.5, y + 0.5, size - 1, symbol);
320 | this.stroke();
321 | };
322 |
323 | /**
324 | * Fill poker symbol
325 | * @summary canvas.fillPokerSymbol (x, y, size[, symbol])
326 | * @param {number} [x=0] - The x coordinate of top left corner of card in canvas.
327 | * @param {number} [y=0] - The y coordinate of top left corner of card in canvas.
328 | * @param {number} [size=200] - Height pixel of card. The ratio of card width and height is fixed to 3:4.
329 | * @param {string} [symbol='O'] - The name of symbol. Value is case insensitive and should be one of below:
330 | * ['h', 'hearts', 'd', 'diamonds', 's', 'spades', 'c', 'clubs']
331 | * 'h', 'd', 's', 'c' are abbreviation
332 | * ['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'O', 'JOKER']
333 | * 'O'(letter O) is abbreviation of 'JOKER'
334 | * ['R', 'CROWN'] // crown, a part of crown, to jointing a crown of JOKER card
335 | * ['N', 'NINE'] // Nine, bold '9' for jointing '99' pattern
336 | * @example
337 | * canvas.fillPokerSymbol (0, 0, 200, 'hearts');
338 | */
339 | CanvasRenderingContext2D.prototype.fillPokerSymbol = function(x, y, size, symbol) {
340 | this.drawPokerSymbol(x, y, size, symbol);
341 | this.fill();
342 | };
343 |
344 | /**
345 | * Draw crown
346 | * @summary canvas.drawPokerCrown (x, y, size[, startColor, endColor[, fillColor]])
347 | * @param {number} [x=0] - The x coordinate of top left corner of card in canvas.
348 | * @param {number} [y=0] - The y coordinate of top left corner of card in canvas.
349 | * @param {number} [size=200] - Height pixel of card. The ratio of card width and height is fixed to 3:4.
350 | * @param {string} [startColor='#FDF98B'] - Start color of gradient background color.
351 | * @param {string} [endColor ='#E7BD4F'] - End color of gradient background color.
352 | * @param {string} [fillColor ='#FFFFFF'] - Fill color of jewel of crown.
353 | * @example
354 | * canvas.drawPokerCrown(0, 0, 200);
355 | */
356 | CanvasRenderingContext2D.prototype.drawPokerCrown = function(x, y, size, startColor, endColor, fillColor) {
357 | var fillLinGrad, ax = function(n) {
358 | return x + n * size / 200;
359 | }, ay = function(n) {
360 | return y + n * size / 200;
361 | }, as = function(n) {
362 | return n * size / 200;
363 | };
364 |
365 | startColor = startColor || '#fdf98b';
366 | endColor = endColor || '#e7bd4f';
367 | fillColor = fillColor || '#fff';
368 |
369 | fillLinGrad = this.createLinearGradient(ax(5), ay(5), ax(100), ay(200));
370 | fillLinGrad.addColorStop(0, startColor);
371 | fillLinGrad.addColorStop(1, endColor);
372 |
373 | this.fillStyle = fillLinGrad;
374 | this.fillPokerSymbol(ax(0), ay(0), as(200), 'r');
375 | this.fillRoundRect(ax(88), ay(42), as(23), as(110), as(12));
376 | this.fillPokerSymbol(ax(86), ay(18), as(27), 's');
377 | this.fillRoundRect(ax(40), ay(150), as(120), as(24), as(10));
378 | this.fillStyle = fillColor;
379 | this.fillPokerSymbol(ax(92), ay(26), as(15), 'd');
380 | this.fillPokerSymbol(ax(93), ay(60), as(13), 'h');
381 | this.fillPokerSymbol(ax(93), ay(80), as(13), 'h');
382 | this.fillPokerSymbol(ax(93), ay(100), as(13), 'h');
383 | this.fillPokerSymbol(ax(93), ay(120), as(13), 'h');
384 | this.fillPokerSymbol(ax(93), ay(155), as(13), 'h');
385 | this.fillPokerSymbol(ax(73), ay(155), as(13), 'h');
386 | this.fillPokerSymbol(ax(53), ay(155), as(13), 'h');
387 | this.fillPokerSymbol(ax(113), ay(155), as(13), 'h');
388 | this.fillPokerSymbol(ax(133), ay(155), as(13), 'h');
389 | };
390 |
391 | /**
392 | * Draw blank card
393 | * @summary canvas.drawEmptyCard (x, y, size[, startColor, endColor])
394 | * @param {number} [x=0] - The x coordinate of top left corner of card in canvas.
395 | * @param {number} [y=0] - The y coordinate of top left corner of card in canvas.
396 | * @param {number} [size=200] - Height pixel of card. The ratio of card width and height is fixed to 3:4.
397 | * @example
398 | * canvas.drawEmptyCard(0, 0, 200);
399 | */
400 | CanvasRenderingContext2D.prototype.drawEmptyCard = function(x, y, size) {
401 | var fillLinGrad, ax = function(n) {
402 | return x + n * size / 200;
403 | }, ay = function(n) {
404 | return y + n * size / 200;
405 | }, as = function(n) {
406 | return n * size / 200;
407 | };
408 |
409 | fillLinGrad = this.createLinearGradient(ax(5), ay(5), ax(55), ay(200));
410 | fillLinGrad.addColorStop(0, '#fff');
411 | fillLinGrad.addColorStop(1, '#e0e0e0');
412 |
413 | this.fillStyle = fillLinGrad;
414 | this.fillRoundRect(ax(0), ay(0), as(150), as(200), as(16));
415 | this.strokeStyle = '#666';
416 | this.strokeRoundRect(ax(0), ay(0), as(150), as(200), as(16));
417 | };
418 |
419 | window.Poker = {
420 | /**
421 | * Draw card number side as a image
422 | * @summary Poker.getCardImage (size, suit, point)
423 | * @param {number} [size=200] - Height pixel of card. The ratio of card width and height is fixed to 3:4.
424 | * @param {string} [suit='h'] - Poker suit. The value is case insensitive and it should be one of these value in []:
425 | * ['h', 'hearts', 'd', 'diamonds', 's', 'spades', 'c', 'clubs']
426 | * 'h', 'd', 's', 'c' are abbreviation
427 | * When card point is 'O', 'h' or 'd' means big joker, 's' or 'c' means little joker.
428 | * @param {string} [point='O'] - Card point. The value is case insensitive and it should be one of these value in []:
429 | * ['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'O', 'JOKER']
430 | * 'O'(letter O) is abbreviation of 'JOKER'
431 | * @return {HTMLElement} image
432 | * @example
433 | * document.body.appendChild(Poker.getCardImage(100, 'h', 'Q'));
434 | */
435 | getCardImage: function(size, suit, point) {
436 | var image = document.createElement('img');
437 | image.src = this.getCardData(size, suit, point);
438 | return image;
439 | },
440 |
441 | /**
442 | * Get card number side image data
443 | * @summary Poker.getCardData (size, suit, point)
444 | * @param {number} [size=200] - Height pixel of card. The ratio of card width and height is fixed to 3:4.
445 | * @param {string} [suit='h'] - Poker suit. The value is case insensitive and it should be one of these value in []:
446 | * ['h', 'hearts', 'd', 'diamonds', 's', 'spades', 'c', 'clubs']
447 | * 'h', 'd', 's', 'c' are abbreviation
448 | * When card point is 'O', 'h' or 'd' means big joker, 's' or 'c' means little joker.
449 | * @param {string} [point='O'] - Card point. The value is case insensitive and it should be one of these value in []:
450 | * ['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'O', 'JOKER']
451 | * 'O'(letter O) is abbreviation of 'JOKER'
452 | * @return {string} imageData
453 | * @example
454 | * var imgData = Poker.getCardData(100, 'h', 'Q');
455 | */
456 | getCardData: function(size, suit, point) {
457 | return this.getCardCanvas(size, suit, point).toDataURL();
458 | },
459 |
460 | /**
461 | * Draw card back side as a image
462 | * @summary Poker.getBackImage (size[, foregroundColor, backgroundColor])
463 | * @param {number} [size=200] - Height pixel of card. The ratio of card width and height is fixed to 3:4.
464 | * @param {string} [foregroundColor='#BB5555'] - Foreground color.
465 | * @param {string} [backgroundColor='#AA2222'] - Background color.
466 | * @return {HTMLElement} image
467 | * @example
468 | * document.body.appendChild(Poker.getBackImage(300, '#2E319C', '#7A7BB8'));
469 | */
470 | getBackImage: function(size, foregroundColor, backgroundColor) {
471 | var image = document.createElement('img');
472 | image.src = this.getBackData(size, foregroundColor, backgroundColor);
473 | return image;
474 | },
475 |
476 | /**
477 | * Get card back side image data
478 | * @summary Poker.getBackData (size[, foregroundColor, backgroundColor])
479 | * @param {number} [size=200] - Height pixel of card. The ratio of card width and height is fixed to 3:4.
480 | * @param {string} [foregroundColor='#BB5555'] - Foreground color.
481 | * @param {string} [backgroundColor='#AA2222'] - Background color.
482 | * @return {string} imageData
483 | * @example
484 | * var imageData = Poker.getBackCanvas(300, '#2E319C', '#7A7BB8');
485 | */
486 | getBackData: function(size, foregroundColor, backgroundColor) {
487 | return this.getBackCanvas(size, foregroundColor, backgroundColor).toDataURL();
488 | }
489 | };
490 |
--------------------------------------------------------------------------------
/static/js/net.js:
--------------------------------------------------------------------------------
1 |
2 | PG.Protocol = {
3 | REQ_CHEAT : 1,
4 | RSP_CHEAT : 2,
5 |
6 | REQ_LOGIN : 11,
7 | RSP_LOGIN : 12,
8 |
9 | REQ_ROOM_LIST : 13,
10 | RSP_ROOM_LIST : 14,
11 |
12 | REQ_TABLE_LIST : 15,
13 | RSP_TABLE_LIST : 16,
14 |
15 | REQ_JOIN_ROOM : 17,
16 | RSP_JOIN_ROOM : 18,
17 |
18 | REQ_JOIN_TABLE : 19,
19 | RSP_JOIN_TABLE : 20,
20 |
21 | REQ_NEW_TABLE : 21,
22 | RSP_NEW_TABLE : 22,
23 |
24 | REQ_DEAL_POKER : 31,
25 | RSP_DEAL_POKER : 32,
26 |
27 | REQ_CALL_SCORE : 33,
28 | RSP_CALL_SCORE : 34,
29 |
30 | REQ_SHOW_POKER : 35,
31 | RSP_SHOW_POKER : 36,
32 |
33 | REQ_SHOT_POKER : 37,
34 | RSP_SHOT_POKER : 38,
35 |
36 | REQ_GAME_OVER : 41,
37 | RSP_GAME_OVER : 42,
38 |
39 | REQ_CHAT : 43,
40 | RSP_CHAT : 44,
41 |
42 | REQ_RESTART : 45,
43 | RSP_RESTART : 46
44 | };
45 |
46 | PG.Socket = {
47 | websocket: null,
48 | onmessage: null
49 | };
50 |
51 | PG.Socket.connect = function(onopen, onmessage, onerror) {
52 |
53 | if (this.websocket != null) {
54 | return;
55 | }
56 |
57 | // if (localStorage.hasOwnProperty("port")){
58 | // this.websocket = new WebSocket("ws://" + window.location.host +":" + localStorage.getItem("port") + "/ws");
59 | // } else {
60 | this.websocket = new WebSocket("ws://" + window.location.host + "/ws");
61 | // }
62 |
63 | this.websocket.binaryType = 'arraybuffer';
64 | this.websocket.onopen = function(evt) {
65 | console.log("CONNECTED");
66 | onopen();
67 | };
68 |
69 | this.websocket.onerror = function(evt) {
70 | console.log('CONNECT ERROR: ' + evt.data);
71 | onerror();
72 | };
73 |
74 | this.websocket.onclose = function(evt) {
75 | console.log("DISCONNECTED");
76 | this.websocket = null;
77 | };
78 |
79 | this.websocket.onmessage = function(evt) {
80 | console.log('RSP: ' + evt.data);
81 | onmessage(JSON.parse(evt.data));
82 | };
83 | };
84 |
85 | PG.Socket.send = function(msg) {
86 | console.log('REQ: ' + msg);
87 | this.websocket.send(JSON.stringify(msg));
88 | };
89 |
--------------------------------------------------------------------------------
/static/js/phaser-input.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * phaser-input - version 2.0.5
3 | * Adds input boxes to Phaser like CanvasInput, but also works for WebGL and Mobile, made for Phaser only.
4 | *
5 | * OrangeGames
6 | * Build at 02-06-2017
7 | * Released under MIT License
8 | */
9 |
10 | var __extends = (this && this.__extends) || (function () {
11 | var extendStatics = Object.setPrototypeOf ||
12 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
13 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
14 | return function (d, b) {
15 | extendStatics(d, b);
16 | function __() { this.constructor = d; }
17 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
18 | };
19 | })();
20 | var PhaserInput;
21 | (function (PhaserInput) {
22 | var InputType;
23 | (function (InputType) {
24 | InputType[InputType["text"] = 0] = "text";
25 | InputType[InputType["password"] = 1] = "password";
26 | InputType[InputType["number"] = 2] = "number";
27 | })(InputType = PhaserInput.InputType || (PhaserInput.InputType = {}));
28 | var InputElement = (function () {
29 | function InputElement(game, id, type, value, focusIn, focusOut) {
30 | if (type === void 0) { type = InputType.text; }
31 | if (value === void 0) { value = ''; }
32 | var _this = this;
33 | this.id = id;
34 | this.type = type;
35 | this.game = game;
36 | this.focusIn = focusIn;
37 | this.focusOut = focusOut;
38 | var canvasTopX = this.game.canvas.getBoundingClientRect().top + document.body.scrollTop;
39 | this.element = document.createElement('input');
40 | this.element.id = id;
41 | this.element.style.position = 'absolute';
42 | this.element.style.top = canvasTopX + 'px';
43 | this.element.style.left = (-40).toString() + 'px';
44 | this.element.style.width = (10).toString() + 'px';
45 | this.element.style.height = (10).toString() + 'px';
46 | this.element.style.border = '0px';
47 | this.element.value = this.value;
48 | this.element.type = InputType[type];
49 | this.element.addEventListener('focusin', function () {
50 | if (_this.focusIn instanceof Phaser.Signal) {
51 | _this.focusIn.dispatch();
52 | }
53 | });
54 | this.element.addEventListener('focusout', function () {
55 | if (_this.focusOut instanceof Phaser.Signal) {
56 | _this.focusOut.dispatch();
57 | }
58 | });
59 | document.body.appendChild(this.element);
60 | }
61 | InputElement.prototype.addKeyUpListener = function (callback) {
62 | this.keyUpCallback = callback;
63 | document.addEventListener('keyup', this.keyUpCallback);
64 | this.element.addEventListener('input', this.keyUpCallback);
65 | };
66 | InputElement.prototype.blockKeyDownEvents = function () {
67 | document.addEventListener('keydown', this.preventKeyPropagation);
68 | };
69 | InputElement.prototype.preventKeyPropagation = function (evt) {
70 | if (evt.stopPropagation) {
71 | evt.stopPropagation();
72 | }
73 | else {
74 | event.cancelBubble = true;
75 | }
76 | };
77 | InputElement.prototype.unblockKeyDownEvents = function () {
78 | document.removeEventListener('keydown', this.preventKeyPropagation);
79 | };
80 | InputElement.prototype.removeEventListener = function () {
81 | document.removeEventListener('keyup', this.keyUpCallback);
82 | this.element.removeEventListener('input', this.keyUpCallback);
83 | };
84 | InputElement.prototype.destroy = function () {
85 | document.body.removeChild(this.element);
86 | };
87 | InputElement.prototype.setMax = function (max, min) {
88 | if (max === undefined) {
89 | return;
90 | }
91 | if (this.type === InputType.text || this.type === InputType.password) {
92 | this.element.maxLength = parseInt(max, 10);
93 | }
94 | else if (this.type === InputType.number) {
95 | this.element.max = max;
96 | if (min === undefined) {
97 | return;
98 | }
99 | this.element.min = min;
100 | }
101 | };
102 | Object.defineProperty(InputElement.prototype, "value", {
103 | get: function () {
104 | return this.element.value;
105 | },
106 | set: function (value) {
107 | this.element.value = value;
108 | },
109 | enumerable: true,
110 | configurable: true
111 | });
112 | InputElement.prototype.focus = function () {
113 | var _this = this;
114 | this.element.focus();
115 | if (!this.game.device.desktop && this.game.device.chrome) {
116 | var originalWidth_1 = window.innerWidth, originalHeight_1 = window.innerHeight;
117 | var kbAppeared_1 = false;
118 | var interval_1 = setInterval(function () {
119 | if (originalWidth_1 > window.innerWidth || originalHeight_1 > window.innerHeight) {
120 | kbAppeared_1 = true;
121 | }
122 | if (kbAppeared_1 && originalWidth_1 === window.innerWidth && originalHeight_1 === window.innerHeight) {
123 | if (_this.focusOut instanceof Phaser.Signal) {
124 | _this.focusOut.dispatch();
125 | }
126 | clearInterval(interval_1);
127 | }
128 | }, 50);
129 | }
130 | };
131 | InputElement.prototype.blur = function () {
132 | this.element.blur();
133 | };
134 | Object.defineProperty(InputElement.prototype, "hasSelection", {
135 | get: function () {
136 | if (this.type === InputType.number) {
137 | return false;
138 | }
139 | return this.element.selectionStart !== this.element.selectionEnd;
140 | },
141 | enumerable: true,
142 | configurable: true
143 | });
144 | Object.defineProperty(InputElement.prototype, "caretStart", {
145 | get: function () {
146 | return this.element.selectionEnd;
147 | },
148 | enumerable: true,
149 | configurable: true
150 | });
151 | Object.defineProperty(InputElement.prototype, "caretEnd", {
152 | get: function () {
153 | return this.element.selectionStart;
154 | },
155 | enumerable: true,
156 | configurable: true
157 | });
158 | InputElement.prototype.getCaretPosition = function () {
159 | if (this.type === InputType.number) {
160 | return -1;
161 | }
162 | return this.element.selectionStart;
163 | };
164 | InputElement.prototype.setCaretPosition = function (pos) {
165 | if (this.type === InputType.number) {
166 | return;
167 | }
168 | this.element.setSelectionRange(pos, pos);
169 | };
170 | return InputElement;
171 | }());
172 | PhaserInput.InputElement = InputElement;
173 | })(PhaserInput || (PhaserInput = {}));
174 | var PhaserInput;
175 | (function (PhaserInput) {
176 | var ForceCase;
177 | (function (ForceCase) {
178 | ForceCase[ForceCase["none"] = 0] = "none";
179 | ForceCase[ForceCase["lower"] = 1] = "lower";
180 | ForceCase[ForceCase["upper"] = 2] = "upper";
181 | })(ForceCase = PhaserInput.ForceCase || (PhaserInput.ForceCase = {}));
182 | var InputField = (function (_super) {
183 | __extends(InputField, _super);
184 | function InputField(game, x, y, inputOptions) {
185 | if (inputOptions === void 0) { inputOptions = {}; }
186 | var _this = _super.call(this, game, x, y) || this;
187 | _this.focusOutOnEnter = true;
188 | _this.placeHolder = null;
189 | _this.box = null;
190 | _this.focus = false;
191 | _this.value = '';
192 | _this.windowScale = 1;
193 | _this.blockInput = true;
194 | _this.focusIn = new Phaser.Signal();
195 | _this.focusOut = new Phaser.Signal();
196 | _this.blink = true;
197 | _this.cnt = 0;
198 | _this.inputOptions = inputOptions;
199 | _this.inputOptions.width = (typeof inputOptions.width === 'number') ? inputOptions.width : 150;
200 | _this.inputOptions.padding = (typeof inputOptions.padding === 'number') ? inputOptions.padding : 0;
201 | _this.inputOptions.textAlign = inputOptions.textAlign || 'left';
202 | _this.inputOptions.type = inputOptions.type || PhaserInput.InputType.text;
203 | _this.inputOptions.forceCase = (inputOptions.forceCase) ? inputOptions.forceCase : ForceCase.none;
204 | _this.inputOptions.borderRadius = (typeof inputOptions.borderRadius === 'number') ? inputOptions.borderRadius : 0;
205 | _this.inputOptions.height = (typeof inputOptions.height === 'number') ? inputOptions.height : 14;
206 | _this.inputOptions.fillAlpha = (inputOptions.fillAlpha === undefined) ? 1 : inputOptions.fillAlpha;
207 | _this.inputOptions.selectionColor = inputOptions.selectionColor || 'rgba(179, 212, 253, 0.8)';
208 | _this.inputOptions.zoom = (!game.device.desktop) ? inputOptions.zoom || false : false;
209 | _this.box = new PhaserInput.InputBox(_this.game, inputOptions);
210 | _this.setTexture(_this.box.generateTexture());
211 | _this.textMask = new PhaserInput.TextMask(_this.game, inputOptions);
212 | _this.addChild(_this.textMask);
213 | _this.domElement = new PhaserInput.InputElement(_this.game, 'phaser-input-' + (Math.random() * 10000 | 0).toString(), _this.inputOptions.type, _this.value, _this.focusIn, _this.focusOut);
214 | _this.domElement.setMax(_this.inputOptions.max, _this.inputOptions.min);
215 | _this.selection = new PhaserInput.SelectionHighlight(_this.game, _this.inputOptions);
216 | _this.selection.mask = _this.textMask;
217 | _this.addChild(_this.selection);
218 | if (inputOptions.placeHolder && inputOptions.placeHolder.length > 0) {
219 | _this.placeHolder = new Phaser.Text(game, _this.inputOptions.padding, _this.inputOptions.padding, inputOptions.placeHolder, {
220 | font: inputOptions.font || '14px Arial',
221 | fontWeight: inputOptions.fontWeight || 'normal',
222 | fill: inputOptions.placeHolderColor || '#bfbebd'
223 | });
224 | _this.placeHolder.mask = _this.textMask;
225 | _this.addChild(_this.placeHolder);
226 | }
227 | _this.cursor = new Phaser.Text(game, _this.inputOptions.padding, _this.inputOptions.padding - 2, '|', {
228 | font: inputOptions.font || '14px Arial',
229 | fontWeight: inputOptions.fontWeight || 'normal',
230 | fill: inputOptions.cursorColor || '#000000'
231 | });
232 | _this.cursor.visible = false;
233 | _this.addChild(_this.cursor);
234 | _this.text = new Phaser.Text(game, _this.inputOptions.padding, _this.inputOptions.padding, '', {
235 | font: inputOptions.font || '14px Arial',
236 | fontWeight: inputOptions.fontWeight || 'normal',
237 | fill: inputOptions.fill || '#000000'
238 | });
239 | _this.text.mask = _this.textMask;
240 | _this.addChild(_this.text);
241 | _this.offscreenText = new Phaser.Text(game, _this.inputOptions.padding, _this.inputOptions.padding, '', {
242 | font: inputOptions.font || '14px Arial',
243 | fontWeight: inputOptions.fontWeight || 'normal',
244 | fill: inputOptions.fill || '#000000'
245 | });
246 | _this.updateTextAlignment();
247 | _this.inputEnabled = true;
248 | _this.input.useHandCursor = true;
249 | _this.game.input.onDown.add(_this.checkDown, _this);
250 | _this.focusOut.add(function () {
251 | if (PhaserInput.KeyboardOpen) {
252 | _this.endFocus();
253 | if (_this.inputOptions.zoom) {
254 | _this.zoomOut();
255 | }
256 | }
257 | });
258 | return _this;
259 | }
260 | Object.defineProperty(InputField.prototype, "width", {
261 | get: function () {
262 | return this.inputOptions.width;
263 | },
264 | set: function (width) {
265 | this.inputOptions.width = width;
266 | this.box.resize(width);
267 | this.textMask.resize(width);
268 | this.updateTextAlignment();
269 | },
270 | enumerable: true,
271 | configurable: true
272 | });
273 | InputField.prototype.updateTextAlignment = function () {
274 | switch (this.inputOptions.textAlign) {
275 | case 'left':
276 | this.text.anchor.set(0, 0);
277 | this.text.x = this.inputOptions.padding;
278 | if (null !== this.placeHolder) {
279 | this.placeHolder.anchor.set(0, 0);
280 | }
281 | this.cursor.x = this.inputOptions.padding + this.getCaretPosition();
282 | break;
283 | case 'center':
284 | this.text.anchor.set(0.5, 0);
285 | this.text.x = this.inputOptions.padding + this.inputOptions.width / 2;
286 | if (null !== this.placeHolder) {
287 | this.placeHolder.anchor.set(0.5, 0);
288 | this.placeHolder.x = this.inputOptions.padding + this.inputOptions.width / 2;
289 | }
290 | this.cursor.x = this.inputOptions.padding + this.inputOptions.width / 2 - this.text.width / 2 + this.getCaretPosition();
291 | break;
292 | case 'right':
293 | this.text.anchor.set(1, 0);
294 | this.text.x = this.inputOptions.padding + this.inputOptions.width;
295 | if (null !== this.placeHolder) {
296 | this.placeHolder.anchor.set(1, 0);
297 | this.placeHolder.x = this.inputOptions.padding + this.inputOptions.width;
298 | }
299 | this.cursor.x = this.inputOptions.padding + this.inputOptions.width;
300 | break;
301 | }
302 | };
303 | InputField.prototype.checkDown = function (e) {
304 | if (!this.value) {
305 | this.resetText();
306 | }
307 | if (this.input.checkPointerOver(e)) {
308 | if (this.focus) {
309 | this.setCaretOnclick(e);
310 | return;
311 | }
312 | if (this.inputOptions.zoom && !PhaserInput.Zoomed) {
313 | this.zoomIn();
314 | }
315 | this.startFocus();
316 | }
317 | else {
318 | if (this.focus === true) {
319 | this.endFocus();
320 | if (this.inputOptions.zoom) {
321 | this.zoomOut();
322 | }
323 | }
324 | }
325 | };
326 | InputField.prototype.update = function () {
327 | this.text.update();
328 | if (this.placeHolder) {
329 | this.placeHolder.update();
330 | }
331 | if (!this.focus) {
332 | return;
333 | }
334 | if (this.cnt !== 30) {
335 | return this.cnt++;
336 | }
337 | this.cursor.visible = this.blink;
338 | this.blink = !this.blink;
339 | this.cnt = 0;
340 | };
341 | InputField.prototype.endFocus = function () {
342 | var _this = this;
343 | if (!this.focus) {
344 | return;
345 | }
346 | this.domElement.removeEventListener();
347 | if (this.blockInput === true) {
348 | this.domElement.unblockKeyDownEvents();
349 | }
350 | this.focus = false;
351 | if (this.value.length === 0 && null !== this.placeHolder) {
352 | this.placeHolder.visible = true;
353 | }
354 | this.cursor.visible = false;
355 | if (this.game.device.desktop) {
356 | setTimeout(function () {
357 | _this.domElement.blur();
358 | }, 0);
359 | }
360 | else {
361 | this.domElement.blur();
362 | }
363 | if (!this.game.device.desktop) {
364 | PhaserInput.KeyboardOpen = false;
365 | PhaserInput.onKeyboardClose.dispatch();
366 | }
367 | };
368 | InputField.prototype.startFocus = function () {
369 | var _this = this;
370 | this.focus = true;
371 | if (null !== this.placeHolder) {
372 | this.placeHolder.visible = false;
373 | }
374 | if (this.game.device.desktop) {
375 | setTimeout(function () {
376 | _this.keyUpProcessor();
377 | }, 0);
378 | }
379 | else {
380 | this.keyUpProcessor();
381 | }
382 | if (!this.game.device.desktop) {
383 | PhaserInput.KeyboardOpen = true;
384 | PhaserInput.onKeyboardOpen.dispatch();
385 | }
386 | };
387 | InputField.prototype.keyUpProcessor = function () {
388 | this.domElement.addKeyUpListener(this.keyListener.bind(this));
389 | this.domElement.focus();
390 | if (this.blockInput === true) {
391 | this.domElement.blockKeyDownEvents();
392 | }
393 | };
394 | InputField.prototype.updateText = function () {
395 | var text = '';
396 | if (this.inputOptions.type === PhaserInput.InputType.password) {
397 | for (var i = 0; i < this.value.length; i++) {
398 | text += '*';
399 | }
400 | }
401 | else if (this.inputOptions.type === PhaserInput.InputType.number) {
402 | var val = parseInt(this.value);
403 | if (val < parseInt(this.inputOptions.min)) {
404 | text = this.value = this.domElement.value = this.inputOptions.min;
405 | }
406 | else if (val > parseInt(this.inputOptions.max)) {
407 | text = this.value = this.domElement.value = this.inputOptions.max;
408 | }
409 | else {
410 | text = this.value;
411 | }
412 | }
413 | else {
414 | text = this.value;
415 | }
416 | this.text.setText(text);
417 | if (this.text.width > this.inputOptions.width) {
418 | this.text.anchor.x = 1;
419 | this.text.x = this.inputOptions.padding + this.inputOptions.width;
420 | }
421 | else {
422 | switch (this.inputOptions.textAlign) {
423 | case 'left':
424 | this.text.anchor.set(0, 0);
425 | this.text.x = this.inputOptions.padding;
426 | break;
427 | case 'center':
428 | this.text.anchor.set(0.5, 0);
429 | this.text.x = this.inputOptions.padding + this.inputOptions.width / 2;
430 | break;
431 | case 'right':
432 | this.text.anchor.set(1, 0);
433 | this.text.x = this.inputOptions.padding + this.inputOptions.width;
434 | break;
435 | }
436 | }
437 | };
438 | InputField.prototype.updateCursor = function () {
439 | if (this.text.width > this.inputOptions.width || this.inputOptions.textAlign === 'right') {
440 | this.cursor.x = this.inputOptions.padding + this.inputOptions.width;
441 | }
442 | else {
443 | switch (this.inputOptions.textAlign) {
444 | case 'left':
445 | this.cursor.x = this.inputOptions.padding + this.getCaretPosition();
446 | break;
447 | case 'center':
448 | this.cursor.x = this.inputOptions.padding + this.inputOptions.width / 2 - this.text.width / 2 + this.getCaretPosition();
449 | break;
450 | }
451 | }
452 | };
453 | InputField.prototype.getCaretPosition = function () {
454 | var caretPosition = this.domElement.getCaretPosition();
455 | if (-1 === caretPosition) {
456 | return this.text.width;
457 | }
458 | var text = this.value;
459 | if (this.inputOptions.type === PhaserInput.InputType.password) {
460 | text = '';
461 | for (var i = 0; i < this.value.length; i++) {
462 | text += '*';
463 | }
464 | }
465 | this.offscreenText.setText(text.slice(0, caretPosition));
466 | return this.offscreenText.width;
467 | };
468 | InputField.prototype.setCaretOnclick = function (e) {
469 | var localX = (this.text.toLocal(new PIXI.Point(e.x, e.y), this.game.world)).x;
470 | if (this.inputOptions.textAlign && this.inputOptions.textAlign === 'center') {
471 | localX += this.text.width / 2;
472 | }
473 | var characterWidth = this.text.width / this.value.length;
474 | var index = 0;
475 | for (var i = 0; i < this.value.length; i++) {
476 | if (localX >= i * characterWidth && localX <= (i + 1) * characterWidth) {
477 | index = i;
478 | break;
479 | }
480 | }
481 | if (localX > (this.value.length - 1) * characterWidth) {
482 | index = this.value.length;
483 | }
484 | this.startFocus();
485 | this.domElement.setCaretPosition(index);
486 | this.updateCursor();
487 | };
488 | InputField.prototype.updateSelection = function () {
489 | if (this.domElement.hasSelection) {
490 | var text = this.value;
491 | if (this.inputOptions.type === PhaserInput.InputType.password) {
492 | text = '';
493 | for (var i = 0; i < this.value.length; i++) {
494 | text += '*';
495 | }
496 | }
497 | text = text.substring(this.domElement.caretStart, this.domElement.caretEnd);
498 | this.offscreenText.setText(text);
499 | this.selection.updateSelection(this.offscreenText.getBounds());
500 | switch (this.inputOptions.textAlign) {
501 | case 'left':
502 | this.selection.x = this.inputOptions.padding;
503 | break;
504 | case 'center':
505 | this.selection.x = this.inputOptions.padding + this.inputOptions.width / 2 - this.text.width / 2;
506 | break;
507 | }
508 | }
509 | else {
510 | this.selection.clear();
511 | }
512 | };
513 | InputField.prototype.zoomIn = function () {
514 | if (PhaserInput.Zoomed) {
515 | return;
516 | }
517 | var bounds = this.getBounds();
518 | if (window.innerHeight > window.innerWidth) {
519 | this.windowScale = this.game.width / (bounds.width * 1.5);
520 | }
521 | else {
522 | this.windowScale = (this.game.width / 2) / (bounds.width * 1.5);
523 | }
524 | var offsetX = ((this.game.width - bounds.width * 1.5) / 2) / this.windowScale;
525 | this.game.world.scale.set(this.game.world.scale.x * this.windowScale, this.game.world.scale.y * this.windowScale);
526 | this.game.world.pivot.set(bounds.x - offsetX, bounds.y - this.inputOptions.padding * 2);
527 | PhaserInput.Zoomed = true;
528 | };
529 | InputField.prototype.zoomOut = function () {
530 | if (!PhaserInput.Zoomed) {
531 | return;
532 | }
533 | this.game.world.scale.set(this.game.world.scale.x / this.windowScale, this.game.world.scale.y / this.windowScale);
534 | this.game.world.pivot.set(0, 0);
535 | PhaserInput.Zoomed = false;
536 | };
537 | InputField.prototype.keyListener = function (evt) {
538 | this.value = this.getFormattedText(this.domElement.value);
539 | if (evt.keyCode === 13) {
540 | if (this.focusOutOnEnter) {
541 | this.endFocus();
542 | }
543 | return;
544 | }
545 | this.updateText();
546 | this.updateCursor();
547 | this.updateSelection();
548 | evt.preventDefault();
549 | };
550 | InputField.prototype.destroy = function (destroyChildren) {
551 | this.game.input.onDown.remove(this.checkDown, this);
552 | this.focusIn.removeAll();
553 | this.focusOut.removeAll();
554 | this.domElement.destroy();
555 | _super.prototype.destroy.call(this, destroyChildren);
556 | };
557 | InputField.prototype.resetText = function () {
558 | this.setText();
559 | };
560 | InputField.prototype.setText = function (text) {
561 | if (text === void 0) { text = ''; }
562 | if (null !== this.placeHolder) {
563 | if (text.length > 0) {
564 | this.placeHolder.visible = false;
565 | }
566 | else {
567 | this.placeHolder.visible = true;
568 | }
569 | }
570 | this.value = this.getFormattedText(text);
571 | this.domElement.value = this.value;
572 | this.updateText();
573 | this.updateCursor();
574 | this.endFocus();
575 | };
576 | InputField.prototype.getFormattedText = function (text) {
577 | switch (this.inputOptions.forceCase) {
578 | default:
579 | case ForceCase.none:
580 | return text;
581 | case ForceCase.lower:
582 | return text.toLowerCase();
583 | case ForceCase.upper:
584 | return text.toUpperCase();
585 | }
586 | };
587 | return InputField;
588 | }(Phaser.Sprite));
589 | PhaserInput.InputField = InputField;
590 | })(PhaserInput || (PhaserInput = {}));
591 | var PhaserInput;
592 | (function (PhaserInput) {
593 | var InputBox = (function (_super) {
594 | __extends(InputBox, _super);
595 | function InputBox(game, inputOptions) {
596 | var _this = _super.call(this, game, 0, 0) || this;
597 | _this.bgColor = (inputOptions.backgroundColor) ? parseInt(inputOptions.backgroundColor.slice(1), 16) : 0xffffff;
598 | _this.borderRadius = inputOptions.borderRadius || 0;
599 | _this.borderWidth = inputOptions.borderWidth || 1;
600 | _this.borderColor = (inputOptions.borderColor) ? parseInt(inputOptions.borderColor.slice(1), 16) : 0x959595;
601 | _this.boxAlpha = inputOptions.fillAlpha;
602 | _this.padding = inputOptions.padding;
603 | var height = inputOptions.height;
604 | var width = inputOptions.width;
605 | var height;
606 | if (inputOptions.font) {
607 | height = Math.max(parseInt(inputOptions.font.substr(0, inputOptions.font.indexOf('px')), 10), height);
608 | }
609 | _this.boxHeight = _this.padding * 2 + height;
610 | var width = inputOptions.width;
611 | _this.boxWidth = _this.padding * 2 + width;
612 | _this.drawBox();
613 | return _this;
614 | }
615 | InputBox.prototype.resize = function (newWidth) {
616 | this.boxWidth = this.padding * 2 + newWidth;
617 | this.drawBox();
618 | };
619 | InputBox.prototype.drawBox = function () {
620 | this.clear()
621 | .beginFill(this.bgColor, this.boxAlpha)
622 | .lineStyle(this.borderWidth, this.borderColor, this.boxAlpha);
623 | if (this.borderRadius > 0) {
624 | this.drawRoundedRect(0, 0, this.boxWidth, this.boxHeight, this.borderRadius);
625 | }
626 | else {
627 | this.drawRect(0, 0, this.boxWidth, this.boxHeight);
628 | }
629 | };
630 | return InputBox;
631 | }(Phaser.Graphics));
632 | PhaserInput.InputBox = InputBox;
633 | })(PhaserInput || (PhaserInput = {}));
634 | var PhaserInput;
635 | (function (PhaserInput) {
636 | var SelectionHighlight = (function (_super) {
637 | __extends(SelectionHighlight, _super);
638 | function SelectionHighlight(game, inputOptions) {
639 | var _this = _super.call(this, game, inputOptions.padding, inputOptions.padding) || this;
640 | _this.inputOptions = inputOptions;
641 | return _this;
642 | }
643 | SelectionHighlight.prototype.updateSelection = function (rect) {
644 | var color = Phaser.Color.webToColor(this.inputOptions.selectionColor);
645 | this.clear();
646 | this.beginFill(SelectionHighlight.rgb2hex(color), color.a);
647 | this.drawRect(rect.x, rect.y, rect.width, rect.height - this.inputOptions.padding);
648 | };
649 | SelectionHighlight.rgb2hex = function (color) {
650 | return parseInt(("0" + color.r.toString(16)).slice(-2) +
651 | ("0" + color.g.toString(16)).slice(-2) +
652 | ("0" + color.b.toString(16)).slice(-2), 16);
653 | };
654 | return SelectionHighlight;
655 | }(Phaser.Graphics));
656 | PhaserInput.SelectionHighlight = SelectionHighlight;
657 | })(PhaserInput || (PhaserInput = {}));
658 | var PhaserInput;
659 | (function (PhaserInput) {
660 | var TextMask = (function (_super) {
661 | __extends(TextMask, _super);
662 | function TextMask(game, inputOptions) {
663 | var _this = _super.call(this, game, inputOptions.padding, inputOptions.padding) || this;
664 | var height = inputOptions.height;
665 | if (inputOptions.font) {
666 | height = Math.max(parseInt(inputOptions.font.substr(0, inputOptions.font.indexOf('px')), 10), height);
667 | }
668 | _this.maskWidth = inputOptions.width;
669 | _this.maskHeight = height * 1.3;
670 | _this.drawMask();
671 | return _this;
672 | }
673 | TextMask.prototype.resize = function (newWidth) {
674 | this.maskWidth = newWidth;
675 | this.drawMask();
676 | };
677 | TextMask.prototype.drawMask = function () {
678 | this.clear()
679 | .beginFill(0x000000)
680 | .drawRect(0, 0, this.maskWidth, this.maskHeight)
681 | .endFill();
682 | };
683 | return TextMask;
684 | }(Phaser.Graphics));
685 | PhaserInput.TextMask = TextMask;
686 | })(PhaserInput || (PhaserInput = {}));
687 | var PhaserInput;
688 | (function (PhaserInput) {
689 | PhaserInput.Zoomed = false;
690 | PhaserInput.KeyboardOpen = false;
691 | PhaserInput.onKeyboardOpen = new Phaser.Signal();
692 | PhaserInput.onKeyboardClose = new Phaser.Signal();
693 | var Plugin = (function (_super) {
694 | __extends(Plugin, _super);
695 | function Plugin(game, parent) {
696 | var _this = _super.call(this, game, parent) || this;
697 | _this.addInputFieldFactory();
698 | return _this;
699 | }
700 | Plugin.prototype.addInputFieldFactory = function () {
701 | Phaser.GameObjectFactory.prototype.inputField = function (x, y, inputOptions, group) {
702 | if (group === undefined) {
703 | group = this.world;
704 | }
705 | var nineSliceObject = new PhaserInput.InputField(this.game, x, y, inputOptions);
706 | return group.add(nineSliceObject);
707 | };
708 | Phaser.GameObjectCreator.prototype.inputField = function (x, y, inputOptions) {
709 | return new PhaserInput.InputField(this.game, x, y, inputOptions);
710 | };
711 | };
712 | return Plugin;
713 | }(Phaser.Plugin));
714 | Plugin.Zoomed = false;
715 | Plugin.KeyboardOpen = false;
716 | Plugin.onKeyboardOpen = new Phaser.Signal();
717 | Plugin.onKeyboardClose = new Phaser.Signal();
718 | PhaserInput.Plugin = Plugin;
719 | })(PhaserInput || (PhaserInput = {}));
720 | //# sourceMappingURL=phaser-input.js.map
--------------------------------------------------------------------------------
/static/js/phaser-input.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * phaser-input - version 2.0.5
3 | * Adds input boxes to Phaser like CanvasInput, but also works for WebGL and Mobile, made for Phaser only.
4 | *
5 | * OrangeGames
6 | * Build at 02-06-2017
7 | * Released under MIT License
8 | */
9 |
10 | var __extends=this&&this.__extends||function(){var a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,b){a.__proto__=b}||function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c])};return function(b,c){function d(){this.constructor=b}a(b,c),b.prototype=null===c?Object.create(c):(d.prototype=c.prototype,new d)}}(),PhaserInput;!function(a){var b;!function(a){a[a.text=0]="text",a[a.password=1]="password",a[a.number=2]="number"}(b=a.InputType||(a.InputType={}));var c=function(){function a(a,c,d,e,f,g){void 0===d&&(d=b.text),void 0===e&&(e="");var h=this;this.id=c,this.type=d,this.game=a,this.focusIn=f,this.focusOut=g;var i=this.game.canvas.getBoundingClientRect().top+document.body.scrollTop;this.element=document.createElement("input"),this.element.id=c,this.element.style.position="absolute",this.element.style.top=i+"px",this.element.style.left=(-40).toString()+"px",this.element.style.width=10..toString()+"px",this.element.style.height=10..toString()+"px",this.element.style.border="0px",this.element.value=this.value,this.element.type=b[d],this.element.addEventListener("focusin",function(){h.focusIn instanceof Phaser.Signal&&h.focusIn.dispatch()}),this.element.addEventListener("focusout",function(){h.focusOut instanceof Phaser.Signal&&h.focusOut.dispatch()}),document.body.appendChild(this.element)}return a.prototype.addKeyUpListener=function(a){this.keyUpCallback=a,document.addEventListener("keyup",this.keyUpCallback),this.element.addEventListener("input",this.keyUpCallback)},a.prototype.blockKeyDownEvents=function(){document.addEventListener("keydown",this.preventKeyPropagation)},a.prototype.preventKeyPropagation=function(a){a.stopPropagation?a.stopPropagation():event.cancelBubble=!0},a.prototype.unblockKeyDownEvents=function(){document.removeEventListener("keydown",this.preventKeyPropagation)},a.prototype.removeEventListener=function(){document.removeEventListener("keyup",this.keyUpCallback),this.element.removeEventListener("input",this.keyUpCallback)},a.prototype.destroy=function(){document.body.removeChild(this.element)},a.prototype.setMax=function(a,c){if(void 0!==a)if(this.type===b.text||this.type===b.password)this.element.maxLength=parseInt(a,10);else if(this.type===b.number){if(this.element.max=a,void 0===c)return;this.element.min=c}},Object.defineProperty(a.prototype,"value",{get:function(){return this.element.value},set:function(a){this.element.value=a},enumerable:!0,configurable:!0}),a.prototype.focus=function(){var a=this;if(this.element.focus(),!this.game.device.desktop&&this.game.device.chrome)var b=window.innerWidth,c=window.innerHeight,d=!1,e=setInterval(function(){(b>window.innerWidth||c>window.innerHeight)&&(d=!0),d&&b===window.innerWidth&&c===window.innerHeight&&(a.focusOut instanceof Phaser.Signal&&a.focusOut.dispatch(),clearInterval(e))},50)},a.prototype.blur=function(){this.element.blur()},Object.defineProperty(a.prototype,"hasSelection",{get:function(){return this.type===b.number?!1:this.element.selectionStart!==this.element.selectionEnd},enumerable:!0,configurable:!0}),Object.defineProperty(a.prototype,"caretStart",{get:function(){return this.element.selectionEnd},enumerable:!0,configurable:!0}),Object.defineProperty(a.prototype,"caretEnd",{get:function(){return this.element.selectionStart},enumerable:!0,configurable:!0}),a.prototype.getCaretPosition=function(){return this.type===b.number?-1:this.element.selectionStart},a.prototype.setCaretPosition=function(a){this.type!==b.number&&this.element.setSelectionRange(a,a)},a}();a.InputElement=c}(PhaserInput||(PhaserInput={}));var PhaserInput;!function(a){var b;!function(a){a[a.none=0]="none",a[a.lower=1]="lower",a[a.upper=2]="upper"}(b=a.ForceCase||(a.ForceCase={}));var c=function(c){function d(d,e,f,g){void 0===g&&(g={});var h=c.call(this,d,e,f)||this;return h.focusOutOnEnter=!0,h.placeHolder=null,h.box=null,h.focus=!1,h.value="",h.windowScale=1,h.blockInput=!0,h.focusIn=new Phaser.Signal,h.focusOut=new Phaser.Signal,h.blink=!0,h.cnt=0,h.inputOptions=g,h.inputOptions.width="number"==typeof g.width?g.width:150,h.inputOptions.padding="number"==typeof g.padding?g.padding:0,h.inputOptions.textAlign=g.textAlign||"left",h.inputOptions.type=g.type||a.InputType.text,h.inputOptions.forceCase=g.forceCase?g.forceCase:b.none,h.inputOptions.borderRadius="number"==typeof g.borderRadius?g.borderRadius:0,h.inputOptions.height="number"==typeof g.height?g.height:14,h.inputOptions.fillAlpha=void 0===g.fillAlpha?1:g.fillAlpha,h.inputOptions.selectionColor=g.selectionColor||"rgba(179, 212, 253, 0.8)",h.inputOptions.zoom=d.device.desktop?!1:g.zoom||!1,h.box=new a.InputBox(h.game,g),h.setTexture(h.box.generateTexture()),h.textMask=new a.TextMask(h.game,g),h.addChild(h.textMask),h.domElement=new a.InputElement(h.game,"phaser-input-"+(1e4*Math.random()|0).toString(),h.inputOptions.type,h.value,h.focusIn,h.focusOut),h.domElement.setMax(h.inputOptions.max,h.inputOptions.min),h.selection=new a.SelectionHighlight(h.game,h.inputOptions),h.selection.mask=h.textMask,h.addChild(h.selection),g.placeHolder&&g.placeHolder.length>0&&(h.placeHolder=new Phaser.Text(d,h.inputOptions.padding,h.inputOptions.padding,g.placeHolder,{font:g.font||"14px Arial",fontWeight:g.fontWeight||"normal",fill:g.placeHolderColor||"#bfbebd"}),h.placeHolder.mask=h.textMask,h.addChild(h.placeHolder)),h.cursor=new Phaser.Text(d,h.inputOptions.padding,h.inputOptions.padding-2,"|",{font:g.font||"14px Arial",fontWeight:g.fontWeight||"normal",fill:g.cursorColor||"#000000"}),h.cursor.visible=!1,h.addChild(h.cursor),h.text=new Phaser.Text(d,h.inputOptions.padding,h.inputOptions.padding,"",{font:g.font||"14px Arial",fontWeight:g.fontWeight||"normal",fill:g.fill||"#000000"}),h.text.mask=h.textMask,h.addChild(h.text),h.offscreenText=new Phaser.Text(d,h.inputOptions.padding,h.inputOptions.padding,"",{font:g.font||"14px Arial",fontWeight:g.fontWeight||"normal",fill:g.fill||"#000000"}),h.updateTextAlignment(),h.inputEnabled=!0,h.input.useHandCursor=!0,h.game.input.onDown.add(h.checkDown,h),h.focusOut.add(function(){a.KeyboardOpen&&(h.endFocus(),h.inputOptions.zoom&&h.zoomOut())}),h}return __extends(d,c),Object.defineProperty(d.prototype,"width",{get:function(){return this.inputOptions.width},set:function(a){this.inputOptions.width=a,this.box.resize(a),this.textMask.resize(a),this.updateTextAlignment()},enumerable:!0,configurable:!0}),d.prototype.updateTextAlignment=function(){switch(this.inputOptions.textAlign){case"left":this.text.anchor.set(0,0),this.text.x=this.inputOptions.padding,null!==this.placeHolder&&this.placeHolder.anchor.set(0,0),this.cursor.x=this.inputOptions.padding+this.getCaretPosition();break;case"center":this.text.anchor.set(.5,0),this.text.x=this.inputOptions.padding+this.inputOptions.width/2,null!==this.placeHolder&&(this.placeHolder.anchor.set(.5,0),this.placeHolder.x=this.inputOptions.padding+this.inputOptions.width/2),this.cursor.x=this.inputOptions.padding+this.inputOptions.width/2-this.text.width/2+this.getCaretPosition();break;case"right":this.text.anchor.set(1,0),this.text.x=this.inputOptions.padding+this.inputOptions.width,null!==this.placeHolder&&(this.placeHolder.anchor.set(1,0),this.placeHolder.x=this.inputOptions.padding+this.inputOptions.width),this.cursor.x=this.inputOptions.padding+this.inputOptions.width}},d.prototype.checkDown=function(b){if(this.value||this.resetText(),this.input.checkPointerOver(b)){if(this.focus)return void this.setCaretOnclick(b);this.inputOptions.zoom&&!a.Zoomed&&this.zoomIn(),this.startFocus()}else this.focus===!0&&(this.endFocus(),this.inputOptions.zoom&&this.zoomOut())},d.prototype.update=function(){if(this.text.update(),this.placeHolder&&this.placeHolder.update(),this.focus){if(30!==this.cnt)return this.cnt++;this.cursor.visible=this.blink,this.blink=!this.blink,this.cnt=0}},d.prototype.endFocus=function(){var b=this;this.focus&&(this.domElement.removeEventListener(),this.blockInput===!0&&this.domElement.unblockKeyDownEvents(),this.focus=!1,0===this.value.length&&null!==this.placeHolder&&(this.placeHolder.visible=!0),this.cursor.visible=!1,this.game.device.desktop?setTimeout(function(){b.domElement.blur()},0):this.domElement.blur(),this.game.device.desktop||(a.KeyboardOpen=!1,a.onKeyboardClose.dispatch()))},d.prototype.startFocus=function(){var b=this;this.focus=!0,null!==this.placeHolder&&(this.placeHolder.visible=!1),this.game.device.desktop?setTimeout(function(){b.keyUpProcessor()},0):this.keyUpProcessor(),this.game.device.desktop||(a.KeyboardOpen=!0,a.onKeyboardOpen.dispatch())},d.prototype.keyUpProcessor=function(){this.domElement.addKeyUpListener(this.keyListener.bind(this)),this.domElement.focus(),this.blockInput===!0&&this.domElement.blockKeyDownEvents()},d.prototype.updateText=function(){var b="";if(this.inputOptions.type===a.InputType.password)for(var c=0;cparseInt(this.inputOptions.max)?this.value=this.domElement.value=this.inputOptions.max:this.value}else b=this.value;if(this.text.setText(b),this.text.width>this.inputOptions.width)this.text.anchor.x=1,this.text.x=this.inputOptions.padding+this.inputOptions.width;else switch(this.inputOptions.textAlign){case"left":this.text.anchor.set(0,0),this.text.x=this.inputOptions.padding;break;case"center":this.text.anchor.set(.5,0),this.text.x=this.inputOptions.padding+this.inputOptions.width/2;break;case"right":this.text.anchor.set(1,0),this.text.x=this.inputOptions.padding+this.inputOptions.width}},d.prototype.updateCursor=function(){if(this.text.width>this.inputOptions.width||"right"===this.inputOptions.textAlign)this.cursor.x=this.inputOptions.padding+this.inputOptions.width;else switch(this.inputOptions.textAlign){case"left":this.cursor.x=this.inputOptions.padding+this.getCaretPosition();break;case"center":this.cursor.x=this.inputOptions.padding+this.inputOptions.width/2-this.text.width/2+this.getCaretPosition()}},d.prototype.getCaretPosition=function(){var b=this.domElement.getCaretPosition();if(-1===b)return this.text.width;var c=this.value;if(this.inputOptions.type===a.InputType.password){c="";for(var d=0;d=e*c&&(e+1)*c>=b){d=e;break}b>(this.value.length-1)*c&&(d=this.value.length),this.startFocus(),this.domElement.setCaretPosition(d),this.updateCursor()},d.prototype.updateSelection=function(){if(this.domElement.hasSelection){var b=this.value;if(this.inputOptions.type===a.InputType.password){b="";for(var c=0;cwindow.innerWidth?this.windowScale=this.game.width/(1.5*b.width):this.windowScale=this.game.width/2/(1.5*b.width);var c=(this.game.width-1.5*b.width)/2/this.windowScale;this.game.world.scale.set(this.game.world.scale.x*this.windowScale,this.game.world.scale.y*this.windowScale),this.game.world.pivot.set(b.x-c,b.y-2*this.inputOptions.padding),a.Zoomed=!0}},d.prototype.zoomOut=function(){a.Zoomed&&(this.game.world.scale.set(this.game.world.scale.x/this.windowScale,this.game.world.scale.y/this.windowScale),this.game.world.pivot.set(0,0),a.Zoomed=!1)},d.prototype.keyListener=function(a){return this.value=this.getFormattedText(this.domElement.value),13===a.keyCode?void(this.focusOutOnEnter&&this.endFocus()):(this.updateText(),this.updateCursor(),this.updateSelection(),void a.preventDefault())},d.prototype.destroy=function(a){this.game.input.onDown.remove(this.checkDown,this),this.focusIn.removeAll(),this.focusOut.removeAll(),this.domElement.destroy(),c.prototype.destroy.call(this,a)},d.prototype.resetText=function(){this.setText()},d.prototype.setText=function(a){void 0===a&&(a=""),null!==this.placeHolder&&(a.length>0?this.placeHolder.visible=!1:this.placeHolder.visible=!0),this.value=this.getFormattedText(a),this.domElement.value=this.value,this.updateText(),this.updateCursor(),this.endFocus()},d.prototype.getFormattedText=function(a){switch(this.inputOptions.forceCase){default:case b.none:return a;case b.lower:return a.toLowerCase();case b.upper:return a.toUpperCase()}},d}(Phaser.Sprite);a.InputField=c}(PhaserInput||(PhaserInput={}));var PhaserInput;!function(a){var b=function(a){function b(b,c){var d=a.call(this,b,0,0)||this;d.bgColor=c.backgroundColor?parseInt(c.backgroundColor.slice(1),16):16777215,d.borderRadius=c.borderRadius||0,d.borderWidth=c.borderWidth||1,d.borderColor=c.borderColor?parseInt(c.borderColor.slice(1),16):9803157,d.boxAlpha=c.fillAlpha,d.padding=c.padding;var e,e=c.height,f=c.width;c.font&&(e=Math.max(parseInt(c.font.substr(0,c.font.indexOf("px")),10),e)),d.boxHeight=2*d.padding+e;var f=c.width;return d.boxWidth=2*d.padding+f,d.drawBox(),d}return __extends(b,a),b.prototype.resize=function(a){this.boxWidth=2*this.padding+a,this.drawBox()},b.prototype.drawBox=function(){this.clear().beginFill(this.bgColor,this.boxAlpha).lineStyle(this.borderWidth,this.borderColor,this.boxAlpha),this.borderRadius>0?this.drawRoundedRect(0,0,this.boxWidth,this.boxHeight,this.borderRadius):this.drawRect(0,0,this.boxWidth,this.boxHeight)},b}(Phaser.Graphics);a.InputBox=b}(PhaserInput||(PhaserInput={}));var PhaserInput;!function(a){var b=function(a){function b(b,c){var d=a.call(this,b,c.padding,c.padding)||this;return d.inputOptions=c,d}return __extends(b,a),b.prototype.updateSelection=function(a){var c=Phaser.Color.webToColor(this.inputOptions.selectionColor);this.clear(),this.beginFill(b.rgb2hex(c),c.a),this.drawRect(a.x,a.y,a.width,a.height-this.inputOptions.padding)},b.rgb2hex=function(a){return parseInt(("0"+a.r.toString(16)).slice(-2)+("0"+a.g.toString(16)).slice(-2)+("0"+a.b.toString(16)).slice(-2),16)},b}(Phaser.Graphics);a.SelectionHighlight=b}(PhaserInput||(PhaserInput={}));var PhaserInput;!function(a){var b=function(a){function b(b,c){var d=a.call(this,b,c.padding,c.padding)||this,e=c.height;return c.font&&(e=Math.max(parseInt(c.font.substr(0,c.font.indexOf("px")),10),e)),d.maskWidth=c.width,d.maskHeight=1.3*e,d.drawMask(),d}return __extends(b,a),b.prototype.resize=function(a){this.maskWidth=a,this.drawMask()},b.prototype.drawMask=function(){this.clear().beginFill(0).drawRect(0,0,this.maskWidth,this.maskHeight).endFill()},b}(Phaser.Graphics);a.TextMask=b}(PhaserInput||(PhaserInput={}));var PhaserInput;!function(a){a.Zoomed=!1,a.KeyboardOpen=!1,a.onKeyboardOpen=new Phaser.Signal,a.onKeyboardClose=new Phaser.Signal;var b=function(b){function c(a,c){var d=b.call(this,a,c)||this;return d.addInputFieldFactory(),d}return __extends(c,b),c.prototype.addInputFieldFactory=function(){Phaser.GameObjectFactory.prototype.inputField=function(b,c,d,e){void 0===e&&(e=this.world);var f=new a.InputField(this.game,b,c,d);return e.add(f)},Phaser.GameObjectCreator.prototype.inputField=function(b,c,d){return new a.InputField(this.game,b,c,d)}},c}(Phaser.Plugin);b.Zoomed=!1,b.KeyboardOpen=!1,b.onKeyboardOpen=new Phaser.Signal,b.onKeyboardClose=new Phaser.Signal,a.Plugin=b}(PhaserInput||(PhaserInput={}));
--------------------------------------------------------------------------------
/static/js/player.js:
--------------------------------------------------------------------------------
1 | PG.createPlay = function (seat, game) {
2 | var player = seat == 0 ? new PG.Player(seat, game) : new PG.NetPlayer(seat, game);
3 | var xy = [
4 | PG.PW / 2, game.world.height - PG.PH - 10,
5 | game.world.width - PG.PW / 2, 94,
6 | PG.PW / 2, 94
7 | ];
8 | player.initUI(xy[seat * 2], xy[seat * 2 + 1]);
9 | if (seat == 0) {
10 | player.initShotLayer();
11 | } else if (seat == 1) {
12 | player.uiHead.scale.set(-1, 1);
13 | }
14 | return player;
15 | };
16 |
17 | PG.Player = function (seat, game) {
18 | this.uid = seat;
19 | this.seat = seat;
20 | this.game = game;
21 |
22 | this.pokerInHand = [];
23 | this._pokerPic = {};
24 | this.isLandlord = false;
25 |
26 | this.hintPoker = [];
27 | this.isDraging = false;
28 | };
29 |
30 | PG.Player.prototype.initUI = function (sx, sy) {
31 | this.uiHead = this.game.add.sprite(sx, sy, 'btn', 'icon_default.png');
32 | this.uiHead.anchor.set(0.5, 1);
33 | };
34 |
35 | PG.Player.prototype.updateInfo = function (uid, name) {
36 | this.uid = uid;
37 | if (uid == -1) {
38 | this.uiHead.frameName = 'icon_default.png';
39 | } else {
40 | this.uiHead.frameName = 'icon_farmer.png';
41 | }
42 | };
43 |
44 | PG.Player.prototype.cleanPokers = function () {
45 |
46 | var length = this.pokerInHand.length;
47 | for (var i = 0; i < length; i++) {
48 | var pid = this.pokerInHand[i];
49 | var p = this.findAPoker(pid);
50 | p.kill();
51 | }
52 | this.pokerInHand = [];
53 | }
54 |
55 | PG.Player.prototype.initShotLayer = function () {
56 | this.shotLayer = this.game.add.group();
57 | var group = this.shotLayer;
58 |
59 | var sy = this.game.world.height * 0.6;
60 | var pass = this.game.make.button(0, sy, "btn", this.onPass, this, 'pass.png', 'pass.png', 'pass.png');
61 | pass.anchor.set(0.5, 0);
62 | group.add(pass);
63 | var hint = this.game.make.button(0, sy, "btn", this.onHint, this, 'hint.png', 'hint.png', 'hint.png');
64 | hint.anchor.set(0.5, 0);
65 | group.add(hint);
66 | var shot = this.game.make.button(0, sy, "btn", this.onShot, this, 'shot.png', 'shot.png', 'shot.png');
67 | shot.anchor.set(0.5, 0);
68 | group.add(shot);
69 |
70 | group.forEach(function (child) {
71 | child.kill();
72 | });
73 | };
74 |
75 | PG.Player.prototype.setLandlord = function () {
76 | this.isLandlord = true;
77 | this.uiHead.frameName = 'icon_landlord.png';
78 | };
79 |
80 | PG.Player.prototype.say = function (str) {
81 |
82 | var style = {font: "22px Arial", fill: "#ffffff", align: "center"};
83 | var sx = this.uiHead.x + this.uiHead.width / 2 + 10;
84 | var sy = this.uiHead.y - this.uiHead.height * 0.5;
85 | var text = this.game.add.text(sx, sy, str, style);
86 | if (this.uiHead.scale.x == -1) {
87 | text.x = text.x - text.width - 10;
88 | }
89 | this.game.time.events.add(2000, text.destroy, text);
90 | };
91 |
92 | PG.Player.prototype.onInputDown = function (poker, pointer) {
93 | this.isDraging = true;
94 | this.onSelectPoker(poker, pointer);
95 | };
96 |
97 | PG.Player.prototype.onInputUp = function (poker, pointer) {
98 | this.isDraging = false;
99 | //this.onSelectPoker(poker, pointer);
100 | };
101 |
102 | PG.Player.prototype.onInputOver = function (poker, pointer) {
103 | if (this.isDraging) {
104 | this.onSelectPoker(poker, pointer);
105 | }
106 | };
107 |
108 | PG.Player.prototype.onSelectPoker = function (poker, pointer) {
109 | var index = this.hintPoker.indexOf(poker.id);
110 | if (index == -1) {
111 | poker.y = this.game.world.height - PG.PH * 0.8;
112 | this.hintPoker.push(poker.id);
113 | } else {
114 | poker.y = this.game.world.height - PG.PH * 0.5;
115 | this.hintPoker.splice(index, 1);
116 | }
117 | };
118 |
119 | PG.Player.prototype.onPass = function (btn) {
120 | this.game.finishPlay([]);
121 | this.pokerUnSelected(this.hintPoker);
122 | this.hintPoker = [];
123 | btn.parent.forEach(function (child) {
124 | child.kill();
125 | });
126 | };
127 |
128 | PG.Player.prototype.onHint = function (btn) {
129 | if (this.hintPoker.length == 0) {
130 | this.hintPoker = this.lastTurnPoker;
131 | } else {
132 | this.pokerUnSelected(this.hintPoker);
133 | if (this.lastTurnPoker.length > 0 && !PG.Poker.canCompare(this.hintPoker, this.lastTurnPoker)) {
134 | this.hintPoker = [];
135 | }
136 | }
137 | var bigger = this.hint(this.hintPoker);
138 | if (bigger.length == 0) {
139 | if (this.hintPoker == this.lastTurnPoker) {
140 | this.say("没有能大过的牌");
141 | } else {
142 | this.pokerUnSelected(this.hintPoker);
143 | }
144 | } else {
145 | this.pokerSelected(bigger);
146 | }
147 | this.hintPoker = bigger;
148 | };
149 |
150 | PG.Player.prototype.onShot = function (btn) {
151 | if (this.hintPoker.length == 0) {
152 | return;
153 | }
154 | var code = this.canPlay(this.game.isLastShotPlayer() ? [] : this.game.tablePoker, this.hintPoker);
155 | if (code) {
156 | this.say(code);
157 | return;
158 | }
159 | this.game.finishPlay(this.hintPoker);
160 | this.hintPoker = [];
161 | btn.parent.forEach(function (child) {
162 | child.kill();
163 | });
164 | };
165 |
166 |
167 | PG.Player.prototype.hint = function (lastTurnPoker) {
168 | var cards;
169 | var handCards = PG.Poker.toCards(this.pokerInHand);
170 | if (lastTurnPoker.length === 0) {
171 | cards = PG.Rule.bestShot(handCards);
172 | } else {
173 | cards = PG.Rule.cardsAbove(handCards, PG.Poker.toCards(lastTurnPoker));
174 | }
175 |
176 | return PG.Poker.toPokers(this.pokerInHand, cards);
177 | };
178 |
179 | PG.Player.prototype.canPlay = function (lastTurnPoker, shotPoker) {
180 | var cardsA = PG.Poker.toCards(shotPoker);
181 | var valueA = PG.Rule.cardsValue(cardsA);
182 | if (!valueA[0]){
183 | return '出牌不合法';
184 | }
185 | var cardsB = PG.Poker.toCards(lastTurnPoker);
186 | if (cardsB.length == 0) {
187 | return '';
188 | }
189 | var valueB = PG.Rule.cardsValue(cardsB);
190 | if (valueA[0] != valueB[0] && valueA[1] < 1000) {
191 | return '出牌类型跟上家不一致';
192 | }
193 |
194 | if (valueA[1] > valueB[1]) {
195 | return '';
196 | }
197 | return '出牌需要大于上家';
198 | };
199 |
200 | PG.Player.prototype.playPoker = function (lastTurnPoker) {
201 | this.lastTurnPoker = lastTurnPoker;
202 |
203 | var group = this.shotLayer;
204 | var step = this.game.world.width / 6;
205 | var sx = this.game.world.width / 2 - 0.5 * step;
206 | if (!this.game.isLastShotPlayer()) {
207 | sx -= 0.5 * step;
208 | var pass = group.getAt(0);
209 | pass.centerX = sx;
210 | sx += step;
211 | pass.revive();
212 | }
213 | var hint = group.getAt(1);
214 | hint.centerX = sx;
215 | hint.revive();
216 | var shot = group.getAt(2);
217 | shot.centerX = sx + step;
218 | shot.revive();
219 |
220 | this.enableInput();
221 | };
222 |
223 | PG.Player.prototype.sortPoker = function () {
224 | this.pokerInHand.sort(PG.Poker.comparePoker);
225 | };
226 |
227 | PG.Player.prototype.dealPoker = function () {
228 | this.sortPoker();
229 | var length = this.pokerInHand.length;
230 | for (var i = 0; i < length; i++) {
231 | var pid = this.pokerInHand[i];
232 | var p = new PG.Poker(this.game, pid, pid);
233 | this.game.world.add(p);
234 | this.pushAPoker(p);
235 | this.dealPokerAnim(p, i);
236 | }
237 | };
238 |
239 | PG.Player.prototype.dealPokerAnim = function (p, i) {
240 | //to(properties, duration, ease, autoStart, delay, repeat, yoyo)
241 | this.game.add.tween(p).to({
242 | x: this.game.world.width / 2 + PG.PW * 0.44 * (i - 8.5),
243 | y: this.game.world.height - PG.PH / 2
244 | }, 500, Phaser.Easing.Default, true, 50 * i);
245 | };
246 |
247 | PG.Player.prototype.arrangePoker = function () {
248 | var count = this.pokerInHand.length;
249 | var gap = Math.min(this.game.world.width / count, PG.PW * 0.44);
250 | for (var i = 0; i < count; i++) {
251 | var pid = this.pokerInHand[i];
252 | var p = this.findAPoker(pid);
253 | p.bringToTop();
254 | this.game.add.tween(p).to({x: this.game.world.width / 2 + (i - count / 2) * gap}, 600, Phaser.Easing.Default, true);
255 | }
256 | };
257 |
258 | PG.Player.prototype.pushAPoker = function (poker) {
259 | this._pokerPic[poker.id] = poker;
260 |
261 | poker.events.onInputDown.add(this.onInputDown, this);
262 | poker.events.onInputUp.add(this.onInputUp, this);
263 | poker.events.onInputOver.add(this.onInputOver, this);
264 | };
265 |
266 | PG.Player.prototype.removeAPoker = function (pid) {
267 | var length = this.pokerInHand.length;
268 | for (var i = 0; i < length; i++) {
269 | if (this.pokerInHand[i] === pid) {
270 | this.pokerInHand.splice(i, 1);
271 | delete this._pokerPic[pid];
272 | return;
273 | }
274 | }
275 | console.log('Error: REMOVE POKER ', pid);
276 | };
277 |
278 | PG.Player.prototype.removeAllPoker = function () {
279 | var length = this.pokerInHand.length;
280 | for (var i = 0; i < length; i++) {
281 | this.pokerInHand.splice(i, 1);
282 | delete this._pokerPic[pid];
283 | }
284 | console.log('Error: REMOVE POKER ', pid);
285 | };
286 |
287 |
288 | PG.Player.prototype.findAPoker = function (pid) {
289 | var poker = this._pokerPic[pid];
290 | if (poker === undefined) {
291 | console.log('Error: FIND POKER ', pid);
292 | }
293 | return poker;
294 | };
295 |
296 | PG.Player.prototype.enableInput = function () {
297 | var length = this.pokerInHand.length;
298 | for (var i = 0; i < length; i++) {
299 | var p = this.findAPoker(this.pokerInHand[i]);
300 | p.inputEnabled = true;
301 | }
302 | };
303 |
304 | PG.Player.prototype.pokerSelected = function (pokers) {
305 | for (var i = 0; i < pokers.length; i++) {
306 | var p = this.findAPoker(pokers[i]);
307 | p.y = this.game.world.height - PG.PH * 0.8;
308 | }
309 | };
310 |
311 | PG.Player.prototype.pokerUnSelected = function (pokers) {
312 | for (var i = 0; i < pokers.length; i++) {
313 | var p = this.findAPoker(pokers[i]);
314 | p.y = this.game.world.height - PG.PH / 2;
315 | }
316 | };
317 |
318 |
--------------------------------------------------------------------------------
/static/js/player_net.js:
--------------------------------------------------------------------------------
1 | PG.NetPlayer = function (seat, game) {
2 | PG.Player.call(this, seat, game);
3 | this._pokerPic = [];
4 | };
5 |
6 | PG.NetPlayer.prototype = Object.create(PG.Player.prototype);
7 | PG.NetPlayer.prototype.constructor = PG.NetPlayer;
8 |
9 | PG.NetPlayer.prototype.pushAPoker = function (poker) {
10 | this._pokerPic.push(poker);
11 | this.updateLeftPoker();
12 | };
13 |
14 | PG.NetPlayer.prototype.removeAPoker = function (pid) {
15 | for (var i = this.pokerInHand.length - 1; i >= 0; i--) {
16 | if (this.pokerInHand[i] === pid) {
17 | this.pokerInHand.splice(i, 1);
18 | break
19 | }
20 | }
21 | if (i == -1) {
22 | this.pokerInHand.pop();
23 | }
24 | for (var i = this._pokerPic.length - 1; i >= 0; i--) {
25 | if (this._pokerPic[i].id === pid) {
26 | this._pokerPic.splice(i, 1);
27 | break
28 | }
29 | }
30 | if (i == -1) {
31 | this._pokerPic.pop();
32 | }
33 | this.updateLeftPoker();
34 | };
35 |
36 | PG.NetPlayer.prototype.arrangePoker = function () {
37 | if (this.pokerInHand.length > 0 && this.pokerInHand[0] < 54) {
38 | // PG.Player.prototype.arrangePoker.call(this);
39 | this.reDealPoker();
40 | }
41 | };
42 |
43 | PG.NetPlayer.prototype.replacePoker = function (pokers, start) {
44 | if (this.pokerInHand.length !== pokers.length - start) {
45 | console.log("ERROR ReplacePoker:", this.pokerInHand, pokers);
46 | }
47 | if (this._pokerPic.length !== pokers.length - start) {
48 | console.log("ERROR ReplacePoker:", this._pokerPic, pokers);
49 | }
50 | var length = this.pokerInHand.length;
51 | for (var i = 0; i < length; i++) {
52 | this.pokerInHand[i] = pokers[start + i];
53 | this._pokerPic[i].id = pokers[start + i];
54 | this._pokerPic[i].frame = pokers[start + i];
55 | }
56 | };
57 |
58 | PG.NetPlayer.prototype.findAPoker = function (pid) {
59 | for (var i = this._pokerPic.length - 1; i >= 0; i--) {
60 | if (this._pokerPic[i].id == pid) {
61 | return this._pokerPic[i];
62 | }
63 | }
64 | return this._pokerPic[this._pokerPic.length - 1];
65 | };
66 |
67 | PG.NetPlayer.prototype.reDealPoker = function () {
68 | this.sortPoker();
69 | var length = this.pokerInHand.length;
70 | for (var i = 0; i < length; i++) {
71 | var pid = this.pokerInHand[i];
72 | var p = this.findAPoker(pid);
73 | p.bringToTop();
74 | this.dealPokerAnim(p, this.seat == 1 ? length-1-i : i);
75 | }
76 | };
77 |
78 | PG.NetPlayer.prototype.cleanPokers = function () {
79 |
80 | var length = this.pokerInHand.length;
81 | for (var i = 0; i < length; i++) {
82 | var pid = this.pokerInHand[i];
83 | var p = this.findAPoker(pid);
84 | p.kill();
85 | }
86 | this.pokerInHand = [];
87 | }
88 |
89 | PG.NetPlayer.prototype.dealPokerAnim = function (p, i) {
90 | var width = this.game.world.width;
91 | if (p.id > 53) {
92 | this.game.add.tween(p).to({
93 | x: this.seat == 1 ? width - PG.PW/2 : PG.PW/2,
94 | y: this.seat == 1 ? this.uiHead.y + PG.PH/2 + 10 : this.uiHead.y + PG.PH/2 + 10
95 | }, 500, Phaser.Easing.Default, true, 25 + 50 * i);
96 | } else {
97 | this.game.add.tween(p).to({
98 | x: this.seat == 1 ? (width - PG.PW/2) - (i * PG.PW * 0.44) : PG.PW/2 + i * PG.PW * 0.44,
99 | y: this.seat == 1 ? this.uiHead.y + PG.PH/2 + 10 : this.uiHead.y + PG.PH * 1.5 + 20
100 | }, 500, Phaser.Easing.Default, true, 50 * i);
101 | }
102 | };
103 |
104 | PG.NetPlayer.prototype.initUI = function (sx, sy) {
105 | PG.Player.prototype.initUI.call(this, sx, sy);
106 | var style = {font: "22px Arial", fill: "#ffffff", align: "center"};
107 | this.uiLeftPoker = this.game.add.text(sx, sy + PG.PH + 10, '17', style);
108 | this.uiLeftPoker.anchor.set(0.5, 0);
109 | this.uiLeftPoker.kill();
110 |
111 | var style = {font: "20px Arial", fill: "#c8c8c8", align: "center"};
112 | if (this.seat == 1) {
113 | this.uiName = this.game.add.text(sx - 40, sy - 80, '等待玩家加入', style);
114 | this.uiName.anchor.set(1, 0);
115 | } else {
116 | this.uiName = this.game.add.text(sx + 40, sy - 80, '等待玩家加入', style);
117 | this.uiName.anchor.set(0, 0);
118 | }
119 | };
120 |
121 |
122 | PG.NetPlayer.prototype.updateInfo = function (uid, name) {
123 | PG.Player.prototype.updateInfo.call(this, uid, name);
124 | if (uid == -1) {
125 | this.uiName.text = '等待玩家加入';
126 | } else {
127 | this.uiName.text = name;
128 | }
129 | };
130 |
131 | PG.NetPlayer.prototype.updateLeftPoker = function () {
132 | var len = this.pokerInHand.length;
133 | if (len > 0) {
134 | this.uiLeftPoker.text = "" + this.pokerInHand.length;
135 | this.uiLeftPoker.revive();
136 | } else {
137 | this.uiLeftPoker.kill();
138 | }
139 | };
140 |
--------------------------------------------------------------------------------
/static/js/rule.js:
--------------------------------------------------------------------------------
1 | PG.Poker = function (game, id, frame) {
2 |
3 | Phaser.Sprite.call(this, game, game.world.width / 2, game.world.height * 0.4, 'poker', frame);
4 |
5 | this.anchor.set(0.5);
6 | this.id = id;
7 |
8 | return this;
9 | };
10 |
11 | PG.Poker.prototype = Object.create(Phaser.Sprite.prototype);
12 | PG.Poker.prototype.constructor = PG.Poker;
13 |
14 | PG.Poker.comparePoker = function (a, b) {
15 |
16 | if (a instanceof Array) {
17 | a = a[0];
18 | b = b[0];
19 | }
20 |
21 |
22 | if (a >= 52 || b >= 52) {
23 | return -(a - b);
24 | }
25 | a = a % 13;
26 | b = b % 13;
27 | if (a == 1 || a == 0) {
28 | a += 13;
29 | }
30 | if (b == 1 || b == 0) {
31 | b += 13;
32 | }
33 | return -(a - b);
34 | };
35 |
36 | PG.Poker.toCards = function (pokers) {
37 | var cards = [];
38 | for (var i = 0; i < pokers.length; i++) {
39 | var pid = pokers[i];
40 | if (pid instanceof Array) {
41 | pid = pid[0];
42 | }
43 | if (pid == 52) {
44 | cards.push('W');
45 | } else if (pid == 53) {
46 | cards.push('w');
47 | } else {
48 | cards.push("A234567890JQK"[pid % 13]);
49 | }
50 | }
51 | return cards;
52 |
53 | };
54 |
55 | PG.Poker.canCompare = function (pokersA, pokersB) {
56 | var cardsA = this.toCards(pokersA);
57 | var cardsB = this.toCards(pokersB);
58 | return PG.Rule.cardsValue(cardsA)[0] == PG.Rule.cardsValue(cardsB)[0];
59 | };
60 |
61 | PG.Poker.toPokers = function (pokerInHands, cards) {
62 | var pokers = [];
63 | for (var i = 0; i < cards.length; i++) {
64 | var candidates = this.toPoker(cards[i]);
65 | for (var j = 0; j < candidates.length; j++) {
66 | if (pokerInHands.indexOf(candidates[j]) != -1 && pokers.indexOf(candidates[j]) == -1) {
67 | pokers.push(candidates[j]);
68 | break
69 | }
70 | }
71 | }
72 | return pokers;
73 | };
74 |
75 | PG.Poker.toPoker = function (card) {
76 |
77 | var cards = "A234567890JQK";
78 | for (var i = 0; i < 13; i++) {
79 | if (card == cards[i]) {
80 | return [i, i + 13, i + 13 * 2, i + 13 * 3];
81 | }
82 | }
83 | if (card == 'W') {
84 | return [52];
85 | } else if (card == 'w') {
86 | return [53];
87 | }
88 | return [54];
89 |
90 | };
91 |
92 | PG.Rule = {};
93 |
94 | PG.Rule.cardsAbove = function (handCards, turnCards) {
95 |
96 | var turnValue = this.cardsValue(turnCards);
97 | if (turnValue[0] == '') {
98 | return '';
99 | }
100 | handCards.sort(this.sorter);
101 | var oneRule = PG.RuleList[turnValue[0]];
102 | for (var i = turnValue[1] + 1; i < oneRule.length; i++) {
103 | if (this.containsAll(handCards, oneRule[i])) {
104 | return oneRule[i];
105 | }
106 | }
107 |
108 | if (turnValue[1] < 1000) {
109 | oneRule = PG.RuleList['bomb'];
110 | for (var i = 0; i < oneRule.length; i++) {
111 | if (this.containsAll(handCards, oneRule[i])) {
112 | return oneRule[i];
113 | }
114 | }
115 | if (this.containsAll(handCards, 'wW')) {
116 | return 'wW';
117 | }
118 | }
119 |
120 | return '';
121 | };
122 |
123 | PG.Rule.bestShot = function (handCards) {
124 |
125 | handCards.sort(this.sorter);
126 | var shot = '';
127 | var len = this._CardsType.length;
128 | for (var i = 2; i < len; i++) {
129 | var oneRule = PG.RuleList[this._CardsType[i]];
130 | for (var j = 0; j < oneRule.length; j++) {
131 | if (oneRule[j].length > shot.length && this.containsAll(handCards, oneRule[j])) {
132 | shot = oneRule[j];
133 | }
134 | }
135 | }
136 |
137 | if (shot == '') {
138 | oneRule = PG.RuleList['bomb'];
139 | for (var i = 0; i < oneRule.length; i++) {
140 | if (this.containsAll(handCards, oneRule[i])) {
141 | return oneRule[i];
142 | }
143 | }
144 | if (this.containsAll(handCards, 'wW'))
145 | return 'wW';
146 | }
147 |
148 | return shot;
149 | };
150 |
151 | PG.Rule._CardsType = [
152 | 'rocket', 'bomb',
153 | 'single', 'pair', 'trio', 'trio_pair', 'trio_single',
154 | 'seq_single5', 'seq_single6', 'seq_single7', 'seq_single8', 'seq_single9', 'seq_single10', 'seq_single11', 'seq_single12',
155 | 'seq_pair3', 'seq_pair4', 'seq_pair5', 'seq_pair6', 'seq_pair7', 'seq_pair8', 'seq_pair9', 'seq_pair10',
156 | 'seq_trio2', 'seq_trio3', 'seq_trio4', 'seq_trio5', 'seq_trio6',
157 | 'seq_trio_pair2', 'seq_trio_pair3', 'seq_trio_pair4', 'seq_trio_pair5',
158 | 'seq_trio_single2', 'seq_trio_single3', 'seq_trio_single4', 'seq_trio_single5',
159 | 'bomb_pair', 'bomb_single'];
160 |
161 | PG.Rule.sorter = function (a, b) {
162 | var card_str = '34567890JQKA2wW';
163 | return card_str.indexOf(a) - card_str.indexOf(b);
164 | };
165 |
166 | PG.Rule.index_of = function (array, ele) {
167 | if (array[0].length != ele.length) {
168 | return -1;
169 | }
170 | for (var i = 0, l = array.length; i < l; i++) {
171 | if (array[i] == ele) {
172 | return i;
173 | }
174 | }
175 | return -1;
176 | };
177 |
178 | PG.Rule.containsAll = function (parent, child) {
179 | var index = 0;
180 | for (var i = 0, l = child.length; i < l; i++) {
181 | index = parent.indexOf(child[i], index);
182 | if (index == -1) {
183 | return false;
184 | }
185 | index += 1;
186 | }
187 | return true;
188 | };
189 |
190 | PG.Rule.cardsValue = function (cards) {
191 |
192 | if (typeof(cards) != 'string') {
193 | cards.sort(this.sorter);
194 | cards = cards.join('');
195 | }
196 |
197 | if (cards == 'wW')
198 | return ['rocket', 2000];
199 |
200 | var index = this.index_of(PG.RuleList['bomb'], cards);
201 | if (index >= 0)
202 | return ['bomb', 1000 + index];
203 |
204 | var length = this._CardsType.length;
205 | for (var i = 2; i < length; i++) {
206 | var typeName = this._CardsType[i];
207 | var index = this.index_of(PG.RuleList[typeName], cards);
208 | if (index >= 0)
209 | return [typeName, index];
210 | }
211 | console.log('Error: UNKNOWN TYPE ', cards);
212 | return ['', 0];
213 | };
214 |
215 | PG.Rule.compare = function (cardsA, cardsB) {
216 |
217 | if (cardsA.length == 0 && cardsB.length == 0) {
218 | return 0;
219 | }
220 | if (cardsA.length == 0) {
221 | return -1;
222 | }
223 | if (cardsB.length == 0) {
224 | return 1;
225 | }
226 |
227 | var valueA = this.cardsValue(cardsA);
228 | var valueB = this.cardsValue(cardsB);
229 |
230 | if ((valueA[1] < 1000 && valueB[1] < 1000) && (valueA[0] != valueB[0])) {
231 | console.log('Error: Compare ', cardsA, cardsB);
232 | }
233 |
234 | return valueA[1] - valueB[1];
235 | };
236 |
237 | PG.Rule.shufflePoker = function () {
238 | var pokers = [];
239 | for (var i = 0; i < 54; i++) {
240 | pokers.push(i);
241 | }
242 |
243 | var currentIndex = pokers.length, temporaryValue, randomIndex;
244 | while (0 != currentIndex) {
245 | randomIndex = Math.floor(Math.random() * currentIndex);
246 | currentIndex -= 1;
247 |
248 | temporaryValue = pokers[currentIndex];
249 | pokers[currentIndex] = pokers[randomIndex];
250 | pokers[randomIndex] = temporaryValue;
251 | }
252 | return pokers;
253 | }
254 | ;
255 |
256 |
--------------------------------------------------------------------------------
/static/s/bg3.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwg255/landlord/a0d77dedbc3efb73b044678ba6da1c7a65f939fe/static/s/bg3.ogg
--------------------------------------------------------------------------------
/templates/poker.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 斗地主
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
35 |
36 |
37 |
--------------------------------------------------------------------------------