broken_seals_roguelike/game/player/Body.gd

442 lines
11 KiB
GDScript

# Copyright Péter Magyar relintai@gmail.com
# MIT License, functionality from this class needs to be protable to the entity spell system
# Copyright (c) 2019-2020 Péter Magyar
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
extends Node2D
export(String) var world_path : String = "../.."
export(NodePath) var character_skeleton_path : NodePath = "Character"
const BASE_SPEED = 60.0
const MOUSE_TARGET_MAX_OFFSET : int = 10
var input_direction : Vector2
var target_position : Vector2
var path : PoolVector2Array
var current_movement_target : Vector2
var camera : Camera2D
var last_mouse_over : Entity = null
var world : Node2D = null
var _controlled : bool = false
var sleep : bool = false
var sleep_recheck_timer : float = 0
var dead : bool = false
var death_timer : float = 0
var entity : Entity
var character_skeleton : CharacterSkeleton2D
var visibility_update_timer : float = randi()
var tile_size : int = 32
var _nameplate : Node
var init : bool = false
var touches : Array = Array()
var touch_zoom : bool = false
func _ready():
rpc_config("sset_position", MultiplayerAPI.RPC_MODE_REMOTE)
rpc_config("cset_position", MultiplayerAPI.RPC_MODE_REMOTE)
func _enter_tree() -> void:
world = get_node(world_path) as Node2D
tile_size = get_node("/root/Main").get_tile_size()
if init:
return
camera = get_node_or_null("Camera") as Camera2D
if camera:
camera.current = true
set_process_input(false)
set_process_unhandled_input(false)
character_skeleton = get_node(character_skeleton_path)
entity = get_node("..")
entity.character_skeleton_set(character_skeleton)
# entity.connect("notification_ccast", self, "on_notification_ccast")
entity.connect("diesd", self, "on_diesd")
entity.connect("onc_entity_controller_changed", self, "on_c_controlled_changed")
owner = entity
on_c_controlled_changed()
transform = entity.get_transform_2d(true)
init = true
func set_visibility(val : bool) -> void:
if val:
show()
if _nameplate:
_nameplate.show()
elif !val:
hide()
if _nameplate:
_nameplate.hide()
func _unhandled_input(event: InputEvent) -> void:
#Not sure why yet, but _unhandled_input gets called even after set_process_unhandled_input(false)
if !entity.getc_is_controlled():
return
if event.is_action_pressed("left"):
try_move(-1, 0)
get_tree().set_input_as_handled()
return
elif event.is_action_pressed("right"):
try_move(1, 0)
get_tree().set_input_as_handled()
return
elif event.is_action_pressed("up"):
try_move(0, -1)
get_tree().set_input_as_handled()
return
elif event.is_action_pressed("down"):
try_move(0, 1)
get_tree().set_input_as_handled()
return
elif event.is_action_pressed("wait"):
world.player_moved()
get_tree().set_input_as_handled()
return
if event is InputEventMouseMotion and event.device != -1:
cmouseover(event.position)
get_tree().set_input_as_handled()
if event is InputEventMouseButton:
if event.button_index == BUTTON_WHEEL_DOWN and event.device != -1:
if camera == null:
return
if camera.zoom.x >= 2:
return
else:
camera.zoom += Vector2(event.factor, event.factor) * 0.01
elif event.button_index == BUTTON_WHEEL_UP and event.device != -1:
if camera == null:
return
if camera.zoom.x <= 0.1:
return
else:
camera.zoom -= Vector2(event.factor, event.factor) * 0.01
elif event.button_index == BUTTON_LEFT and event.device != -1:
if event.pressed:
var pos : Vector2 = world.make_canvas_position_local(event.position)
pos -= transform.origin
if pos.length() < tile_size / 2:
#wait
world.player_moved()
return
var mx : int = 0
var my : int = 0
if abs(pos.x) > tile_size / 2:
if pos.x >= 0:
mx = 1
else:
mx = -1
if abs(pos.y) > tile_size / 2:
if pos.y >= 0:
my = 1
else:
my = -1
try_move(mx, my)
# if not event.pressed and event.button_index == BUTTON_LEFT and event.device != -1:
# if mouse_down_delta.length() < MOUSE_TARGET_MAX_OFFSET:
# target(event.position)
if event.pressed and event.button_index == BUTTON_RIGHT and event.device != -1:
target(event.position)
get_tree().set_input_as_handled()
if event is InputEventScreenTouch:
if event.pressed:
var touch : Array = [
event.index,
event.position,
]
touches.append(touch)
if touches.size() > 1:
touch_zoom = true
else:
if !touch_zoom && !target(event.position, true):
var pos : Vector2 = world.make_canvas_position_local(event.position)
pos -= transform.origin
if pos.length() < tile_size / 2:
#wait
world.player_moved()
else:
var mx : int = 0
var my : int = 0
if abs(pos.x) > tile_size / 2:
if pos.x >= 0:
mx = 1
else:
mx = -1
if abs(pos.y) > tile_size / 2:
if pos.y >= 0:
my = 1
else:
my = -1
try_move(mx, my)
for i in range(touches.size()):
if touches[i][0] == event.index:
touches.remove(i)
break
if touch_zoom and touches.size() == 0:
touch_zoom = false
get_tree().set_input_as_handled()
if event is InputEventScreenDrag && touches.size() == 2:
if camera == null:
return
var found : bool = false
for t in touches:
if event.index == t[0]:
found = true
break
if !found:
return
var otp : Vector2
var ttp : Vector2
if touches[0][0] == event.index:
otp = touches[1][1]
ttp = touches[0][1]
else:
otp = touches[0][1]
ttp = touches[1][1]
var olen : float = (otp - ttp).length()
var currlen : float = (otp - event.position).length()
if olen > currlen:
if camera.zoom.x >= 2:
return
else:
var l : float = event.relative.length()
camera.zoom += Vector2(l, l) * 0.01 * 72 / OS.get_screen_dpi()
else:
if camera.zoom.x <= 0.1:
return
else:
var l : float = event.relative.length()
camera.zoom -= Vector2(l, l) * 0.01 * 72 / OS.get_screen_dpi()
get_tree().set_input_as_handled()
func try_move(dx, dy):
var state : int = entity.state_getc()
if state & EntityEnums.ENTITY_STATE_TYPE_FLAG_ROOT != 0 or state & EntityEnums.ENTITY_STATE_TYPE_FLAG_STUN != 0:
return
var tp : Vector2 = get_tile_position()
tp.x += dx
tp.y += dy
if !world.is_position_walkable(tp.x, tp.y):
return
#todo
#world.can_interact
set_tile_position(tp)
if entity.getc_is_controlled():
world.player_moved()
func move_towards_target():
var state : int = entity.state_getc()
if state & EntityEnums.ENTITY_STATE_TYPE_FLAG_ROOT != 0 or state & EntityEnums.ENTITY_STATE_TYPE_FLAG_STUN != 0:
return
var t : Entity = entity.target_getc()
if !t:
return
var bp : Vector2 = t.body_get().get_tile_position()
var my_point = world.nav_graph.get_closest_point(get_tile_position())
var target_point = world.nav_graph.get_closest_point(bp)
var path = world.nav_graph.get_point_path(my_point, target_point)
if path:
assert(path.size() > 1)
var move_tile = Vector2(path[1].x, path[1].y)
if move_tile == bp:
return
for e in world.enemies:
if e.body_get().get_tile_position() == move_tile:
return
set_tile_position(move_tile)
func get_tile_position() -> Vector2:
return Vector2(int(transform.origin.x / tile_size), int(transform.origin.y / tile_size))
func set_tile_position(pos : Vector2) -> void:
transform.origin = pos * tile_size + Vector2(tile_size / 2, tile_size / 2)
func target(position : Vector2, keep_target : bool = false) -> bool:
position = world.make_canvas_position_local(position)
var pos : Vector2 = world.pixel_to_tile(position.x, position.y)
var enemy : Entity = world.get_enemy_at_tile(pos.x, pos.y)
if enemy:
if !enemy.body_get().visible:
return false
if entity.target_getc() != enemy:
entity.target_crequest_change(enemy.get_path())
return true
else:
if !keep_target:
entity.target_crequest_change(NodePath())
return false
func cmouseover(position : Vector2):
position = world.make_canvas_position_local(position)
var pos : Vector2 = world.pixel_to_tile(position.x, position.y)
var enemy : Entity = world.get_enemy_at_tile(pos.x, pos.y)
if enemy:
if !enemy.body_get().visible:
return false
if last_mouse_over != null and last_mouse_over != entity:
if is_instance_valid(last_mouse_over):
last_mouse_over.notification_cmouse_exit()
last_mouse_over = null
if last_mouse_over == null:
entity.notification_cmouse_enter()
last_mouse_over = entity
return
if last_mouse_over != null:
last_mouse_over.notification_cmouse_exit()
last_mouse_over = null
func on_c_controlled_changed():
#create camera and pivot if true
_controlled = entity.getc_is_controlled()
if _controlled:
if _nameplate:
_nameplate.queue_free()
camera = Camera2D.new()
camera.zoom = get_node("/root/Main").get_world_scale()
#camera.make_current()
add_child(camera)
#var uiscn : PackedScene = ResourceLoader.load("res://ui/player_ui/player_ui.tscn")
#var ui = uiscn.instance()
var ui = DataManager.request_instance(DataManager.PLAYER_UI_INSTANCE)
add_child(ui)
# set_process_input(true)
set_process_unhandled_input(true)
set_visibility(true)
else:
if camera:
camera.queue_free()
camera = null
# set_process_input(false)
set_process_unhandled_input(false)
var nameplatescn : PackedScene = ResourceLoader.load("res://ui/nameplates/NamePlate.tscn")
_nameplate = nameplatescn.instance()
get_parent().add_child(_nameplate)
set_visibility(false)
func sset_position(pposition : Vector2) -> void:
if multiplayer.network_peer and multiplayer.is_network_server():
entity.vrpc("cset_position", position)
if _controlled:
cset_position(position)
func cset_position(pposition : Vector2) -> void:
pposition = pposition
func on_diesd(entity):
pass