From 3aba577eda0d322c1cd2ca917e3396316c800e37 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 4 Feb 2018 02:40:31 -0500 Subject: [PATCH] Complete AI with state updates --- scenes/ai/ai-player.tscn | 2 -- scenes/ai/heroes/0.tscn | 3 --- scripts/ai/ai-player.gd | 52 +++++++++++++++++++++++++++++++++++++++++++++ scripts/lobby.gd | 7 ++++-- scripts/player.gd | 55 ++++++++++++++---------------------------------- 5 files changed, 73 insertions(+), 46 deletions(-) create mode 100644 scripts/ai/ai-player.gd diff --git a/scenes/ai/ai-player.tscn b/scenes/ai/ai-player.tscn index 8e2c194..99742fd 100644 --- a/scenes/ai/ai-player.tscn +++ b/scenes/ai/ai-player.tscn @@ -6,7 +6,5 @@ [node name="RigidBody" index="0" instance=ExtResource( 1 )] script = ExtResource( 2 ) -tp_camera = null -master_only = null diff --git a/scenes/ai/heroes/0.tscn b/scenes/ai/heroes/0.tscn index d84ac67..db5f424 100644 --- a/scenes/ai/heroes/0.tscn +++ b/scenes/ai/heroes/0.tscn @@ -4,7 +4,4 @@ [node name="RigidBody" instance=ExtResource( 1 )] -tp_camera = NodePath("TPCamera") -master_only = NodePath("MasterOnly") - diff --git a/scripts/ai/ai-player.gd b/scripts/ai/ai-player.gd new file mode 100644 index 0000000..a02c818 --- /dev/null +++ b/scripts/ai/ai-player.gd @@ -0,0 +1,52 @@ +extends "res://scripts/player.gd" + +var time = 0 + +func _ready(): + ._ready() + read_recording() + +func _physics_process(delta): + time += delta + +func _integrate_forces(state): + play_keys(state) + +func read_recording(): + + # Gather all existing recordings + var possible = [] + var begin = "%d-%d" % [player_info.level, player_info.hero] + var path = "res://recordings/" + var dir = Directory.new() + dir.open(path) + dir.list_dir_begin() + while true: + var fname = dir.get_next() + print(fname) + if fname == "": + # Indicates end of directory + break + if fname.begins_with(begin): + possible.append(fname) + dir.list_dir_end() + + # Now pick a random one + var fname = possible[randi() % possible.size()] + + # Read the file into recording.events for later use + var frec = File.new() + frec.open(path + fname, File.READ) + recording = parse_json(frec.get_as_text()) + frec.close() + +func play_keys(phys_state): + # states[0] is first state + # states[0][0] is first state's time + while float(recording[0][0]) <= time: + # states[0][1] is first state's STATE + var state = recording.pop_front()[1] + for i in range(state.size()): + state[i] = str2var(state[i]) + set_status(state) + phys_state.integrate_forces() diff --git a/scripts/lobby.gd b/scripts/lobby.gd index 73b18a3..38c98b0 100644 --- a/scripts/lobby.gd +++ b/scripts/lobby.gd @@ -20,8 +20,8 @@ func setup_options(): opts.add('-hero', 'r', 'Your choice of hero (index)') opts.add('-level', 'r', 'Your choice of level (index) - server only!') opts.add('-start-game', false, 'Join as a client and immediately start the game') - opts.add('-ai', false, 'Run this client as AI') - opts.add('-record', false, 'Record this play for AI later') + opts.add('-ai', true, 'Run this client as AI') + opts.add('-record', true, 'Record this play for AI later') opts.add('-h', false, "Print help") return opts @@ -209,8 +209,11 @@ sync func pre_configure_game(level): for p in player_info: var hero = player_info[p].hero var player = load("res://scenes/heroes/" + str(hero) + ".tscn").instance() + if "is_ai" in player_info[p]: + player = load("res://scenes/ai/heroes/" + str(hero) + ".tscn").instance() player.set_name(str(p)) player.set_network_master(p) + player_info[p].level = level player.player_info = player_info[p] get_node("/root/Level/Players").call_deferred("add_child", player) diff --git a/scripts/player.gd b/scripts/player.gd index 2899f3d..4b21a44 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -22,7 +22,7 @@ var movement_charge = 0.15 # In percent per meter (except when heroes change tha const fall_height = -50 var debug_node -var recording = { "time": 0, "events": [], "spawn": Vector3() } +var recording = { "time": 0, "states": [], "spawn": Vector3() } slave var slave_transform = Basis() slave var slave_lin_v = Vector3() @@ -58,40 +58,6 @@ func spawn(): set_translation(placement) set_linear_velocity(Vector3()) -func event_to_obj(event): - var d = {} - if event is InputEventMouseMotion: - d.relative = {} - d.relative.x = event.relative.x - d.relative.y = event.relative.y - d.type = "motion" - if event is InputEventKey: - d.scancode = event.scancode - d.pressed = event.pressed - d.type = "key" - if event is InputEventMouseButton: - d.button_index = event.button_index - d.pressed = event.pressed - d.type = "mb" - return d - -func apply_dict(from, to): - if typeof(from) != TYPE_DICTIONARY: - return from - else: - for key in from: - to[key] = apply_dict(from[key], to[key]) - return to - -func obj_to_event(d): - var e - if d.type == "motion": e = InputEventMouseMotion.new() - if d.type == "key": e = InputEventKey.new() - if d.type == "mb": e = InputEventMouseButton.new() - d.erase("type") # Not in the event - apply_dict(d, e) - return e - func _input(event): if is_network_master(): if Input.is_action_just_pressed("switch_hero"): @@ -99,8 +65,6 @@ func _input(event): # Quit the game: if Input.is_action_pressed("quit"): quit() - if "record" in player_info: - recording.events.append([recording.time, event_to_obj(event)]) func toggle_mouse_capture(): if (Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED): @@ -115,10 +79,18 @@ func set_rotation(): get_node("Yaw").set_rotation(Vector3(0, deg2rad(get_node(tp_camera).cam_yaw), 0)) get_node("Yaw/Pitch").set_rotation(Vector3(deg2rad(-get_node(tp_camera).cam_pitch), 0, 0)) +func record_status(status): + if "record" in player_info: + for i in range(status.size()): + status[i] = var2str(status[i]) + recording.states.append([recording.time, status]) + func _integrate_forces(state): if is_network_master(): control_player(state) - rpc_unreliable("set_status", get_status()) + var status = get_status() + rpc_unreliable("set_status", status) + record_status(status) set_rotation() slave func set_status(s): @@ -198,6 +170,9 @@ func _process(delta): if get_translation().y < fall_height: spawn() switch_hero_interface() + + if "record" in player_info: + recording.time += delta func switch_hero_interface(): # Interface needs the mouse! @@ -228,6 +203,8 @@ sync func switch_hero(hero): func _exit_scene(): Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + if "record" in player_info: + write_recording() # Functions # ========= @@ -236,7 +213,7 @@ func write_recording(): var save = File.new() var fname = "res://recordings/%d-%d-%d.rec" % [player_info.level, player_info.hero, randi() % 10000] save.open(fname, File.WRITE) - save.store_line(to_json(recording)) + save.store_line(to_json(recording.states)) save.close() # Quits the game: