Merge pull request #25 from josephbmanley/feature/player
Character Controller!
This commit is contained in:
commit
dca2ce0599
@ -0,0 +1,3 @@
|
|||||||
|
source_md5="8aa11b5093dfa6e5ad8586cfd48b6e88"
|
||||||
|
dest_md5="1922a2eba76828665cd680a720666fe8"
|
||||||
|
|
Binary file not shown.
BIN
client/art/entities/player/dev_player.aseprite
Normal file
BIN
client/art/entities/player/dev_player.aseprite
Normal file
Binary file not shown.
BIN
client/art/entities/player/dev_player.png
Normal file
BIN
client/art/entities/player/dev_player.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 545 B |
34
client/art/entities/player/dev_player.png.import
Normal file
34
client/art/entities/player/dev_player.png.import
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="StreamTexture"
|
||||||
|
path="res://.import/dev_player.png-a3e490acef445550df71bca0ea3173dc.stex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://art/entities/player/dev_player.png"
|
||||||
|
dest_files=[ "res://.import/dev_player.png-a3e490acef445550df71bca0ea3173dc.stex" ]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_mode=0
|
||||||
|
compress/bptc_ldr=0
|
||||||
|
compress/normal_map=0
|
||||||
|
flags/repeat=0
|
||||||
|
flags/filter=false
|
||||||
|
flags/mipmaps=false
|
||||||
|
flags/anisotropic=false
|
||||||
|
flags/srgb=2
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/HDR_as_SRGB=false
|
||||||
|
process/invert_color=false
|
||||||
|
stream=false
|
||||||
|
size_limit=0
|
||||||
|
detect_3d=true
|
||||||
|
svg/scale=1.0
|
@ -7,7 +7,7 @@ custom_features=""
|
|||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter=""
|
include_filter=""
|
||||||
exclude_filter=""
|
exclude_filter=""
|
||||||
export_path=""
|
export_path="builds/Family.x86_64"
|
||||||
patch_list=PoolStringArray( )
|
patch_list=PoolStringArray( )
|
||||||
script_export_mode=1
|
script_export_mode=1
|
||||||
script_encryption_key=""
|
script_encryption_key=""
|
||||||
|
8
client/nodes/Systems/PlayerPuppeteer.tscn
Normal file
8
client/nodes/Systems/PlayerPuppeteer.tscn
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[gd_scene load_steps=3 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://scripts/systems/PlayerPuppeteer.gd" type="Script" id=1]
|
||||||
|
[ext_resource path="res://nodes/entities/PlayerPuppet.tscn" type="PackedScene" id=2]
|
||||||
|
|
||||||
|
[node name="PlayerPuppeteer" type="Node2D"]
|
||||||
|
script = ExtResource( 1 )
|
||||||
|
puppet_template = ExtResource( 2 )
|
21
client/nodes/entities/Player.tscn
Normal file
21
client/nodes/entities/Player.tscn
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[gd_scene load_steps=3 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://art/entities/player/dev_player.png" type="Texture" id=1]
|
||||||
|
[ext_resource path="res://scripts/entities/Player.gd" type="Script" id=2]
|
||||||
|
|
||||||
|
[node name="Player" type="KinematicBody2D"]
|
||||||
|
script = ExtResource( 2 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_group_": true
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="Sprite" type="Sprite" parent="."]
|
||||||
|
texture = ExtResource( 1 )
|
||||||
|
|
||||||
|
[node name="Camera2D" type="Camera2D" parent="."]
|
||||||
|
current = true
|
||||||
|
zoom = Vector2( 0.5, 0.5 )
|
||||||
|
|
||||||
|
[node name="Line2D" type="Line2D" parent="."]
|
||||||
|
width = 4.0
|
||||||
|
default_color = Color( 1, 0, 0, 1 )
|
6
client/nodes/entities/PlayerPuppet.tscn
Normal file
6
client/nodes/entities/PlayerPuppet.tscn
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[gd_scene load_steps=2 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://art/entities/player/dev_player.png" type="Texture" id=1]
|
||||||
|
|
||||||
|
[node name="PlayerPuppet" type="Sprite"]
|
||||||
|
texture = ExtResource( 1 )
|
File diff suppressed because it is too large
Load Diff
@ -107,6 +107,14 @@ config/icon="res://icon.png"
|
|||||||
Nakama="*res://addons/com.heroiclabs.nakama/Nakama.gd"
|
Nakama="*res://addons/com.heroiclabs.nakama/Nakama.gd"
|
||||||
ServerConnection="*res://scripts/singletons/ServerConnection.gd"
|
ServerConnection="*res://scripts/singletons/ServerConnection.gd"
|
||||||
|
|
||||||
|
[input]
|
||||||
|
|
||||||
|
move_to_cursor={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":1,"pressed":false,"doubleclick":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
[rendering]
|
[rendering]
|
||||||
|
|
||||||
quality/driver/driver_name="GLES2"
|
quality/driver/driver_name="GLES2"
|
||||||
|
File diff suppressed because one or more lines are too long
39
client/scripts/entities/Player.gd
Normal file
39
client/scripts/entities/Player.gd
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
extends KinematicBody2D
|
||||||
|
|
||||||
|
signal finished_moving
|
||||||
|
|
||||||
|
export var base_movement_speed = 100
|
||||||
|
export(NodePath) var world
|
||||||
|
|
||||||
|
onready var navigation : Navigation2D = get_node(world)
|
||||||
|
var path : PoolVector2Array
|
||||||
|
var move : bool = false
|
||||||
|
|
||||||
|
func _process(delta):
|
||||||
|
|
||||||
|
# Click for movement target
|
||||||
|
if Input.is_action_just_pressed("move_to_cursor"):
|
||||||
|
path = navigation.get_simple_path(global_position, get_global_mouse_position())
|
||||||
|
move = true
|
||||||
|
|
||||||
|
if move:
|
||||||
|
move_along_path(base_movement_speed * delta)
|
||||||
|
ServerConnection.send_player_position(global_position)
|
||||||
|
|
||||||
|
func move_along_path(distance : float):
|
||||||
|
var start_point := global_position
|
||||||
|
for i in range(path.size()):
|
||||||
|
var distance_to_next := start_point.distance_to(path[0])
|
||||||
|
if distance <= distance_to_next and distance >= 0.0:
|
||||||
|
global_position = start_point.linear_interpolate(path[0], distance/distance_to_next)
|
||||||
|
break
|
||||||
|
elif distance < 0.0:
|
||||||
|
global_position = path[0]
|
||||||
|
emit_signal("finished_moving")
|
||||||
|
print("DONE")
|
||||||
|
move = false
|
||||||
|
break
|
||||||
|
distance -= distance_to_next
|
||||||
|
start_point = path[0]
|
||||||
|
path.remove(0)
|
||||||
|
|
@ -1,6 +1,9 @@
|
|||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
signal tile_update(tile_data)
|
signal tile_update(tile_data)
|
||||||
|
signal player_joined(user_id)
|
||||||
|
signal player_left(user_id)
|
||||||
|
signal player_pos_update(user_id, pos)
|
||||||
|
|
||||||
const KEY := "defaultkey"
|
const KEY := "defaultkey"
|
||||||
const SERVER_ENDPOINT := "nakama.cloudsumu.com"
|
const SERVER_ENDPOINT := "nakama.cloudsumu.com"
|
||||||
@ -9,9 +12,11 @@ var _session : NakamaSession
|
|||||||
var _client : NakamaClient = Nakama.create_client(KEY, SERVER_ENDPOINT, 7350, "http")
|
var _client : NakamaClient = Nakama.create_client(KEY, SERVER_ENDPOINT, 7350, "http")
|
||||||
var _socket : NakamaSocket
|
var _socket : NakamaSocket
|
||||||
var _precenses : Dictionary = {}
|
var _precenses : Dictionary = {}
|
||||||
|
var _world_id
|
||||||
|
|
||||||
enum OPCODE {
|
enum OPCODE {
|
||||||
tile_update = 1
|
tile_update = 1,
|
||||||
|
update_position = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
func authenticate_async(email : String, password : String) -> NakamaException:
|
func authenticate_async(email : String, password : String) -> NakamaException:
|
||||||
@ -43,6 +48,7 @@ func connect_to_server_async() -> NakamaException:
|
|||||||
var result : NakamaAsyncResult = yield(_socket.connect_async(_session), "completed")
|
var result : NakamaAsyncResult = yield(_socket.connect_async(_session), "completed")
|
||||||
if not result.is_exception():
|
if not result.is_exception():
|
||||||
_socket.connect("received_match_state", self, "_on_socket_received_match_state")
|
_socket.connect("received_match_state", self, "_on_socket_received_match_state")
|
||||||
|
_socket.connect("received_match_presence", self, "_on_received_match_presence")
|
||||||
_socket.connect("closed", self, "_on_socket_closed")
|
_socket.connect("closed", self, "_on_socket_closed")
|
||||||
return null
|
return null
|
||||||
return result.exception
|
return result.exception
|
||||||
@ -57,9 +63,12 @@ func join_world_async() -> Dictionary:
|
|||||||
if match_join_result.is_exception():
|
if match_join_result.is_exception():
|
||||||
print("Join match error: %s - %s" % [match_join_result.exception.status_code, match_join_result.exception.message])
|
print("Join match error: %s - %s" % [match_join_result.exception.status_code, match_join_result.exception.message])
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
_world_id = world.payload
|
||||||
|
|
||||||
for precense in match_join_result.presences:
|
for precense in match_join_result.presences:
|
||||||
_precenses[precense.user_id] = precense
|
_precenses[precense.user_id] = precense
|
||||||
|
emit_signal("player_joined", precense.user_id)
|
||||||
|
|
||||||
print("Joined matched with %s other players!" % _precenses.size())
|
print("Joined matched with %s other players!" % _precenses.size())
|
||||||
|
|
||||||
@ -67,9 +76,26 @@ func join_world_async() -> Dictionary:
|
|||||||
|
|
||||||
func _on_socket_closed():
|
func _on_socket_closed():
|
||||||
_socket = null
|
_socket = null
|
||||||
|
|
||||||
|
func _on_received_match_presence(match_precense : NakamaRTAPI.MatchPresenceEvent):
|
||||||
|
for precense in match_precense.joins:
|
||||||
|
print("%s joined the game!" % precense.username)
|
||||||
|
_precenses[precense.user_id] = precense
|
||||||
|
emit_signal("player_joined", precense.user_id)
|
||||||
|
|
||||||
|
for precense in match_precense.leaves:
|
||||||
|
print("%s left the game!" % precense.username)
|
||||||
|
_precenses.erase(precense.user_id)
|
||||||
|
emit_signal("player_left", precense.user_id)
|
||||||
|
|
||||||
func _on_socket_received_match_state(match_state: NakamaRTAPI.MatchData):
|
func _on_socket_received_match_state(match_state: NakamaRTAPI.MatchData):
|
||||||
match match_state.op_code:
|
match match_state.op_code:
|
||||||
OPCODE.tile_update:
|
OPCODE.tile_update:
|
||||||
emit_signal("tile_update", JSON.parse(match_state.data).result)
|
emit_signal("tile_update", JSON.parse(match_state.data).result)
|
||||||
|
OPCODE.update_position:
|
||||||
|
var pos_data = JSON.parse(match_state.data).result
|
||||||
|
emit_signal("player_pos_update", pos_data["player"], Vector2(float(pos_data["x"]), float(pos_data["y"])))
|
||||||
|
|
||||||
|
func send_player_position(position : Vector2) -> void:
|
||||||
|
_socket.send_match_state_async(_world_id, OPCODE.update_position, JSON.print({X = str(position.x), Y = str(position.y)}))
|
||||||
|
|
||||||
|
31
client/scripts/systems/PlayerPuppeteer.gd
Normal file
31
client/scripts/systems/PlayerPuppeteer.gd
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
extends Node2D
|
||||||
|
|
||||||
|
export(NodePath) var puppet_parent
|
||||||
|
export(Resource) var puppet_template
|
||||||
|
|
||||||
|
onready var puppet_parent_node : Node = get_node(puppet_parent)
|
||||||
|
|
||||||
|
var puppets : Dictionary = {}
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
ServerConnection.connect("player_joined", self, "on_player_join")
|
||||||
|
ServerConnection.connect("player_left", self, "on_player_leave")
|
||||||
|
ServerConnection.connect("player_pos_update", self, "on_player_pos_update")
|
||||||
|
|
||||||
|
|
||||||
|
func on_player_join(user_id):
|
||||||
|
if user_id != ServerConnection._session.user_id:
|
||||||
|
var new_puppet : Node = puppet_template.instance()
|
||||||
|
new_puppet.name = "Player: " + user_id
|
||||||
|
puppet_parent_node.add_child(new_puppet)
|
||||||
|
puppets[user_id] = new_puppet
|
||||||
|
|
||||||
|
func on_player_leave(user_id):
|
||||||
|
if user_id != ServerConnection._session.user_id:
|
||||||
|
var player_puppet : Node = puppets[user_id]
|
||||||
|
player_puppet.queue_free()
|
||||||
|
|
||||||
|
func on_player_pos_update(user_id, pos):
|
||||||
|
if user_id != ServerConnection._session.user_id:
|
||||||
|
var player_puppet : Node2D = puppets[user_id]
|
||||||
|
player_puppet.global_position = pos
|
@ -3,10 +3,14 @@ package control
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
"github.com/heroiclabs/nakama-common/runtime"
|
"github.com/heroiclabs/nakama-common/runtime"
|
||||||
|
"github.com/josephbmanley/family/server/plugin/entities"
|
||||||
"github.com/josephbmanley/family/server/plugin/gamemap"
|
"github.com/josephbmanley/family/server/plugin/gamemap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maxRenderDistance int = 32
|
||||||
|
|
||||||
// OpCode represents a enum for valid OpCodes
|
// OpCode represents a enum for valid OpCodes
|
||||||
// used by the match logic
|
// used by the match logic
|
||||||
type OpCode int64
|
type OpCode int64
|
||||||
@ -14,6 +18,8 @@ type OpCode int64
|
|||||||
const (
|
const (
|
||||||
// OpCodeTileUpdate is used for tile updates
|
// OpCodeTileUpdate is used for tile updates
|
||||||
OpCodeTileUpdate = 1
|
OpCodeTileUpdate = 1
|
||||||
|
// OpCodeUpdatePosition is used for player position updates
|
||||||
|
OpCodeUpdatePosition = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// Match is the object registered
|
// Match is the object registered
|
||||||
@ -24,20 +30,27 @@ type Match struct{}
|
|||||||
// Nakama match methods
|
// Nakama match methods
|
||||||
type MatchState struct {
|
type MatchState struct {
|
||||||
presences map[string]runtime.Presence
|
presences map[string]runtime.Presence
|
||||||
|
players map[string]entities.PlayerEntity
|
||||||
inputs map[string]string
|
inputs map[string]string
|
||||||
positions map[string]map[string]int
|
|
||||||
names map[string]string
|
|
||||||
worldMap *gamemap.WorldMap
|
worldMap *gamemap.WorldMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPrecenseList returns an array of current precenes in an array
|
||||||
|
func (state *MatchState) GetPrecenseList() []runtime.Presence {
|
||||||
|
precenseList := []runtime.Presence{}
|
||||||
|
for _, precense := range state.presences {
|
||||||
|
precenseList = append(precenseList, precense)
|
||||||
|
}
|
||||||
|
return precenseList
|
||||||
|
}
|
||||||
|
|
||||||
// MatchInit is called when a new match is created
|
// MatchInit is called when a new match is created
|
||||||
func (m *Match) MatchInit(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, params map[string]interface{}) (interface{}, int, string) {
|
func (m *Match) MatchInit(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, params map[string]interface{}) (interface{}, int, string) {
|
||||||
|
|
||||||
state := &MatchState{
|
state := &MatchState{
|
||||||
presences: map[string]runtime.Presence{},
|
presences: map[string]runtime.Presence{},
|
||||||
inputs: map[string]string{},
|
inputs: map[string]string{},
|
||||||
positions: map[string]map[string]int{},
|
players: map[string]entities.PlayerEntity{},
|
||||||
names: map[string]string{},
|
|
||||||
worldMap: gamemap.IntializeMap(),
|
worldMap: gamemap.IntializeMap(),
|
||||||
}
|
}
|
||||||
tickRate := 10
|
tickRate := 10
|
||||||
@ -77,13 +90,24 @@ func (m *Match) MatchJoin(ctx context.Context, logger runtime.Logger, db *sql.DB
|
|||||||
// Add presence to map
|
// Add presence to map
|
||||||
mState.presences[precense.GetUserId()] = precense
|
mState.presences[precense.GetUserId()] = precense
|
||||||
|
|
||||||
// Set player spawn pos
|
player := entities.PlayerEntity{
|
||||||
mState.positions[precense.GetUserId()] = map[string]int{"x": 16, "y": 16}
|
X: 16,
|
||||||
|
Y: 16,
|
||||||
|
Presence: precense,
|
||||||
|
}
|
||||||
|
|
||||||
mState.names[precense.GetUserId()] = "User"
|
if jsonObj, err := player.GetPosJSON(); err != nil {
|
||||||
|
logger.Error(err.Error())
|
||||||
|
} else {
|
||||||
|
if sendErr := dispatcher.BroadcastMessage(OpCodeUpdatePosition, jsonObj, []runtime.Presence{precense}, player.Presence, true); sendErr != nil {
|
||||||
|
logger.Error(sendErr.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mState.players[precense.GetUserId()] = player
|
||||||
|
|
||||||
// Get intial tile data around player
|
// Get intial tile data around player
|
||||||
if regionData, err := mState.worldMap.GetJSONRegionAround(16, 16, 8); err != nil {
|
if regionData, err := mState.worldMap.GetJSONRegionAround(player.X, player.Y, maxRenderDistance); err != nil {
|
||||||
logger.Error(err.Error())
|
logger.Error(err.Error())
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -92,6 +116,16 @@ func (m *Match) MatchJoin(ctx context.Context, logger runtime.Logger, db *sql.DB
|
|||||||
logger.Error(sendErr.Error())
|
logger.Error(sendErr.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, otherPlayer := range mState.players {
|
||||||
|
// Broadcast player data to client
|
||||||
|
if jsonObj, err := otherPlayer.GetPosJSON(); err != nil {
|
||||||
|
logger.Error(err.Error())
|
||||||
|
} else {
|
||||||
|
if sendErr := dispatcher.BroadcastMessage(OpCodeUpdatePosition, jsonObj, []runtime.Presence{precense}, otherPlayer.Presence, true); sendErr != nil {
|
||||||
|
logger.Error(sendErr.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return mState
|
return mState
|
||||||
}
|
}
|
||||||
@ -104,19 +138,41 @@ func (m *Match) MatchLeave(ctx context.Context, logger runtime.Logger, db *sql.D
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
for _, presence := range presences {
|
for _, presence := range presences {
|
||||||
delete(mState.presences, presence.GetUserId())
|
if _, ok := mState.presences[presence.GetUserId()]; ok {
|
||||||
|
delete(mState.presences, presence.GetUserId())
|
||||||
|
}
|
||||||
|
if _, ok := mState.players[presence.GetUserId()]; ok {
|
||||||
|
delete(mState.players, presence.GetUserId())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return mState
|
return mState
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchLoop is code that is executed every tick
|
// MatchLoop is code that is executed every tick
|
||||||
func (m *Match) MatchLoop(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, messages []runtime.MatchData) interface{} {
|
func (m *Match) MatchLoop(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, messages []runtime.MatchData) interface{} {
|
||||||
// Custom code to:
|
mState, ok := state.(*MatchState)
|
||||||
// - Process the messages received.
|
if !ok {
|
||||||
// - Update the match state based on the messages and time elapsed.
|
logger.Error("Invalid match state on leave!")
|
||||||
// - Broadcast new data messages to match participants.
|
return state
|
||||||
|
}
|
||||||
|
for _, message := range messages {
|
||||||
|
if message.GetOpCode() == OpCodeUpdatePosition {
|
||||||
|
player := mState.players[message.GetUserId()]
|
||||||
|
|
||||||
return state
|
if response, err := player.ParsePositionRequest(message.GetData()); err == nil {
|
||||||
|
player.UpdateBasedOnResponse(response)
|
||||||
|
if jsonObject, err := player.GetPosJSON(); err == nil {
|
||||||
|
dispatcher.BroadcastMessage(OpCodeUpdatePosition, jsonObject, mState.GetPrecenseList(), player.Presence, false)
|
||||||
|
logger.Info("Yes")
|
||||||
|
} else {
|
||||||
|
logger.Error(fmt.Sprintf("Failed to get player json: %s", err.Error))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.Error(fmt.Sprintf("Failed to parse update pos request: %s", err.Error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mState
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchTerminate is code that is executed when the match ends
|
// MatchTerminate is code that is executed when the match ends
|
||||||
|
55
server/plugin/entities/entities.go
Normal file
55
server/plugin/entities/entities.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/heroiclabs/nakama-common/runtime"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PlayerEntity is the go struct representing the player's location
|
||||||
|
type PlayerEntity struct {
|
||||||
|
Presence runtime.Presence
|
||||||
|
X float64
|
||||||
|
Y float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlayerPosResponse struct that represents client data
|
||||||
|
type PlayerPosResponse struct {
|
||||||
|
X string
|
||||||
|
Y string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePositionRequest parses data from client
|
||||||
|
func (p *PlayerEntity) ParsePositionRequest(data []byte) (PlayerPosResponse, error) {
|
||||||
|
var response PlayerPosResponse
|
||||||
|
err := json.Unmarshal(data, &response)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//UpdateBasedOnResponse updates the player object based on a response object
|
||||||
|
func (p *PlayerEntity) UpdateBasedOnResponse(response PlayerPosResponse) error {
|
||||||
|
if fx, err := strconv.ParseFloat(response.X, 64); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
p.X = fx
|
||||||
|
if fy, err := strconv.ParseFloat(response.Y, 64); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
p.Y = fy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPosJSON returns the player's position as a JSON object
|
||||||
|
func (p *PlayerEntity) GetPosJSON() ([]byte, error) {
|
||||||
|
playerMap := map[string]string{
|
||||||
|
"player": p.Presence.GetUserId(),
|
||||||
|
"x": fmt.Sprintf("%f", p.X),
|
||||||
|
"y": fmt.Sprintf("%f", p.Y),
|
||||||
|
}
|
||||||
|
jsonData, err := json.Marshal(playerMap)
|
||||||
|
return jsonData, err
|
||||||
|
}
|
@ -7,14 +7,14 @@ import (
|
|||||||
|
|
||||||
// WorldMap is the data structure used game world
|
// WorldMap is the data structure used game world
|
||||||
type WorldMap struct {
|
type WorldMap struct {
|
||||||
data [64][64]int
|
data [256][256]int
|
||||||
max_x int
|
max_x int
|
||||||
max_y int
|
max_y int
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTile method is used to grab a tile value with error checking
|
// GetTile method is used to grab a tile value with error checking
|
||||||
func (m WorldMap) GetTile(x int, y int) (int, error) {
|
func (m WorldMap) GetTile(x int, y int) (int, error) {
|
||||||
if x > m.max_x || y > m.max_y {
|
if x > m.max_x || y > m.max_y || x < 0 || y < 0 {
|
||||||
return -1, fmt.Errorf("Map out of bounds error: %d, %d", x, y)
|
return -1, fmt.Errorf("Map out of bounds error: %d, %d", x, y)
|
||||||
}
|
}
|
||||||
return m.data[x][y], nil
|
return m.data[x][y], nil
|
||||||
@ -27,11 +27,10 @@ func (m WorldMap) GetJSONRegion(startX, endX, startY, endY int) ([]byte, error)
|
|||||||
for x := startX; x < endX; x++ {
|
for x := startX; x < endX; x++ {
|
||||||
regionMap[x] = map[int]int{}
|
regionMap[x] = map[int]int{}
|
||||||
for y := startY; y < endY; y++ {
|
for y := startY; y < endY; y++ {
|
||||||
if result, err := m.GetTile(x, y); err != nil {
|
|
||||||
return nil, err
|
// GetTile and ignore out of bounds errors
|
||||||
} else {
|
result, _ := m.GetTile(x, y)
|
||||||
regionMap[x][y] = result
|
regionMap[x][y] = result
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,8 +39,10 @@ func (m WorldMap) GetJSONRegion(startX, endX, startY, endY int) ([]byte, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetJSONRegionAround returns a JSON object of tile data from a center point
|
// GetJSONRegionAround returns a JSON object of tile data from a center point
|
||||||
func (m WorldMap) GetJSONRegionAround(centerX, centerY, regionRadius int) ([]byte, error) {
|
func (m WorldMap) GetJSONRegionAround(centerX float64, centerY float64, regionRadius int) ([]byte, error) {
|
||||||
jsonString, err := m.GetJSONRegion(centerX-regionRadius, centerX+regionRadius, centerY-regionRadius, centerY+regionRadius)
|
var xCenter int = int(centerX)
|
||||||
|
var yCenter int = int(centerY)
|
||||||
|
jsonString, err := m.GetJSONRegion(xCenter-regionRadius, xCenter+regionRadius, yCenter-regionRadius, yCenter+regionRadius)
|
||||||
return jsonString, err
|
return jsonString, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,9 +50,9 @@ func (m WorldMap) GetJSONRegionAround(centerX, centerY, regionRadius int) ([]byt
|
|||||||
// generate WorldMap objects
|
// generate WorldMap objects
|
||||||
func IntializeMap() *WorldMap {
|
func IntializeMap() *WorldMap {
|
||||||
worldMap := new(WorldMap)
|
worldMap := new(WorldMap)
|
||||||
worldMap.max_x = 64
|
worldMap.max_x = 256
|
||||||
worldMap.max_y = 64
|
worldMap.max_y = 256
|
||||||
worldMap.data = [64][64]int{}
|
worldMap.data = [256][256]int{}
|
||||||
for x := 0; x < worldMap.max_x; x++ {
|
for x := 0; x < worldMap.max_x; x++ {
|
||||||
for y := 0; y < worldMap.max_y; y++ {
|
for y := 0; y < worldMap.max_y; y++ {
|
||||||
worldMap.data[x][y] = 0
|
worldMap.data[x][y] = 0
|
||||||
|
Reference in New Issue
Block a user