mirror of
https://github.com/genxium/DelayNoMore
synced 2025-10-09 00:26:39 +00:00
Initial commit.
This commit is contained in:
13
battle_srv/models/barrier.go
Normal file
13
battle_srv/models/barrier.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/ByteArena/box2d"
|
||||
)
|
||||
|
||||
type Barrier struct {
|
||||
X float64
|
||||
Y float64
|
||||
Type uint32
|
||||
Boundary *Polygon2D
|
||||
CollidableBody *box2d.B2Body
|
||||
}
|
19
battle_srv/models/bullet.go
Normal file
19
battle_srv/models/bullet.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/ByteArena/box2d"
|
||||
)
|
||||
|
||||
type Bullet struct {
|
||||
LocalIdInBattle int32 `json:"-"`
|
||||
LinearSpeed float64 `json:"-"`
|
||||
X float64 `json:"-"`
|
||||
Y float64 `json:"-"`
|
||||
Removed bool `json:"-"`
|
||||
Dir *Direction `json:"-"`
|
||||
StartAtPoint *Vec2D `json:"-"`
|
||||
EndAtPoint *Vec2D `json:"-"`
|
||||
DamageBoundary *Polygon2D `json:"-"`
|
||||
CollidableBody *box2d.B2Body `json:"-"`
|
||||
RemovedAtFrameId int32 `json:"-"`
|
||||
}
|
81
battle_srv/models/helper.go
Normal file
81
battle_srv/models/helper.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
. "server/common"
|
||||
"server/storage"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func exist(t string, cond sq.Eq) (bool, error) {
|
||||
c, err := getCount(t, cond)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return c >= 1, nil
|
||||
}
|
||||
|
||||
func getCount(t string, cond sq.Eq) (int, error) {
|
||||
query, args, err := sq.Select("count(1)").From(t).Where(cond).ToSql()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
//Logger.Debug("getCount", zap.String("sql", query), zap.Any("args", args))
|
||||
var c int
|
||||
err = storage.MySQLManagerIns.Get(&c, query, args...)
|
||||
return c, err
|
||||
}
|
||||
|
||||
func insert(t string, cols []string, vs []interface{}) (sql.Result, error) {
|
||||
query, args, err := sq.Insert(t).Columns(cols...).Values(vs...).ToSql()
|
||||
Logger.Debug("txInsert", zap.String("sql", query))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := storage.MySQLManagerIns.Exec(query, args...)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func txInsert(tx *sqlx.Tx, t string, cols []string, vs []interface{}) (sql.Result, error) {
|
||||
query, args, err := sq.Insert(t).Columns(cols...).Values(vs...).ToSql()
|
||||
//Logger.Debug("txInsert", zap.String("sql", query))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := tx.Exec(query, args...)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func getFields(t string, fields []string, cond sq.Eq, dest interface{}) error {
|
||||
query, args, err := sq.Select(fields...).From(t).Where(cond).Limit(1).ToSql()
|
||||
Logger.Debug("getFields", zap.String("sql", query), zap.Any("args", args))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = storage.MySQLManagerIns.Get(dest, query, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
func getObj(t string, cond sq.Eq, dest interface{}) error {
|
||||
query, args, err := sq.Select("*").From(t).Where(cond).Limit(1).ToSql()
|
||||
Logger.Debug("getObj", zap.String("sql", query), zap.Any("args", args))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = storage.MySQLManagerIns.Get(dest, query, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
func getList(t string, cond sq.Eq, dest interface{}) error {
|
||||
query, args, err := sq.Select("*").From(t).Where(cond).ToSql()
|
||||
Logger.Debug("getList", zap.String("sql", query), zap.Any("args", args))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = storage.MySQLManagerIns.Select(dest, query, args...)
|
||||
//Logger.Debug("getList", zap.Error(err))
|
||||
return err
|
||||
}
|
164
battle_srv/models/in_range_player_collection.go
Normal file
164
battle_srv/models/in_range_player_collection.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "github.com/logrusorgru/aurora"
|
||||
)
|
||||
|
||||
type InRangePlayerCollection struct {
|
||||
MaxSize int `json:"-"`
|
||||
CurrentSize int `json:"-"`
|
||||
CurrentNodePointer *InRangePlayerNode `json:"-"`
|
||||
InRangePlayerMap map[int32]*InRangePlayerNode `json:"-"`
|
||||
}
|
||||
|
||||
func (p *InRangePlayerCollection) Init(maxSize int) *InRangePlayerCollection {
|
||||
p = &InRangePlayerCollection{
|
||||
MaxSize: maxSize,
|
||||
CurrentSize: 0,
|
||||
CurrentNodePointer: nil,
|
||||
InRangePlayerMap: make(map[int32]*InRangePlayerNode),
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *InRangePlayerCollection) Print() {
|
||||
fmt.Println(Cyan(fmt.Sprintf("{ \n MaxSize: %d, \n CurrentSize: %d, \n }", p.MaxSize, p.CurrentSize)))
|
||||
}
|
||||
|
||||
func (p *InRangePlayerCollection) AppendPlayer(player *Player) *InRangePlayerNode {
|
||||
if nil != p.InRangePlayerMap[player.Id] { //如果该玩家已存在返回nil
|
||||
return nil
|
||||
} else {
|
||||
//p.CurrentSize
|
||||
size := p.CurrentSize + 1
|
||||
if size > p.MaxSize { //超出守护塔的承载范围
|
||||
fmt.Println(Red(fmt.Sprintf("Error: InRangePlayerCollection overflow, MaxSize: %d, NowSize: %d", p.MaxSize, size)))
|
||||
return nil
|
||||
}
|
||||
p.CurrentSize = size
|
||||
|
||||
node := InRangePlayerNode{
|
||||
Prev: nil,
|
||||
Next: nil,
|
||||
player: player,
|
||||
}
|
||||
|
||||
p.InRangePlayerMap[player.Id] = &node
|
||||
|
||||
{ //p.CurrentNodePointer
|
||||
if p.CurrentNodePointer == nil { //刚init好的情况
|
||||
p.CurrentNodePointer = &node
|
||||
} else { //加到最后面相当于循环链表prepend
|
||||
p.CurrentNodePointer.PrependNode(&node)
|
||||
}
|
||||
}
|
||||
|
||||
return &node
|
||||
}
|
||||
}
|
||||
|
||||
func (p *InRangePlayerCollection) RemovePlayerById(playerId int32) {
|
||||
nodePointer := p.InRangePlayerMap[playerId]
|
||||
|
||||
delete(p.InRangePlayerMap, playerId)
|
||||
|
||||
{ //p.CurrentNodePointer
|
||||
if p.CurrentNodePointer == nodePointer { //如果正准备攻击这个玩家,将指针移动到Next
|
||||
p.CurrentNodePointer = nodePointer.Next
|
||||
}
|
||||
}
|
||||
|
||||
//Remove from the linked list
|
||||
nodePointer.RemoveFromLink()
|
||||
|
||||
p.CurrentSize = p.CurrentSize - 1
|
||||
}
|
||||
|
||||
func (p *InRangePlayerCollection) NextPlayerToAttack() *InRangePlayerNode {
|
||||
if p.CurrentNodePointer.Next != nil {
|
||||
p.CurrentNodePointer = p.CurrentNodePointer.Next
|
||||
} else {
|
||||
//继续攻击当前玩家
|
||||
}
|
||||
return p.CurrentNodePointer
|
||||
}
|
||||
|
||||
//TODO: 完成重构
|
||||
|
||||
/// Doubly circular linked list Implement
|
||||
type InRangePlayerNode struct {
|
||||
Prev *InRangePlayerNode
|
||||
Next *InRangePlayerNode
|
||||
player *Player
|
||||
}
|
||||
|
||||
func (node *InRangePlayerNode) AppendNode(newNode *InRangePlayerNode) *InRangePlayerNode {
|
||||
if node == nil {
|
||||
return newNode
|
||||
} else if node.Next == nil && node.Prev == nil {
|
||||
node.Prev = newNode
|
||||
node.Next = newNode
|
||||
newNode.Prev = node
|
||||
newNode.Next = node
|
||||
return node
|
||||
} else {
|
||||
oldNext := node.Next
|
||||
node.Next = newNode
|
||||
newNode.Next = oldNext
|
||||
oldNext.Prev = newNode
|
||||
newNode.Prev = node
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
func (node *InRangePlayerNode) PrependNode(newNode *InRangePlayerNode) *InRangePlayerNode {
|
||||
if node == nil { //没有节点的情况
|
||||
return newNode
|
||||
} else if node.Next == nil && node.Prev == nil { //单个节点的情况
|
||||
node.Prev = newNode
|
||||
node.Next = newNode
|
||||
newNode.Prev = node
|
||||
newNode.Next = node
|
||||
return node
|
||||
} else {
|
||||
oldPrev := node.Prev
|
||||
node.Prev = newNode
|
||||
newNode.Prev = oldPrev
|
||||
oldPrev.Next = newNode
|
||||
newNode.Next = node
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
func (node *InRangePlayerNode) RemoveFromLink() {
|
||||
if node == nil {
|
||||
return
|
||||
} else if node.Next == nil && node.Prev == nil {
|
||||
node = nil //Wait for GC
|
||||
} else {
|
||||
prev := node.Prev
|
||||
next := node.Next
|
||||
prev.Next = next
|
||||
next.Prev = prev
|
||||
node = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (node *InRangePlayerNode) Print() {
|
||||
if node == nil {
|
||||
fmt.Println("No player in range")
|
||||
} else if node.Next == nil && node.Prev == nil {
|
||||
fmt.Println(Red(node.player.Id))
|
||||
} else {
|
||||
now := node.Next
|
||||
fmt.Printf("%d ", Red(node.player.Id))
|
||||
for node != now {
|
||||
fmt.Printf("%d ", Green(now.player.Id))
|
||||
now = now.Next
|
||||
}
|
||||
fmt.Println("")
|
||||
}
|
||||
}
|
||||
|
||||
/// End Doubly circular linked list Implement
|
132
battle_srv/models/math.go
Normal file
132
battle_srv/models/math.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ByteArena/box2d"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Use type `float64` for json unmarshalling of numbers.
|
||||
type Direction struct {
|
||||
Dx int32 `json:"dx,omitempty"`
|
||||
Dy int32 `json:"dy,omitempty"`
|
||||
}
|
||||
|
||||
type Vec2D struct {
|
||||
X float64 `json:"x,omitempty"`
|
||||
Y float64 `json:"y,omitempty"`
|
||||
}
|
||||
|
||||
func CreateVec2DFromB2Vec2(b2V2 box2d.B2Vec2) *Vec2D {
|
||||
return &Vec2D{
|
||||
X: b2V2.X,
|
||||
Y: b2V2.Y,
|
||||
}
|
||||
}
|
||||
|
||||
func (v2 *Vec2D) ToB2Vec2() box2d.B2Vec2 {
|
||||
return box2d.MakeB2Vec2(v2.X, v2.Y)
|
||||
}
|
||||
|
||||
type Polygon2D struct {
|
||||
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.
|
||||
Points []*Vec2D `json:"-"`
|
||||
|
||||
/*
|
||||
When used to represent a "polyline directly drawn in a `Tmx file`", we can initialize both "Anchor" and "Points" simultaneously.
|
||||
|
||||
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`".
|
||||
|
||||
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 MoveDynamicBody(body *box2d.B2Body, pToTargetPos *box2d.B2Vec2, inSeconds float64) {
|
||||
if body.GetType() != box2d.B2BodyType.B2_dynamicBody {
|
||||
return
|
||||
}
|
||||
body.SetTransform(*pToTargetPos, 0.0)
|
||||
body.SetLinearVelocity(box2d.MakeB2Vec2(0.0, 0.0))
|
||||
body.SetAngularVelocity(0.0)
|
||||
}
|
||||
|
||||
func PrettyPrintFixture(fix *box2d.B2Fixture) {
|
||||
fmt.Printf("\t\tfriction:\t%v\n", fix.M_friction)
|
||||
fmt.Printf("\t\trestitution:\t%v\n", fix.M_restitution)
|
||||
fmt.Printf("\t\tdensity:\t%v\n", fix.M_density)
|
||||
fmt.Printf("\t\tisSensor:\t%v\n", fix.M_isSensor)
|
||||
fmt.Printf("\t\tfilter.categoryBits:\t%d\n", fix.M_filter.CategoryBits)
|
||||
fmt.Printf("\t\tfilter.maskBits:\t%d\n", fix.M_filter.MaskBits)
|
||||
fmt.Printf("\t\tfilter.groupIndex:\t%d\n", fix.M_filter.GroupIndex)
|
||||
|
||||
switch fix.M_shape.GetType() {
|
||||
case box2d.B2Shape_Type.E_circle:
|
||||
{
|
||||
s := fix.M_shape.(*box2d.B2CircleShape)
|
||||
fmt.Printf("\t\tb2CircleShape shape: {\n")
|
||||
fmt.Printf("\t\t\tradius:\t%v\n", s.M_radius)
|
||||
fmt.Printf("\t\t\toffset:\t%v\n", s.M_p)
|
||||
fmt.Printf("\t\t}\n")
|
||||
}
|
||||
break
|
||||
|
||||
case box2d.B2Shape_Type.E_polygon:
|
||||
{
|
||||
s := fix.M_shape.(*box2d.B2PolygonShape)
|
||||
fmt.Printf("\t\tb2PolygonShape shape: {\n")
|
||||
for i := 0; i < s.M_count; i++ {
|
||||
fmt.Printf("\t\t\t%v\n", s.M_vertices[i])
|
||||
}
|
||||
fmt.Printf("\t\t}\n")
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func PrettyPrintBody(body *box2d.B2Body) {
|
||||
bodyIndex := body.M_islandIndex
|
||||
|
||||
fmt.Printf("{\n")
|
||||
fmt.Printf("\tHeapRAM addr:\t%p\n", body)
|
||||
fmt.Printf("\ttype:\t%d\n", body.M_type)
|
||||
fmt.Printf("\tposition:\t%v\n", body.GetPosition())
|
||||
fmt.Printf("\tangle:\t%v\n", body.M_sweep.A)
|
||||
fmt.Printf("\tlinearVelocity:\t%v\n", body.GetLinearVelocity())
|
||||
fmt.Printf("\tangularVelocity:\t%v\n", body.GetAngularVelocity())
|
||||
fmt.Printf("\tlinearDamping:\t%v\n", body.M_linearDamping)
|
||||
fmt.Printf("\tangularDamping:\t%v\n", body.M_angularDamping)
|
||||
fmt.Printf("\tallowSleep:\t%d\n", body.M_flags&box2d.B2Body_Flags.E_autoSleepFlag)
|
||||
fmt.Printf("\tawake:\t%d\n", body.M_flags&box2d.B2Body_Flags.E_awakeFlag)
|
||||
fmt.Printf("\tfixedRotation:\t%d\n", body.M_flags&box2d.B2Body_Flags.E_fixedRotationFlag)
|
||||
fmt.Printf("\tbullet:\t%d\n", body.M_flags&box2d.B2Body_Flags.E_bulletFlag)
|
||||
fmt.Printf("\tactive:\t%d\n", body.M_flags&box2d.B2Body_Flags.E_activeFlag)
|
||||
fmt.Printf("\tgravityScale:\t%v\n", body.M_gravityScale)
|
||||
fmt.Printf("\tislandIndex:\t%v\n", bodyIndex)
|
||||
fmt.Printf("\tfixtures: {\n")
|
||||
for f := body.M_fixtureList; f != nil; f = f.M_next {
|
||||
PrettyPrintFixture(f)
|
||||
}
|
||||
fmt.Printf("\t}\n")
|
||||
fmt.Printf("}\n")
|
||||
}
|
||||
|
||||
func Distance(pt1 *Vec2D, pt2 *Vec2D) float64 {
|
||||
dx := pt1.X - pt2.X
|
||||
dy := pt1.Y - pt2.Y
|
||||
return math.Sqrt(dx*dx + dy*dy)
|
||||
}
|
203
battle_srv/models/pb_type_convert.go
Normal file
203
battle_srv/models/pb_type_convert.go
Normal file
@@ -0,0 +1,203 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
pb "server/pb_output"
|
||||
)
|
||||
|
||||
func toPbVec2D(modelInstance *Vec2D) *pb.Vec2D {
|
||||
toRet := &pb.Vec2D{
|
||||
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 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 ToPbStrToBattleColliderInfo(intervalToPing int32, willKickIfInactiveFor int32, boundRoomId int32, stageName string, modelInstance1 StrToVec2DListMap, modelInstance2 StrToPolygon2DListMap, stageDiscreteW int32, stageDiscreteH int32, stageTileW int32, stageTileH int32) *pb.BattleColliderInfo {
|
||||
toRet := &pb.BattleColliderInfo{
|
||||
IntervalToPing: intervalToPing,
|
||||
WillKickIfInactiveFor: willKickIfInactiveFor,
|
||||
BoundRoomId: boundRoomId,
|
||||
StageName: stageName,
|
||||
StrToVec2DListMap: make(map[string]*pb.Vec2DList, 0),
|
||||
StrToPolygon2DListMap: make(map[string]*pb.Polygon2DList, 0),
|
||||
StageDiscreteW: stageDiscreteW,
|
||||
StageDiscreteH: stageDiscreteH,
|
||||
StageTileW: stageTileW,
|
||||
StageTileH: stageTileH,
|
||||
}
|
||||
for k, v := range modelInstance1 {
|
||||
toRet.StrToVec2DListMap[k] = toPbVec2DList(v)
|
||||
}
|
||||
for k, v := range modelInstance2 {
|
||||
toRet.StrToPolygon2DListMap[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 {
|
||||
return toRet
|
||||
}
|
||||
|
||||
for k, last := range modelInstances {
|
||||
toRet[k] = &pb.Player{
|
||||
Id: last.Id,
|
||||
X: last.X,
|
||||
Y: last.Y,
|
||||
Dir: &pb.Direction{
|
||||
Dx: last.Dir.Dx,
|
||||
Dy: last.Dir.Dy,
|
||||
},
|
||||
Speed: last.Speed,
|
||||
BattleState: last.BattleState,
|
||||
Score: last.Score,
|
||||
Removed: last.Removed,
|
||||
JoinIndex: last.JoinIndex,
|
||||
}
|
||||
}
|
||||
|
||||
return toRet
|
||||
}
|
||||
|
||||
func toPbTreasures(modelInstances map[int32]*Treasure) map[int32]*pb.Treasure {
|
||||
toRet := make(map[int32]*pb.Treasure, 0)
|
||||
if nil == modelInstances {
|
||||
return toRet
|
||||
}
|
||||
|
||||
for k, last := range modelInstances {
|
||||
toRet[k] = &pb.Treasure{
|
||||
Id: last.Id,
|
||||
LocalIdInBattle: last.LocalIdInBattle,
|
||||
Score: last.Score,
|
||||
X: last.X,
|
||||
Y: last.Y,
|
||||
Removed: last.Removed,
|
||||
Type: last.Type,
|
||||
}
|
||||
}
|
||||
|
||||
return toRet
|
||||
}
|
||||
|
||||
func toPbTraps(modelInstances map[int32]*Trap) map[int32]*pb.Trap {
|
||||
toRet := make(map[int32]*pb.Trap, 0)
|
||||
if nil == modelInstances {
|
||||
return toRet
|
||||
}
|
||||
|
||||
for k, last := range modelInstances {
|
||||
toRet[k] = &pb.Trap{
|
||||
Id: last.Id,
|
||||
LocalIdInBattle: last.LocalIdInBattle,
|
||||
X: last.X,
|
||||
Y: last.Y,
|
||||
Removed: last.Removed,
|
||||
Type: last.Type,
|
||||
}
|
||||
}
|
||||
|
||||
return toRet
|
||||
}
|
||||
|
||||
func toPbBullets(modelInstances map[int32]*Bullet) map[int32]*pb.Bullet {
|
||||
toRet := make(map[int32]*pb.Bullet, 0)
|
||||
if nil == modelInstances {
|
||||
return toRet
|
||||
}
|
||||
|
||||
for k, last := range modelInstances {
|
||||
if nil == last.StartAtPoint || nil == last.EndAtPoint {
|
||||
continue
|
||||
}
|
||||
toRet[k] = &pb.Bullet{
|
||||
LocalIdInBattle: last.LocalIdInBattle,
|
||||
LinearSpeed: last.LinearSpeed,
|
||||
X: last.X,
|
||||
Y: last.Y,
|
||||
Removed: last.Removed,
|
||||
StartAtPoint: &pb.Vec2D{
|
||||
X: last.StartAtPoint.X,
|
||||
Y: last.StartAtPoint.Y,
|
||||
},
|
||||
EndAtPoint: &pb.Vec2D{
|
||||
X: last.EndAtPoint.X,
|
||||
Y: last.EndAtPoint.Y,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return toRet
|
||||
}
|
||||
|
||||
func toPbSpeedShoes(modelInstances map[int32]*SpeedShoe) map[int32]*pb.SpeedShoe {
|
||||
toRet := make(map[int32]*pb.SpeedShoe, 0)
|
||||
if nil == modelInstances {
|
||||
return toRet
|
||||
}
|
||||
|
||||
for k, last := range modelInstances {
|
||||
toRet[k] = &pb.SpeedShoe{
|
||||
Id: last.Id,
|
||||
LocalIdInBattle: last.LocalIdInBattle,
|
||||
X: last.X,
|
||||
Y: last.Y,
|
||||
Removed: last.Removed,
|
||||
Type: last.Type,
|
||||
}
|
||||
}
|
||||
|
||||
return toRet
|
||||
}
|
||||
|
||||
func toPbGuardTowers(modelInstances map[int32]*GuardTower) map[int32]*pb.GuardTower {
|
||||
toRet := make(map[int32]*pb.GuardTower, 0)
|
||||
if nil == modelInstances {
|
||||
return toRet
|
||||
}
|
||||
|
||||
for k, last := range modelInstances {
|
||||
toRet[k] = &pb.GuardTower{
|
||||
Id: last.Id,
|
||||
LocalIdInBattle: last.LocalIdInBattle,
|
||||
X: last.X,
|
||||
Y: last.Y,
|
||||
Removed: last.Removed,
|
||||
Type: last.Type,
|
||||
}
|
||||
}
|
||||
|
||||
return toRet
|
||||
}
|
142
battle_srv/models/player.go
Normal file
142
battle_srv/models/player.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/ByteArena/box2d"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type PlayerBattleState struct {
|
||||
ADDED_PENDING_BATTLE_COLLIDER_ACK int32
|
||||
READDED_PENDING_BATTLE_COLLIDER_ACK int32
|
||||
ACTIVE int32
|
||||
DISCONNECTED int32
|
||||
LOST int32
|
||||
EXPELLED_DURING_GAME int32
|
||||
EXPELLED_IN_DISMISSAL int32
|
||||
}
|
||||
|
||||
var PlayerBattleStateIns PlayerBattleState
|
||||
|
||||
func InitPlayerBattleStateIns() {
|
||||
PlayerBattleStateIns = PlayerBattleState{
|
||||
ADDED_PENDING_BATTLE_COLLIDER_ACK: 0,
|
||||
READDED_PENDING_BATTLE_COLLIDER_ACK: 1,
|
||||
ACTIVE: 2,
|
||||
DISCONNECTED: 3,
|
||||
LOST: 4,
|
||||
EXPELLED_DURING_GAME: 5,
|
||||
EXPELLED_IN_DISMISSAL: 6,
|
||||
}
|
||||
}
|
||||
|
||||
type Player struct {
|
||||
Id int32 `json:"id,omitempty" db:"id"`
|
||||
X float64 `json:"x,omitempty"`
|
||||
Y float64 `json:"y,omitempty"`
|
||||
Dir *Direction `json:"dir,omitempty"`
|
||||
Speed int32 `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"`
|
||||
DisplayName string `json:"displayName,omitempty" db:"display_name"`
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
|
||||
FrozenAtGmtMillis int64 `json:"-" db:"-"`
|
||||
AddSpeedAtGmtMillis int64 `json:"-" db:"-"`
|
||||
CreatedAt int64 `json:"-" db:"created_at"`
|
||||
UpdatedAt int64 `json:"-" db:"updated_at"`
|
||||
DeletedAt NullInt64 `json:"-" db:"deleted_at"`
|
||||
TutorialStage int `json:"-" db:"tutorial_stage"`
|
||||
CollidableBody *box2d.B2Body `json:"-"`
|
||||
AckingFrameId int32 `json:"ackingFrameId"`
|
||||
AckingInputFrameId int32 `json:"-"`
|
||||
LastSentInputFrameId int32 `json:"-"`
|
||||
}
|
||||
|
||||
func ExistPlayerByName(name string) (bool, error) {
|
||||
return exist("player", sq.Eq{"name": name, "deleted_at": nil})
|
||||
}
|
||||
|
||||
func GetPlayerByName(name string) (*Player, error) {
|
||||
return getPlayer(sq.Eq{"name": name, "deleted_at": nil})
|
||||
}
|
||||
|
||||
func GetPlayerById(id int) (*Player, error) {
|
||||
return getPlayer(sq.Eq{"id": id, "deleted_at": nil})
|
||||
}
|
||||
|
||||
func getPlayer(cond sq.Eq) (*Player, error) {
|
||||
var p Player
|
||||
err := getObj("player", cond, &p)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
p.Dir = &Direction{
|
||||
Dx: 0,
|
||||
Dy: 0,
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func (p *Player) Insert(tx *sqlx.Tx) error {
|
||||
result, err := txInsert(tx, "player", []string{"name", "display_name", "created_at", "updated_at", "avatar"},
|
||||
[]interface{}{p.Name, p.DisplayName, p.CreatedAt, p.UpdatedAt, p.Avatar})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Id = int32(id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Update(tx *sqlx.Tx, id int32, p *Player) (bool, error) {
|
||||
query, args, err := sq.Update("player").
|
||||
Set("display_name", p.DisplayName).
|
||||
Set("avatar", p.Avatar).
|
||||
Where(sq.Eq{"id": id}).ToSql()
|
||||
|
||||
fmt.Println(query)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
result, err := tx.Exec(query, args...)
|
||||
if err != nil {
|
||||
fmt.Println("ERRRRRRR: ")
|
||||
fmt.Println(err)
|
||||
return false, err
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return rowsAffected >= 1, nil
|
||||
}
|
||||
|
||||
func UpdatePlayerTutorialStage(tx *sqlx.Tx, id int) (bool, error) {
|
||||
query, args, err := sq.Update("player").
|
||||
Set("tutorial_stage", 1).
|
||||
Where(sq.Eq{"tutorial_stage": 0, "id": id}).ToSql()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
result, err := tx.Exec(query, args...)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return rowsAffected >= 1, nil
|
||||
}
|
35
battle_srv/models/player_auth_binding.go
Normal file
35
battle_srv/models/player_auth_binding.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type PlayerAuthBinding struct {
|
||||
Channel int `db:"channel"`
|
||||
CreatedAt int64 `db:"created_at"`
|
||||
DeletedAt NullInt64 `db:"deleted_at"`
|
||||
ExtAuthID string `db:"ext_auth_id"`
|
||||
PlayerID int `db:"player_id"`
|
||||
UpdatedAt int64 `db:"updated_at"`
|
||||
}
|
||||
|
||||
func (p *PlayerAuthBinding) Insert(tx *sqlx.Tx) error {
|
||||
_, err := txInsert(tx, "player_auth_binding", []string{"channel", "created_at", "ext_auth_id",
|
||||
"player_id", "updated_at"},
|
||||
[]interface{}{p.Channel, p.CreatedAt, p.ExtAuthID, p.PlayerID, p.UpdatedAt})
|
||||
return err
|
||||
}
|
||||
|
||||
func GetPlayerAuthBinding(channel int, extAuthID string) (*PlayerAuthBinding, error) {
|
||||
var p PlayerAuthBinding
|
||||
err := getObj("player_auth_binding",
|
||||
sq.Eq{"channel": channel, "ext_auth_id": extAuthID, "deleted_at": nil},
|
||||
&p)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return &p, nil
|
||||
}
|
110
battle_srv/models/player_login.go
Normal file
110
battle_srv/models/player_login.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
. "server/common"
|
||||
"server/common/utils"
|
||||
"server/storage"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
)
|
||||
|
||||
type PlayerLogin struct {
|
||||
CreatedAt int64 `db:"created_at"`
|
||||
DeletedAt NullInt64 `db:"deleted_at"`
|
||||
DisplayName NullString `db:"display_name"`
|
||||
Avatar string `db:"avatar"`
|
||||
FromPublicIP NullString `db:"from_public_ip"`
|
||||
ID int `db:"id"`
|
||||
IntAuthToken string `db:"int_auth_token"`
|
||||
PlayerID int `db:"player_id"`
|
||||
UpdatedAt int64 `db:"updated_at"`
|
||||
}
|
||||
|
||||
func (p *PlayerLogin) Insert() error {
|
||||
result, err := insert("player_login", []string{"created_at", "display_name",
|
||||
"from_public_ip", "int_auth_token", "player_id", "updated_at", "avatar"},
|
||||
[]interface{}{p.CreatedAt, p.DisplayName, p.FromPublicIP, p.IntAuthToken,
|
||||
p.PlayerID, p.UpdatedAt, p.Avatar})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.ID = int(id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetPlayerLoginByToken(token string) (*PlayerLogin, error) {
|
||||
var p PlayerLogin
|
||||
err := getObj("player_login",
|
||||
sq.Eq{"int_auth_token": token, "deleted_at": nil},
|
||||
&p)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func GetPlayerLoginByPlayerId(playerId int) (*PlayerLogin, error) {
|
||||
var p PlayerLogin
|
||||
err := getObj("player_login",
|
||||
sq.Eq{"player_id": playerId, "deleted_at": nil},
|
||||
&p)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func GetPlayerIdByToken(token string) (int, error) {
|
||||
var p PlayerLogin
|
||||
err := getFields("player_login", []string{"player_id"},
|
||||
sq.Eq{"int_auth_token": token, "deleted_at": nil},
|
||||
&p)
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, nil
|
||||
}
|
||||
return p.PlayerID, nil
|
||||
}
|
||||
|
||||
// TODO 封装到helper
|
||||
func DelPlayerLoginByToken(token string) error {
|
||||
query, args, err := sq.Update("player_login").Set("deleted_at", utils.UnixtimeMilli()).
|
||||
Where(sq.Eq{"int_auth_token": token}).ToSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//Logger.Debug(query, args)
|
||||
_, err = storage.MySQLManagerIns.Exec(query, args...)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func EnsuredPlayerLoginByToken(id int, token string) (bool, error) {
|
||||
return exist("player_login", sq.Eq{"int_auth_token": token, "deleted_at": nil, "player_id": id})
|
||||
}
|
||||
|
||||
func EnsuredPlayerLoginById(id int) (bool, error) {
|
||||
return exist("player_login", sq.Eq{"player_id": id, "deleted_at": nil})
|
||||
}
|
||||
|
||||
func CleanExpiredPlayerLoginToken() error {
|
||||
now := utils.UnixtimeMilli()
|
||||
max := now - int64(Constants.Player.IntAuthTokenTTLSeconds*1000)
|
||||
|
||||
query, args, err := sq.Update("player_login").Set("deleted_at", now).
|
||||
Where(sq.LtOrEq{"created_at": max}).ToSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = storage.MySQLManagerIns.Exec(query, args...)
|
||||
return err
|
||||
}
|
129
battle_srv/models/player_wallet.go
Normal file
129
battle_srv/models/player_wallet.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
. "server/common"
|
||||
"server/common/utils"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type PlayerWallet struct {
|
||||
CreatedAt int64 `json:"-" db:"created_at"`
|
||||
DeletedAt NullInt64 `json:"-" db:"deleted_at"`
|
||||
Gem int `json:"gem" db:"gem"`
|
||||
ID int `json:"-" db:"id"`
|
||||
UpdatedAt int64 `json:"-" db:"updated_at"`
|
||||
}
|
||||
|
||||
func (p *PlayerWallet) Insert(tx *sqlx.Tx) error {
|
||||
_, err := txInsert(tx, "player_wallet", []string{"id", "created_at", "updated_at"},
|
||||
[]interface{}{p.ID, p.CreatedAt, p.UpdatedAt})
|
||||
return err
|
||||
}
|
||||
|
||||
func GetPlayerWalletById(id int) (*PlayerWallet, error) {
|
||||
var p PlayerWallet
|
||||
err := getObj("player_wallet", sq.Eq{"id": id, "deleted_at": nil}, &p)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func CostPlayerWallet(tx *sqlx.Tx, id int, currency int, val int) (int, error) {
|
||||
var column string
|
||||
switch currency {
|
||||
case Constants.Player.Diamond:
|
||||
column = "diamond"
|
||||
case Constants.Player.Energy:
|
||||
column = "energy"
|
||||
case Constants.Player.Gold:
|
||||
column = "gold"
|
||||
}
|
||||
if column == "" {
|
||||
Logger.Debug("CostPlayerWallet Error Currency",
|
||||
zap.Int("currency", currency), zap.Int("val", val))
|
||||
return Constants.RetCode.MysqlError, errors.New("error currency")
|
||||
}
|
||||
|
||||
now := utils.UnixtimeMilli()
|
||||
query, args, err := sq.Update("player_wallet").
|
||||
Set(column, sq.Expr(column+"-?", val)).Set("updated_at", now).
|
||||
Where(sq.Eq{"id": id, "deleted_at": nil}).
|
||||
Where(sq.GtOrEq{column: val}).ToSql()
|
||||
|
||||
Logger.Debug("CostPlayerWallet", zap.String("sql", query), zap.Any("args", args))
|
||||
if err != nil {
|
||||
return Constants.RetCode.MysqlError, err
|
||||
}
|
||||
result, err := tx.Exec(query, args...)
|
||||
if err != nil {
|
||||
return Constants.RetCode.MysqlError, err
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return Constants.RetCode.MysqlError, err
|
||||
}
|
||||
ok := rowsAffected >= 1
|
||||
Logger.Debug("CostPlayerWallet", zap.Int64("rowsAffected", rowsAffected),
|
||||
zap.Bool("cost", ok))
|
||||
if !ok {
|
||||
var ret int
|
||||
switch currency {
|
||||
case Constants.Player.Diamond:
|
||||
ret = Constants.RetCode.LackOfDiamond
|
||||
case Constants.Player.Energy:
|
||||
ret = Constants.RetCode.LackOfEnergy
|
||||
case Constants.Player.Gold:
|
||||
ret = Constants.RetCode.LackOfGold
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func AddPlayerWallet(tx *sqlx.Tx, id int, currency int, val int) (int, error) {
|
||||
var column string
|
||||
switch currency {
|
||||
case Constants.Player.Diamond:
|
||||
column = "diamond"
|
||||
case Constants.Player.Energy:
|
||||
column = "energy"
|
||||
case Constants.Player.Gold:
|
||||
column = "gold"
|
||||
}
|
||||
if column == "" {
|
||||
Logger.Debug("CostPlayerWallet Error Currency",
|
||||
zap.Int("currency", currency), zap.Int("val", val))
|
||||
return Constants.RetCode.MysqlError, errors.New("error currency")
|
||||
}
|
||||
|
||||
now := utils.UnixtimeMilli()
|
||||
query, args, err := sq.Update("player_wallet").
|
||||
Set(column, sq.Expr(column+"+?", val)).Set("updated_at", now).
|
||||
Where(sq.Eq{"id": id, "deleted_at": nil}).ToSql()
|
||||
|
||||
Logger.Debug("AddPlayerWallet", zap.String("sql", query), zap.Any("args", args))
|
||||
if err != nil {
|
||||
return Constants.RetCode.MysqlError, err
|
||||
}
|
||||
result, err := tx.Exec(query, args...)
|
||||
if err != nil {
|
||||
return Constants.RetCode.MysqlError, err
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return Constants.RetCode.MysqlError, err
|
||||
}
|
||||
ok := rowsAffected >= 1
|
||||
Logger.Debug("AddPlayerWallet", zap.Int64("rowsAffected", rowsAffected),
|
||||
zap.Bool("add", ok))
|
||||
if !ok {
|
||||
return Constants.RetCode.UnknownError, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
14
battle_srv/models/pumpkin.go
Normal file
14
battle_srv/models/pumpkin.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package models
|
||||
|
||||
import "github.com/ByteArena/box2d"
|
||||
|
||||
type Pumpkin struct {
|
||||
LocalIdInBattle int32 `json:"localIdInBattle,omitempty"`
|
||||
LinearSpeed float64 `json:"linearSpeed,omitempty"`
|
||||
X float64 `json:"x,omitempty"`
|
||||
Y float64 `json:"y,omitempty"`
|
||||
Removed bool `json:"removed,omitempty"`
|
||||
Dir *Direction `json:"-"`
|
||||
CollidableBody *box2d.B2Body `json:"-"`
|
||||
RemovedAtFrameId int32 `json:"-"`
|
||||
}
|
73
battle_srv/models/ringbuf.go
Normal file
73
battle_srv/models/ringbuf.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package models
|
||||
|
||||
type RingBuffer struct {
|
||||
Ed int32 // write index
|
||||
St int32 // read index
|
||||
EdFrameId int32
|
||||
StFrameId int32
|
||||
N int32
|
||||
Cnt int32 // the count of valid elements in the buffer, used mainly to distinguish what "st == ed" means for "Pop" and "Get" methods
|
||||
Eles []interface{}
|
||||
}
|
||||
|
||||
func NewRingBuffer(n int32) *RingBuffer {
|
||||
return &RingBuffer{
|
||||
Ed: 0,
|
||||
St: 0,
|
||||
N: n,
|
||||
Cnt: 0,
|
||||
Eles: make([]interface{}, n),
|
||||
}
|
||||
}
|
||||
|
||||
func (rb *RingBuffer) Put(pItem interface{}) {
|
||||
rb.Eles[rb.Ed] = pItem
|
||||
rb.EdFrameId++
|
||||
rb.Cnt++
|
||||
rb.Ed++
|
||||
if rb.Ed >= rb.N {
|
||||
rb.Ed -= rb.N // Deliberately not using "%" operator for performance concern
|
||||
}
|
||||
}
|
||||
|
||||
func (rb *RingBuffer) Pop() interface{} {
|
||||
if 0 == rb.Cnt {
|
||||
return nil
|
||||
}
|
||||
pItem := rb.Eles[rb.St]
|
||||
rb.StFrameId++
|
||||
rb.Cnt--
|
||||
rb.St++
|
||||
if rb.St >= rb.N {
|
||||
rb.St -= rb.N
|
||||
}
|
||||
return pItem
|
||||
}
|
||||
|
||||
func (rb *RingBuffer) GetByOffset(offsetFromSt int32) interface{} {
|
||||
if 0 == rb.Cnt {
|
||||
return nil
|
||||
}
|
||||
arrIdx := rb.St + offsetFromSt
|
||||
if rb.St < rb.Ed {
|
||||
// case#1: 0...st...ed...N-1
|
||||
if rb.St <= arrIdx && arrIdx < rb.Ed {
|
||||
return rb.Eles[arrIdx]
|
||||
}
|
||||
} else {
|
||||
// if rb.St >= rb.Ed
|
||||
// case#2: 0...ed...st...N-1
|
||||
if arrIdx >= rb.N {
|
||||
arrIdx -= rb.N
|
||||
}
|
||||
if arrIdx >= rb.St || arrIdx < rb.Ed {
|
||||
return rb.Eles[arrIdx]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rb *RingBuffer) GetByFrameId(frameId int32) interface{} {
|
||||
return rb.GetByOffset(frameId - rb.StFrameId)
|
||||
}
|
1450
battle_srv/models/room.go
Normal file
1450
battle_srv/models/room.go
Normal file
File diff suppressed because it is too large
Load Diff
138
battle_srv/models/room_heap_manager.go
Normal file
138
battle_srv/models/room_heap_manager.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"fmt"
|
||||
"github.com/gorilla/websocket"
|
||||
"go.uber.org/zap"
|
||||
. "server/common"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Reference https://github.com/genxium/GoStructPrac.
|
||||
type RoomHeap []*Room
|
||||
type RoomMap map[int32]*Room
|
||||
|
||||
var (
|
||||
// NOTE: For the package exported instances of non-primitive types to be accessed as singletons, they must be of pointer types.
|
||||
RoomHeapMux *sync.Mutex
|
||||
RoomHeapManagerIns *RoomHeap
|
||||
RoomMapManagerIns *RoomMap
|
||||
)
|
||||
|
||||
func (pPq *RoomHeap) PrintInOrder() {
|
||||
pq := *pPq
|
||||
fmt.Printf("The RoomHeap instance now contains:\n")
|
||||
for i := 0; i < len(pq); i++ {
|
||||
fmt.Printf("{index: %d, roomID: %d, score: %.2f} ", i, pq[i].Id, pq[i].Score)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
func (pq RoomHeap) Len() int { return len(pq) }
|
||||
|
||||
func (pq RoomHeap) Less(i, j int) bool {
|
||||
return pq[i].Score > pq[j].Score
|
||||
}
|
||||
|
||||
func (pq *RoomHeap) Swap(i, j int) {
|
||||
(*pq)[i], (*pq)[j] = (*pq)[j], (*pq)[i]
|
||||
(*pq)[i].Index = i
|
||||
(*pq)[j].Index = j
|
||||
}
|
||||
|
||||
func (pq *RoomHeap) Push(pItem interface{}) {
|
||||
// NOTE: Must take input param type `*Room` here.
|
||||
n := len(*pq)
|
||||
pItem.(*Room).Index = n
|
||||
*pq = append(*pq, pItem.(*Room))
|
||||
}
|
||||
|
||||
func (pq *RoomHeap) Pop() interface{} {
|
||||
old := *pq
|
||||
n := len(old)
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
pItem := old[n-1]
|
||||
if pItem.Score <= float32(0.0) {
|
||||
return nil
|
||||
}
|
||||
pItem.Index = -1 // for safety
|
||||
*pq = old[0 : n-1]
|
||||
// NOTE: Must return instance which is directly castable to type `*Room` here.
|
||||
return pItem
|
||||
}
|
||||
|
||||
func (pq *RoomHeap) update(pItem *Room, Score float32) {
|
||||
// NOTE: Must use type `*Room` here.
|
||||
heap.Fix(pq, pItem.Index)
|
||||
}
|
||||
|
||||
func (pq *RoomHeap) Update(pItem *Room, Score float32) {
|
||||
pq.update(pItem, Score)
|
||||
}
|
||||
|
||||
func PrintRoomMap() {
|
||||
fmt.Printf("The RoomMap instance now contains:\n")
|
||||
for _, pR := range *RoomMapManagerIns {
|
||||
fmt.Printf("{roomID: %d, score: %.2f} ", pR.Id, pR.Score)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
func InitRoomHeapManager() {
|
||||
RoomHeapMux = new(sync.Mutex)
|
||||
// Init "pseudo class constants".
|
||||
InitRoomBattleStateIns()
|
||||
InitPlayerBattleStateIns()
|
||||
initialCountOfRooms := 32
|
||||
pq := make(RoomHeap, initialCountOfRooms)
|
||||
roomMap := make(RoomMap, initialCountOfRooms)
|
||||
|
||||
for i := 0; i < initialCountOfRooms; i++ {
|
||||
roomCapacity := 2
|
||||
joinIndexBooleanArr := make([]bool, roomCapacity)
|
||||
for index, _ := range joinIndexBooleanArr {
|
||||
joinIndexBooleanArr[index] = false
|
||||
}
|
||||
currentRoomBattleState := RoomBattleStateIns.IDLE
|
||||
pq[i] = &Room{
|
||||
Id: int32(i + 1),
|
||||
Players: make(map[int32]*Player),
|
||||
PlayerDownsyncSessionDict: make(map[int32]*websocket.Conn),
|
||||
PlayerSignalToCloseDict: make(map[int32]SignalToCloseConnCbType),
|
||||
Capacity: roomCapacity,
|
||||
Score: calRoomScore(0, roomCapacity, currentRoomBattleState),
|
||||
State: currentRoomBattleState,
|
||||
Index: i,
|
||||
Tick: 0,
|
||||
EffectivePlayerCount: 0,
|
||||
//BattleDurationNanos: int64(5 * 1000 * 1000 * 1000),
|
||||
BattleDurationNanos: int64(30 * 1000 * 1000 * 1000),
|
||||
ServerFPS: 60,
|
||||
Treasures: make(map[int32]*Treasure),
|
||||
Traps: make(map[int32]*Trap),
|
||||
GuardTowers: make(map[int32]*GuardTower),
|
||||
Bullets: make(map[int32]*Bullet),
|
||||
SpeedShoes: make(map[int32]*SpeedShoe),
|
||||
Barriers: make(map[int32]*Barrier),
|
||||
Pumpkins: make(map[int32]*Pumpkin),
|
||||
AccumulatedLocalIdForBullets: 0,
|
||||
AllPlayerInputsBuffer: NewRingBuffer(1024),
|
||||
LastAllConfirmedInputFrameId: -1,
|
||||
LastAllConfirmedInputFrameIdWithChange: -1,
|
||||
LastAllConfirmedInputList: make([]uint64, roomCapacity),
|
||||
InputDelayFrames: 4,
|
||||
InputScaleFrames: 2,
|
||||
JoinIndexBooleanArr: joinIndexBooleanArr,
|
||||
}
|
||||
roomMap[pq[i].Id] = pq[i]
|
||||
pq[i].ChooseStage()
|
||||
}
|
||||
heap.Init(&pq)
|
||||
RoomHeapManagerIns = &pq
|
||||
RoomMapManagerIns = &roomMap
|
||||
Logger.Info("The RoomHeapManagerIns has been initialized:", zap.Any("addr", fmt.Sprintf("%p", RoomHeapManagerIns)), zap.Any("size", len(*RoomHeapManagerIns)))
|
||||
Logger.Info("The RoomMapManagerIns has been initialized:", zap.Any("size", len(*RoomMapManagerIns)))
|
||||
}
|
17
battle_srv/models/speed_shoe.go
Normal file
17
battle_srv/models/speed_shoe.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/ByteArena/box2d"
|
||||
)
|
||||
|
||||
type SpeedShoe struct {
|
||||
Id int32 `json:"id,omitempty"`
|
||||
LocalIdInBattle int32 `json:"localIdInBattle,omitempty"`
|
||||
X float64 `json:"x,omitempty"`
|
||||
Y float64 `json:"y,omitempty"`
|
||||
Removed bool `json:"removed,omitempty"`
|
||||
Type int32 `json:"type,omitempty"`
|
||||
PickupBoundary *Polygon2D `json:"-"`
|
||||
CollidableBody *box2d.B2Body `json:"-"`
|
||||
RemovedAtFrameId int32 `json:"-"`
|
||||
}
|
537
battle_srv/models/tiled_map.go
Normal file
537
battle_srv/models/tiled_map.go
Normal file
@@ -0,0 +1,537 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/base64"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ByteArena/box2d"
|
||||
"go.uber.org/zap"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
. "server/common"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
LOW_SCORE_TREASURE_TYPE = 1
|
||||
HIGH_SCORE_TREASURE_TYPE = 2
|
||||
|
||||
SPEED_SHOES_TYPE = 3
|
||||
|
||||
LOW_SCORE_TREASURE_SCORE = 100
|
||||
HIGH_SCORE_TREASURE_SCORE = 200
|
||||
|
||||
FLIPPED_HORIZONTALLY_FLAG uint32 = 0x80000000
|
||||
FLIPPED_VERTICALLY_FLAG uint32 = 0x40000000
|
||||
FLIPPED_DIAGONALLY_FLAG uint32 = 0x20000000
|
||||
)
|
||||
|
||||
// For either a "*.tmx" or "*.tsx" file. [begins]
|
||||
type TmxOrTsxProperty struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Value string `xml:"value,attr"`
|
||||
}
|
||||
|
||||
type TmxOrTsxProperties struct {
|
||||
Property []*TmxOrTsxProperty `xml:"property"`
|
||||
}
|
||||
|
||||
type TmxOrTsxPolyline struct {
|
||||
Points string `xml:"points,attr"`
|
||||
}
|
||||
|
||||
type TmxOrTsxObject struct {
|
||||
Id int `xml:"id,attr"`
|
||||
Gid *int `xml:"gid,attr"`
|
||||
X float64 `xml:"x,attr"`
|
||||
Y float64 `xml:"y,attr"`
|
||||
Properties *TmxOrTsxProperties `xml:"properties"`
|
||||
Polyline *TmxOrTsxPolyline `xml:"polyline"`
|
||||
Width *float64 `xml:"width,attr"`
|
||||
Height *float64 `xml:"height,attr"`
|
||||
}
|
||||
|
||||
type TmxOrTsxObjectGroup struct {
|
||||
Draworder string `xml:"draworder,attr"`
|
||||
Name string `xml:"name,attr"`
|
||||
Objects []*TmxOrTsxObject `xml:"object"`
|
||||
}
|
||||
|
||||
type TmxOrTsxImage struct {
|
||||
Source string `xml:"source,attr"`
|
||||
Width int `xml:"width,attr"`
|
||||
Height int `xml:"height,attr"`
|
||||
}
|
||||
|
||||
// For either a "*.tmx" or "*.tsx" file. [ends]
|
||||
|
||||
// Within a "*.tsx" file. [begins]
|
||||
type Tsx struct {
|
||||
Name string `xml:"name,attr"`
|
||||
TileWidth int `xml:"tilewidth,attr"`
|
||||
TileHeight int `xml:"tileheight,attr"`
|
||||
TileCount int `xml:"tilecount,attr"`
|
||||
Columns int `xml:"columns,attr"`
|
||||
Image []*TmxOrTsxImage `xml:"image"`
|
||||
Tiles []*TsxTile `xml:"tile"`
|
||||
}
|
||||
|
||||
type TsxTile struct {
|
||||
Id int `xml:"id,attr"`
|
||||
ObjectGroup *TmxOrTsxObjectGroup `xml:"objectgroup"`
|
||||
Properties *TmxOrTsxProperties `xml:"properties"`
|
||||
}
|
||||
|
||||
// Within a "*.tsx" file. [ends]
|
||||
|
||||
// Within a "*.tmx" file. [begins]
|
||||
type TmxLayerDecodedTileData struct {
|
||||
Id uint32
|
||||
Tileset *TmxTileset
|
||||
FlipHorizontal bool
|
||||
FlipVertical bool
|
||||
FlipDiagonal bool
|
||||
}
|
||||
|
||||
type TmxLayerEncodedData struct {
|
||||
Encoding string `xml:"encoding,attr"`
|
||||
Compression string `xml:"compression,attr"`
|
||||
Value string `xml:",chardata"`
|
||||
}
|
||||
|
||||
type TmxLayer struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Width int `xml:"width,attr"`
|
||||
Height int `xml:"height,attr"`
|
||||
Data *TmxLayerEncodedData `xml:"data"`
|
||||
Tile []*TmxLayerDecodedTileData
|
||||
}
|
||||
|
||||
type TmxTileset struct {
|
||||
FirstGid uint32 `xml:"firstgid,attr"`
|
||||
Name string `xml:"name,attr"`
|
||||
TileWidth int `xml:"tilewidth,attr"`
|
||||
TileHeight int `xml:"tileheight,attr"`
|
||||
Images []*TmxOrTsxImage `xml:"image"`
|
||||
Source string `xml:"source,attr"`
|
||||
}
|
||||
|
||||
type TmxMap struct {
|
||||
Version string `xml:"version,attr"`
|
||||
Orientation string `xml:"orientation,attr"`
|
||||
Width int `xml:"width,attr"`
|
||||
Height int `xml:"height,attr"`
|
||||
TileWidth int `xml:"tilewidth,attr"`
|
||||
TileHeight int `xml:"tileheight,attr"`
|
||||
Properties []*TmxOrTsxProperties `xml:"properties"`
|
||||
Tilesets []*TmxTileset `xml:"tileset"`
|
||||
Layers []*TmxLayer `xml:"layer"`
|
||||
ObjectGroups []*TmxOrTsxObjectGroup `xml:"objectgroup"`
|
||||
}
|
||||
|
||||
// Within a "*.tmx" file. [ends]
|
||||
|
||||
func (d *TmxLayerEncodedData) decodeBase64() ([]byte, error) {
|
||||
r := bytes.NewReader([]byte(strings.TrimSpace(d.Value)))
|
||||
decr := base64.NewDecoder(base64.StdEncoding, r)
|
||||
if d.Compression == "zlib" {
|
||||
rclose, err := zlib.NewReader(decr)
|
||||
if err != nil {
|
||||
Logger.Error("tmx data decode zlib error: ", zap.Any("encoding", d.Encoding), zap.Any("compression", d.Compression), zap.Any("value", d.Value))
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.ReadAll(rclose)
|
||||
}
|
||||
Logger.Error("tmx data decode invalid compression: ", zap.Any("encoding", d.Encoding), zap.Any("compression", d.Compression), zap.Any("value", d.Value))
|
||||
return nil, errors.New("Invalid compression.")
|
||||
}
|
||||
|
||||
func (l *TmxLayer) decodeBase64() ([]uint32, error) {
|
||||
databytes, err := l.Data.decodeBase64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if l.Width == 0 || l.Height == 0 {
|
||||
return nil, errors.New("Zero width or height.")
|
||||
}
|
||||
if len(databytes) != l.Height*l.Width*4 {
|
||||
Logger.Error("TmxLayer decodeBase64 invalid data bytes:", zap.Any("width", l.Width), zap.Any("height", l.Height), zap.Any("data lenght", len(databytes)))
|
||||
return nil, errors.New("Data length error.")
|
||||
}
|
||||
dindex := 0
|
||||
gids := make([]uint32, l.Height*l.Width)
|
||||
for h := 0; h < l.Height; h++ {
|
||||
for w := 0; w < l.Width; w++ {
|
||||
gid := uint32(databytes[dindex]) |
|
||||
uint32(databytes[dindex+1])<<8 |
|
||||
uint32(databytes[dindex+2])<<16 |
|
||||
uint32(databytes[dindex+3])<<24
|
||||
dindex += 4
|
||||
gids[h*l.Width+w] = gid
|
||||
}
|
||||
}
|
||||
return gids, nil
|
||||
}
|
||||
|
||||
type Vec2DList []*Vec2D
|
||||
type Polygon2DList []*Polygon2D
|
||||
type StrToVec2DListMap map[string]*Vec2DList // Note that it's deliberately NOT using "map[string]Vec2DList", for the easy of passing return value to "models/room.go".
|
||||
type StrToPolygon2DListMap map[string]*Polygon2DList // Note that it's deliberately NOT using "map[string]Polygon2DList", for the easy of passing return value to "models/room.go".
|
||||
|
||||
func TmxPolylineToPolygon2DInB2World(pTmxMapIns *TmxMap, singleObjInTmxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline) (*Polygon2D, error) {
|
||||
if nil == targetPolyline {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
singleValueArray := strings.Split(targetPolyline.Points, " ")
|
||||
pointsCount := len(singleValueArray)
|
||||
|
||||
if pointsCount >= box2d.B2_maxPolygonVertices {
|
||||
return nil, errors.New(fmt.Sprintf("During `TmxPolylineToPolygon2DInB2World`, you have a polygon with pointsCount == %v, exceeding or equal to box2d.B2_maxPolygonVertices == %v, of polyines [%v]", pointsCount, box2d.B2_maxPolygonVertices, singleValueArray))
|
||||
}
|
||||
|
||||
theUntransformedAnchor := &Vec2D{
|
||||
X: singleObjInTmxFile.X,
|
||||
Y: singleObjInTmxFile.Y,
|
||||
}
|
||||
theTransformedAnchor := pTmxMapIns.continuousObjLayerOffsetToContinuousMapNodePos(theUntransformedAnchor)
|
||||
thePolygon2DFromPolyline := &Polygon2D{
|
||||
Anchor: &theTransformedAnchor,
|
||||
Points: make([]*Vec2D, len(singleValueArray)),
|
||||
}
|
||||
|
||||
for k, value := range singleValueArray {
|
||||
thePolygon2DFromPolyline.Points[k] = &Vec2D{}
|
||||
for kk, v := range strings.Split(value, ",") {
|
||||
coordinateValue, err := strconv.ParseFloat(v, 64)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
if 0 == (kk % 2) {
|
||||
thePolygon2DFromPolyline.Points[k].X = (coordinateValue)
|
||||
} else {
|
||||
thePolygon2DFromPolyline.Points[k].Y = (coordinateValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Transform to B2World space coordinate.
|
||||
tmp := &Vec2D{
|
||||
X: thePolygon2DFromPolyline.Points[k].X,
|
||||
Y: thePolygon2DFromPolyline.Points[k].Y,
|
||||
}
|
||||
transformedTmp := pTmxMapIns.continuousObjLayerVecToContinuousMapNodeVec(tmp)
|
||||
thePolygon2DFromPolyline.Points[k].X = transformedTmp.X
|
||||
thePolygon2DFromPolyline.Points[k].Y = transformedTmp.Y
|
||||
}
|
||||
|
||||
return thePolygon2DFromPolyline, nil
|
||||
}
|
||||
|
||||
func TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns *TmxMap, singleObjInTsxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline, pTsxIns *Tsx) (*Polygon2D, error) {
|
||||
if nil == targetPolyline {
|
||||
return nil, nil
|
||||
}
|
||||
var factorHalf float64 = 0.5
|
||||
offsetFromTopLeftInTileLocalCoordX := singleObjInTsxFile.X
|
||||
offsetFromTopLeftInTileLocalCoordY := singleObjInTsxFile.Y
|
||||
|
||||
singleValueArray := strings.Split(targetPolyline.Points, " ")
|
||||
pointsCount := len(singleValueArray)
|
||||
|
||||
if pointsCount >= box2d.B2_maxPolygonVertices {
|
||||
return nil, errors.New(fmt.Sprintf("During `TsxPolylineToOffsetsWrtTileCenterInB2World`, you have a polygon with pointsCount == %v, exceeding or equal to box2d.B2_maxPolygonVertices == %v", pointsCount, box2d.B2_maxPolygonVertices))
|
||||
}
|
||||
|
||||
thePolygon2DFromPolyline := &Polygon2D{
|
||||
Anchor: nil,
|
||||
Points: make([]*Vec2D, pointsCount),
|
||||
TileWidth: pTsxIns.TileWidth,
|
||||
TileHeight: pTsxIns.TileHeight,
|
||||
}
|
||||
|
||||
/*
|
||||
[WARNING] In this case, the "Treasure"s and "GuardTower"s are put into Tmx file as "ImageObject"s, of each the "ProportionalAnchor" is (0.5, 0). Therefore we calculate that "thePolygon2DFromPolyline.Points" are "offsets(in B2World) w.r.t. the BottomCenter". See https://shimo.im/docs/SmLJJhXm2C8XMzZT for details.
|
||||
*/
|
||||
|
||||
for k, value := range singleValueArray {
|
||||
thePolygon2DFromPolyline.Points[k] = &Vec2D{}
|
||||
for kk, v := range strings.Split(value, ",") {
|
||||
coordinateValue, err := strconv.ParseFloat(v, 64)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
if 0 == (kk % 2) {
|
||||
// W.r.t. center.
|
||||
thePolygon2DFromPolyline.Points[k].X = (coordinateValue + offsetFromTopLeftInTileLocalCoordX) - factorHalf*float64(pTsxIns.TileWidth)
|
||||
} else {
|
||||
// W.r.t. bottom.
|
||||
thePolygon2DFromPolyline.Points[k].Y = float64(pTsxIns.TileHeight) - (coordinateValue + offsetFromTopLeftInTileLocalCoordY)
|
||||
}
|
||||
}
|
||||
|
||||
// No need to transform for B2World space coordinate because the marks in a Tsx file is already rectilinear.
|
||||
}
|
||||
|
||||
return thePolygon2DFromPolyline, nil
|
||||
}
|
||||
|
||||
func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, firstGid int, gidBoundariesMapInB2World map[int]StrToPolygon2DListMap) error {
|
||||
pTsxIns := &Tsx{}
|
||||
err := xml.Unmarshal(byteArrOfTsxFile, pTsxIns)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
/*
|
||||
// For debug-printing only. -- YFLu, 2019-09-04.
|
||||
|
||||
reserializedTmxMap, err := pTmxMapIns.ToXML()
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
*/
|
||||
|
||||
for _, tile := range pTsxIns.Tiles {
|
||||
globalGid := (firstGid + int(tile.Id))
|
||||
/**
|
||||
Per tile xml str could be
|
||||
|
||||
```
|
||||
<tile id="13">
|
||||
<objectgroup draworder="index">
|
||||
<object id="1" x="-154" y="-159">
|
||||
<properties>
|
||||
<property name="boundary_type" value="guardTower"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -95,179 18,407 361,434 458,168 333,-7"/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
</tile>
|
||||
```
|
||||
, we currently REQUIRE that "`an object of a tile` with ONE OR MORE polylines must come with a single corresponding '<property name=`type` value=`...` />', and viceversa".
|
||||
|
||||
Refer to https://shimo.im/docs/SmLJJhXm2C8XMzZT for how we theoretically fit a "Polyline in Tsx" into a "Polygon2D" and then into the corresponding "B2BodyDef & B2Body in the `world of colliding bodies`".
|
||||
*/
|
||||
|
||||
theObjGroup := tile.ObjectGroup
|
||||
if nil == theObjGroup {
|
||||
continue
|
||||
}
|
||||
for _, singleObj := range theObjGroup.Objects {
|
||||
if nil == singleObj.Polyline {
|
||||
// Temporarily omit those non-polyline-containing objects.
|
||||
continue
|
||||
}
|
||||
if nil == singleObj.Properties.Property || "boundary_type" != singleObj.Properties.Property[0].Name {
|
||||
continue
|
||||
}
|
||||
|
||||
key := singleObj.Properties.Property[0].Value
|
||||
|
||||
var theStrToPolygon2DListMap StrToPolygon2DListMap
|
||||
if existingStrToPolygon2DListMap, ok := gidBoundariesMapInB2World[globalGid]; ok {
|
||||
theStrToPolygon2DListMap = existingStrToPolygon2DListMap
|
||||
} else {
|
||||
gidBoundariesMapInB2World[globalGid] = make(StrToPolygon2DListMap, 0)
|
||||
theStrToPolygon2DListMap = gidBoundariesMapInB2World[globalGid]
|
||||
}
|
||||
|
||||
var pThePolygon2DList *Polygon2DList
|
||||
if _, ok := theStrToPolygon2DListMap[key]; ok {
|
||||
pThePolygon2DList = theStrToPolygon2DListMap[key]
|
||||
} else {
|
||||
thePolygon2DList := make(Polygon2DList, 0)
|
||||
theStrToPolygon2DListMap[key] = &thePolygon2DList
|
||||
pThePolygon2DList = theStrToPolygon2DListMap[key]
|
||||
}
|
||||
|
||||
thePolygon2DFromPolyline, err := TsxPolylineToOffsetsWrtTileCenterInB2World(pTmxMapIns, singleObj, singleObj.Polyline, pTsxIns)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
*pThePolygon2DList = append(*pThePolygon2DList, thePolygon2DFromPolyline)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMapInB2World map[int]StrToPolygon2DListMap) (int32, int32, int32, int32, StrToVec2DListMap, StrToPolygon2DListMap, error) {
|
||||
toRetStrToVec2DListMap := make(StrToVec2DListMap, 0)
|
||||
toRetStrToPolygon2DListMap := make(StrToPolygon2DListMap, 0)
|
||||
/*
|
||||
Note that both
|
||||
- "Vec2D"s of "toRetStrToVec2DListMap", and
|
||||
- "Polygon2D"s of "toRetStrToPolygon2DListMap"
|
||||
|
||||
are already transformed into the "coordinate of B2World".
|
||||
|
||||
-- YFLu
|
||||
*/
|
||||
|
||||
for _, objGroup := range pTmxMapIns.ObjectGroups {
|
||||
switch objGroup.Name {
|
||||
case "PlayerStartingPos":
|
||||
var pTheVec2DListToCache *Vec2DList
|
||||
_, ok := toRetStrToVec2DListMap[objGroup.Name]
|
||||
if false == ok {
|
||||
theVec2DListToCache := make(Vec2DList, 0)
|
||||
toRetStrToVec2DListMap[objGroup.Name] = &theVec2DListToCache
|
||||
pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name]
|
||||
} else {
|
||||
pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name]
|
||||
}
|
||||
for _, singleObjInTmxFile := range objGroup.Objects {
|
||||
theUntransformedPos := &Vec2D{
|
||||
X: singleObjInTmxFile.X,
|
||||
Y: singleObjInTmxFile.Y,
|
||||
}
|
||||
thePosInWorld := pTmxMapIns.continuousObjLayerOffsetToContinuousMapNodePos(theUntransformedPos)
|
||||
*pTheVec2DListToCache = append(*pTheVec2DListToCache, &thePosInWorld)
|
||||
}
|
||||
case "Pumpkin", "SpeedShoe":
|
||||
case "Barrier":
|
||||
/*
|
||||
Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is located exactly in an overlapping with "Polygon2D.Points[0]" w.r.t. B2World.
|
||||
|
||||
-- YFLu
|
||||
*/
|
||||
var pThePolygon2DListToCache *Polygon2DList
|
||||
_, ok := toRetStrToPolygon2DListMap[objGroup.Name]
|
||||
if false == ok {
|
||||
thePolygon2DListToCache := make(Polygon2DList, 0)
|
||||
toRetStrToPolygon2DListMap[objGroup.Name] = &thePolygon2DListToCache
|
||||
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
|
||||
} else {
|
||||
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
|
||||
}
|
||||
|
||||
for _, singleObjInTmxFile := range objGroup.Objects {
|
||||
if nil == singleObjInTmxFile.Polyline {
|
||||
continue
|
||||
}
|
||||
if nil == singleObjInTmxFile.Properties.Property || "boundary_type" != singleObjInTmxFile.Properties.Property[0].Name || "barrier" != singleObjInTmxFile.Properties.Property[0].Value {
|
||||
continue
|
||||
}
|
||||
|
||||
thePolygon2DInWorld, err := TmxPolylineToPolygon2DInB2World(pTmxMapIns, singleObjInTmxFile, singleObjInTmxFile.Polyline)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
*pThePolygon2DListToCache = append(*pThePolygon2DListToCache, thePolygon2DInWorld)
|
||||
}
|
||||
case "LowScoreTreasure", "GuardTower", "HighScoreTreasure":
|
||||
/*
|
||||
Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" ISN'T located exactly in an overlapping with "Polygon2D.Points[0]" w.r.t. B2World, refer to "https://shimo.im/docs/SmLJJhXm2C8XMzZT" for details.
|
||||
|
||||
-- YFLu
|
||||
*/
|
||||
for _, singleObjInTmxFile := range objGroup.Objects {
|
||||
if nil == singleObjInTmxFile.Gid {
|
||||
continue
|
||||
}
|
||||
theGlobalGid := singleObjInTmxFile.Gid
|
||||
theStrToPolygon2DListMap, ok := gidBoundariesMapInB2World[*theGlobalGid]
|
||||
if false == ok {
|
||||
continue
|
||||
}
|
||||
|
||||
pThePolygon2DList, ok := theStrToPolygon2DListMap[objGroup.Name]
|
||||
if false == ok {
|
||||
continue
|
||||
}
|
||||
|
||||
var pThePolygon2DListToCache *Polygon2DList
|
||||
_, ok = toRetStrToPolygon2DListMap[objGroup.Name]
|
||||
if false == ok {
|
||||
thePolygon2DListToCache := make(Polygon2DList, 0)
|
||||
toRetStrToPolygon2DListMap[objGroup.Name] = &thePolygon2DListToCache
|
||||
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
|
||||
} else {
|
||||
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
|
||||
}
|
||||
|
||||
for _, thePolygon2D := range *pThePolygon2DList {
|
||||
theUntransformedBottomCenterAsAnchor := &Vec2D{
|
||||
X: singleObjInTmxFile.X,
|
||||
Y: singleObjInTmxFile.Y,
|
||||
}
|
||||
|
||||
theTransformedBottomCenterAsAnchor := pTmxMapIns.continuousObjLayerOffsetToContinuousMapNodePos(theUntransformedBottomCenterAsAnchor)
|
||||
|
||||
thePolygon2DInWorld := &Polygon2D{
|
||||
Anchor: &theTransformedBottomCenterAsAnchor,
|
||||
Points: make([]*Vec2D, len(thePolygon2D.Points)),
|
||||
TileWidth: thePolygon2D.TileWidth,
|
||||
TileHeight: thePolygon2D.TileHeight,
|
||||
}
|
||||
if nil != singleObjInTmxFile.Width && nil != singleObjInTmxFile.Height {
|
||||
thePolygon2DInWorld.TmxObjectWidth = *singleObjInTmxFile.Width
|
||||
thePolygon2DInWorld.TmxObjectHeight = *singleObjInTmxFile.Height
|
||||
}
|
||||
for kk, p := range thePolygon2D.Points {
|
||||
// [WARNING] It's intentionally recreating a copy of "Vec2D" here.
|
||||
thePolygon2DInWorld.Points[kk] = &Vec2D{
|
||||
X: p.X,
|
||||
Y: p.Y,
|
||||
}
|
||||
}
|
||||
*pThePolygon2DListToCache = append(*pThePolygon2DListToCache, thePolygon2DInWorld)
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
return int32(pTmxMapIns.Width), int32(pTmxMapIns.Height), int32(pTmxMapIns.TileWidth), int32(pTmxMapIns.TileHeight), toRetStrToVec2DListMap, toRetStrToPolygon2DListMap, nil
|
||||
}
|
||||
|
||||
func (pTmxMap *TmxMap) ToXML() (string, error) {
|
||||
ret, err := xml.Marshal(pTmxMap)
|
||||
return string(ret[:]), err
|
||||
}
|
||||
|
||||
type TileRectilinearSize struct {
|
||||
Width float64
|
||||
Height float64
|
||||
}
|
||||
|
||||
func (pTmxMapIns *TmxMap) continuousObjLayerVecToContinuousMapNodeVec(continuousObjLayerVec *Vec2D) Vec2D {
|
||||
var tileRectilinearSize TileRectilinearSize
|
||||
tileRectilinearSize.Width = float64(pTmxMapIns.TileWidth)
|
||||
tileRectilinearSize.Height = float64(pTmxMapIns.TileHeight)
|
||||
tileSizeUnifiedLength := math.Sqrt(tileRectilinearSize.Width*tileRectilinearSize.Width*0.25 + tileRectilinearSize.Height*tileRectilinearSize.Height*0.25)
|
||||
isometricObjectLayerPointOffsetScaleFactor := (tileSizeUnifiedLength / tileRectilinearSize.Height)
|
||||
cosineThetaRadian := (tileRectilinearSize.Width * 0.5) / tileSizeUnifiedLength
|
||||
sineThetaRadian := (tileRectilinearSize.Height * 0.5) / tileSizeUnifiedLength
|
||||
|
||||
transMat := [...][2]float64{
|
||||
{isometricObjectLayerPointOffsetScaleFactor * cosineThetaRadian, -isometricObjectLayerPointOffsetScaleFactor * cosineThetaRadian},
|
||||
{-isometricObjectLayerPointOffsetScaleFactor * sineThetaRadian, -isometricObjectLayerPointOffsetScaleFactor * sineThetaRadian},
|
||||
}
|
||||
convertedVecX := transMat[0][0]*continuousObjLayerVec.X + transMat[0][1]*continuousObjLayerVec.Y
|
||||
convertedVecY := transMat[1][0]*continuousObjLayerVec.X + transMat[1][1]*continuousObjLayerVec.Y
|
||||
converted := Vec2D{
|
||||
X: convertedVecX,
|
||||
Y: convertedVecY,
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func (pTmxMapIns *TmxMap) continuousObjLayerOffsetToContinuousMapNodePos(continuousObjLayerOffset *Vec2D) Vec2D {
|
||||
layerOffset := Vec2D{
|
||||
X: 0,
|
||||
Y: float64(pTmxMapIns.Height*pTmxMapIns.TileHeight) * 0.5,
|
||||
}
|
||||
|
||||
calibratedVec := continuousObjLayerOffset
|
||||
convertedVec := pTmxMapIns.continuousObjLayerVecToContinuousMapNodeVec(calibratedVec)
|
||||
|
||||
toRet := Vec2D{
|
||||
X: layerOffset.X + convertedVec.X,
|
||||
Y: layerOffset.Y + convertedVec.Y,
|
||||
}
|
||||
|
||||
return toRet
|
||||
}
|
39
battle_srv/models/trap.go
Normal file
39
battle_srv/models/trap.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/ByteArena/box2d"
|
||||
)
|
||||
|
||||
type Trap struct {
|
||||
Id int32 `json:"id,omitempty"`
|
||||
LocalIdInBattle int32 `json:"localIdInBattle,omitempty"`
|
||||
Type int32 `json:"type,omitempty"`
|
||||
X float64 `json:"x,omitempty"`
|
||||
Y float64 `json:"y,omitempty"`
|
||||
Removed bool `json:"removed,omitempty"`
|
||||
PickupBoundary *Polygon2D `json:"-"`
|
||||
TrapBullets []*Bullet `json:"-"`
|
||||
CollidableBody *box2d.B2Body `json:"-"`
|
||||
RemovedAtFrameId int32 `json:"-"`
|
||||
}
|
||||
|
||||
type GuardTower struct {
|
||||
Id int32 `json:"id,omitempty"`
|
||||
LocalIdInBattle int32 `json:"localIdInBattle,omitempty"`
|
||||
Type int32 `json:"type,omitempty"`
|
||||
X float64 `json:"x,omitempty"`
|
||||
Y float64 `json:"y,omitempty"`
|
||||
Removed bool `json:"removed,omitempty"`
|
||||
PickupBoundary *Polygon2D `json:"-"`
|
||||
TrapBullets []*Bullet `json:"-"`
|
||||
CollidableBody *box2d.B2Body `json:"-"`
|
||||
RemovedAtFrameId int32 `json:"-"`
|
||||
|
||||
InRangePlayers *InRangePlayerCollection `json:"-"`
|
||||
LastAttackTick int64 `json:"-"`
|
||||
|
||||
TileWidth float64 `json:"-"`
|
||||
TileHeight float64 `json:"-"`
|
||||
WidthInB2World float64 `json:"-"`
|
||||
HeightInB2World float64 `json:"-"`
|
||||
}
|
18
battle_srv/models/treasure.go
Normal file
18
battle_srv/models/treasure.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/ByteArena/box2d"
|
||||
)
|
||||
|
||||
type Treasure struct {
|
||||
Id int32 `json:"id,omitempty"`
|
||||
LocalIdInBattle int32 `json:"localIdInBattle,omitempty"`
|
||||
Score int32 `json:"score,omitempty"`
|
||||
X float64 `json:"x,omitempty"`
|
||||
Y float64 `json:"y,omitempty"`
|
||||
Removed bool `json:"removed,omitempty"`
|
||||
Type int32 `json:"type,omitempty"`
|
||||
|
||||
PickupBoundary *Polygon2D `json:"-"`
|
||||
CollidableBody *box2d.B2Body `json:"-"`
|
||||
}
|
74
battle_srv/models/type.go
Normal file
74
battle_srv/models/type.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type NullInt64 struct {
|
||||
sql.NullInt64
|
||||
}
|
||||
|
||||
func NewNullInt64(s int64) NullInt64 {
|
||||
ns := NullInt64{}
|
||||
ns.Int64 = s
|
||||
ns.Valid = true
|
||||
return ns
|
||||
}
|
||||
|
||||
func (v NullInt64) MarshalJSON() ([]byte, error) {
|
||||
if v.Valid {
|
||||
return json.Marshal(v.Int64)
|
||||
} else {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *NullInt64) UnmarshalJSON(data []byte) error {
|
||||
var s *int64
|
||||
//Logger.Debugf("%s\n", data)
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
if s != nil {
|
||||
v.Valid = true
|
||||
v.Int64 = *s
|
||||
} else {
|
||||
v.Valid = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullString struct {
|
||||
sql.NullString
|
||||
}
|
||||
|
||||
func NewNullString(s string) NullString {
|
||||
ns := NullString{}
|
||||
ns.String = s
|
||||
ns.Valid = true
|
||||
return ns
|
||||
}
|
||||
|
||||
func (v NullString) MarshalJSON() ([]byte, error) {
|
||||
if v.Valid {
|
||||
return json.Marshal(v.String)
|
||||
} else {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *NullString) UnmarshalJSON(data []byte) error {
|
||||
var s *string
|
||||
//Logger.Debugf("%s\n", data)
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
if s != nil {
|
||||
v.Valid = true
|
||||
v.String = *s
|
||||
} else {
|
||||
v.Valid = false
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user