Compare commits
74 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
52be2a6a79 | ||
|
fa491b357d | ||
|
695eacaabc | ||
|
0324b584a5 | ||
|
70e552f5f0 | ||
|
c58e690a47 | ||
|
1593965950 | ||
|
2a1105efa4 | ||
|
04de4666d5 | ||
|
2290c57c1c | ||
|
24d5ad9dc8 | ||
|
fdc296531a | ||
|
becc56f672 | ||
|
58c18ab7ae | ||
|
024d527f3d | ||
|
9b29edaaa1 | ||
|
360f2fc22b | ||
|
2dbc529978 | ||
|
d21f59cafa | ||
|
335fef66ef | ||
|
52480ab29f | ||
|
971f6461ab | ||
|
061aa449c9 | ||
|
78dd9ecd85 | ||
|
d4226137b6 | ||
|
e432026fec | ||
|
3e7718ed04 | ||
|
b78dd54431 | ||
|
22fb72afbc | ||
|
7b9172c27b | ||
|
7e12853a73 | ||
|
95dcc2ef17 | ||
|
63164569b1 | ||
|
8a4efd023b | ||
|
b031fc1c61 | ||
|
4369729d9c | ||
|
2d080ad134 | ||
|
bd9beec5e5 | ||
|
89a54211e1 | ||
|
a4ebde3e07 | ||
|
41967b11f7 | ||
|
98daeff408 | ||
|
320e98361e | ||
|
15a062af10 | ||
|
3f4e49656a | ||
|
f97ce22cef | ||
|
901b189c5a | ||
|
e5ed8124e8 | ||
|
885443c2b1 | ||
|
aa795fcee5 | ||
|
cb3c19a339 | ||
|
d3d3629618 | ||
|
f37f4337de | ||
|
1a3b3a0a7a | ||
|
4f1ce0d71a | ||
|
1f728071a9 | ||
|
4b68917337 | ||
|
0cbf968228 | ||
|
ec2a21dbe7 | ||
|
dc6402c2b7 | ||
|
8038b393e0 | ||
|
4e0f7b52d4 | ||
|
486c46f608 | ||
|
6d075877ec | ||
|
fe826b393b | ||
|
c69aa25353 | ||
|
0f4d067c06 | ||
|
cff31d295c | ||
|
150e30db2a | ||
|
bc8989a0e6 | ||
|
1959a7fd9a | ||
|
3baaf1d52c | ||
|
62f10e0877 | ||
|
c3c7854e92 |
26
README.md
@@ -1,11 +1,25 @@
|
|||||||
# Preface
|
# Preface
|
||||||
|
|
||||||
This project is a demo for a websocket-based input synchronization method inspired by [GGPO](https://www.ggpo.net/).
|
This project is a demo for a websocket-based rollback netcode inspired by [GGPO](https://github.com/pond3r/ggpo/blob/master/doc/README.md).
|
||||||

|
|
||||||
|
|
||||||
Please checkout [this demo video](https://pan.baidu.com/s/1aM6e8IWaJszFCYAsRjt19g?pwd=z02c) to see whether the source codes are doing what you expect for synchronization.
|
_(the following gif is sped up to ~1.5x for file size reduction, kindly note that around ~11s countdown, the attack animation is resumed from a partial progress)_
|
||||||
|

|
||||||
|
|
||||||
The video mainly shows the following feature (yet I'm not surprised if they're not obvious): when a player didn't have its input arrived at the backend in time (e.g. due to local lag, network delay or reconnection), backend forces confirmation of a prediction of its own and sends the confirmed input together w/ a reference render frame to that player.
|
Please also checkout [this demo video](https://pan.baidu.com/s/1U1wb7KWyHorZElNWcS5HHA?pwd=30wh) to see how this demo carries out a full 60fps synchronization with the help of _batched input upsync/downsync_ for satisfying network I/O performance.
|
||||||
|
|
||||||
|
The video mainly shows the following features.
|
||||||
|
- The backend receives inputs from frontend peers and broadcasts back for synchronization.
|
||||||
|
- The game is recovered for a player upon reconnection.
|
||||||
|
- Both backend(Golang) and frontend(JavaScript) execute collision detection and handle collision contacts by the same algorithm. The backend dynamics is togglable by [Room.BackendDynamicsEnabled](https://github.com/genxium/DelayNoMore/blob/v0.5.2/battle_srv/models/room.go#L813), but **when turned off the game couldn't support recovery upon reconnection**.
|
||||||
|
|
||||||
|
_(how input delay roughly works)_
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
_(how rollback-and-chase in this project roughly works)_
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
# 1. Building & running
|
# 1. Building & running
|
||||||
|
|
||||||
@@ -19,7 +33,7 @@ The video mainly shows the following feature (yet I'm not surprised if they're n
|
|||||||
- [protobuf CLI](https://developers.google.com/protocol-buffers/docs/downloads) (optional, only for development)
|
- [protobuf CLI](https://developers.google.com/protocol-buffers/docs/downloads) (optional, only for development)
|
||||||
|
|
||||||
### Frontend
|
### Frontend
|
||||||
- [CocosCreator v2.2.1](https://www.cocos.com/en/cocos-creator-2-2-1-released-with-performance-improvements) (mandatory, **ONLY AVAILABLE on Windows or OSX and should be exactly this version**, DON'T use any other version because CocosCreator is well-known for new versions not being backward incompatible)
|
- [CocosCreator v2.2.1](https://www.cocos.com/en/cocos-creator-2-2-1-released-with-performance-improvements) (mandatory, **ONLY AVAILABLE on Windows or OSX and should be exactly this version**, DON'T use any other version because CocosCreator is well-known for new versions not being backward compatible)
|
||||||
- [protojs](https://www.npmjs.com/package/protojs) (optional, only for development)
|
- [protojs](https://www.npmjs.com/package/protojs) (optional, only for development)
|
||||||
|
|
||||||
## 1.2 Provisioning
|
## 1.2 Provisioning
|
||||||
@@ -64,7 +78,7 @@ The easy way is to try out 2 players with test accounts on a same machine.
|
|||||||
- Open one browser instance, visit _http://localhost:7456?expectedRoomId=1_, input `add`on the username box and click to request a captcha, this is a test account so a captcha would be returned by the backend and filled automatically (as shown in the figure below), then click and click to proceed to a matching scene.
|
- Open one browser instance, visit _http://localhost:7456?expectedRoomId=1_, input `add`on the username box and click to request a captcha, this is a test account so a captcha would be returned by the backend and filled automatically (as shown in the figure below), then click and click to proceed to a matching scene.
|
||||||
- Open another browser instance, visit _http://localhost:7456?expectedRoomId=1_, input `bdd`on the username box and click to request a captcha, this is another test account so a captcha would be returned by the backend and filled automatically, then click and click to proceed, when matched a `battle`(but no competition rule yet) would start.
|
- Open another browser instance, visit _http://localhost:7456?expectedRoomId=1_, input `bdd`on the username box and click to request a captcha, this is another test account so a captcha would be returned by the backend and filled automatically, then click and click to proceed, when matched a `battle`(but no competition rule yet) would start.
|
||||||
- Try out the onscreen virtual joysticks to move the cars and see if their movements are in-sync.
|
- Try out the onscreen virtual joysticks to move the cars and see if their movements are in-sync.
|
||||||

|

|
||||||
|
|
||||||
## 2 Troubleshooting
|
## 2 Troubleshooting
|
||||||
|
|
||||||
|
@@ -1,6 +1,12 @@
|
|||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"battle_srv/api"
|
||||||
|
. "battle_srv/common"
|
||||||
|
"battle_srv/common/utils"
|
||||||
|
"battle_srv/models"
|
||||||
|
. "battle_srv/protos"
|
||||||
|
"battle_srv/storage"
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
@@ -10,11 +16,6 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"server/api"
|
|
||||||
. "server/common"
|
|
||||||
"server/common/utils"
|
|
||||||
"server/models"
|
|
||||||
"server/storage"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
. "dnmshared"
|
. "dnmshared"
|
||||||
@@ -79,7 +80,6 @@ func (p *playerController) SMSCaptchaGet(c *gin.Context) {
|
|||||||
c.Set(api.RET, Constants.RetCode.UnknownError)
|
c.Set(api.RET, Constants.RetCode.UnknownError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Redis剩余时长校验
|
|
||||||
if ttl >= ConstVals.Player.CaptchaMaxTTL {
|
if ttl >= ConstVals.Player.CaptchaMaxTTL {
|
||||||
Logger.Info("There's an existing SmsCaptcha record in Redis-server: ", zap.String("key", redisKey), zap.Duration("ttl", ttl))
|
Logger.Info("There's an existing SmsCaptcha record in Redis-server: ", zap.String("key", redisKey), zap.Duration("ttl", ttl))
|
||||||
c.Set(api.RET, Constants.RetCode.SmsCaptchaRequestedTooFrequently)
|
c.Set(api.RET, Constants.RetCode.SmsCaptchaRequestedTooFrequently)
|
||||||
@@ -89,7 +89,6 @@ func (p *playerController) SMSCaptchaGet(c *gin.Context) {
|
|||||||
pass := false
|
pass := false
|
||||||
var succRet int
|
var succRet int
|
||||||
if Conf.General.ServerEnv == SERVER_ENV_TEST {
|
if Conf.General.ServerEnv == SERVER_ENV_TEST {
|
||||||
// 测试环境,优先从数据库校验`player.name`,不通过再走机器人magic name校验
|
|
||||||
player, err := models.GetPlayerByName(req.Num)
|
player, err := models.GetPlayerByName(req.Num)
|
||||||
if nil == err && nil != player {
|
if nil == err && nil != player {
|
||||||
pass = true
|
pass = true
|
||||||
@@ -98,7 +97,6 @@ func (p *playerController) SMSCaptchaGet(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !pass {
|
if !pass {
|
||||||
// 机器人magic name校验,不通过再走手机号校验
|
|
||||||
player, err := models.GetPlayerByName(req.Num)
|
player, err := models.GetPlayerByName(req.Num)
|
||||||
if nil == err && nil != player {
|
if nil == err && nil != player {
|
||||||
pass = true
|
pass = true
|
||||||
@@ -111,7 +109,6 @@ func (p *playerController) SMSCaptchaGet(c *gin.Context) {
|
|||||||
succRet = Constants.RetCode.Ok
|
succRet = Constants.RetCode.Ok
|
||||||
pass = true
|
pass = true
|
||||||
}
|
}
|
||||||
// Hardecoded 只验证国内手机号格式
|
|
||||||
if req.CountryCode == "86" {
|
if req.CountryCode == "86" {
|
||||||
if RE_CHINA_PHONE_NUM.MatchString(req.Num) {
|
if RE_CHINA_PHONE_NUM.MatchString(req.Num) {
|
||||||
succRet = Constants.RetCode.Ok
|
succRet = Constants.RetCode.Ok
|
||||||
@@ -133,7 +130,6 @@ func (p *playerController) SMSCaptchaGet(c *gin.Context) {
|
|||||||
}{Ret: succRet}
|
}{Ret: succRet}
|
||||||
var captcha string
|
var captcha string
|
||||||
if ttl >= 0 {
|
if ttl >= 0 {
|
||||||
// 已有未过期的旧验证码记录,续验证码有效期。
|
|
||||||
storage.RedisManagerIns.Expire(redisKey, ConstVals.Player.CaptchaExpire)
|
storage.RedisManagerIns.Expire(redisKey, ConstVals.Player.CaptchaExpire)
|
||||||
captcha = storage.RedisManagerIns.Get(redisKey).Val()
|
captcha = storage.RedisManagerIns.Get(redisKey).Val()
|
||||||
if ttl >= ConstVals.Player.CaptchaExpire/4 {
|
if ttl >= ConstVals.Player.CaptchaExpire/4 {
|
||||||
@@ -147,7 +143,6 @@ func (p *playerController) SMSCaptchaGet(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
Logger.Info("Extended ttl of existing SMSCaptcha record in Redis:", zap.String("key", redisKey), zap.String("captcha", captcha))
|
Logger.Info("Extended ttl of existing SMSCaptcha record in Redis:", zap.String("key", redisKey), zap.String("captcha", captcha))
|
||||||
} else {
|
} else {
|
||||||
// 校验通过,进行验证码生成处理
|
|
||||||
captcha = strconv.Itoa(utils.Rand.Number(1000, 9999))
|
captcha = strconv.Itoa(utils.Rand.Number(1000, 9999))
|
||||||
if succRet == Constants.RetCode.Ok {
|
if succRet == Constants.RetCode.Ok {
|
||||||
getSmsCaptchaRespErrorCode := sendSMSViaVendor(req.Num, req.CountryCode, captcha)
|
getSmsCaptchaRespErrorCode := sendSMSViaVendor(req.Num, req.CountryCode, captcha)
|
||||||
@@ -234,7 +229,6 @@ func (p *playerController) WechatLogin(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//baseInfo ResAccessToken 获取用户授权access_token的返回结果
|
|
||||||
baseInfo, err := utils.WechatIns.GetOauth2Basic(req.Authcode)
|
baseInfo, err := utils.WechatIns.GetOauth2Basic(req.Authcode)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -250,7 +244,6 @@ func (p *playerController) WechatLogin(c *gin.Context) {
|
|||||||
c.Set(api.RET, Constants.RetCode.WechatServerError)
|
c.Set(api.RET, Constants.RetCode.WechatServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//fserver不会返回openId
|
|
||||||
userInfo.OpenID = baseInfo.OpenID
|
userInfo.OpenID = baseInfo.OpenID
|
||||||
|
|
||||||
player, err := p.maybeCreatePlayerWechatAuthBinding(userInfo)
|
player, err := p.maybeCreatePlayerWechatAuthBinding(userInfo)
|
||||||
@@ -316,7 +309,6 @@ func (p *playerController) WechatGameLogin(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//baseInfo ResAccessToken 获取用户授权access_token的返回结果
|
|
||||||
baseInfo, err := utils.WechatGameIns.GetOauth2Basic(req.Authcode)
|
baseInfo, err := utils.WechatGameIns.GetOauth2Basic(req.Authcode)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -337,7 +329,6 @@ func (p *playerController) WechatGameLogin(c *gin.Context) {
|
|||||||
c.Set(api.RET, Constants.RetCode.WechatServerError)
|
c.Set(api.RET, Constants.RetCode.WechatServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//fserver不会返回openId
|
|
||||||
userInfo.OpenID = baseInfo.OpenID
|
userInfo.OpenID = baseInfo.OpenID
|
||||||
|
|
||||||
player, err := p.maybeCreatePlayerWechatGameAuthBinding(userInfo)
|
player, err := p.maybeCreatePlayerWechatGameAuthBinding(userInfo)
|
||||||
@@ -395,7 +386,6 @@ func (p *playerController) IntAuthTokenLogin(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//kobako: 从player获取display name等
|
|
||||||
player, err := models.GetPlayerById(playerLogin.PlayerID)
|
player, err := models.GetPlayerById(playerLogin.PlayerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logger.Error("Get player by id in IntAuthTokenLogin function error: ", zap.Error(err))
|
Logger.Error("Get player by id in IntAuthTokenLogin function error: ", zap.Error(err))
|
||||||
@@ -479,7 +469,6 @@ func (p *playerController) TokenAuth(c *gin.Context) {
|
|||||||
c.Abort()
|
c.Abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 以下是内部私有函数
|
|
||||||
func (p *playerController) maybeCreateNewPlayer(req smsCaptchaReq) (*models.Player, error) {
|
func (p *playerController) maybeCreateNewPlayer(req smsCaptchaReq) (*models.Player, error) {
|
||||||
extAuthID := req.extAuthID()
|
extAuthID := req.extAuthID()
|
||||||
if Conf.General.ServerEnv == SERVER_ENV_TEST {
|
if Conf.General.ServerEnv == SERVER_ENV_TEST {
|
||||||
@@ -492,7 +481,7 @@ func (p *playerController) maybeCreateNewPlayer(req smsCaptchaReq) (*models.Play
|
|||||||
Logger.Info("Got a test env player:", zap.Any("phonenum", req.Num), zap.Any("playerId", player.Id))
|
Logger.Info("Got a test env player:", zap.Any("phonenum", req.Num), zap.Any("playerId", player.Id))
|
||||||
return player, nil
|
return player, nil
|
||||||
}
|
}
|
||||||
} else { //正式环境检查是否为bot用户
|
} else {
|
||||||
botPlayer, err := models.GetPlayerByName(req.Num)
|
botPlayer, err := models.GetPlayerByName(req.Num)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logger.Error("Seeking bot player error:", zap.Error(err))
|
Logger.Error("Seeking bot player error:", zap.Error(err))
|
||||||
@@ -537,29 +526,31 @@ func (p *playerController) maybeCreatePlayerWechatAuthBinding(userInfo utils.Use
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if player != nil {
|
if player != nil {
|
||||||
{ //更新玩家姓名及头像
|
updateInfo := models.Player{
|
||||||
updateInfo := models.Player{
|
PlayerDownsync: PlayerDownsync{
|
||||||
Avatar: userInfo.HeadImgURL,
|
Avatar: userInfo.HeadImgURL,
|
||||||
DisplayName: userInfo.Nickname,
|
DisplayName: userInfo.Nickname,
|
||||||
}
|
},
|
||||||
tx := storage.MySQLManagerIns.MustBegin()
|
}
|
||||||
defer tx.Rollback()
|
tx := storage.MySQLManagerIns.MustBegin()
|
||||||
ok, err := models.Update(tx, player.Id, &updateInfo)
|
defer tx.Rollback()
|
||||||
if err != nil && ok != true {
|
ok, err := models.Update(tx, player.Id, &updateInfo)
|
||||||
return nil, err
|
if err != nil && ok != true {
|
||||||
} else {
|
return nil, err
|
||||||
tx.Commit()
|
} else {
|
||||||
}
|
tx.Commit()
|
||||||
}
|
}
|
||||||
return player, nil
|
return player, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
now := utils.UnixtimeMilli()
|
now := utils.UnixtimeMilli()
|
||||||
player := models.Player{
|
player := models.Player{
|
||||||
CreatedAt: now,
|
PlayerDownsync: PlayerDownsync{
|
||||||
UpdatedAt: now,
|
DisplayName: userInfo.Nickname,
|
||||||
DisplayName: userInfo.Nickname,
|
Avatar: userInfo.HeadImgURL,
|
||||||
Avatar: userInfo.HeadImgURL,
|
},
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: now,
|
||||||
}
|
}
|
||||||
return p.createNewPlayer(player, userInfo.OpenID, int(Constants.AuthChannel.Wechat))
|
return p.createNewPlayer(player, userInfo.OpenID, int(Constants.AuthChannel.Wechat))
|
||||||
}
|
}
|
||||||
@@ -575,29 +566,31 @@ func (p *playerController) maybeCreatePlayerWechatGameAuthBinding(userInfo utils
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if player != nil {
|
if player != nil {
|
||||||
{ //更新玩家姓名及头像
|
updateInfo := models.Player{
|
||||||
updateInfo := models.Player{
|
PlayerDownsync: PlayerDownsync{
|
||||||
Avatar: userInfo.HeadImgURL,
|
Avatar: userInfo.HeadImgURL,
|
||||||
DisplayName: userInfo.Nickname,
|
DisplayName: userInfo.Nickname,
|
||||||
}
|
},
|
||||||
tx := storage.MySQLManagerIns.MustBegin()
|
}
|
||||||
defer tx.Rollback()
|
tx := storage.MySQLManagerIns.MustBegin()
|
||||||
ok, err := models.Update(tx, player.Id, &updateInfo)
|
defer tx.Rollback()
|
||||||
if err != nil && ok != true {
|
ok, err := models.Update(tx, player.Id, &updateInfo)
|
||||||
return nil, err
|
if err != nil && ok != true {
|
||||||
} else {
|
return nil, err
|
||||||
tx.Commit()
|
} else {
|
||||||
}
|
tx.Commit()
|
||||||
}
|
}
|
||||||
return player, nil
|
return player, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
now := utils.UnixtimeMilli()
|
now := utils.UnixtimeMilli()
|
||||||
player := models.Player{
|
player := models.Player{
|
||||||
CreatedAt: now,
|
PlayerDownsync: PlayerDownsync{
|
||||||
UpdatedAt: now,
|
DisplayName: userInfo.Nickname,
|
||||||
DisplayName: userInfo.Nickname,
|
Avatar: userInfo.HeadImgURL,
|
||||||
Avatar: userInfo.HeadImgURL,
|
},
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: now,
|
||||||
}
|
}
|
||||||
return p.createNewPlayer(player, userInfo.OpenID, int(Constants.AuthChannel.WechatGame))
|
return p.createNewPlayer(player, userInfo.OpenID, int(Constants.AuthChannel.WechatGame))
|
||||||
}
|
}
|
||||||
@@ -672,15 +665,13 @@ func sendSMSViaVendor(mobile string, nationcode string, captchaCode string) int
|
|||||||
Nationcode: nationcode,
|
Nationcode: nationcode,
|
||||||
}
|
}
|
||||||
var captchaExpireMin string
|
var captchaExpireMin string
|
||||||
//短信有效期hardcode
|
|
||||||
if Conf.General.ServerEnv == SERVER_ENV_TEST {
|
if Conf.General.ServerEnv == SERVER_ENV_TEST {
|
||||||
//测试环境下有效期为20秒 先hardcode了
|
captchaExpireMin = "0.5" // Hardcoded
|
||||||
captchaExpireMin = "0.5"
|
|
||||||
} else {
|
} else {
|
||||||
captchaExpireMin = strconv.Itoa(int(ConstVals.Player.CaptchaExpire) / 60000000000)
|
captchaExpireMin = strconv.Itoa(int(ConstVals.Player.CaptchaExpire) / 60000000000)
|
||||||
}
|
}
|
||||||
params := [2]string{captchaCode, captchaExpireMin}
|
params := [2]string{captchaCode, captchaExpireMin}
|
||||||
appkey := "41a5142feff0b38ade02ea12deee9741" // TODO: Should read from config file!
|
appkey := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" // TODO: Should read from config file!
|
||||||
rand := strconv.Itoa(utils.Rand.Number(1000, 9999))
|
rand := strconv.Itoa(utils.Rand.Number(1000, 9999))
|
||||||
now := utils.UnixtimeSec()
|
now := utils.UnixtimeSec()
|
||||||
|
|
||||||
@@ -694,7 +685,7 @@ func sendSMSViaVendor(mobile string, nationcode string, captchaCode string) int
|
|||||||
Extend: "",
|
Extend: "",
|
||||||
Params: ¶ms,
|
Params: ¶ms,
|
||||||
Sig: sig,
|
Sig: sig,
|
||||||
Sign: "洛克互娱",
|
Sign: "YYYYYYYYYYYYYYYYY",
|
||||||
Tel: tel,
|
Tel: tel,
|
||||||
Time: now,
|
Time: now,
|
||||||
Tpl_id: 207399,
|
Tpl_id: 207399,
|
||||||
@@ -705,7 +696,7 @@ func sendSMSViaVendor(mobile string, nationcode string, captchaCode string) int
|
|||||||
Logger.Info("json marshal", zap.Any("err:", err))
|
Logger.Info("json marshal", zap.Any("err:", err))
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
resp, err := http.Post("https://yun.tim.qq.com/v5/tlssmssvr/sendsms?sdkappid=1400150185&random="+rand,
|
resp, err := http.Post("https://yun.tim.qq.com/v5/tlssmssvr/sendsms?sdkappid=uuuuuuuuuuuuuuuuuuuuuuuu&random="+rand,
|
||||||
"application/json",
|
"application/json",
|
||||||
req)
|
req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
. "battle_srv/common"
|
||||||
|
. "battle_srv/configs"
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
. "dnmshared"
|
. "dnmshared"
|
||||||
@@ -11,8 +13,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
. "server/common"
|
|
||||||
. "server/configs"
|
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -250,10 +250,6 @@ func (w *wechat) getTicketFromServer() (ticket resTicket, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//jsAPITicketCacheKey := fmt.Sprintf("jsapi_ticket_%s", w.config.AppID)
|
|
||||||
//expires := ticket.ExpiresIn - 1500
|
|
||||||
//set
|
|
||||||
//err = js.Cache.Set(jsAPITicketCacheKey, ticket.Ticket, time.Duration(expires)*time.Second)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,9 +272,6 @@ func (w *wechat) getAccessTokenFromServer() (accessToken string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//accessTokenCacheKey := fmt.Sprintf("access_token_%s", w.config.AppID)
|
|
||||||
//expires := r.ExpiresIn - 1500
|
|
||||||
//set to redis err = ctx.Cache.Set(accessTokenCacheKey, r.AccessToken, time.Duration(expires)*time.Second)
|
|
||||||
accessToken = r.AccessToken
|
accessToken = r.AccessToken
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,16 @@
|
|||||||
package env_tools
|
package env_tools
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
. "battle_srv/common"
|
||||||
|
"battle_srv/common/utils"
|
||||||
|
"battle_srv/models"
|
||||||
|
. "battle_srv/protos"
|
||||||
|
"battle_srv/storage"
|
||||||
. "dnmshared"
|
. "dnmshared"
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
. "server/common"
|
|
||||||
"server/common/utils"
|
|
||||||
"server/models"
|
|
||||||
"server/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoadPreConf() {
|
func LoadPreConf() {
|
||||||
@@ -71,7 +72,6 @@ func createMysqlData(rows *sqlx.Rows, v string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加上tableName参数, 用于pre_conf_data.sqlite里bot_player表的复用 --kobako
|
|
||||||
func maybeCreateNewPlayerFromBotTable(db *sqlx.DB, tableName string) {
|
func maybeCreateNewPlayerFromBotTable(db *sqlx.DB, tableName string) {
|
||||||
var ls []*dbBotPlayer
|
var ls []*dbBotPlayer
|
||||||
err := db.Select(&ls, "SELECT name, magic_phone_country_code, magic_phone_num, display_name FROM "+tableName)
|
err := db.Select(&ls, "SELECT name, magic_phone_country_code, magic_phone_num, display_name FROM "+tableName)
|
||||||
@@ -88,7 +88,6 @@ func maybeCreateNewPlayerFromBotTable(db *sqlx.DB, tableName string) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
query = storage.MySQLManagerIns.Rebind(query)
|
query = storage.MySQLManagerIns.Rebind(query)
|
||||||
// existNames := make([]string, len(ls), len(ls))
|
|
||||||
var existPlayers []*models.Player
|
var existPlayers []*models.Player
|
||||||
err = storage.MySQLManagerIns.Select(&existPlayers, query, args...)
|
err = storage.MySQLManagerIns.Select(&existPlayers, query, args...)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
@@ -99,13 +98,11 @@ func maybeCreateNewPlayerFromBotTable(db *sqlx.DB, tableName string) {
|
|||||||
var flag bool
|
var flag bool
|
||||||
for _, v := range existPlayers {
|
for _, v := range existPlayers {
|
||||||
if botPlayer.Name == v.Name {
|
if botPlayer.Name == v.Name {
|
||||||
// 已有数据,合并处理
|
|
||||||
flag = true
|
flag = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !flag {
|
if !flag {
|
||||||
// 找不到,新增
|
|
||||||
Logger.Debug("create", zap.Any(tableName, botPlayer))
|
Logger.Debug("create", zap.Any(tableName, botPlayer))
|
||||||
err := createNewBotPlayer(botPlayer)
|
err := createNewBotPlayer(botPlayer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -120,11 +117,14 @@ func createNewBotPlayer(p *dbBotPlayer) error {
|
|||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
now := utils.UnixtimeMilli()
|
now := utils.UnixtimeMilli()
|
||||||
player := models.Player{
|
player := models.Player{
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
Name: p.Name,
|
PlayerDownsync: PlayerDownsync{
|
||||||
DisplayName: p.DisplayName,
|
Name: p.Name,
|
||||||
|
DisplayName: p.DisplayName,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err := player.Insert(tx)
|
err := player.Insert(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
package env_tools
|
package env_tools
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
. "battle_srv/common"
|
||||||
|
"battle_srv/common/utils"
|
||||||
|
"battle_srv/models"
|
||||||
|
. "battle_srv/protos"
|
||||||
|
"battle_srv/storage"
|
||||||
. "dnmshared"
|
. "dnmshared"
|
||||||
. "server/common"
|
|
||||||
"server/common/utils"
|
|
||||||
"server/models"
|
|
||||||
"server/storage"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
@@ -75,9 +76,11 @@ func createNewPlayer(p *dbTestPlayer) error {
|
|||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
now := utils.UnixtimeMilli()
|
now := utils.UnixtimeMilli()
|
||||||
player := models.Player{
|
player := models.Player{
|
||||||
|
PlayerDownsync: PlayerDownsync{
|
||||||
|
Name: p.Name,
|
||||||
|
},
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
Name: p.Name,
|
|
||||||
}
|
}
|
||||||
err := player.Insert(tx)
|
err := player.Insert(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
module server
|
module battle_srv
|
||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"battle_srv/api"
|
||||||
|
"battle_srv/api/v1"
|
||||||
|
. "battle_srv/common"
|
||||||
|
"battle_srv/env_tools"
|
||||||
|
"battle_srv/models"
|
||||||
|
"battle_srv/storage"
|
||||||
|
"battle_srv/ws"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"server/api"
|
|
||||||
"server/api/v1"
|
|
||||||
. "server/common"
|
|
||||||
"server/env_tools"
|
|
||||||
"server/models"
|
|
||||||
"server/storage"
|
|
||||||
"server/ws"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "dnmshared"
|
. "dnmshared/sharedprotos"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Barrier struct {
|
type Barrier struct {
|
||||||
|
@@ -1,85 +1,33 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "dnmshared"
|
. "battle_srv/protos"
|
||||||
pb "server/pb_output"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func toPbVec2D(modelInstance *Vec2D) *pb.Vec2D {
|
func toPbPlayers(modelInstances map[int32]*Player, withMetaInfo bool) map[int32]*PlayerDownsync {
|
||||||
toRet := &pb.Vec2D{
|
toRet := make(map[int32]*PlayerDownsync, 0)
|
||||||
X: modelInstance.X,
|
|
||||||
Y: modelInstance.Y,
|
|
||||||
}
|
|
||||||
return toRet
|
|
||||||
}
|
|
||||||
|
|
||||||
func toPbPolygon2D(modelInstance *Polygon2D) *pb.Polygon2D {
|
|
||||||
toRet := &pb.Polygon2D{
|
|
||||||
Anchor: toPbVec2D(modelInstance.Anchor),
|
|
||||||
Points: make([]*pb.Vec2D, len(modelInstance.Points)),
|
|
||||||
}
|
|
||||||
for index, p := range modelInstance.Points {
|
|
||||||
toRet.Points[index] = toPbVec2D(p)
|
|
||||||
}
|
|
||||||
return toRet
|
|
||||||
}
|
|
||||||
|
|
||||||
func toPbVec2DList(modelInstance *Vec2DList) *pb.Vec2DList {
|
|
||||||
toRet := &pb.Vec2DList{
|
|
||||||
Vec2DList: make([]*pb.Vec2D, len(*modelInstance)),
|
|
||||||
}
|
|
||||||
for k, v := range *modelInstance {
|
|
||||||
toRet.Vec2DList[k] = toPbVec2D(v)
|
|
||||||
}
|
|
||||||
return toRet
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToPbVec2DListMap(modelInstances map[string]*Vec2DList) map[string]*pb.Vec2DList {
|
|
||||||
toRet := make(map[string]*pb.Vec2DList, len(modelInstances))
|
|
||||||
for k, v := range modelInstances {
|
|
||||||
toRet[k] = toPbVec2DList(v)
|
|
||||||
}
|
|
||||||
return toRet
|
|
||||||
}
|
|
||||||
|
|
||||||
func toPbPolygon2DList(modelInstance *Polygon2DList) *pb.Polygon2DList {
|
|
||||||
toRet := &pb.Polygon2DList{
|
|
||||||
Polygon2DList: make([]*pb.Polygon2D, len(*modelInstance)),
|
|
||||||
}
|
|
||||||
for k, v := range *modelInstance {
|
|
||||||
toRet.Polygon2DList[k] = toPbPolygon2D(v)
|
|
||||||
}
|
|
||||||
return toRet
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToPbPolygon2DListMap(modelInstances map[string]*Polygon2DList) map[string]*pb.Polygon2DList {
|
|
||||||
toRet := make(map[string]*pb.Polygon2DList, len(modelInstances))
|
|
||||||
for k, v := range modelInstances {
|
|
||||||
toRet[k] = toPbPolygon2DList(v)
|
|
||||||
}
|
|
||||||
return toRet
|
|
||||||
}
|
|
||||||
|
|
||||||
func toPbPlayers(modelInstances map[int32]*Player) map[int32]*pb.Player {
|
|
||||||
toRet := make(map[int32]*pb.Player, 0)
|
|
||||||
if nil == modelInstances {
|
if nil == modelInstances {
|
||||||
return toRet
|
return toRet
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, last := range modelInstances {
|
for k, last := range modelInstances {
|
||||||
toRet[k] = &pb.Player{
|
toRet[k] = &PlayerDownsync{
|
||||||
Id: last.Id,
|
Id: last.Id,
|
||||||
X: last.X,
|
VirtualGridX: last.VirtualGridX,
|
||||||
Y: last.Y,
|
VirtualGridY: last.VirtualGridY,
|
||||||
Dir: &pb.Direction{
|
DirX: last.DirX,
|
||||||
Dx: last.Dir.Dx,
|
DirY: last.DirY,
|
||||||
Dy: last.Dir.Dy,
|
ColliderRadius: last.ColliderRadius,
|
||||||
},
|
Speed: last.Speed,
|
||||||
Speed: last.Speed,
|
BattleState: last.BattleState,
|
||||||
BattleState: last.BattleState,
|
Score: last.Score,
|
||||||
Score: last.Score,
|
Removed: last.Removed,
|
||||||
Removed: last.Removed,
|
JoinIndex: last.JoinIndex,
|
||||||
JoinIndex: last.JoinIndex,
|
}
|
||||||
|
if withMetaInfo {
|
||||||
|
toRet[k].Name = last.Name
|
||||||
|
toRet[k].DisplayName = last.DisplayName
|
||||||
|
toRet[k].Avatar = last.Avatar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,11 +1,13 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
. "battle_srv/protos"
|
||||||
|
"battle_srv/storage"
|
||||||
. "dnmshared"
|
. "dnmshared"
|
||||||
"fmt"
|
"fmt"
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PlayerBattleState struct {
|
type PlayerBattleState struct {
|
||||||
@@ -33,30 +35,18 @@ func InitPlayerBattleStateIns() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Player struct {
|
type Player struct {
|
||||||
Id int32 `json:"id,omitempty" db:"id"`
|
PlayerDownsync
|
||||||
X float64 `json:"x,omitempty"`
|
|
||||||
Y float64 `json:"y,omitempty"`
|
|
||||||
Dir *Direction `json:"dir,omitempty"`
|
|
||||||
Speed float64 `json:"speed,omitempty"`
|
|
||||||
BattleState int32 `json:"battleState,omitempty"`
|
|
||||||
LastMoveGmtMillis int32 `json:"lastMoveGmtMillis,omitempty"`
|
|
||||||
Score int32 `json:"score,omitempty"`
|
|
||||||
Removed bool `json:"removed,omitempty"`
|
|
||||||
JoinIndex int32
|
|
||||||
|
|
||||||
Name string `json:"name,omitempty" db:"name"`
|
// DB only fields
|
||||||
DisplayName string `json:"displayName,omitempty" db:"display_name"`
|
CreatedAt int64 `db:"created_at"`
|
||||||
Avatar string `json:"avatar,omitempty"`
|
UpdatedAt int64 `db:"updated_at"`
|
||||||
|
DeletedAt NullInt64 `db:"deleted_at"`
|
||||||
|
TutorialStage int `db:"tutorial_stage"`
|
||||||
|
|
||||||
FrozenAtGmtMillis int64 `json:"-" db:"-"`
|
// other in-battle info fields
|
||||||
AddSpeedAtGmtMillis int64 `json:"-" db:"-"`
|
LastSentInputFrameId int32
|
||||||
CreatedAt int64 `json:"-" db:"created_at"`
|
AckingFrameId int32
|
||||||
UpdatedAt int64 `json:"-" db:"updated_at"`
|
AckingInputFrameId int32
|
||||||
DeletedAt NullInt64 `json:"-" db:"deleted_at"`
|
|
||||||
TutorialStage int `json:"-" db:"tutorial_stage"`
|
|
||||||
AckingFrameId int32 `json:"ackingFrameId"`
|
|
||||||
AckingInputFrameId int32 `json:"-"`
|
|
||||||
LastSentInputFrameId int32 `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExistPlayerByName(name string) (bool, error) {
|
func ExistPlayerByName(name string) (bool, error) {
|
||||||
@@ -72,15 +62,43 @@ func GetPlayerById(id int) (*Player, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getPlayer(cond sq.Eq) (*Player, error) {
|
func getPlayer(cond sq.Eq) (*Player, error) {
|
||||||
var p Player
|
p := Player{}
|
||||||
err := getObj("player", cond, &p)
|
pd := PlayerDownsync{}
|
||||||
if err == sql.ErrNoRows {
|
query, args, err := sq.Select("*").From("player").Where(cond).Limit(1).ToSql()
|
||||||
return nil, nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
p.Dir = &Direction{
|
rows, err := storage.MySQLManagerIns.Queryx(query, args...)
|
||||||
Dx: 0,
|
if err != nil {
|
||||||
Dy: 0,
|
return nil, err
|
||||||
}
|
}
|
||||||
|
cols, err := rows.Columns()
|
||||||
|
if nil != err {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for rows.Next() {
|
||||||
|
// TODO: Do it more elegantly, but by now I don't have time to learn reflection of Golang
|
||||||
|
vals := rowValues(rows, cols)
|
||||||
|
for i, col := range cols {
|
||||||
|
val := *vals[i].(*interface{})
|
||||||
|
if "id" == col {
|
||||||
|
pd.Id = int32(val.(int64))
|
||||||
|
}
|
||||||
|
if "name" == col {
|
||||||
|
switch v := val.(type) {
|
||||||
|
case []byte:
|
||||||
|
pd.Name = string(v)
|
||||||
|
default:
|
||||||
|
pd.Name = fmt.Sprintf("%v", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if "created_at" == col {
|
||||||
|
p.CreatedAt = int64(val.(int64))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logger.Info("Queried player from db", zap.Any("cond", cond), zap.Any("p", p), zap.Any("pd", pd), zap.Any("cols", cols), zap.Any("rowValues", vals))
|
||||||
|
}
|
||||||
|
p.PlayerDownsync = pd
|
||||||
return &p, nil
|
return &p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,8 +129,6 @@ func Update(tx *sqlx.Tx, id int32, p *Player) (bool, error) {
|
|||||||
}
|
}
|
||||||
result, err := tx.Exec(query, args...)
|
result, err := tx.Exec(query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("ERRRRRRR: ")
|
|
||||||
fmt.Println(err)
|
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
rowsAffected, err := result.RowsAffected()
|
rowsAffected, err := result.RowsAffected()
|
||||||
|
@@ -1,15 +1,26 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"battle_srv/storage"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
. "dnmshared"
|
. "dnmshared"
|
||||||
"server/storage"
|
|
||||||
|
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func rowValues(rows *sqlx.Rows, cols []string) []interface{} {
|
||||||
|
results := make([]interface{}, len(cols))
|
||||||
|
for i := range results {
|
||||||
|
results[i] = new(interface{})
|
||||||
|
}
|
||||||
|
if err := rows.Scan(results[:]...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
func exist(t string, cond sq.Eq) (bool, error) {
|
func exist(t string, cond sq.Eq) (bool, error) {
|
||||||
c, err := getCount(t, cond)
|
c, err := getCount(t, cond)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
. "battle_srv/common"
|
||||||
|
"battle_srv/common/utils"
|
||||||
|
"battle_srv/storage"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
. "server/common"
|
|
||||||
"server/common/utils"
|
|
||||||
"server/storage"
|
|
||||||
|
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
)
|
)
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
. "battle_srv/common"
|
||||||
|
"battle_srv/common/utils"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
. "dnmshared"
|
. "dnmshared"
|
||||||
"errors"
|
"errors"
|
||||||
. "server/common"
|
|
||||||
"server/common/utils"
|
|
||||||
|
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
1603
battle_srv/protos/room_downsync_frame.pb.go
Normal file
@@ -1,7 +1,7 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "server/common"
|
. "battle_srv/common"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
. "battle_srv/common"
|
||||||
"fmt"
|
"fmt"
|
||||||
. "server/common"
|
|
||||||
|
|
||||||
"github.com/go-redis/redis"
|
"github.com/go-redis/redis"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
package ws
|
package ws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
. "battle_srv/common"
|
||||||
|
"battle_srv/models"
|
||||||
|
pb "battle_srv/protos"
|
||||||
"container/heap"
|
"container/heap"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -8,14 +11,12 @@ import (
|
|||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"net/http"
|
"net/http"
|
||||||
. "server/common"
|
|
||||||
"server/models"
|
|
||||||
pb "server/pb_output"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "dnmshared"
|
. "dnmshared"
|
||||||
|
"runtime/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -104,7 +105,7 @@ func Serve(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
Logger.Warn("Recovered from: ", zap.Any("panic", r))
|
Logger.Error("Recovered from: ", zap.Any("panic", r))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
/**
|
/**
|
||||||
@@ -246,8 +247,8 @@ func Serve(c *gin.Context) {
|
|||||||
bciFrame := &pb.BattleColliderInfo{
|
bciFrame := &pb.BattleColliderInfo{
|
||||||
BoundRoomId: pRoom.Id,
|
BoundRoomId: pRoom.Id,
|
||||||
StageName: pRoom.StageName,
|
StageName: pRoom.StageName,
|
||||||
StrToVec2DListMap: models.ToPbVec2DListMap(pRoom.RawBattleStrToVec2DListMap),
|
StrToVec2DListMap: pRoom.StrToVec2DListMap,
|
||||||
StrToPolygon2DListMap: models.ToPbPolygon2DListMap(pRoom.RawBattleStrToPolygon2DListMap),
|
StrToPolygon2DListMap: pRoom.StrToPolygon2DListMap,
|
||||||
StageDiscreteW: pRoom.StageDiscreteW,
|
StageDiscreteW: pRoom.StageDiscreteW,
|
||||||
StageDiscreteH: pRoom.StageDiscreteH,
|
StageDiscreteH: pRoom.StageDiscreteH,
|
||||||
StageTileW: pRoom.StageTileW,
|
StageTileW: pRoom.StageTileW,
|
||||||
@@ -263,9 +264,15 @@ func Serve(c *gin.Context) {
|
|||||||
InputFrameUpsyncDelayTolerance: pRoom.InputFrameUpsyncDelayTolerance,
|
InputFrameUpsyncDelayTolerance: pRoom.InputFrameUpsyncDelayTolerance,
|
||||||
MaxChasingRenderFramesPerUpdate: pRoom.MaxChasingRenderFramesPerUpdate,
|
MaxChasingRenderFramesPerUpdate: pRoom.MaxChasingRenderFramesPerUpdate,
|
||||||
PlayerBattleState: pThePlayer.BattleState, // For frontend to know whether it's rejoining
|
PlayerBattleState: pThePlayer.BattleState, // For frontend to know whether it's rejoining
|
||||||
RollbackEstimatedDt: pRoom.RollbackEstimatedDt,
|
|
||||||
RollbackEstimatedDtMillis: pRoom.RollbackEstimatedDtMillis,
|
RollbackEstimatedDtMillis: pRoom.RollbackEstimatedDtMillis,
|
||||||
RollbackEstimatedDtNanos: pRoom.RollbackEstimatedDtNanos,
|
RollbackEstimatedDtNanos: pRoom.RollbackEstimatedDtNanos,
|
||||||
|
|
||||||
|
WorldToVirtualGridRatio: pRoom.WorldToVirtualGridRatio,
|
||||||
|
VirtualGridToWorldRatio: pRoom.VirtualGridToWorldRatio,
|
||||||
|
|
||||||
|
SpAtkLookupFrames: pRoom.SpAtkLookupFrames,
|
||||||
|
RenderCacheSize: pRoom.RenderCacheSize,
|
||||||
|
MeleeSkillConfig: pRoom.MeleeSkillConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &pb.WsResp{
|
resp := &pb.WsResp{
|
||||||
@@ -354,7 +361,7 @@ func Serve(c *gin.Context) {
|
|||||||
receivingLoopAgainstPlayer := func() error {
|
receivingLoopAgainstPlayer := func() error {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
Logger.Warn("Goroutine `receivingLoopAgainstPlayer`, recovery spot#1, recovered from: ", zap.Any("panic", r))
|
Logger.Error("Goroutine `receivingLoopAgainstPlayer`, recovery spot#1, recovered from: ", zap.Any("panic", r), zap.Any("callstack", debug.Stack()))
|
||||||
}
|
}
|
||||||
Logger.Info("Goroutine `receivingLoopAgainstPlayer` is stopped for:", zap.Any("playerId", playerId), zap.Any("roomId", pRoom.Id))
|
Logger.Info("Goroutine `receivingLoopAgainstPlayer` is stopped for:", zap.Any("playerId", playerId), zap.Any("roomId", pRoom.Id))
|
||||||
}()
|
}()
|
||||||
|
BIN
charts/AttackTriggerCases.jpg
Normal file
After Width: | Height: | Size: 176 KiB |
BIN
charts/AvoidingFloatingPointAccumulationErr.jpg
Normal file
After Width: | Height: | Size: 144 KiB |
1
charts/DelayNoMore.drawio
Normal file
BIN
charts/InputDelayIntro.jpg
Normal file
After Width: | Height: | Size: 123 KiB |
9
charts/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Double playback speed of a video
|
||||||
|
```
|
||||||
|
ffmpeg -i input.mp4 -filter:v "setpts=0.5*PTS" output.mp4
|
||||||
|
```
|
||||||
|
|
||||||
|
# GIF creation cmd reference
|
||||||
|
```
|
||||||
|
ffmpeg -ss 12 -t 13 -i input.mp4 -vf "fps=10,scale=480:-1" -loop 0 output.gif
|
||||||
|
```
|
BIN
charts/RollbackAndChase.jpg
Normal file
After Width: | Height: | Size: 190 KiB |
BIN
charts/melee_attack_fractional_anim_resume_spedup.gif
Normal file
After Width: | Height: | Size: 5.7 MiB |
Before Width: | Height: | Size: 684 KiB After Width: | Height: | Size: 684 KiB |
@@ -3,11 +3,12 @@ module viscol
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
dnmshared v0.0.0
|
||||||
|
battle_srv v0.0.0
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||||
github.com/hajimehoshi/ebiten/v2 v2.4.7
|
github.com/hajimehoshi/ebiten/v2 v2.4.7
|
||||||
github.com/solarlune/resolv v0.5.1
|
github.com/solarlune/resolv v0.5.1
|
||||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69
|
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69
|
||||||
dnmshared v0.0.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -22,6 +23,8 @@ require (
|
|||||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
|
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
|
||||||
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 // indirect
|
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 // indirect
|
||||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
|
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
|
||||||
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace dnmshared => ../dnmshared
|
replace dnmshared => ../dnmshared
|
||||||
|
replace battle_srv => ../battle_srv
|
||||||
|
@@ -7,6 +7,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad h1:kX51IjbsJP
|
|||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/hajimehoshi/bitmapfont/v2 v2.2.1 h1:y7zcy02/UgO24IL3COqYtrRZzhRucNBtmCo/SNU648k=
|
github.com/hajimehoshi/bitmapfont/v2 v2.2.1 h1:y7zcy02/UgO24IL3COqYtrRZzhRucNBtmCo/SNU648k=
|
||||||
github.com/hajimehoshi/bitmapfont/v2 v2.2.1/go.mod h1:wjrYAy8vKgj9JsFgnYAOK346/uvE22TlmqouzdnYIs0=
|
github.com/hajimehoshi/bitmapfont/v2 v2.2.1/go.mod h1:wjrYAy8vKgj9JsFgnYAOK346/uvE22TlmqouzdnYIs0=
|
||||||
github.com/hajimehoshi/ebiten/v2 v2.4.7 h1:XuvB7R0Rbw/O7g6vNU8gqr5b9e7MNhhAONMSsyreLDI=
|
github.com/hajimehoshi/ebiten/v2 v2.4.7 h1:XuvB7R0Rbw/O7g6vNU8gqr5b9e7MNhhAONMSsyreLDI=
|
||||||
@@ -93,4 +95,8 @@ golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR
|
|||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||||
|
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
@@ -85,8 +85,9 @@ type Game struct {
|
|||||||
|
|
||||||
func NewGame() *Game {
|
func NewGame() *Game {
|
||||||
|
|
||||||
// stageName := "simple" // Use this for calibration
|
// stageName := "simple" // Use this for calibration in isometric orientation
|
||||||
stageName := "richsoil"
|
// stageName := "richsoil"
|
||||||
|
stageName := "dungeon"
|
||||||
stageDiscreteW, stageDiscreteH, stageTileW, stageTileH, playerPosMap, barrierMap, err := parseStage(stageName)
|
stageDiscreteW, stageDiscreteH, stageTileW, stageTileH, playerPosMap, barrierMap, err := parseStage(stageName)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -160,7 +161,6 @@ func (g *Game) DebugDraw(screen *ebiten.Image, space *resolv.Space) {
|
|||||||
ebitenutil.DrawLine(screen, cx, cy+ch, cx, cy, drawColor)
|
ebitenutil.DrawLine(screen, cx, cy+ch, cx, cy, drawColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Layout(w, h int) (int, int) {
|
func (g *Game) Layout(w, h int) (int, int) {
|
||||||
|
@@ -1,15 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
. "battle_srv/protos"
|
||||||
. "dnmshared"
|
. "dnmshared"
|
||||||
|
. "dnmshared/sharedprotos"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
|
||||||
"github.com/solarlune/resolv"
|
"github.com/solarlune/resolv"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
"math"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type WorldColliderDisplay struct {
|
type WorldColliderDisplay struct {
|
||||||
@@ -22,7 +21,7 @@ func (world *WorldColliderDisplay) Init() {
|
|||||||
|
|
||||||
func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTileW, stageTileH int32, playerPosMap StrToVec2DListMap, barrierMap StrToPolygon2DListMap) *WorldColliderDisplay {
|
func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTileW, stageTileH int32, playerPosMap StrToVec2DListMap, barrierMap StrToPolygon2DListMap) *WorldColliderDisplay {
|
||||||
|
|
||||||
playerList := *(playerPosMap["PlayerStartingPos"])
|
playerPosList := *(playerPosMap["PlayerStartingPos"])
|
||||||
barrierList := *(barrierMap["Barrier"])
|
barrierList := *(barrierMap["Barrier"])
|
||||||
|
|
||||||
world := &WorldColliderDisplay{Game: game}
|
world := &WorldColliderDisplay{Game: game}
|
||||||
@@ -35,52 +34,127 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
|
|||||||
spaceOffsetX := float64(spaceW) * 0.5
|
spaceOffsetX := float64(spaceW) * 0.5
|
||||||
spaceOffsetY := float64(spaceH) * 0.5
|
spaceOffsetY := float64(spaceH) * 0.5
|
||||||
|
|
||||||
// TODO: Move collider y-axis transformation to a "dnmshared"
|
virtualGridToWorldRatio := 0.1
|
||||||
playerColliderRadius := float64(12) // hardcoded
|
playerDefaultSpeed := 20
|
||||||
space := resolv.NewSpace(int(spaceW), int(spaceH), 16, 16)
|
minStep := (int(float64(playerDefaultSpeed)*virtualGridToWorldRatio) << 2)
|
||||||
for _, player := range playerList {
|
playerColliderRadius := float64(24)
|
||||||
playerCollider := resolv.NewObject(player.X+spaceOffsetX, player.Y+spaceOffsetY, playerColliderRadius*2, playerColliderRadius*2, "Player")
|
playerColliders := make([]*resolv.Object, len(playerPosList.Eles))
|
||||||
playerColliderShape := resolv.NewCircle(0, 0, playerColliderRadius*2)
|
space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep)
|
||||||
playerCollider.SetShape(playerColliderShape)
|
for i, playerPos := range playerPosList.Eles {
|
||||||
|
playerCollider := GenerateRectCollider(playerPos.X, playerPos.Y, playerColliderRadius*2, playerColliderRadius*2, spaceOffsetX, spaceOffsetY, "Player") // [WARNING] Deliberately not using a circle because "resolv v0.5.1" doesn't yet align circle center with space cell center, regardless of the "specified within-object offset"
|
||||||
|
Logger.Info(fmt.Sprintf("Player Collider#%d: player world pos =(%.2f, %.2f), shape=%v", i, playerPos.X, playerPos.Y, ConvexPolygonStr(playerCollider.Shape.(*resolv.ConvexPolygon))))
|
||||||
|
playerColliders[i] = playerCollider
|
||||||
space.Add(playerCollider)
|
space.Add(playerCollider)
|
||||||
}
|
}
|
||||||
|
|
||||||
barrierLocalId := 0
|
barrierLocalId := 0
|
||||||
for _, barrierUnaligned := range barrierList {
|
for _, barrierUnaligned := range barrierList.Eles {
|
||||||
barrier := AlignPolygon2DToBoundingBox(barrierUnaligned)
|
barrierCollider := GenerateConvexPolygonCollider(barrierUnaligned, spaceOffsetX, spaceOffsetY, "Barrier")
|
||||||
|
Logger.Info(fmt.Sprintf("Added barrier: shape=%v", ConvexPolygonStr(barrierCollider.Shape.(*resolv.ConvexPolygon))))
|
||||||
var w float64 = 0
|
|
||||||
var h float64 = 0
|
|
||||||
|
|
||||||
for i, pi := range barrier.Points {
|
|
||||||
for j, pj := range barrier.Points {
|
|
||||||
if i == j {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if math.Abs(pj.X-pi.X) > w {
|
|
||||||
w = math.Abs(pj.X - pi.X)
|
|
||||||
}
|
|
||||||
if math.Abs(pj.Y-pi.Y) > h {
|
|
||||||
h = math.Abs(pj.Y - pi.Y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
barrierColliderShape := resolv.NewConvexPolygon()
|
|
||||||
for i := 0; i < len(barrier.Points); i++ {
|
|
||||||
p := barrier.Points[i]
|
|
||||||
barrierColliderShape.AddPoints(p.X, p.Y)
|
|
||||||
}
|
|
||||||
|
|
||||||
barrierCollider := resolv.NewObject(barrier.Anchor.X+spaceOffsetX, barrier.Anchor.Y+spaceOffsetY, w, h, "Barrier")
|
|
||||||
barrierCollider.SetShape(barrierColliderShape)
|
|
||||||
|
|
||||||
space.Add(barrierCollider)
|
space.Add(barrierCollider)
|
||||||
|
|
||||||
barrierLocalId++
|
barrierLocalId++
|
||||||
}
|
}
|
||||||
|
|
||||||
world.Space = space
|
world.Space = space
|
||||||
|
|
||||||
|
moveToCollide := false
|
||||||
|
if moveToCollide {
|
||||||
|
newVx, newVy := int32(-2959), int32(-2261)
|
||||||
|
effPushback := Vec2D{X: float64(0), Y: float64(0)}
|
||||||
|
toTestPlayerCollider := playerColliders[0]
|
||||||
|
toTestPlayerCollider.X, toTestPlayerCollider.Y = VirtualGridToPolygonColliderAnchorPos(newVx, newVy, playerColliderRadius, playerColliderRadius, spaceOffsetX, spaceOffsetY, virtualGridToWorldRatio)
|
||||||
|
|
||||||
|
Logger.Info(fmt.Sprintf("Checking collision for virtual (%d, %d), now playerShape=%v", newVx, newVy, ConvexPolygonStr(toTestPlayerCollider.Shape.(*resolv.ConvexPolygon))))
|
||||||
|
|
||||||
|
toTestPlayerCollider.Update()
|
||||||
|
if collision := toTestPlayerCollider.Check(0, 0); collision != nil {
|
||||||
|
playerShape := toTestPlayerCollider.Shape.(*resolv.ConvexPolygon)
|
||||||
|
for _, obj := range collision.Objects {
|
||||||
|
barrierShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||||
|
if overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, playerShape, barrierShape); overlapped {
|
||||||
|
Logger.Warn(fmt.Sprintf("Overlapped: a=%v, b=%v, pushbackX=%v, pushbackY=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), pushbackX, pushbackY))
|
||||||
|
effPushback.X += pushbackX
|
||||||
|
effPushback.Y += pushbackY
|
||||||
|
} else {
|
||||||
|
Logger.Warn(fmt.Sprintf("Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), overlapResult))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toTestPlayerCollider.X -= effPushback.X
|
||||||
|
toTestPlayerCollider.Y -= effPushback.Y
|
||||||
|
toTestPlayerCollider.Update()
|
||||||
|
Logger.Info(fmt.Sprintf("effPushback={%v, %v}", effPushback.X, effPushback.Y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
meleeBullet := &MeleeBullet{
|
||||||
|
// for offender
|
||||||
|
StartupFrames: int32(18),
|
||||||
|
ActiveFrames: int32(1),
|
||||||
|
RecoveryFrames: int32(61),
|
||||||
|
RecoveryFramesOnBlock: int32(61),
|
||||||
|
RecoveryFramesOnHit: int32(61),
|
||||||
|
Moveforward: &Vec2D{
|
||||||
|
X: 0,
|
||||||
|
Y: 0,
|
||||||
|
},
|
||||||
|
HitboxOffset: float64(24.0),
|
||||||
|
HitboxSize: &Vec2D{
|
||||||
|
X: float64(45.0),
|
||||||
|
Y: float64(32.0),
|
||||||
|
},
|
||||||
|
|
||||||
|
// for defender
|
||||||
|
HitStunFrames: int32(18),
|
||||||
|
BlockStunFrames: int32(9),
|
||||||
|
Pushback: float64(22.0),
|
||||||
|
ReleaseTriggerType: int32(1), // 1: rising-edge, 2: falling-edge
|
||||||
|
Damage: int32(5),
|
||||||
|
}
|
||||||
|
bulletLeftToRight := true
|
||||||
|
if bulletLeftToRight {
|
||||||
|
xfac := float64(1.0)
|
||||||
|
offenderWx, offenderWy := playerPosList.Eles[0].X, playerPosList.Eles[0].Y
|
||||||
|
bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
|
||||||
|
|
||||||
|
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, spaceOffsetX, spaceOffsetY, "MeleeBullet")
|
||||||
|
space.Add(newBulletCollider)
|
||||||
|
bulletShape := newBulletCollider.Shape.(*resolv.ConvexPolygon)
|
||||||
|
Logger.Warn(fmt.Sprintf("bullet ->: Added bullet collider to space: a=%v", ConvexPolygonStr(bulletShape)))
|
||||||
|
|
||||||
|
if collision := newBulletCollider.Check(0, 0); collision != nil {
|
||||||
|
for _, obj := range collision.Objects {
|
||||||
|
objShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||||
|
if overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, bulletShape, objShape); overlapped {
|
||||||
|
Logger.Warn(fmt.Sprintf("bullet ->: Overlapped: a=%v, b=%v, pushbackX=%v, pushbackY=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(objShape), pushbackX, pushbackY))
|
||||||
|
} else {
|
||||||
|
Logger.Warn(fmt.Sprintf("bullet ->: Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(objShape), overlapResult))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bulletRightToLeft := true
|
||||||
|
if bulletRightToLeft {
|
||||||
|
xfac := float64(-1.0)
|
||||||
|
offenderWx, offenderWy := playerPosList.Eles[1].X, playerPosList.Eles[1].Y
|
||||||
|
bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
|
||||||
|
|
||||||
|
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, spaceOffsetX, spaceOffsetY, "MeleeBullet")
|
||||||
|
space.Add(newBulletCollider)
|
||||||
|
bulletShape := newBulletCollider.Shape.(*resolv.ConvexPolygon)
|
||||||
|
Logger.Warn(fmt.Sprintf("bullet <-: Added bullet collider to space: a=%v", ConvexPolygonStr(bulletShape)))
|
||||||
|
|
||||||
|
if collision := newBulletCollider.Check(0, 0); collision != nil {
|
||||||
|
for _, obj := range collision.Objects {
|
||||||
|
objShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||||
|
if overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, bulletShape, objShape); overlapped {
|
||||||
|
Logger.Warn(fmt.Sprintf("bullet <-: Overlapped: a=%v, b=%v, pushbackX=%v, pushbackY=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(objShape), pushbackX, pushbackY))
|
||||||
|
} else {
|
||||||
|
Logger.Warn(fmt.Sprintf("bullet <-: Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(objShape), overlapResult))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return world
|
return world
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,9 +166,11 @@ func (world *WorldColliderDisplay) Draw(screen *ebiten.Image) {
|
|||||||
|
|
||||||
for _, o := range world.Space.Objects() {
|
for _, o := range world.Space.Objects() {
|
||||||
if o.HasTags("Player") {
|
if o.HasTags("Player") {
|
||||||
circle := o.Shape.(*resolv.Circle)
|
|
||||||
drawColor := color.RGBA{0, 255, 0, 255}
|
drawColor := color.RGBA{0, 255, 0, 255}
|
||||||
ebitenutil.DrawCircle(screen, circle.X, circle.Y, circle.Radius, drawColor)
|
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
|
||||||
|
} else if o.HasTags("MeleeBullet") {
|
||||||
|
drawColor := color.RGBA{0, 0, 255, 255}
|
||||||
|
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
|
||||||
} else {
|
} else {
|
||||||
drawColor := color.RGBA{60, 60, 60, 255}
|
drawColor := color.RGBA{60, 60, 60, 255}
|
||||||
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
|
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
|
||||||
|
@@ -1,43 +1,46 @@
|
|||||||
package dnmshared
|
package dnmshared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
. "dnmshared/sharedprotos"
|
||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Use type `float64` for json unmarshalling of numbers.
|
func NormVec2D(dx, dy float64) Vec2D {
|
||||||
type Direction struct {
|
return Vec2D{X: dy, Y: -dx}
|
||||||
Dx int32 `json:"dx,omitempty"`
|
|
||||||
Dy int32 `json:"dy,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Vec2D struct {
|
func AlignPolygon2DToBoundingBox(input *Polygon2D) *Polygon2D {
|
||||||
X float64 `json:"x,omitempty"`
|
// Transform again to put "anchor" at the top-left point of the bounding box for "resolv"
|
||||||
Y float64 `json:"y,omitempty"`
|
boundingBoxTL := &Vec2D{
|
||||||
}
|
X: math.MaxFloat64,
|
||||||
|
Y: math.MaxFloat64,
|
||||||
|
}
|
||||||
|
for _, p := range input.Points {
|
||||||
|
if p.X < boundingBoxTL.X {
|
||||||
|
boundingBoxTL.X = p.X
|
||||||
|
}
|
||||||
|
if p.Y < boundingBoxTL.Y {
|
||||||
|
boundingBoxTL.Y = p.Y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Polygon2D struct {
|
// Now "input.Anchor" should move to "input.Anchor+boundingBoxTL", thus "boundingBoxTL" is also the value of the negative diff for all "input.Points"
|
||||||
Anchor *Vec2D `json:"-"` // This "Polygon2D.Anchor" is used to be assigned to "B2BodyDef.Position", which in turn is used as the position of the FIRST POINT of the polygon.
|
output := &Polygon2D{
|
||||||
Points []*Vec2D `json:"-"`
|
Anchor: &Vec2D{
|
||||||
|
X: input.Anchor.X + boundingBoxTL.X,
|
||||||
|
Y: input.Anchor.Y + boundingBoxTL.Y,
|
||||||
|
},
|
||||||
|
Points: make([]*Vec2D, len(input.Points)),
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
for i, p := range input.Points {
|
||||||
When used to represent a "polyline directly drawn in a `Tmx file`", we can initialize both "Anchor" and "Points" simultaneously.
|
output.Points[i] = &Vec2D{
|
||||||
|
X: p.X - boundingBoxTL.X,
|
||||||
|
Y: p.Y - boundingBoxTL.Y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Yet when used to represent a "polyline drawn in a `Tsx file`", we have to first initialize "Points w.r.t. center of the tile-rectangle", and then "Anchor(initially nil) of the tile positioned in the `Tmx file`".
|
return output
|
||||||
|
|
||||||
Refer to https://shimo.im/docs/SmLJJhXm2C8XMzZT for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
[WARNING] Used to cache "`TileWidth & TileHeight` of a Tsx file" only.
|
|
||||||
*/
|
|
||||||
TileWidth int
|
|
||||||
TileHeight int
|
|
||||||
|
|
||||||
/*
|
|
||||||
[WARNING] Used to cache "`Width & TileHeight` of an object in Tmx file" only.
|
|
||||||
*/
|
|
||||||
TmxObjectWidth float64
|
|
||||||
TmxObjectHeight float64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Distance(pt1 *Vec2D, pt2 *Vec2D) float64 {
|
func Distance(pt1 *Vec2D, pt2 *Vec2D) float64 {
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
module tiled
|
module dnmshared
|
||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
259
dnmshared/resolv_helper.go
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
package dnmshared
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "dnmshared/sharedprotos"
|
||||||
|
"fmt"
|
||||||
|
"github.com/kvartborg/vector"
|
||||||
|
"github.com/solarlune/resolv"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConvexPolygonStr(body *resolv.ConvexPolygon) string {
|
||||||
|
var s []string = make([]string, len(body.Points))
|
||||||
|
for i, p := range body.Points {
|
||||||
|
s[i] = fmt.Sprintf("[%.2f, %.2f]", p[0]+body.X, p[1]+body.Y)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("{\n%s\n}", strings.Join(s, ",\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRectCollider(origX, origY, w, h, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
|
||||||
|
cx, cy := WorldToPolygonColliderAnchorPos(origX, origY, w*0.5, h*0.5, spaceOffsetX, spaceOffsetY)
|
||||||
|
return GenerateRectColliderInCollisionSpace(cx, cy, w, h, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRectColliderInCollisionSpace(cx, cy, w, h float64, tag string) *resolv.Object {
|
||||||
|
collider := resolv.NewObject(cx, cy, w, h, tag)
|
||||||
|
shape := resolv.NewRectangle(0, 0, w, h)
|
||||||
|
collider.SetShape(shape)
|
||||||
|
return collider
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateConvexPolygonCollider(unalignedSrc *Polygon2D, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
|
||||||
|
aligned := AlignPolygon2DToBoundingBox(unalignedSrc)
|
||||||
|
var w, h float64 = 0, 0
|
||||||
|
|
||||||
|
shape := resolv.NewConvexPolygon()
|
||||||
|
for i, pi := range aligned.Points {
|
||||||
|
for j, pj := range aligned.Points {
|
||||||
|
if i == j {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if math.Abs(pj.X-pi.X) > w {
|
||||||
|
w = math.Abs(pj.X - pi.X)
|
||||||
|
}
|
||||||
|
if math.Abs(pj.Y-pi.Y) > h {
|
||||||
|
h = math.Abs(pj.Y - pi.Y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(aligned.Points); i++ {
|
||||||
|
p := aligned.Points[i]
|
||||||
|
shape.AddPoints(p.X, p.Y)
|
||||||
|
}
|
||||||
|
|
||||||
|
collider := resolv.NewObject(aligned.Anchor.X+spaceOffsetX, aligned.Anchor.Y+spaceOffsetY, w, h, tag)
|
||||||
|
collider.SetShape(shape)
|
||||||
|
|
||||||
|
return collider
|
||||||
|
}
|
||||||
|
|
||||||
|
func CalcPushbacks(oldDx, oldDy float64, playerShape, barrierShape *resolv.ConvexPolygon) (bool, float64, float64, *SatResult) {
|
||||||
|
origX, origY := playerShape.Position()
|
||||||
|
defer func() {
|
||||||
|
playerShape.SetPosition(origX, origY)
|
||||||
|
}()
|
||||||
|
playerShape.SetPosition(origX+oldDx, origY+oldDy)
|
||||||
|
overlapResult := &SatResult{
|
||||||
|
Overlap: 0,
|
||||||
|
OverlapX: 0,
|
||||||
|
OverlapY: 0,
|
||||||
|
AContainedInB: true,
|
||||||
|
BContainedInA: true,
|
||||||
|
Axis: vector.Vector{0, 0},
|
||||||
|
}
|
||||||
|
if overlapped := IsPolygonPairOverlapped(playerShape, barrierShape, overlapResult); overlapped {
|
||||||
|
pushbackX, pushbackY := overlapResult.Overlap*overlapResult.OverlapX, overlapResult.Overlap*overlapResult.OverlapY
|
||||||
|
return true, pushbackX, pushbackY, overlapResult
|
||||||
|
} else {
|
||||||
|
return false, 0, 0, overlapResult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SatResult struct {
|
||||||
|
Overlap float64
|
||||||
|
OverlapX float64
|
||||||
|
OverlapY float64
|
||||||
|
AContainedInB bool
|
||||||
|
BContainedInA bool
|
||||||
|
Axis vector.Vector
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPolygonPairOverlapped(a, b *resolv.ConvexPolygon, result *SatResult) bool {
|
||||||
|
aCnt, bCnt := len(a.Points), len(b.Points)
|
||||||
|
// Single point case
|
||||||
|
if 1 == aCnt && 1 == bCnt {
|
||||||
|
if nil != result {
|
||||||
|
result.Overlap = 0
|
||||||
|
}
|
||||||
|
return a.Points[0].X() == b.Points[0].X() && a.Points[0].Y() == b.Points[0].Y()
|
||||||
|
}
|
||||||
|
|
||||||
|
if 1 < aCnt {
|
||||||
|
for _, axis := range a.SATAxes() {
|
||||||
|
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if 1 < bCnt {
|
||||||
|
for _, axis := range b.SATAxes() {
|
||||||
|
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e vector.Vector, result *SatResult) bool {
|
||||||
|
/*
|
||||||
|
[WARNING] This function is deliberately made private, it shouldn't be used alone (i.e. not along the norms of a polygon), otherwise the pushbacks calculated would be meaningless.
|
||||||
|
|
||||||
|
Consider the following example
|
||||||
|
a: {
|
||||||
|
anchor: [1337.19 1696.74]
|
||||||
|
points: [[0 0] [24 0] [24 24] [0 24]]
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
anchor: [1277.72 1570.56]
|
||||||
|
points: [[642.57 319.16] [0 319.16] [5.73 0] [643.75 0.90]]
|
||||||
|
}
|
||||||
|
|
||||||
|
e = (-2.98, 1.49).Unit()
|
||||||
|
*/
|
||||||
|
|
||||||
|
var aStart, aEnd, bStart, bEnd float64 = math.MaxFloat64, -math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64
|
||||||
|
for _, p := range a.Points {
|
||||||
|
dot := (p.X()+a.X)*e.X() + (p.Y()+a.Y)*e.Y()
|
||||||
|
|
||||||
|
if aStart > dot {
|
||||||
|
aStart = dot
|
||||||
|
}
|
||||||
|
|
||||||
|
if aEnd < dot {
|
||||||
|
aEnd = dot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range b.Points {
|
||||||
|
dot := (p.X()+b.X)*e.X() + (p.Y()+b.Y)*e.Y()
|
||||||
|
|
||||||
|
if bStart > dot {
|
||||||
|
bStart = dot
|
||||||
|
}
|
||||||
|
|
||||||
|
if bEnd < dot {
|
||||||
|
bEnd = dot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if aStart > bEnd || aEnd < bStart {
|
||||||
|
// Separated by unit vector "e"
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if nil != result {
|
||||||
|
result.Axis = e
|
||||||
|
overlap := float64(0)
|
||||||
|
|
||||||
|
if aStart < bStart {
|
||||||
|
result.AContainedInB = false
|
||||||
|
|
||||||
|
if aEnd < bEnd {
|
||||||
|
overlap = aEnd - bStart
|
||||||
|
result.BContainedInA = false
|
||||||
|
} else {
|
||||||
|
option1 := aEnd - bStart
|
||||||
|
option2 := bEnd - aStart
|
||||||
|
if option1 < option2 {
|
||||||
|
overlap = option1
|
||||||
|
} else {
|
||||||
|
overlap = -option2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.BContainedInA = false
|
||||||
|
|
||||||
|
if aEnd > bEnd {
|
||||||
|
overlap = aStart - bEnd
|
||||||
|
result.AContainedInB = false
|
||||||
|
} else {
|
||||||
|
option1 := aEnd - bStart
|
||||||
|
option2 := bEnd - aStart
|
||||||
|
if option1 < option2 {
|
||||||
|
overlap = option1
|
||||||
|
} else {
|
||||||
|
overlap = -option2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentOverlap := result.Overlap
|
||||||
|
absoluteOverlap := overlap
|
||||||
|
if overlap < 0 {
|
||||||
|
absoluteOverlap = -overlap
|
||||||
|
}
|
||||||
|
|
||||||
|
if 0 == currentOverlap || currentOverlap > absoluteOverlap {
|
||||||
|
var sign float64 = 1
|
||||||
|
if overlap < 0 {
|
||||||
|
sign = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Overlap = absoluteOverlap
|
||||||
|
result.OverlapX = e.X() * sign
|
||||||
|
result.OverlapY = e.Y() * sign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the specified unit vector "e" doesn't separate "a" and "b", overlap result is generated
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio float64) (int32, int32) {
|
||||||
|
// [WARNING] Introduces loss of precision!
|
||||||
|
// In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains.
|
||||||
|
var virtualGridX int32 = int32(math.Round(wx * worldToVirtualGridRatio))
|
||||||
|
var virtualGridY int32 = int32(math.Round(wy * worldToVirtualGridRatio))
|
||||||
|
return virtualGridX, virtualGridY
|
||||||
|
}
|
||||||
|
|
||||||
|
func VirtualGridToWorldPos(vx, vy int32, virtualGridToWorldRatio float64) (float64, float64) {
|
||||||
|
// No loss of precision
|
||||||
|
var wx float64 = float64(vx) * virtualGridToWorldRatio
|
||||||
|
var wy float64 = float64(vy) * virtualGridToWorldRatio
|
||||||
|
return wx, wy
|
||||||
|
}
|
||||||
|
|
||||||
|
func WorldToPolygonColliderAnchorPos(wx, wy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
|
||||||
|
return wx - halfBoundingW + collisionSpaceOffsetX, wy - halfBoundingH + collisionSpaceOffsetY
|
||||||
|
}
|
||||||
|
|
||||||
|
func PolygonColliderAnchorToWorldPos(cx, cy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
|
||||||
|
return cx + halfBoundingW - collisionSpaceOffsetX, cy + halfBoundingH - collisionSpaceOffsetY
|
||||||
|
}
|
||||||
|
|
||||||
|
func PolygonColliderAnchorToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64, worldToVirtualGridRatio float64) (int32, int32) {
|
||||||
|
wx, wy := PolygonColliderAnchorToWorldPos(cx, cy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY)
|
||||||
|
return WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio)
|
||||||
|
}
|
||||||
|
|
||||||
|
func VirtualGridToPolygonColliderAnchorPos(vx, vy int32, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY float64, virtualGridToWorldRatio float64) (float64, float64) {
|
||||||
|
wx, wy := VirtualGridToWorldPos(vx, vy, virtualGridToWorldRatio)
|
||||||
|
return WorldToPolygonColliderAnchorPos(wx, wy, halfBoundingW, halfBoundingH, collisionSpaceOffsetX, collisionSpaceOffsetY)
|
||||||
|
}
|
427
dnmshared/sharedprotos/geometry.pb.go
Normal file
@@ -0,0 +1,427 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.28.1
|
||||||
|
// protoc v3.21.4
|
||||||
|
// source: geometry.proto
|
||||||
|
|
||||||
|
package sharedprotos
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Direction struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Dx int32 `protobuf:"varint,1,opt,name=dx,proto3" json:"dx,omitempty"`
|
||||||
|
Dy int32 `protobuf:"varint,2,opt,name=dy,proto3" json:"dy,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Direction) Reset() {
|
||||||
|
*x = Direction{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_geometry_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Direction) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Direction) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Direction) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_geometry_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Direction.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Direction) Descriptor() ([]byte, []int) {
|
||||||
|
return file_geometry_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Direction) GetDx() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Dx
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Direction) GetDy() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Dy
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Vec2D struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
X float64 `protobuf:"fixed64,1,opt,name=x,proto3" json:"x,omitempty"`
|
||||||
|
Y float64 `protobuf:"fixed64,2,opt,name=y,proto3" json:"y,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Vec2D) Reset() {
|
||||||
|
*x = Vec2D{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_geometry_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Vec2D) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Vec2D) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Vec2D) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_geometry_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Vec2D.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Vec2D) Descriptor() ([]byte, []int) {
|
||||||
|
return file_geometry_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Vec2D) GetX() float64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.X
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Vec2D) GetY() float64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Y
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Polygon2D struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Anchor *Vec2D `protobuf:"bytes,1,opt,name=anchor,proto3" json:"anchor,omitempty"`
|
||||||
|
Points []*Vec2D `protobuf:"bytes,2,rep,name=points,proto3" json:"points,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Polygon2D) Reset() {
|
||||||
|
*x = Polygon2D{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_geometry_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Polygon2D) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Polygon2D) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Polygon2D) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_geometry_proto_msgTypes[2]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Polygon2D.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Polygon2D) Descriptor() ([]byte, []int) {
|
||||||
|
return file_geometry_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Polygon2D) GetAnchor() *Vec2D {
|
||||||
|
if x != nil {
|
||||||
|
return x.Anchor
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Polygon2D) GetPoints() []*Vec2D {
|
||||||
|
if x != nil {
|
||||||
|
return x.Points
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Vec2DList struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Eles []*Vec2D `protobuf:"bytes,1,rep,name=eles,proto3" json:"eles,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Vec2DList) Reset() {
|
||||||
|
*x = Vec2DList{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_geometry_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Vec2DList) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Vec2DList) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Vec2DList) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_geometry_proto_msgTypes[3]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Vec2DList.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Vec2DList) Descriptor() ([]byte, []int) {
|
||||||
|
return file_geometry_proto_rawDescGZIP(), []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Vec2DList) GetEles() []*Vec2D {
|
||||||
|
if x != nil {
|
||||||
|
return x.Eles
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Polygon2DList struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Eles []*Polygon2D `protobuf:"bytes,1,rep,name=eles,proto3" json:"eles,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Polygon2DList) Reset() {
|
||||||
|
*x = Polygon2DList{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_geometry_proto_msgTypes[4]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Polygon2DList) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Polygon2DList) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Polygon2DList) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_geometry_proto_msgTypes[4]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Polygon2DList.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Polygon2DList) Descriptor() ([]byte, []int) {
|
||||||
|
return file_geometry_proto_rawDescGZIP(), []int{4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Polygon2DList) GetEles() []*Polygon2D {
|
||||||
|
if x != nil {
|
||||||
|
return x.Eles
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_geometry_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_geometry_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x0e, 0x67, 0x65, 0x6f, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
|
0x12, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x2b,
|
||||||
|
0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x64,
|
||||||
|
0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x64, 0x78, 0x12, 0x0e, 0x0a, 0x02, 0x64,
|
||||||
|
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x64, 0x79, 0x22, 0x23, 0x0a, 0x05, 0x56,
|
||||||
|
0x65, 0x63, 0x32, 0x44, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52,
|
||||||
|
0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x79,
|
||||||
|
0x22, 0x65, 0x0a, 0x09, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x12, 0x2b, 0x0a,
|
||||||
|
0x06, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e,
|
||||||
|
0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x56, 0x65, 0x63,
|
||||||
|
0x32, 0x44, 0x52, 0x06, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x12, 0x2b, 0x0a, 0x06, 0x70, 0x6f,
|
||||||
|
0x69, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x68, 0x61,
|
||||||
|
0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x56, 0x65, 0x63, 0x32, 0x44, 0x52,
|
||||||
|
0x06, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x34, 0x0a, 0x09, 0x56, 0x65, 0x63, 0x32, 0x44,
|
||||||
|
0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x65, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||||
|
0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
|
0x73, 0x2e, 0x56, 0x65, 0x63, 0x32, 0x44, 0x52, 0x04, 0x65, 0x6c, 0x65, 0x73, 0x22, 0x3c, 0x0a,
|
||||||
|
0x0d, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2b,
|
||||||
|
0x0a, 0x04, 0x65, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73,
|
||||||
|
0x68, 0x61, 0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x50, 0x6f, 0x6c, 0x79,
|
||||||
|
0x67, 0x6f, 0x6e, 0x32, 0x44, 0x52, 0x04, 0x65, 0x6c, 0x65, 0x73, 0x42, 0x18, 0x5a, 0x16, 0x64,
|
||||||
|
0x6e, 0x6d, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_geometry_proto_rawDescOnce sync.Once
|
||||||
|
file_geometry_proto_rawDescData = file_geometry_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_geometry_proto_rawDescGZIP() []byte {
|
||||||
|
file_geometry_proto_rawDescOnce.Do(func() {
|
||||||
|
file_geometry_proto_rawDescData = protoimpl.X.CompressGZIP(file_geometry_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_geometry_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_geometry_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||||
|
var file_geometry_proto_goTypes = []interface{}{
|
||||||
|
(*Direction)(nil), // 0: sharedprotos.Direction
|
||||||
|
(*Vec2D)(nil), // 1: sharedprotos.Vec2D
|
||||||
|
(*Polygon2D)(nil), // 2: sharedprotos.Polygon2D
|
||||||
|
(*Vec2DList)(nil), // 3: sharedprotos.Vec2DList
|
||||||
|
(*Polygon2DList)(nil), // 4: sharedprotos.Polygon2DList
|
||||||
|
}
|
||||||
|
var file_geometry_proto_depIdxs = []int32{
|
||||||
|
1, // 0: sharedprotos.Polygon2D.anchor:type_name -> sharedprotos.Vec2D
|
||||||
|
1, // 1: sharedprotos.Polygon2D.points:type_name -> sharedprotos.Vec2D
|
||||||
|
1, // 2: sharedprotos.Vec2DList.eles:type_name -> sharedprotos.Vec2D
|
||||||
|
2, // 3: sharedprotos.Polygon2DList.eles:type_name -> sharedprotos.Polygon2D
|
||||||
|
4, // [4:4] is the sub-list for method output_type
|
||||||
|
4, // [4:4] is the sub-list for method input_type
|
||||||
|
4, // [4:4] is the sub-list for extension type_name
|
||||||
|
4, // [4:4] is the sub-list for extension extendee
|
||||||
|
0, // [0:4] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_geometry_proto_init() }
|
||||||
|
func file_geometry_proto_init() {
|
||||||
|
if File_geometry_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_geometry_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Direction); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_geometry_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Vec2D); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_geometry_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Polygon2D); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_geometry_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Vec2DList); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_geometry_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Polygon2DList); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_geometry_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 5,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_geometry_proto_goTypes,
|
||||||
|
DependencyIndexes: file_geometry_proto_depIdxs,
|
||||||
|
MessageInfos: file_geometry_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_geometry_proto = out.File
|
||||||
|
file_geometry_proto_rawDesc = nil
|
||||||
|
file_geometry_proto_goTypes = nil
|
||||||
|
file_geometry_proto_depIdxs = nil
|
||||||
|
}
|
@@ -3,6 +3,7 @@ package dnmshared
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/zlib"
|
"compress/zlib"
|
||||||
|
. "dnmshared/sharedprotos"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -173,8 +174,6 @@ func (l *TmxLayer) decodeBase64() ([]uint32, error) {
|
|||||||
return gids, nil
|
return gids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Vec2DList []*Vec2D
|
|
||||||
type Polygon2DList []*Polygon2D
|
|
||||||
type StrToVec2DListMap map[string]*Vec2DList
|
type StrToVec2DListMap map[string]*Vec2DList
|
||||||
type StrToPolygon2DListMap map[string]*Polygon2DList
|
type StrToPolygon2DListMap map[string]*Polygon2DList
|
||||||
|
|
||||||
@@ -233,10 +232,8 @@ func tsxPolylineToOffsetsWrtTileCenter(pTmxMapIns *TmxMap, singleObjInTsxFile *T
|
|||||||
pointsCount := len(singleValueArray)
|
pointsCount := len(singleValueArray)
|
||||||
|
|
||||||
thePolygon2DFromPolyline := &Polygon2D{
|
thePolygon2DFromPolyline := &Polygon2D{
|
||||||
Anchor: nil,
|
Anchor: nil,
|
||||||
Points: make([]*Vec2D, pointsCount),
|
Points: make([]*Vec2D, pointsCount),
|
||||||
TileWidth: pTsxIns.TileWidth,
|
|
||||||
TileHeight: pTsxIns.TileHeight,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -327,16 +324,17 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f
|
|||||||
if _, ok := theStrToPolygon2DListMap[key]; ok {
|
if _, ok := theStrToPolygon2DListMap[key]; ok {
|
||||||
pThePolygon2DList = theStrToPolygon2DListMap[key]
|
pThePolygon2DList = theStrToPolygon2DListMap[key]
|
||||||
} else {
|
} else {
|
||||||
thePolygon2DList := make(Polygon2DList, 0)
|
pThePolygon2DList = &Polygon2DList{
|
||||||
theStrToPolygon2DListMap[key] = &thePolygon2DList
|
Eles: make([]*Polygon2D, 0),
|
||||||
pThePolygon2DList = theStrToPolygon2DListMap[key]
|
}
|
||||||
|
theStrToPolygon2DListMap[key] = pThePolygon2DList
|
||||||
}
|
}
|
||||||
|
|
||||||
thePolygon2DFromPolyline, err := tsxPolylineToOffsetsWrtTileCenter(pTmxMapIns, singleObj, singleObj.Polyline, pTsxIns)
|
thePolygon2DFromPolyline, err := tsxPolylineToOffsetsWrtTileCenter(pTmxMapIns, singleObj, singleObj.Polyline, pTsxIns)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
*pThePolygon2DList = append(*pThePolygon2DList, thePolygon2DFromPolyline)
|
pThePolygon2DList.Eles = append(pThePolygon2DList.Eles, thePolygon2DFromPolyline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -352,8 +350,10 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToP
|
|||||||
var pTheVec2DListToCache *Vec2DList
|
var pTheVec2DListToCache *Vec2DList
|
||||||
_, ok := toRetStrToVec2DListMap[objGroup.Name]
|
_, ok := toRetStrToVec2DListMap[objGroup.Name]
|
||||||
if false == ok {
|
if false == ok {
|
||||||
theVec2DListToCache := make(Vec2DList, 0)
|
pTheVec2DListToCache = &Vec2DList{
|
||||||
toRetStrToVec2DListMap[objGroup.Name] = &theVec2DListToCache
|
Eles: make([]*Vec2D, 0),
|
||||||
|
}
|
||||||
|
toRetStrToVec2DListMap[objGroup.Name] = pTheVec2DListToCache
|
||||||
}
|
}
|
||||||
pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name]
|
pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name]
|
||||||
for _, singleObjInTmxFile := range objGroup.Objects {
|
for _, singleObjInTmxFile := range objGroup.Objects {
|
||||||
@@ -362,17 +362,18 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToP
|
|||||||
Y: singleObjInTmxFile.Y,
|
Y: singleObjInTmxFile.Y,
|
||||||
}
|
}
|
||||||
thePosInWorld := pTmxMapIns.continuousObjLayerOffsetToContinuousMapNodePos(theUntransformedPos)
|
thePosInWorld := pTmxMapIns.continuousObjLayerOffsetToContinuousMapNodePos(theUntransformedPos)
|
||||||
*pTheVec2DListToCache = append(*pTheVec2DListToCache, &thePosInWorld)
|
pTheVec2DListToCache.Eles = append(pTheVec2DListToCache.Eles, &thePosInWorld)
|
||||||
}
|
}
|
||||||
case "Barrier":
|
case "Barrier":
|
||||||
// Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is exactly overlapping with "Polygon2D.Points[0]".
|
// Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is exactly overlapping with "Polygon2D.Points[0]".
|
||||||
var pThePolygon2DListToCache *Polygon2DList
|
var pThePolygon2DListToCache *Polygon2DList
|
||||||
_, ok := toRetStrToPolygon2DListMap[objGroup.Name]
|
_, ok := toRetStrToPolygon2DListMap[objGroup.Name]
|
||||||
if false == ok {
|
if false == ok {
|
||||||
thePolygon2DListToCache := make(Polygon2DList, 0)
|
pThePolygon2DListToCache = &Polygon2DList{
|
||||||
toRetStrToPolygon2DListMap[objGroup.Name] = &thePolygon2DListToCache
|
Eles: make([]*Polygon2D, 0),
|
||||||
|
}
|
||||||
|
toRetStrToPolygon2DListMap[objGroup.Name] = pThePolygon2DListToCache
|
||||||
}
|
}
|
||||||
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
|
|
||||||
|
|
||||||
for _, singleObjInTmxFile := range objGroup.Objects {
|
for _, singleObjInTmxFile := range objGroup.Objects {
|
||||||
if nil == singleObjInTmxFile.Polyline {
|
if nil == singleObjInTmxFile.Polyline {
|
||||||
@@ -386,7 +387,7 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToP
|
|||||||
if nil != err {
|
if nil != err {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
*pThePolygon2DListToCache = append(*pThePolygon2DListToCache, thePolygon2DInWorld)
|
pThePolygon2DListToCache.Eles = append(pThePolygon2DListToCache.Eles, thePolygon2DInWorld)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@@ -405,6 +406,12 @@ type TileRectilinearSize struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pTmxMapIns *TmxMap) continuousObjLayerVecToContinuousMapNodeVec(continuousObjLayerVec *Vec2D) Vec2D {
|
func (pTmxMapIns *TmxMap) continuousObjLayerVecToContinuousMapNodeVec(continuousObjLayerVec *Vec2D) Vec2D {
|
||||||
|
if "orthogonal" == pTmxMapIns.Orientation {
|
||||||
|
return Vec2D{
|
||||||
|
X: continuousObjLayerVec.X,
|
||||||
|
Y: -continuousObjLayerVec.Y,
|
||||||
|
}
|
||||||
|
}
|
||||||
var tileRectilinearSize TileRectilinearSize
|
var tileRectilinearSize TileRectilinearSize
|
||||||
tileRectilinearSize.Width = float64(pTmxMapIns.TileWidth)
|
tileRectilinearSize.Width = float64(pTmxMapIns.TileWidth)
|
||||||
tileRectilinearSize.Height = float64(pTmxMapIns.TileHeight)
|
tileRectilinearSize.Height = float64(pTmxMapIns.TileHeight)
|
||||||
@@ -427,55 +434,24 @@ func (pTmxMapIns *TmxMap) continuousObjLayerVecToContinuousMapNodeVec(continuous
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pTmxMapIns *TmxMap) continuousObjLayerOffsetToContinuousMapNodePos(continuousObjLayerOffset *Vec2D) Vec2D {
|
func (pTmxMapIns *TmxMap) continuousObjLayerOffsetToContinuousMapNodePos(continuousObjLayerOffset *Vec2D) Vec2D {
|
||||||
layerOffset := Vec2D{
|
var layerOffset Vec2D
|
||||||
X: 0,
|
if "orthogonal" == pTmxMapIns.Orientation {
|
||||||
Y: float64(pTmxMapIns.Height*pTmxMapIns.TileHeight) * 0.5,
|
layerOffset = Vec2D{
|
||||||
|
X: -float64(pTmxMapIns.Width*pTmxMapIns.TileWidth) * 0.5,
|
||||||
|
Y: float64(pTmxMapIns.Height*pTmxMapIns.TileHeight) * 0.5,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// "isometric" == pTmxMapIns.Orientation
|
||||||
|
layerOffset = Vec2D{
|
||||||
|
X: 0,
|
||||||
|
Y: float64(pTmxMapIns.Height*pTmxMapIns.TileHeight) * 0.5,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
calibratedVec := continuousObjLayerOffset
|
convertedVec := pTmxMapIns.continuousObjLayerVecToContinuousMapNodeVec(continuousObjLayerOffset)
|
||||||
convertedVec := pTmxMapIns.continuousObjLayerVecToContinuousMapNodeVec(calibratedVec)
|
|
||||||
|
|
||||||
toRet := Vec2D{
|
return Vec2D{
|
||||||
X: layerOffset.X + convertedVec.X,
|
X: layerOffset.X + convertedVec.X,
|
||||||
Y: layerOffset.Y + convertedVec.Y,
|
Y: layerOffset.Y + convertedVec.Y,
|
||||||
}
|
}
|
||||||
|
|
||||||
return toRet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AlignPolygon2DToBoundingBox(input *Polygon2D) *Polygon2D {
|
|
||||||
// Transform again to put "anchor" at the top-left point of the bounding box for "resolv"
|
|
||||||
float64Max := float64(99999999999999.9)
|
|
||||||
boundingBoxTL := &Vec2D{
|
|
||||||
X: float64Max,
|
|
||||||
Y: float64Max,
|
|
||||||
}
|
|
||||||
for _, p := range input.Points {
|
|
||||||
if p.X < boundingBoxTL.X {
|
|
||||||
boundingBoxTL.X = p.X
|
|
||||||
}
|
|
||||||
if p.Y < boundingBoxTL.Y {
|
|
||||||
boundingBoxTL.Y = p.Y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now "input.Anchor" should move to "input.Anchor+boundingBoxTL", thus "boundingBoxTL" is also the value of the negative diff for all "input.Points"
|
|
||||||
output := &Polygon2D{
|
|
||||||
Anchor: &Vec2D{
|
|
||||||
X: input.Anchor.X+boundingBoxTL.X,
|
|
||||||
Y: input.Anchor.Y+boundingBoxTL.Y,
|
|
||||||
},
|
|
||||||
Points: make([]*Vec2D, len(input.Points)),
|
|
||||||
TileWidth: input.TileWidth,
|
|
||||||
TileHeight: input.TileHeight,
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, p := range input.Points {
|
|
||||||
output.Points[i] = &Vec2D{
|
|
||||||
X: p.X-boundingBoxTL.X,
|
|
||||||
Y: p.Y-boundingBoxTL.Y,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
BIN
dragonBonesAnimProjects/SoldierElf.dbproj
Normal file
BIN
dragonBonesAnimProjects/SoldierFireGhost.dbproj
Normal file
BIN
dragonBonesAnimProjects/SoldierWaterGhost.dbproj
Normal file
1
dragonBonesAnimProjects/library/SoldierElf.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"width":128,"imagePath":"SoldierElf_tex.png","height":128,"name":"SoldierElf","SubTexture":[{"frameHeight":45,"y":1,"frameX":0,"width":34,"frameY":0,"height":44,"name":"cape","frameWidth":34,"x":70},{"width":10,"y":107,"height":14,"name":"shouder_l","x":74},{"width":11,"y":107,"height":14,"name":"forearm_l","x":61},{"width":15,"y":93,"height":16,"name":"hand_l","x":1},{"width":30,"y":61,"height":30,"name":"weapon_hand_l","x":1},{"width":8,"y":101,"height":11,"name":"thigh_l","x":86},{"width":12,"y":93,"height":17,"name":"calf_l","x":18},{"width":20,"y":113,"height":8,"name":"foot_l","x":39},{"width":28,"y":61,"height":31,"name":"pelvis","x":33},{"width":8,"y":88,"height":11,"name":"thigh_r","x":77},{"width":12,"y":88,"height":17,"name":"calf_r","x":63},{"width":20,"y":113,"height":8,"name":"foot_r","x":17},{"width":13,"y":94,"height":12,"name":"shouder_r","x":45},{"width":67,"y":1,"height":58,"name":"chest","x":1},{"width":11,"y":94,"height":17,"name":"forearm_r","x":32},{"width":14,"y":111,"height":13,"name":"hand_r","x":1},{"frameHeight":39,"y":47,"frameX":-2,"width":34,"frameY":0,"height":39,"name":"we_bl_4_f_1","frameWidth":36,"x":70}]}
|
BIN
dragonBonesAnimProjects/library/SoldierElf.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
1
dragonBonesAnimProjects/library/SoldierFireGhost.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"imagePath":"Soldier_02_tex.png","width":128,"name":"Soldier_02","SubTexture":[{"x":53,"y":44,"width":23,"name":"biu","height":22},{"x":76,"y":68,"width":9,"name":"rightArm","height":14},{"y":35,"frameY":0,"height":32,"frameWidth":29,"frameX":-1,"frameHeight":32,"width":27,"name":"yinmoqe00","x":89},{"x":53,"y":1,"width":34,"name":"body","height":41},{"x":78,"y":44,"width":9,"name":"rightShoulder","height":13},{"y":50,"frameY":0,"height":18,"frameWidth":19,"frameX":0,"frameHeight":18,"width":19,"name":"rightFrontArm","x":23},{"x":23,"y":70,"width":14,"name":"rightHand","height":14},{"y":68,"frameY":0,"height":12,"frameWidth":12,"frameX":0,"frameHeight":12,"width":12,"name":"leftArm","x":62},{"x":1,"y":73,"width":13,"name":"leftShoulder","height":12},{"x":1,"y":50,"width":20,"name":"leftFrontArm","height":21},{"x":89,"y":1,"width":33,"name":"head","height":32},{"x":1,"y":1,"width":50,"name":"head2","height":47},{"x":44,"y":68,"width":16,"name":"leftHand","height":14},{"y":59,"frameY":-2,"height":4,"frameWidth":8,"frameX":-1,"frameHeight":8,"width":4,"name":"huomiao01","x":78}],"height":128}
|
BIN
dragonBonesAnimProjects/library/SoldierFireGhost.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
1
dragonBonesAnimProjects/library/SoldierWaterGhost.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"SubTexture":[{"width":23,"y":44,"height":22,"name":"biu","x":53},{"width":9,"y":68,"height":14,"name":"rightArm","x":76},{"y":35,"frameX":-1,"frameY":0,"width":27,"frameWidth":29,"height":32,"name":"yinmoqe00","frameHeight":32,"x":89},{"width":34,"y":1,"height":41,"name":"body","x":53},{"width":9,"y":44,"height":13,"name":"rightShoulder","x":78},{"y":50,"frameX":0,"frameY":0,"width":19,"frameWidth":19,"height":18,"name":"rightFrontArm","frameHeight":18,"x":23},{"width":14,"y":70,"height":14,"name":"rightHand","x":23},{"y":68,"frameX":0,"frameY":0,"width":12,"frameWidth":12,"height":12,"name":"leftArm","frameHeight":12,"x":62},{"width":13,"y":73,"height":12,"name":"leftShoulder","x":1},{"width":20,"y":50,"height":21,"name":"leftFrontArm","x":1},{"width":33,"y":1,"height":32,"name":"head","x":89},{"width":50,"y":1,"height":47,"name":"head2","x":1},{"width":16,"y":68,"height":14,"name":"leftHand","x":44},{"y":59,"frameX":-1,"frameY":-2,"width":4,"frameWidth":8,"height":4,"name":"huomiao01","frameHeight":8,"x":78}],"width":128,"height":128,"name":"SoldierWaterGhost","imagePath":"SoldierWaterGhost_tex.png"}
|
BIN
dragonBonesAnimProjects/library/SoldierWaterGhost.png
Normal file
After Width: | Height: | Size: 11 KiB |
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"ver": "1.0.5",
|
|
||||||
"uuid": "bd514df4-095e-4088-9060-d99397a29a4f",
|
|
||||||
"isPlugin": true,
|
|
||||||
"loadPluginInWeb": true,
|
|
||||||
"loadPluginInNative": true,
|
|
||||||
"loadPluginInEditor": false,
|
|
||||||
"subMetas": {}
|
|
||||||
}
|
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ver": "1.0.1",
|
"ver": "1.0.1",
|
||||||
"uuid": "51c54820-d753-4be8-a855-5760eed8f7ef",
|
"uuid": "8f2f76c7-649c-414a-80be-b2daef4ed580",
|
||||||
"isSubpackage": false,
|
"isSubpackage": false,
|
||||||
"subpackageName": "",
|
"subpackageName": "",
|
||||||
"subMetas": {}
|
"subMetas": {}
|
@@ -0,0 +1 @@
|
|||||||
|
{"SubTexture":[{"y":50,"frameX":-2,"frameY":-2,"width":19,"frameWidth":23,"height":19,"name":"biu","frameHeight":22,"x":1},{"width":9,"y":50,"height":14,"name":"rightArm","x":42},{"y":34,"frameX":-6,"frameY":0,"width":20,"frameWidth":29,"height":32,"name":"yinmoqe00","frameHeight":32,"x":88},{"y":1,"frameX":0,"frameY":0,"width":33,"frameWidth":34,"height":39,"name":"body","frameHeight":41,"x":53},{"width":9,"y":56,"height":13,"name":"rightShoulder","x":74},{"y":50,"frameX":0,"frameY":0,"width":18,"frameWidth":19,"height":17,"name":"rightFrontArm","frameHeight":18,"x":22},{"width":14,"y":50,"height":14,"name":"rightHand","x":110},{"width":12,"y":42,"height":12,"name":"leftArm","x":74},{"width":13,"y":66,"height":12,"name":"leftShoulder","x":110},{"y":42,"frameX":-1,"frameY":0,"width":19,"frameWidth":20,"height":21,"name":"leftFrontArm","frameHeight":21,"x":53},{"width":50,"y":1,"height":47,"name":"head2","x":1},{"y":1,"frameX":-1,"frameY":0,"width":32,"frameWidth":33,"height":31,"name":"head","frameHeight":32,"x":88},{"width":16,"y":34,"height":14,"name":"leftHand","x":110},{"y":1,"frameX":-2,"frameY":-3,"width":2,"frameWidth":8,"height":2,"name":"huomiao01","frameHeight":8,"x":122}],"width":128,"height":128,"name":"SoldierWaterGhost","imagePath":"SoldierWaterGhost_tex.png"}
|
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.0",
|
||||||
|
"uuid": "e9e703e9-3589-4713-b889-28b23406d220",
|
||||||
|
"atlasJson": "{\"SubTexture\":[{\"y\":50,\"frameX\":-2,\"frameY\":-2,\"width\":19,\"frameWidth\":23,\"height\":19,\"name\":\"biu\",\"frameHeight\":22,\"x\":1},{\"width\":9,\"y\":50,\"height\":14,\"name\":\"rightArm\",\"x\":42},{\"y\":34,\"frameX\":-6,\"frameY\":0,\"width\":20,\"frameWidth\":29,\"height\":32,\"name\":\"yinmoqe00\",\"frameHeight\":32,\"x\":88},{\"y\":1,\"frameX\":0,\"frameY\":0,\"width\":33,\"frameWidth\":34,\"height\":39,\"name\":\"body\",\"frameHeight\":41,\"x\":53},{\"width\":9,\"y\":56,\"height\":13,\"name\":\"rightShoulder\",\"x\":74},{\"y\":50,\"frameX\":0,\"frameY\":0,\"width\":18,\"frameWidth\":19,\"height\":17,\"name\":\"rightFrontArm\",\"frameHeight\":18,\"x\":22},{\"width\":14,\"y\":50,\"height\":14,\"name\":\"rightHand\",\"x\":110},{\"width\":12,\"y\":42,\"height\":12,\"name\":\"leftArm\",\"x\":74},{\"width\":13,\"y\":66,\"height\":12,\"name\":\"leftShoulder\",\"x\":110},{\"y\":42,\"frameX\":-1,\"frameY\":0,\"width\":19,\"frameWidth\":20,\"height\":21,\"name\":\"leftFrontArm\",\"frameHeight\":21,\"x\":53},{\"width\":50,\"y\":1,\"height\":47,\"name\":\"head2\",\"x\":1},{\"y\":1,\"frameX\":-1,\"frameY\":0,\"width\":32,\"frameWidth\":33,\"height\":31,\"name\":\"head\",\"frameHeight\":32,\"x\":88},{\"width\":16,\"y\":34,\"height\":14,\"name\":\"leftHand\",\"x\":110},{\"y\":1,\"frameX\":-2,\"frameY\":-3,\"width\":2,\"frameWidth\":8,\"height\":2,\"name\":\"huomiao01\",\"frameHeight\":8,\"x\":122}],\"width\":128,\"height\":128,\"name\":\"SoldierWaterGhost\",\"imagePath\":\"SoldierWaterGhost_tex.png\"}",
|
||||||
|
"texture": "def168c3-3f07-43f9-a460-36b397c70a57",
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
After Width: | Height: | Size: 6.8 KiB |
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.3.3",
|
||||||
|
"uuid": "def168c3-3f07-43f9-a460-36b397c70a57",
|
||||||
|
"type": "sprite",
|
||||||
|
"wrapMode": "clamp",
|
||||||
|
"filterMode": "bilinear",
|
||||||
|
"premultiplyAlpha": false,
|
||||||
|
"genMipmaps": false,
|
||||||
|
"packable": true,
|
||||||
|
"platformSettings": {},
|
||||||
|
"subMetas": {
|
||||||
|
"SoldierWaterGhost_tex": {
|
||||||
|
"ver": "1.0.4",
|
||||||
|
"uuid": "52fb0606-bbea-433c-803b-bf5ce936a0df",
|
||||||
|
"rawTextureUuid": "def168c3-3f07-43f9-a460-36b397c70a57",
|
||||||
|
"trimType": "auto",
|
||||||
|
"trimThreshold": 1,
|
||||||
|
"rotated": false,
|
||||||
|
"offsetX": -0.5,
|
||||||
|
"offsetY": 24.5,
|
||||||
|
"trimX": 1,
|
||||||
|
"trimY": 1,
|
||||||
|
"width": 125,
|
||||||
|
"height": 77,
|
||||||
|
"rawWidth": 128,
|
||||||
|
"rawHeight": 128,
|
||||||
|
"borderTop": 0,
|
||||||
|
"borderBottom": 0,
|
||||||
|
"borderLeft": 0,
|
||||||
|
"borderRight": 0,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
frontend/assets/resources/animation/SoldierElf.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.1",
|
||||||
|
"uuid": "2202f4f4-b792-4dea-8302-633315aded66",
|
||||||
|
"isSubpackage": false,
|
||||||
|
"subpackageName": "",
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
{"width":128,"SubTexture":[{"frameWidth":34,"y":1,"frameHeight":45,"width":34,"frameX":0,"height":44,"name":"cape","frameY":0,"x":70},{"width":10,"y":107,"height":14,"name":"shouder_l","x":74},{"width":11,"y":107,"height":14,"name":"forearm_l","x":61},{"width":15,"y":93,"height":16,"name":"hand_l","x":1},{"width":30,"y":61,"height":30,"name":"weapon_hand_l","x":1},{"width":8,"y":88,"height":11,"name":"thigh_l","x":77},{"width":12,"y":93,"height":17,"name":"calf_l","x":18},{"width":20,"y":113,"height":8,"name":"foot_l","x":39},{"width":28,"y":61,"height":31,"name":"pelvis","x":33},{"width":8,"y":101,"height":11,"name":"thigh_r","x":86},{"width":12,"y":88,"height":17,"name":"calf_r","x":63},{"width":20,"y":113,"height":8,"name":"foot_r","x":17},{"width":13,"y":94,"height":12,"name":"shouder_r","x":45},{"width":67,"y":1,"height":58,"name":"chest","x":1},{"width":11,"y":94,"height":17,"name":"forearm_r","x":32},{"width":14,"y":111,"height":13,"name":"hand_r","x":1},{"frameWidth":36,"y":47,"frameHeight":39,"width":34,"frameX":-2,"height":39,"name":"we_bl_4_f_1","frameY":0,"x":70}],"height":128,"name":"SoldierElf","imagePath":"SoldierElf_tex.png"}
|
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.0",
|
||||||
|
"uuid": "24d7bb8f-577c-4e5d-b730-56613ca8685d",
|
||||||
|
"atlasJson": "{\"width\":128,\"SubTexture\":[{\"frameWidth\":34,\"y\":1,\"frameHeight\":45,\"width\":34,\"frameX\":0,\"height\":44,\"name\":\"cape\",\"frameY\":0,\"x\":70},{\"width\":10,\"y\":107,\"height\":14,\"name\":\"shouder_l\",\"x\":74},{\"width\":11,\"y\":107,\"height\":14,\"name\":\"forearm_l\",\"x\":61},{\"width\":15,\"y\":93,\"height\":16,\"name\":\"hand_l\",\"x\":1},{\"width\":30,\"y\":61,\"height\":30,\"name\":\"weapon_hand_l\",\"x\":1},{\"width\":8,\"y\":88,\"height\":11,\"name\":\"thigh_l\",\"x\":77},{\"width\":12,\"y\":93,\"height\":17,\"name\":\"calf_l\",\"x\":18},{\"width\":20,\"y\":113,\"height\":8,\"name\":\"foot_l\",\"x\":39},{\"width\":28,\"y\":61,\"height\":31,\"name\":\"pelvis\",\"x\":33},{\"width\":8,\"y\":101,\"height\":11,\"name\":\"thigh_r\",\"x\":86},{\"width\":12,\"y\":88,\"height\":17,\"name\":\"calf_r\",\"x\":63},{\"width\":20,\"y\":113,\"height\":8,\"name\":\"foot_r\",\"x\":17},{\"width\":13,\"y\":94,\"height\":12,\"name\":\"shouder_r\",\"x\":45},{\"width\":67,\"y\":1,\"height\":58,\"name\":\"chest\",\"x\":1},{\"width\":11,\"y\":94,\"height\":17,\"name\":\"forearm_r\",\"x\":32},{\"width\":14,\"y\":111,\"height\":13,\"name\":\"hand_r\",\"x\":1},{\"frameWidth\":36,\"y\":47,\"frameHeight\":39,\"width\":34,\"frameX\":-2,\"height\":39,\"name\":\"we_bl_4_f_1\",\"frameY\":0,\"x\":70}],\"height\":128,\"name\":\"SoldierElf\",\"imagePath\":\"SoldierElf_tex.png\"}",
|
||||||
|
"texture": "050fb016-1a1f-4341-8367-283bfeddc4a8",
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
After Width: | Height: | Size: 7.1 KiB |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ver": "2.3.3",
|
"ver": "2.3.3",
|
||||||
"uuid": "c30bd4d7-efdc-410c-8bdf-4a3dfc77bebd",
|
"uuid": "050fb016-1a1f-4341-8367-283bfeddc4a8",
|
||||||
"type": "sprite",
|
"type": "sprite",
|
||||||
"wrapMode": "clamp",
|
"wrapMode": "clamp",
|
||||||
"filterMode": "bilinear",
|
"filterMode": "bilinear",
|
||||||
@@ -9,21 +9,21 @@
|
|||||||
"packable": true,
|
"packable": true,
|
||||||
"platformSettings": {},
|
"platformSettings": {},
|
||||||
"subMetas": {
|
"subMetas": {
|
||||||
"Tile_W300_H300_S01": {
|
"SoldierElf_tex": {
|
||||||
"ver": "1.0.4",
|
"ver": "1.0.4",
|
||||||
"uuid": "66b49304-7b5b-442c-92a5-d2b368abf659",
|
"uuid": "c62e1779-f92b-40d3-bf4f-7ab747e33d6e",
|
||||||
"rawTextureUuid": "c30bd4d7-efdc-410c-8bdf-4a3dfc77bebd",
|
"rawTextureUuid": "050fb016-1a1f-4341-8367-283bfeddc4a8",
|
||||||
"trimType": "auto",
|
"trimType": "auto",
|
||||||
"trimThreshold": 1,
|
"trimThreshold": 1,
|
||||||
"rotated": false,
|
"rotated": false,
|
||||||
"offsetX": 4,
|
"offsetX": -11.5,
|
||||||
"offsetY": -24.5,
|
"offsetY": 1.5,
|
||||||
"trimX": 97,
|
"trimX": 1,
|
||||||
"trimY": 85,
|
"trimY": 1,
|
||||||
"width": 114,
|
"width": 103,
|
||||||
"height": 179,
|
"height": 123,
|
||||||
"rawWidth": 300,
|
"rawWidth": 128,
|
||||||
"rawHeight": 300,
|
"rawHeight": 128,
|
||||||
"borderTop": 0,
|
"borderTop": 0,
|
||||||
"borderBottom": 0,
|
"borderBottom": 0,
|
||||||
"borderLeft": 0,
|
"borderLeft": 0,
|
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.1",
|
||||||
|
"uuid": "f1176719-d1d6-4af5-89c6-ddff16ab85fd",
|
||||||
|
"isSubpackage": false,
|
||||||
|
"subpackageName": "",
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
{"width":128,"SubTexture":[{"frameWidth":23,"y":44,"frameHeight":22,"width":22,"frameX":-1,"height":22,"name":"biu","frameY":0,"x":53},{"width":9,"y":68,"height":14,"name":"rightArm","x":76},{"frameWidth":29,"y":35,"frameHeight":32,"width":26,"frameX":-1,"height":32,"name":"yinmoqe00","frameY":0,"x":89},{"width":34,"y":1,"height":41,"name":"body","x":53},{"width":9,"y":44,"height":13,"name":"rightShoulder","x":77},{"width":19,"y":50,"height":18,"name":"rightFrontArm","x":23},{"width":14,"y":70,"height":14,"name":"rightHand","x":23},{"width":12,"y":68,"height":12,"name":"leftArm","x":62},{"width":13,"y":73,"height":12,"name":"leftShoulder","x":1},{"width":20,"y":50,"height":21,"name":"leftFrontArm","x":1},{"width":33,"y":1,"height":32,"name":"head","x":89},{"width":50,"y":1,"height":47,"name":"head2","x":1},{"width":16,"y":68,"height":14,"name":"leftHand","x":44},{"frameWidth":8,"y":59,"frameHeight":8,"width":4,"frameX":-1,"height":4,"name":"huomiao01","frameY":-2,"x":77}],"height":128,"name":"SoldierFireGhost","imagePath":"SoldierFireGhost_tex.png"}
|
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.0",
|
||||||
|
"uuid": "4a9187d5-a9ad-4464-a03c-d2f3cc277051",
|
||||||
|
"atlasJson": "{\"width\":128,\"SubTexture\":[{\"frameWidth\":23,\"y\":44,\"frameHeight\":22,\"width\":22,\"frameX\":-1,\"height\":22,\"name\":\"biu\",\"frameY\":0,\"x\":53},{\"width\":9,\"y\":68,\"height\":14,\"name\":\"rightArm\",\"x\":76},{\"frameWidth\":29,\"y\":35,\"frameHeight\":32,\"width\":26,\"frameX\":-1,\"height\":32,\"name\":\"yinmoqe00\",\"frameY\":0,\"x\":89},{\"width\":34,\"y\":1,\"height\":41,\"name\":\"body\",\"x\":53},{\"width\":9,\"y\":44,\"height\":13,\"name\":\"rightShoulder\",\"x\":77},{\"width\":19,\"y\":50,\"height\":18,\"name\":\"rightFrontArm\",\"x\":23},{\"width\":14,\"y\":70,\"height\":14,\"name\":\"rightHand\",\"x\":23},{\"width\":12,\"y\":68,\"height\":12,\"name\":\"leftArm\",\"x\":62},{\"width\":13,\"y\":73,\"height\":12,\"name\":\"leftShoulder\",\"x\":1},{\"width\":20,\"y\":50,\"height\":21,\"name\":\"leftFrontArm\",\"x\":1},{\"width\":33,\"y\":1,\"height\":32,\"name\":\"head\",\"x\":89},{\"width\":50,\"y\":1,\"height\":47,\"name\":\"head2\",\"x\":1},{\"width\":16,\"y\":68,\"height\":14,\"name\":\"leftHand\",\"x\":44},{\"frameWidth\":8,\"y\":59,\"frameHeight\":8,\"width\":4,\"frameX\":-1,\"height\":4,\"name\":\"huomiao01\",\"frameY\":-2,\"x\":77}],\"height\":128,\"name\":\"SoldierFireGhost\",\"imagePath\":\"SoldierFireGhost_tex.png\"}",
|
||||||
|
"texture": "700d963b-2192-4219-a066-8be5b3db7453",
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
After Width: | Height: | Size: 8.3 KiB |
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.3.3",
|
||||||
|
"uuid": "700d963b-2192-4219-a066-8be5b3db7453",
|
||||||
|
"type": "sprite",
|
||||||
|
"wrapMode": "clamp",
|
||||||
|
"filterMode": "bilinear",
|
||||||
|
"premultiplyAlpha": false,
|
||||||
|
"genMipmaps": false,
|
||||||
|
"packable": true,
|
||||||
|
"platformSettings": {},
|
||||||
|
"subMetas": {
|
||||||
|
"SoldierFireGhost_tex": {
|
||||||
|
"ver": "1.0.4",
|
||||||
|
"uuid": "8ef8a6b3-0bac-4cf1-bba0-ab090f4d9e52",
|
||||||
|
"rawTextureUuid": "700d963b-2192-4219-a066-8be5b3db7453",
|
||||||
|
"trimType": "auto",
|
||||||
|
"trimThreshold": 1,
|
||||||
|
"rotated": false,
|
||||||
|
"offsetX": -2.5,
|
||||||
|
"offsetY": 21,
|
||||||
|
"trimX": 1,
|
||||||
|
"trimY": 1,
|
||||||
|
"width": 121,
|
||||||
|
"height": 84,
|
||||||
|
"rawWidth": 128,
|
||||||
|
"rawHeight": 128,
|
||||||
|
"borderTop": 0,
|
||||||
|
"borderBottom": 0,
|
||||||
|
"borderLeft": 0,
|
||||||
|
"borderRight": 0,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
frontend/assets/resources/map/dungeon.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.1",
|
||||||
|
"uuid": "5650b341-a420-4d79-a969-39a461e13378",
|
||||||
|
"isSubpackage": false,
|
||||||
|
"subpackageName": "",
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
168
frontend/assets/resources/map/dungeon/map.tmx
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="128" height="128" tilewidth="16" tileheight="16" infinite="0" nextlayerid="3" nextobjectid="39">
|
||||||
|
<tileset firstgid="1" source="tiles0.tsx"/>
|
||||||
|
<tileset firstgid="65" source="tiles1.tsx"/>
|
||||||
|
<tileset firstgid="129" source="tiles2.tsx"/>
|
||||||
|
<layer id="2" name="Ground" width="128" height="128">
|
||||||
|
<data encoding="base64" compression="zlib">
|
||||||
|
eJzt2q1u22AYhuEo1UhBtYFWKh/bmRRMRYNFPZPxwcKi7jznSInkWk7teG5ex88FLhQD570d5/PP1WazuQIAAAAAAAAAAAAAAAAAgAmu96r3g/N5bjn0fw5U3aGy/3bv0P+p8XDEdoX0f9//GP3Xp93/YaD/9QJa6f95/fvov26773671zeb7jmhupX+5+2fQH/9qztU9v8yQXUz/fXXX3/9a/ovgf7666+//vrrf1n9/4zc7tue/uvq/9Z4PfLZ7vnz38ZP/cv7T53p4Z7i7/+kv/7667/bl/sO/dff/77T3+9ff/3r+38dMGf/Nv1r+w917+v/0vh1okN/67/l9B/b/nFj/X8pxvYf236r/0UZ6t/X+Efj5kj7bv+uxxPpX9e/r/1NS7f7Z/S/a3zXfzH9Pzrvt/vPTf/l9p+yRjyV/ufvP+YY6M5yzr5D5uy/XUCLJfafa8YfmdJe//X0r6Z/9jGgv/7661/doqp/9fyrpV//Vc+/mv7Z9M+mfzb9s+mfTf9s+mfTP5v+2fTPpn82/bPpn03/bPpn0z+b/tn0z6Z/Nv2z6Z/N+9/Z9M+mfzb//9n0z6Z/Nv2z6Z9N/2z6Z9M/m/7Z0vtT3wEAAAAAAAAAAAAAAAA4v39IY4NC
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<objectgroup id="1" name="PlayerStartingPos">
|
||||||
|
<object id="135" x="1090" y="833.667">
|
||||||
|
<point/>
|
||||||
|
</object>
|
||||||
|
<object id="137" x="1215" y="830.5">
|
||||||
|
<point/>
|
||||||
|
</object>
|
||||||
|
</objectgroup>
|
||||||
|
<objectgroup id="2" name="Barrier">
|
||||||
|
<properties>
|
||||||
|
<property name="type" value="barrier_and_shelter"/>
|
||||||
|
</properties>
|
||||||
|
<object id="8" x="648.242" y="480.606">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 0,18.6667 1041.33,21.3333 1041.33,-1.33333"/>
|
||||||
|
</object>
|
||||||
|
<object id="9" x="650.667" y="1604.67">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 0,18.6667 1041.33,21.3333 1041.33,-1.33333"/>
|
||||||
|
</object>
|
||||||
|
<object id="10" x="634.485" y="505.455">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 4,1110 24,1110 24,-8"/>
|
||||||
|
</object>
|
||||||
|
<object id="11" x="1677.64" y="501.333">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 4,1110 24,1110 24,-8"/>
|
||||||
|
</object>
|
||||||
|
<object id="14" x="688.667" y="464">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 -0.666667,78 33.3333,78 32,-0.666667"/>
|
||||||
|
</object>
|
||||||
|
<object id="15" x="833.333" y="495.333">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 -112,1.33333 -111.333,44.6667 -1.33333,44.6667"/>
|
||||||
|
</object>
|
||||||
|
<object id="17" x="832" y="574">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 -67.3333,0 -67.3333,-76.6667 0.666667,-76"/>
|
||||||
|
</object>
|
||||||
|
<object id="18" x="865.333" y="606.667">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 -210,-0.666667 -210,143.333 0,142.667"/>
|
||||||
|
</object>
|
||||||
|
<object id="19" x="754.667" y="1055.33">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="-2,1.33333 -100,0.666667 -97.3333,-454 -9.33333,-451.333"/>
|
||||||
|
</object>
|
||||||
|
<object id="20" x="769.333" y="747.333">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 -115.333,0.666667 -114.667,160.667 -2,162"/>
|
||||||
|
</object>
|
||||||
|
<object id="21" x="768" y="960.667">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 -18,0.666667 -18.6667,95.3333 0,95.3333"/>
|
||||||
|
</object>
|
||||||
|
<object id="23" x="786" y="1058.67">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 0,-52.6667 -20,-52 -19.3333,-1.33333"/>
|
||||||
|
</object>
|
||||||
|
<object id="24" x="1118.67" y="749.333">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 -0.666667,-94 -254,-93.3333 -256.667,1.33333"/>
|
||||||
|
</object>
|
||||||
|
<object id="25" x="1168" y="975.333">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 0,16.6667 224.667,17.3333 225.333,-2"/>
|
||||||
|
</object>
|
||||||
|
<object id="28" x="1394.67" y="958">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 -210.667,1.33333 -210.667,17.3333 -2,18"/>
|
||||||
|
</object>
|
||||||
|
<object id="29" x="1119" y="654.5">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 0,63.5 272.5,65 273,-1"/>
|
||||||
|
</object>
|
||||||
|
<object id="30" x="1136.5" y="717.5">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 -0.5,17 255,17.5 255,1.5"/>
|
||||||
|
</object>
|
||||||
|
<object id="31" x="1152" y="735.667">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 0,15 80.3333,15.3333 80.3333,-2"/>
|
||||||
|
</object>
|
||||||
|
<object id="32" x="1280.67" y="734.667">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 -0.666667,64.3333 48,65 48,0"/>
|
||||||
|
</object>
|
||||||
|
<object id="34" x="1329" y="783">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 63.3333,0.333333 63,-48.3333 -0.666667,-48.3333"/>
|
||||||
|
</object>
|
||||||
|
<object id="35" x="1296.67" y="799">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 -0.666667,31.3333 31.3333,31.6667 31.3333,-0.333333"/>
|
||||||
|
</object>
|
||||||
|
<object id="36" x="1280" y="848">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 0.333333,62 112,63 111.667,-1.33333"/>
|
||||||
|
</object>
|
||||||
|
<object id="37" x="1392.33" y="911.333">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 -81.3333,-0.666667 -81,45 -0.666667,46.3333"/>
|
||||||
|
</object>
|
||||||
|
<object id="38" x="1344.33" y="800.667">
|
||||||
|
<properties>
|
||||||
|
<property name="boundary_type" value="barrier"/>
|
||||||
|
</properties>
|
||||||
|
<polyline points="0,0 0,46.3333 47,46.3333 47,-1"/>
|
||||||
|
</object>
|
||||||
|
</objectgroup>
|
||||||
|
</map>
|
5
frontend/assets/resources/map/dungeon/map.tmx.meta
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.0.2",
|
||||||
|
"uuid": "1b802c87-1978-4c6a-bd0b-1f6b8526b3ad",
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
4
frontend/assets/resources/map/dungeon/tiles0.tsx
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tileset version="1.2" tiledversion="1.2.3" name="tiles0" tilewidth="16" tileheight="16" tilecount="64" columns="16">
|
||||||
|
<image source="watabou_pixel_dungeon_orig_files/tiles0.png" width="256" height="64"/>
|
||||||
|
</tileset>
|
5
frontend/assets/resources/map/dungeon/tiles0.tsx.meta
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.0.0",
|
||||||
|
"uuid": "d4cf0e72-7454-4310-b975-beb5e81a63ae",
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
4
frontend/assets/resources/map/dungeon/tiles1.tsx
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tileset version="1.2" tiledversion="1.2.3" name="tiles1" tilewidth="16" tileheight="16" tilecount="64" columns="16">
|
||||||
|
<image source="watabou_pixel_dungeon_orig_files/tiles1.png" width="256" height="64"/>
|
||||||
|
</tileset>
|
5
frontend/assets/resources/map/dungeon/tiles1.tsx.meta
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.0.0",
|
||||||
|
"uuid": "0bcabaac-a406-4b3d-9285-814e00c5b09d",
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
4
frontend/assets/resources/map/dungeon/tiles2.tsx
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tileset version="1.2" tiledversion="1.2.3" name="tiles2" tilewidth="16" tileheight="16" tilecount="64" columns="16">
|
||||||
|
<image source="watabou_pixel_dungeon_orig_files/tiles2.png" width="256" height="64"/>
|
||||||
|
</tileset>
|
5
frontend/assets/resources/map/dungeon/tiles2.tsx.meta
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.0.0",
|
||||||
|
"uuid": "2e53500f-f9c8-4b5b-b74c-f2adbc2ec34d",
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.1",
|
||||||
|
"uuid": "2a231040-0e69-42b4-a35b-9690e976e71a",
|
||||||
|
"isSubpackage": false,
|
||||||
|
"subpackageName": "",
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.0.0",
|
||||||
|
"uuid": "acb40b5d-372b-4502-a6bf-f756908f8221",
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ver": "2.3.3",
|
"ver": "2.3.3",
|
||||||
"uuid": "d8e6c175-1f17-48df-a0aa-cdd9785f4d3a",
|
"uuid": "94f42afa-43e1-4936-b5ac-7bcfe252f9e1",
|
||||||
"type": "sprite",
|
"type": "sprite",
|
||||||
"wrapMode": "clamp",
|
"wrapMode": "clamp",
|
||||||
"filterMode": "bilinear",
|
"filterMode": "bilinear",
|
||||||
@@ -9,21 +9,21 @@
|
|||||||
"packable": true,
|
"packable": true,
|
||||||
"platformSettings": {},
|
"platformSettings": {},
|
||||||
"subMetas": {
|
"subMetas": {
|
||||||
"Tile_W256_H256_S01": {
|
"amulet": {
|
||||||
"ver": "1.0.4",
|
"ver": "1.0.4",
|
||||||
"uuid": "4a23290b-bf5a-4849-ac19-6ebd4b7daa59",
|
"uuid": "52580025-ae7a-4c48-878f-f685bc90e737",
|
||||||
"rawTextureUuid": "d8e6c175-1f17-48df-a0aa-cdd9785f4d3a",
|
"rawTextureUuid": "94f42afa-43e1-4936-b5ac-7bcfe252f9e1",
|
||||||
"trimType": "auto",
|
"trimType": "auto",
|
||||||
"trimThreshold": 1,
|
"trimThreshold": 1,
|
||||||
"rotated": false,
|
"rotated": false,
|
||||||
"offsetX": 0,
|
"offsetX": 0,
|
||||||
"offsetY": 0,
|
"offsetY": 0,
|
||||||
"trimX": 0,
|
"trimX": 0,
|
||||||
"trimY": 64,
|
"trimY": 0,
|
||||||
"width": 1280,
|
"width": 32,
|
||||||
"height": 896,
|
"height": 32,
|
||||||
"rawWidth": 1280,
|
"rawWidth": 32,
|
||||||
"rawHeight": 1024,
|
"rawHeight": 32,
|
||||||
"borderTop": 0,
|
"borderTop": 0,
|
||||||
"borderBottom": 0,
|
"borderBottom": 0,
|
||||||
"borderLeft": 0,
|
"borderLeft": 0,
|
After Width: | Height: | Size: 189 B |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ver": "2.3.3",
|
"ver": "2.3.3",
|
||||||
"uuid": "136d09e9-c33c-45dc-abb7-e367d730c814",
|
"uuid": "a9781031-1dba-4214-9167-080fc1920f92",
|
||||||
"type": "sprite",
|
"type": "sprite",
|
||||||
"wrapMode": "clamp",
|
"wrapMode": "clamp",
|
||||||
"filterMode": "bilinear",
|
"filterMode": "bilinear",
|
||||||
@@ -9,21 +9,21 @@
|
|||||||
"packable": true,
|
"packable": true,
|
||||||
"platformSettings": {},
|
"platformSettings": {},
|
||||||
"subMetas": {
|
"subMetas": {
|
||||||
"Tile_W256_H128_S01": {
|
"arcs1": {
|
||||||
"ver": "1.0.4",
|
"ver": "1.0.4",
|
||||||
"uuid": "7acc48f5-d9c9-4438-8794-57a85590bd97",
|
"uuid": "5430edd4-3e0b-4509-a575-d7c7f8db4e31",
|
||||||
"rawTextureUuid": "136d09e9-c33c-45dc-abb7-e367d730c814",
|
"rawTextureUuid": "a9781031-1dba-4214-9167-080fc1920f92",
|
||||||
"trimType": "auto",
|
"trimType": "auto",
|
||||||
"trimThreshold": 1,
|
"trimThreshold": 1,
|
||||||
"rotated": false,
|
"rotated": false,
|
||||||
"offsetX": 0,
|
"offsetX": 0,
|
||||||
"offsetY": 831,
|
"offsetY": 0,
|
||||||
"trimX": 0,
|
"trimX": 0,
|
||||||
"trimY": 0,
|
"trimY": 0,
|
||||||
"width": 2048,
|
"width": 32,
|
||||||
"height": 386,
|
"height": 32,
|
||||||
"rawWidth": 2048,
|
"rawWidth": 32,
|
||||||
"rawHeight": 2048,
|
"rawHeight": 32,
|
||||||
"borderTop": 0,
|
"borderTop": 0,
|
||||||
"borderBottom": 0,
|
"borderBottom": 0,
|
||||||
"borderLeft": 0,
|
"borderLeft": 0,
|
After Width: | Height: | Size: 288 B |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ver": "2.3.3",
|
"ver": "2.3.3",
|
||||||
"uuid": "74245e28-6cec-4960-ac41-5b482ad8fd13",
|
"uuid": "940f2fcc-df03-46ed-ad32-d92880efe501",
|
||||||
"type": "sprite",
|
"type": "sprite",
|
||||||
"wrapMode": "clamp",
|
"wrapMode": "clamp",
|
||||||
"filterMode": "bilinear",
|
"filterMode": "bilinear",
|
||||||
@@ -9,21 +9,21 @@
|
|||||||
"packable": true,
|
"packable": true,
|
||||||
"platformSettings": {},
|
"platformSettings": {},
|
||||||
"subMetas": {
|
"subMetas": {
|
||||||
"Tile_W256_H256_S02": {
|
"arcs2": {
|
||||||
"ver": "1.0.4",
|
"ver": "1.0.4",
|
||||||
"uuid": "8fc46c1f-6fb4-4290-99f3-b773b92312b7",
|
"uuid": "11915948-c7bf-41db-b6eb-74b364df7688",
|
||||||
"rawTextureUuid": "74245e28-6cec-4960-ac41-5b482ad8fd13",
|
"rawTextureUuid": "940f2fcc-df03-46ed-ad32-d92880efe501",
|
||||||
"trimType": "auto",
|
"trimType": "auto",
|
||||||
"trimThreshold": 1,
|
"trimThreshold": 1,
|
||||||
"rotated": false,
|
"rotated": false,
|
||||||
"offsetX": 19.5,
|
"offsetX": 0,
|
||||||
"offsetY": 3.5,
|
"offsetY": 0,
|
||||||
"trimX": 89,
|
"trimX": 0,
|
||||||
"trimY": 0,
|
"trimY": 0,
|
||||||
"width": 1409,
|
"width": 64,
|
||||||
"height": 251,
|
"height": 64,
|
||||||
"rawWidth": 1548,
|
"rawWidth": 64,
|
||||||
"rawHeight": 258,
|
"rawHeight": 64,
|
||||||
"borderTop": 0,
|
"borderTop": 0,
|
||||||
"borderBottom": 0,
|
"borderBottom": 0,
|
||||||
"borderLeft": 0,
|
"borderLeft": 0,
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.3.3",
|
||||||
|
"uuid": "ac193c85-fb5d-4d66-b911-81e2254e0009",
|
||||||
|
"type": "sprite",
|
||||||
|
"wrapMode": "clamp",
|
||||||
|
"filterMode": "bilinear",
|
||||||
|
"premultiplyAlpha": false,
|
||||||
|
"genMipmaps": false,
|
||||||
|
"packable": true,
|
||||||
|
"platformSettings": {},
|
||||||
|
"subMetas": {
|
||||||
|
"avatars": {
|
||||||
|
"ver": "1.0.4",
|
||||||
|
"uuid": "b472e0d1-c866-4a4d-b8f9-4daca275b8db",
|
||||||
|
"rawTextureUuid": "ac193c85-fb5d-4d66-b911-81e2254e0009",
|
||||||
|
"trimType": "auto",
|
||||||
|
"trimThreshold": 1,
|
||||||
|
"rotated": false,
|
||||||
|
"offsetX": -16.5,
|
||||||
|
"offsetY": 2,
|
||||||
|
"trimX": 2,
|
||||||
|
"trimY": 0,
|
||||||
|
"width": 91,
|
||||||
|
"height": 28,
|
||||||
|
"rawWidth": 128,
|
||||||
|
"rawHeight": 32,
|
||||||
|
"borderTop": 0,
|
||||||
|
"borderBottom": 0,
|
||||||
|
"borderLeft": 0,
|
||||||
|
"borderRight": 0,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.3.3",
|
||||||
|
"uuid": "f204f568-cf6e-462e-834e-ef6961dae26e",
|
||||||
|
"type": "sprite",
|
||||||
|
"wrapMode": "clamp",
|
||||||
|
"filterMode": "bilinear",
|
||||||
|
"premultiplyAlpha": false,
|
||||||
|
"genMipmaps": false,
|
||||||
|
"packable": true,
|
||||||
|
"platformSettings": {},
|
||||||
|
"subMetas": {
|
||||||
|
"badges": {
|
||||||
|
"ver": "1.0.4",
|
||||||
|
"uuid": "fec2a102-8c77-4528-8130-488184e8bf38",
|
||||||
|
"rawTextureUuid": "f204f568-cf6e-462e-834e-ef6961dae26e",
|
||||||
|
"trimType": "auto",
|
||||||
|
"trimThreshold": 1,
|
||||||
|
"rotated": false,
|
||||||
|
"offsetX": 0,
|
||||||
|
"offsetY": 0,
|
||||||
|
"trimX": 0,
|
||||||
|
"trimY": 0,
|
||||||
|
"width": 128,
|
||||||
|
"height": 128,
|
||||||
|
"rawWidth": 128,
|
||||||
|
"rawHeight": 128,
|
||||||
|
"borderTop": 0,
|
||||||
|
"borderBottom": 0,
|
||||||
|
"borderLeft": 0,
|
||||||
|
"borderRight": 0,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.3.3",
|
||||||
|
"uuid": "90cc6214-a196-4c59-885e-86c4449d426b",
|
||||||
|
"type": "sprite",
|
||||||
|
"wrapMode": "clamp",
|
||||||
|
"filterMode": "bilinear",
|
||||||
|
"premultiplyAlpha": false,
|
||||||
|
"genMipmaps": false,
|
||||||
|
"packable": true,
|
||||||
|
"platformSettings": {},
|
||||||
|
"subMetas": {
|
||||||
|
"banners": {
|
||||||
|
"ver": "1.0.4",
|
||||||
|
"uuid": "ce394cd8-e574-4170-8c9c-24072e7d6428",
|
||||||
|
"rawTextureUuid": "90cc6214-a196-4c59-885e-86c4449d426b",
|
||||||
|
"trimType": "auto",
|
||||||
|
"trimThreshold": 1,
|
||||||
|
"rotated": false,
|
||||||
|
"offsetX": 2.5,
|
||||||
|
"offsetY": 16.5,
|
||||||
|
"trimX": 5,
|
||||||
|
"trimY": 4,
|
||||||
|
"width": 123,
|
||||||
|
"height": 215,
|
||||||
|
"rawWidth": 128,
|
||||||
|
"rawHeight": 256,
|
||||||
|
"borderTop": 0,
|
||||||
|
"borderBottom": 0,
|
||||||
|
"borderLeft": 0,
|
||||||
|
"borderRight": 0,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.3.3",
|
||||||
|
"uuid": "e0a44b92-2564-401c-8276-b47e279c0039",
|
||||||
|
"type": "sprite",
|
||||||
|
"wrapMode": "clamp",
|
||||||
|
"filterMode": "bilinear",
|
||||||
|
"premultiplyAlpha": false,
|
||||||
|
"genMipmaps": false,
|
||||||
|
"packable": true,
|
||||||
|
"platformSettings": {},
|
||||||
|
"subMetas": {
|
||||||
|
"bat": {
|
||||||
|
"ver": "1.0.4",
|
||||||
|
"uuid": "5ad7b260-8239-4eb0-a436-7bc1c3743433",
|
||||||
|
"rawTextureUuid": "e0a44b92-2564-401c-8276-b47e279c0039",
|
||||||
|
"trimType": "auto",
|
||||||
|
"trimThreshold": 1,
|
||||||
|
"rotated": false,
|
||||||
|
"offsetX": -12,
|
||||||
|
"offsetY": 0.5,
|
||||||
|
"trimX": 0,
|
||||||
|
"trimY": 0,
|
||||||
|
"width": 104,
|
||||||
|
"height": 15,
|
||||||
|
"rawWidth": 128,
|
||||||
|
"rawHeight": 16,
|
||||||
|
"borderTop": 0,
|
||||||
|
"borderBottom": 0,
|
||||||
|
"borderLeft": 0,
|
||||||
|
"borderRight": 0,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.3.3",
|
||||||
|
"uuid": "a83b9bc0-78f0-41ed-acc2-6f88020d4828",
|
||||||
|
"type": "sprite",
|
||||||
|
"wrapMode": "clamp",
|
||||||
|
"filterMode": "bilinear",
|
||||||
|
"premultiplyAlpha": false,
|
||||||
|
"genMipmaps": false,
|
||||||
|
"packable": true,
|
||||||
|
"platformSettings": {},
|
||||||
|
"subMetas": {
|
||||||
|
"bee": {
|
||||||
|
"ver": "1.0.4",
|
||||||
|
"uuid": "a5d1dc1d-5f46-430c-96d3-3e2d2584be13",
|
||||||
|
"rawTextureUuid": "a83b9bc0-78f0-41ed-acc2-6f88020d4828",
|
||||||
|
"trimType": "auto",
|
||||||
|
"trimThreshold": 1,
|
||||||
|
"rotated": false,
|
||||||
|
"offsetX": -40.5,
|
||||||
|
"offsetY": 0,
|
||||||
|
"trimX": 2,
|
||||||
|
"trimY": 0,
|
||||||
|
"width": 171,
|
||||||
|
"height": 16,
|
||||||
|
"rawWidth": 256,
|
||||||
|
"rawHeight": 16,
|
||||||
|
"borderTop": 0,
|
||||||
|
"borderBottom": 0,
|
||||||
|
"borderLeft": 0,
|
||||||
|
"borderRight": 0,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 918 B |
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.3.3",
|
||||||
|
"uuid": "4ba0be3a-32d0-43f1-9640-5fb0f65ea16a",
|
||||||
|
"type": "sprite",
|
||||||
|
"wrapMode": "clamp",
|
||||||
|
"filterMode": "bilinear",
|
||||||
|
"premultiplyAlpha": false,
|
||||||
|
"genMipmaps": false,
|
||||||
|
"packable": true,
|
||||||
|
"platformSettings": {},
|
||||||
|
"subMetas": {
|
||||||
|
"blacksmith": {
|
||||||
|
"ver": "1.0.4",
|
||||||
|
"uuid": "cc238a21-5d38-496a-86c0-3c223cb0977a",
|
||||||
|
"rawTextureUuid": "4ba0be3a-32d0-43f1-9640-5fb0f65ea16a",
|
||||||
|
"trimType": "auto",
|
||||||
|
"trimThreshold": 1,
|
||||||
|
"rotated": false,
|
||||||
|
"offsetX": -6.5,
|
||||||
|
"offsetY": 0,
|
||||||
|
"trimX": 0,
|
||||||
|
"trimY": 0,
|
||||||
|
"width": 51,
|
||||||
|
"height": 16,
|
||||||
|
"rawWidth": 64,
|
||||||
|
"rawHeight": 16,
|
||||||
|
"borderTop": 0,
|
||||||
|
"borderBottom": 0,
|
||||||
|
"borderLeft": 0,
|
||||||
|
"borderRight": 0,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 5.6 KiB |
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.3.3",
|
||||||
|
"uuid": "2ed4fd6c-7c83-49e8-baba-01145a109cc8",
|
||||||
|
"type": "sprite",
|
||||||
|
"wrapMode": "clamp",
|
||||||
|
"filterMode": "bilinear",
|
||||||
|
"premultiplyAlpha": false,
|
||||||
|
"genMipmaps": false,
|
||||||
|
"packable": true,
|
||||||
|
"platformSettings": {},
|
||||||
|
"subMetas": {
|
||||||
|
"brute": {
|
||||||
|
"ver": "1.0.4",
|
||||||
|
"uuid": "eb04bd2a-243f-4ca8-8e58-3bc6cc5e61c1",
|
||||||
|
"rawTextureUuid": "2ed4fd6c-7c83-49e8-baba-01145a109cc8",
|
||||||
|
"trimType": "auto",
|
||||||
|
"trimThreshold": 1,
|
||||||
|
"rotated": false,
|
||||||
|
"offsetX": -61.5,
|
||||||
|
"offsetY": 0,
|
||||||
|
"trimX": 1,
|
||||||
|
"trimY": 0,
|
||||||
|
"width": 131,
|
||||||
|
"height": 32,
|
||||||
|
"rawWidth": 256,
|
||||||
|
"rawHeight": 32,
|
||||||
|
"borderTop": 0,
|
||||||
|
"borderBottom": 0,
|
||||||
|
"borderLeft": 0,
|
||||||
|
"borderRight": 0,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|