extends Node
signal exchange_tracking
signal exchange_begins
signal exchange_ends
signal exchange_finished
enum journey {
NONE,
OUTWARD,
EXCHANGE,
RETURN
}
onready var turba:Node = get_node("/root/turba")
onready var bot_simple:PackedScene = load( 'res://animations/robots/bot.tscn' )
export(Resource) var bot_tmpl:Resource = null
export(float) var reach_radius:float = 0.05
export(float) var reach_dot:float = 0.97
export(float) var near_radius:float = 2
export(float) var max_motion_speed:float = 1.5
export(float) var max_steer_speed:float = 5
export(float,0,1) var motion_inertia:float = 0.4
export(float,0,1) var steer_inertia:float = 0.1
export(float,0,1) var motion_steer_ratio:float = 0.5
export(float,0,0.1) var noise:float = 0.05
export(float,0,2) var motion_multiply:float = 1
export(float,0,1800) var _swap_time:float = 5
export(float,0,1800) var _exchange_time:float = 15
export(NodePath) var _scene_manager:NodePath = ''
export(NodePath) var _main_camera:NodePath = ''
export(float,0,1800) var min_tracking_time:float = 10
export(float,0,1800) var max_tracking_time:float = 20
export(bool) var tracking:bool = false setget set_tracking
export(bool) var play:bool = false setget set_play
onready var scene_manager:Node = get_node( _scene_manager )
onready var main_camera:Camera = get_node( _main_camera )
onready var viewport_size:Vector2 = get_viewport().size
var bots:Array = []
var swap_done:bool = false
var exchange_timer:float = 0
var direction:int = journey.NONE
var request_tracking:bool = false
var tracking_enabled:bool = false
var tracking_time:float = 0
var track_camera:Camera = null
var fov_camera:Camera = null
var avoid_jump:int = 0
var request_pub = null
var request_bot = null
var is_ready:bool = false
var start_count:int = int(OS.get_unix_time()%4)
var start_changed:bool = false
func set_play( b:bool ) -> void:
if turba:
turba.write_log( self.name + " play? " + str(b) )
if !is_ready:
play = b
return
if !is_inside_tree():
play = false
elif play == b:
return
play = b
if !play:
request_pub = null
request_bot = null
direction = journey.NONE
if tracking:
release_tracking()
for b in bots:
stop_bot(b)
else:
direction = journey.OUTWARD
var texture_set:Array = turba.get_flag_texture_pair()
var bi:int = 0
for b in bots:
start_bot(b)
var fm:Resource = turba.get_flag_exchange_mesh( bi )
if fm != null:
b.display.set_flag_mesh( fm )
if bi < texture_set.size():
b.display.set_avatar_tex( load(texture_set[bi].avatar) )
b.display.set_flag_tex( load(texture_set[bi].flag) )
bi += 1
if tracking:
set_tracking( tracking, true )
func set_tracking( b:bool, force:bool = false ) -> void:
if scene_manager == null or main_camera == null:
tracking = false
if !force and tracking == b:
return
tracking = b
if !play:
return
if !tracking:
release_tracking()
else:
request_tracking = true
func release_tracking() -> void:
for b in bots:
b.tracking = false
tracking_enabled = false
request_tracking = false
scene_manager.markov_play( true )
main_camera.track( null )
func start_bot( bdata:Dictionary ) -> void:
if not start_changed:
start_changed = true
start_count += 1
var tid:int = int(start_count%4)
bdata.origin = bdata.root.get_node("origin"+str(tid))
bdata.return = bdata.root.get_node("return"+str(tid))
bdata.target = bdata.root.get_node("target"+str(tid))
#print("start_bot @ start_count ", start_count, " root:", bdata.root)
#print(bdata.origin, " >> origin ", tid, " => ", bdata.root.get_node("origin"+str(tid)))
#print(bdata.return , " >> return ", tid, " => ", bdata.root.get_node("return"+str(tid)))
#print(bdata.target, ">> target ", tid, " => ", bdata.root.get_node("target"+str(tid)))
if bdata.origin== null or bdata.target == null:
print("TOTAL FOIRADE!")
return
if bdata.return == null:
bdata.return = bdata.origin.duplicate()
bdata.root.add_child( bdata.return)
bdata.return.name = "return"+str(tid)
bdata.return.global_transform.basis = bdata.return.global_transform.basis * Basis( Vector3.UP, PI )
var bot:Spatial
if bdata.bot == null:
bot = bot_simple.instance()
bdata.root.add_child( bot )
bot.connect( "robot_move", self, "robot_signal" )
bot.connect( "robot_near", self, "robot_signal" )
bot.connect( "robot_reach", self, "robot_signal" )
# loading of display
bdata.display = bot_tmpl.instance()
bot.add_child( bdata.display )
bdata.bot = bot
#print( "bot LOADED", bdata.display )
else:
bot = bdata.bot
# configuration
bot.global_transform = bdata.origin.global_transform
bot.load_target( bdata.target )
bot.reach_radius = reach_radius
bot.reach_dot = reach_dot
bot.near_radius = near_radius
bot.max_motion_speed = max_motion_speed
bot.max_steer_speed = max_steer_speed
bot.motion_inertia = motion_inertia
bot.steer_inertia = steer_inertia
bot.motion_steer_ratio = motion_steer_ratio
bot.noise = noise
bot.start()
robot_signal( bot )
# disabling tracking
bdata.tracking = false
func stop_bot( bdata:Dictionary ) -> void:
var bot:Spatial = bdata.bot
if bdata.bot != null:
bot.disconnect( "robot_move", self, "robot_signal" )
bot.disconnect( "robot_near", self, "robot_signal" )
bot.disconnect( "robot_reach", self, "robot_signal" )
bdata.display = null # releasing ref to display
bdata.root.remove_child( bot )
bot.queue_free()
bdata.bot = null
bdata.status = -1
func robot_signal( robot:Node ) -> void:
# 0 DEFAULT
# 1 MOVE
# 2 NEAR
# 3 REACH
var active_bots:int = 0
for b in bots:
if b.bot == robot:
b.status = robot.status
match direction:
journey.NONE:
continue
journey.EXCHANGE:
continue
journey.OUTWARD:
if b.status != 3:
active_bots += 1
journey.RETURN:
if b.status < 2:
active_bots += 1
if active_bots == 0:
match direction:
journey.OUTWARD:
# eanbling swap
swap_done = false
# starting timer
exchange_timer = _exchange_time
direction = journey.EXCHANGE
for b in bots:
b.display.set_exchange( true )
emit_signal( "exchange_begins", self )
journey.RETURN:
direction = journey.NONE
set_play( false )
emit_signal( "exchange_finished", self )
func viewport_size_changed() -> void:
viewport_size = get_viewport().size
func pub_ready() -> void:
if scene_manager == null:
fov_camera = null
else:
fov_camera = scene_manager.current_pub.get_node( scene_manager.current_pub.cameras[0] )
if tracking_enabled:
if request_pub == scene_manager.current_pub_path:
main_camera.track( request_bot.bot )
for b in bots:
if b == request_bot:
b.tracking = true
else:
b.tracking = false
if avoid_jump > 0:
avoid_jump -=1
else:
main_camera.slerp_basis( 1 )
request_pub = null
request_bot = null
func _ready() -> void:
is_ready = true
set_play( play )
for c in get_children():
bots.append({
'root': c,
'origin': null,
'target': null,
'return': null,
'bot': null,
'display': null,
'tracking': false,
'candidates': [],
'status': -1
})
track_camera = Camera.new()
add_child(track_camera)
if scene_manager != null:
scene_manager.connect( "pub_ready", self, "pub_ready" )
get_viewport().connect( "size_changed", self, "viewport_size_changed" )
func seek_camera() -> bool:
if scene_manager == null:
return false
# detecting cameras and computing distances
for pub in turba.all_cameras:
if pub == scene_manager.current_pub_path:
continue
for cd in turba.all_cameras[pub].camera_data:
turba.apply_camera_data( track_camera, cd )
for b in bots:
if b.tracking:
continue
if turba.inside_fov( track_camera, b.bot.translation, viewport_size, main_camera.track_near, -1 ):
b.candidates.append({
'distance': ( track_camera.global_transform.origin - b.bot.global_transform.origin ).length(),
'pub': pub
})
# sorting candidates and selecting best pub
var shortest_dist:float = -1
request_pub = null
request_bot = null
for b in bots:
if b.candidates.empty():
continue
if b.candidates.size() > 1:
b.candidates.sort_custom( turba.TurbaSorter, "camera_distance_sort" )
if shortest_dist == -1 or shortest_dist > b.candidates[0].distance:
shortest_dist = b.candidates[0].distance
request_bot = b
request_pub = b.candidates[0].pub
# clean up of candidates
b.candidates = []
b.tracking = false
# loading best pub
if request_pub != null:
scene_manager.markov_load( request_pub )
return true
return false
func _process(delta) -> void:
start_changed = false
if direction == journey.NONE:
return
if request_tracking:
request_tracking = false
scene_manager.markov_play( false )
tracking_enabled = true
tracking_time = 0
avoid_jump = 2
emit_signal( "exchange_tracking", self )
# no camera change during exchange
if direction != journey.EXCHANGE and tracking_enabled:
if fov_camera != null:
for b in bots:
if b.tracking and !turba.inside_fov( fov_camera, b.bot.translation, viewport_size ):
tracking_time = 0
break
# no camera switch if there is already a request posted
if request_pub == null and tracking_time <= 0:
# computing cameras
var mult:float = 1
if !seek_camera():
mult = 0.5
tracking_time = rand_range( min_tracking_time, max_tracking_time ) * mult
else:
tracking_time -= delta
for b in bots:
if b.bot == null:
continue
var bot:Spatial = b.bot
var display:Spatial = b.display
var motion:Vector2 = Vector2( bot.right.dot(bot.delta_translation), bot.forward.dot(bot.delta_translation) ) * bot.motion_speed * motion_multiply
display.set_walk( motion )
if direction == journey.EXCHANGE:
exchange_timer -= delta
if !swap_done and exchange_timer <= _swap_time and bots.size() > 1:
var ftex0:Texture = bots[0].display.tex_flag
var ftex1:Texture = bots[1].display.tex_flag
bots[0].display.set_flag_tex( ftex1 )
bots[1].display.set_flag_tex( ftex0 )
swap_done = true
if exchange_timer <= 0:
direction = journey.RETURN
for b in bots:
b.bot.load_target( b.return )
emit_signal( "exchange_ends", self )