From c232b92e2dde1277324d1f89d0e75ae641e4ac3b Mon Sep 17 00:00:00 2001 From: Anson Bridges Date: Wed, 7 Sep 2022 14:07:30 -0400 Subject: reorganized, ladders, vehicle control --- godot/scripts/GameBase.gd | 107 ++++++++ godot/scripts/Server.gd | 127 +++++++++ godot/scripts/World.gd | 51 ++++ godot/scripts/ballistics/Cannonball.gd | 17 ++ godot/scripts/ballistics/NetworkedProjectile.gd | 29 ++ godot/scripts/cameras/plane_armcam.gd | 26 ++ godot/scripts/cameras/player_firstperson.gd | 33 +++ godot/scripts/characters/player_controller_new.gd | 308 ++++++++++++++++++++++ godot/scripts/machines/Cannon.gd | 79 ++++++ godot/scripts/machines/NetworkedMachine.gd | 54 ++++ godot/scripts/vehicles/Airplane.gd | 106 ++++++++ godot/scripts/vehicles/Gunboat.gd | 100 +++++++ godot/scripts/world_tools.gd | 65 +++++ 13 files changed, 1102 insertions(+) create mode 100644 godot/scripts/GameBase.gd create mode 100644 godot/scripts/Server.gd create mode 100644 godot/scripts/World.gd create mode 100644 godot/scripts/ballistics/Cannonball.gd create mode 100644 godot/scripts/ballistics/NetworkedProjectile.gd create mode 100644 godot/scripts/cameras/plane_armcam.gd create mode 100644 godot/scripts/cameras/player_firstperson.gd create mode 100644 godot/scripts/characters/player_controller_new.gd create mode 100644 godot/scripts/machines/Cannon.gd create mode 100644 godot/scripts/machines/NetworkedMachine.gd create mode 100644 godot/scripts/vehicles/Airplane.gd create mode 100644 godot/scripts/vehicles/Gunboat.gd create mode 100644 godot/scripts/world_tools.gd (limited to 'godot/scripts') diff --git a/godot/scripts/GameBase.gd b/godot/scripts/GameBase.gd new file mode 100644 index 0000000..0df53dd --- /dev/null +++ b/godot/scripts/GameBase.gd @@ -0,0 +1,107 @@ +extends Spatial + +var client +var client_id +var is_local = false +var local_server_tree = null + +var player_name : String +var player_team : String +var player_char = null + +var players_info = {} #dictionary of id : name, team, ping, etc. + +var is_chatting = false + +var winddir = Vector3(1,0,0) + +func _process(delta): + $HUD/Health.text = str(player_char.health) if player_char != null else "" + if is_local: + local_server_tree.idle(delta) + +func _physics_process(delta): + if is_local: + local_server_tree.iteration(delta) + +remote func set_up_server_info(info): + $HUD/ServerJoinMenu/MOTD.text = info["MOTD"] + $HUD/ServerJoinMenu/ServerName.text = info["server_name"] + $DEFAULTCAM.transform = info["cam_pos"] + + rpc_id(1, "_call_on_server", "_client_connection_confirmed", {"id" : client_id, "username" : player_name}) + +remote func load_map(geo_info): + for geo in geo_info: + var prop = load(geo["filename"]).instance() + prop.transform = geo["transform"] + $WORLDGEO.add_child(prop) + +remote func load_entities(entity_info): #machines, players, and projectiles + for entity in entity_info: + var parent_section = get_node(entity["type"]) + var ent = load(entity["filename"]).instance() + ent.name = entity["name"] + ent.set_network_master(entity["net_master"]) + parent_section.add_child(ent, true) + ent.transform = entity["transform"] + ent.mp_init(entity["init_info"]) + +remote func update_players_info(info): + $HUD/ServerJoinMenu/Team1Players.text = "" + $HUD/ServerJoinMenu/Team2Players.text = "" + $HUD/ServerJoinMenu/Spectators.text = "" + players_info = info + for player in players_info.keys(): + var p_team = players_info[player][1] + var p_name = players_info[player][0] + if p_team == "RED": + $HUD/ServerJoinMenu/Team1Players.text += p_name + ", " + elif p_team == "BLUE": + $HUD/ServerJoinMenu/Team2Players.text += p_name + ", " + elif p_team == "SPEC": + $HUD/ServerJoinMenu/Spectators.text += p_name + ", " + +remote func game_update_chars(): + $HUD.update_characters() + +remote func game_chat_msg(msg): + $HUD.ui_chat_msg(msg) + +remotesync func game_hitsound(): + $HUD.ui_play_hitsound() + +remotesync func game_killsound(): + $HUD.ui_play_killsound() + +func select_character(dest): + if player_char == null: + rpc_id(1, "_call_on_server", "_client_request_change_character", {"id" : client_id, "current_char_name" : "NULL", "char_name" : dest}) + else: + rpc_id(1, "_call_on_server", "_client_request_change_character", {"id" : client_id, "current_char_name" : player_char.name, "char_name" : dest}) + +func client_disconnect(): + if player_char != null: + player_char.deselect_character() + client.close_connection() + if is_local: + local_server_tree.free() + back_to_main() + +func _connection_lost(): + if is_local: + local_server_tree.free() + back_to_main() + +func back_to_main(): + var main_menu = load("res://ui/MainMenu.tscn").instance() + get_tree().get_root().add_child(main_menu) + get_tree().get_root().remove_child(self) + queue_free() + +func join_team(team): + if player_char != null: + player_char.deselect_character() + $DEFAULTCAM.current = true + player_team = team + rpc_id(1, "_call_on_server", "_client_change_teams", {"id" : client_id, "team" : team}) diff --git a/godot/scripts/Server.gd b/godot/scripts/Server.gd new file mode 100644 index 0000000..d26af94 --- /dev/null +++ b/godot/scripts/Server.gd @@ -0,0 +1,127 @@ +extends Node + +var world +var motd : String +var server_name : String +var player_limit : int +var connected_player_count : int = 0 +var connected_players = {} + +var server_enet : NetworkedMultiplayerENet +var output +var output_func : String + +func print_line(line): + if output == null: + print("SERVER: " + line) + return + output.call(output_func, line) + +func _ready(): + var signal_status = [ get_tree().connect("network_peer_connected", self, "_client_connect"), + get_tree().connect("network_peer_disconnected", self, "_client_disconnect")] + for sig in signal_status: + if sig != OK: + print("SERVER: Error connecting one or more signals. Error code: " + str(sig)) + +func start_server(_server_name: String, _motd: String, max_players: int, map_path: String, _ip: String, port: int, tree, output_obj, output_f): + output = output_obj + output_func = output_f + server_name = _server_name + motd = _motd + player_limit = max_players + + world = load(map_path) + if world: + world = world.instance() + else: + print_line("Error loading map.") + return + + world.add_child(self) + tree.get_root().add_child(world) + + world.client_id = 1 + + server_enet = NetworkedMultiplayerENet.new() + var srv_stat = server_enet.create_server(port, max_players) + if srv_stat != OK: + print_line("Error creating server. Code: "+str(srv_stat)) + stop_server() + return + tree.set_network_peer(server_enet) + + print_line("Server started successfully.") + +func stop_server(): + print_line("Shutting down server...") + server_enet.close_connection() + print_line("Shut down successfully.") + world.queue_free() + +func _client_disconnect(id): + print_line("Client (ID: "+str(id)+") has disconnected.") + for character in world.get_node("PLAYERS").get_children(): + if character.get_network_master() == id: + character.rpc("set_owner", 1) + connected_players.erase(id) + connected_player_count -= 1 + world.rpc("update_players_info", connected_players) + +func _client_connect(id): + print_line("Client (ID: "+str(id)+") connecting...") + connected_player_count += 1 + connected_players[id] = ["", "SPEC"] #Placeholder for name + + var server_info = {"player_count" : connected_player_count, "server_name" : server_name, "MOTD" : motd, "cam_pos" : world.get_node("DEFAULTCAM").transform} + world.rpc_id(id, "set_up_server_info", server_info) + + var geo_info = [] + for world_geo in world.get_node("WORLDGEO").get_children(): + geo_info.append( { "filename" : world_geo.filename, "transform" : world_geo.transform }) + world.rpc_id(id, "load_map", geo_info) + + var ent_info = [] + for section in ["PLAYERS","MACHINES","BALLISTICS"]: + for item in world.get_node(section).get_children(): + ent_info.append({"type" : section, "net_master" : item.get_network_master(), "name" : item.name, "filename" : item.filename, "transform" : item.transform, "init_info" : item.get_init_info() }) + world.rpc_id(id, "load_entities", ent_info) + +func _client_connection_confirmed(arguments): + print_line("Client (ID: "+str(arguments["id"])+") connected as " +arguments["username"] +".") + connected_players[arguments["id"]] = [arguments["username"], "SPEC"] + world.rpc("update_players_info", connected_players) + +func _client_change_teams(arguments): + print_line(connected_players[arguments["id"]][0] + " ("+ str(arguments["id"]) +") changed to team " + arguments["team"]) + connected_players[arguments["id"]][1] = arguments["team"] + world.rpc("update_players_info", connected_players) + +func _client_request_change_character(arguments): + var dest = world.get_node("PLAYERS/"+arguments["char_name"]) + if dest != null and dest.get_network_master() == 1: + print_line(connected_players[arguments["id"]][0] + " selected character " + arguments["char_name"]) + dest.rpc("set_net_owner", arguments["id"]) + if arguments["current_char_name"] != "NULL": + var old = world.get_node("PLAYERS/"+arguments["current_char_name"]) + old.rpc("set_net_owner", 1) + +func _client_request_control_vehicle(arguments): + var dest_machine = world.get_node(arguments["machine_path"]) + var name = "BOT" if arguments["id"] == 1 else connected_players[arguments["id"]][0] + if dest_machine != null and dest_machine.get_network_master() == 1 and !dest_machine.in_use: + print_line(arguments["char_name"] + "(" + name + ") is controlling " + dest_machine.name) + dest_machine.rpc("set_net_owner", arguments["id"], arguments["char_name"]) + +func _character_death(arguments): + var victim_player = connected_players[arguments["victim_mp_id"]][0] if arguments["victim_mp_id"] != 1 else "" + print_line(arguments["victim"] + " ("+victim_player+") killed by " + arguments["killer"] + " " + arguments["extra"] + ".") + world.rpc("game_chat_msg", arguments["victim"] + " ("+victim_player+") killed by " + arguments["killer"] + " " + arguments["extra"] + ".") + world.get_node("PLAYERS/"+arguments["victim"]).rpc("remove_dead_character") + world.rpc("game_update_chars") + +func _send_chat(arguments): + if !("name" in arguments): + arguments["name"]=connected_players[arguments["id"]][0] + print_line(arguments["name"]+ ": " + arguments["msg"]) + world.rpc("game_chat_msg", arguments["name"]+ ": " + arguments["msg"]) diff --git a/godot/scripts/World.gd b/godot/scripts/World.gd new file mode 100644 index 0000000..fa5a306 --- /dev/null +++ b/godot/scripts/World.gd @@ -0,0 +1,51 @@ +extends Spatial + + +var m = SpatialMaterial.new() +var winddir = Vector3(1,0,0) +onready var pathfinder = get_node("PLAYERS/Player2") +var path = [] +var map_rid +var client_id +var player_char +var players_info = {} + +# Called when the node enters the scene tree for the first time. +func _ready(): + map_rid = NavigationServer.get_maps() + for rid in map_rid: + NavigationServer.map_set_edge_connection_margin(rid,1) + m.flags_unshaded = true + m.flags_use_point_size = true + m.albedo_color = Color.white + +remotesync func update_players_info(info): + players_info = info + +remote func _call_on_server(function, arguments): + print('Remote server call: ' + function) + $Server.call(function, arguments) + +func find_path(to): + pathfinder.nav.set_target_location(to) + var t_path = pathfinder.nav.get_next_location() + pathfinder.should_move = true + t_path = pathfinder.nav.get_nav_path() + print(to) + print(t_path) + draw_path(t_path) + pass + +func draw_path(path_array): + var im = get_node("Draw") + im.set_material_override(m) + im.clear() + im.begin(Mesh.PRIMITIVE_POINTS, null) + im.add_vertex(path_array[0]) + im.add_vertex(path_array[path_array.size() - 1]) + im.end() + im.begin(Mesh.PRIMITIVE_LINE_STRIP, null) + for x in path_array: + im.add_vertex(x) + im.end() + diff --git a/godot/scripts/ballistics/Cannonball.gd b/godot/scripts/ballistics/Cannonball.gd new file mode 100644 index 0000000..14de00c --- /dev/null +++ b/godot/scripts/ballistics/Cannonball.gd @@ -0,0 +1,17 @@ +extends "res://scripts/ballistics/NetworkedProjectile.gd" + +export var drag_constant = 0.3 +var damage_exceptions = [] +var oldvel + +func _physics_process(_delta): + oldvel = linear_velocity + add_force(-1*linear_velocity*drag_constant, Vector3.ZERO) + +func get_init_info(): + return {"linear_velocity" : linear_velocity, "angular_velocity" : angular_velocity, "oldvel" : oldvel, "shooter" : shooter, "shooter_id" : shooter_id} + +func _on_collision(body): + if oldvel.length() > 20 and !damage_exceptions.has(body) and body.has_method("damage"): + body.rpc("damage", oldvel.length(), "blunt", shooter, "using 'cannon'") + damage_exceptions.append(body) diff --git a/godot/scripts/ballistics/NetworkedProjectile.gd b/godot/scripts/ballistics/NetworkedProjectile.gd new file mode 100644 index 0000000..9703fa5 --- /dev/null +++ b/godot/scripts/ballistics/NetworkedProjectile.gd @@ -0,0 +1,29 @@ +extends RigidBody + +var shooter = "WORLD" +var shooter_id = 1 + + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass # Replace with function body. + +func mp_init(init_info): + for variable in init_info.keys(): + set(variable, init_info[variable]) + +remote func update_phys_transform(t, lv, av): + transform = t + linear_velocity = lv + angular_velocity = av + +func _integrate_forces(_state): + if is_network_master(): + rpc("update_phys_transform", transform, linear_velocity, angular_velocity) + +remotesync func net_remove(): + queue_free() + +func net_master_remove(): + if is_network_master(): + rpc("net_remove") diff --git a/godot/scripts/cameras/plane_armcam.gd b/godot/scripts/cameras/plane_armcam.gd new file mode 100644 index 0000000..edb0284 --- /dev/null +++ b/godot/scripts/cameras/plane_armcam.gd @@ -0,0 +1,26 @@ +extends SpringArm + + + +# Called when the node enters the scene tree for the first time. +func _ready(): + Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + $ClippedCamera.add_exception(get_parent()) + add_excluded_object(get_parent().get_rid()) + + +#func _process(delta): + + +func _input(event): + if $ClippedCamera.current: + if Input.is_action_just_pressed("menu"): #toggle mouse capture on esc + if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE: + Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) + else: + Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + + if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: + if event is InputEventMouseMotion: + rotation_degrees.x = clamp(rotation_degrees.x-event.relative.y*0.1,-70,70) + rotation_degrees.y -= event.relative.x*0.1 diff --git a/godot/scripts/cameras/player_firstperson.gd b/godot/scripts/cameras/player_firstperson.gd new file mode 100644 index 0000000..ace49e6 --- /dev/null +++ b/godot/scripts/cameras/player_firstperson.gd @@ -0,0 +1,33 @@ +extends Camera + + +var mouse_axis := Vector2() +var mouse_sensitivity = 12.0 + +func _ready(): + Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + current = false + + +func _input(event): + if current: + if Input.is_action_just_pressed("menu"): #toggle mouse capture on esc + if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE: + Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) + else: + Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) + if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: + mouse_axis = event.relative + if mouse_axis.length() > 0: + var horizontal: float = -mouse_axis.x * (mouse_sensitivity / 100) + var vertical: float = -mouse_axis.y * (mouse_sensitivity / 100) + + mouse_axis = Vector2() + + get_parent().rotate_y(deg2rad(horizontal)) + rotate_x(deg2rad(vertical)) + + var temp_rot: Vector3 = rotation_degrees + temp_rot.x = clamp(temp_rot.x, -90, 90) + get_parent().animationcontroller.rpc("lean",-1*temp_rot.x/90) + rotation_degrees = temp_rot diff --git a/godot/scripts/characters/player_controller_new.gd b/godot/scripts/characters/player_controller_new.gd new file mode 100644 index 0000000..6aec2d9 --- /dev/null +++ b/godot/scripts/characters/player_controller_new.gd @@ -0,0 +1,308 @@ +extends RigidBody + +# Game +export var team = "RED" +export (int) var health = 100 +var weapon = null +var world + +# Camera +export(float) var mouse_sensitivity = 12.0 +export(float) var FOV = 90.0 +var mouse_axis := Vector2() +onready var head: Spatial = $Head +onready var cam: Camera = $Head/Camera + +# Move +var velocity := Vector3() +var direction := Vector3() +var move_axis := Vector2() +var floorspeed := Vector3() +var jumping = false +onready var nav = $NavigationAgent + +# Walk +const FLOOR_MAX_ANGLE: float = deg2rad(46.0) +export(float) var jump_height = 400.0 +var in_water : bool = false +var swim_speed : float = 400.0 + +# Control +var controlling_machine = false #whether character is riding/controlling something +var machine = null +export var is_player = false #whether character is currently controlled by a player +var should_move = false +var ladder_m = null + +#physics +var player_state : PhysicsDirectBodyState +var is_on_floor:bool +export(float) var acceleration = 80.0 +export(int) var walk_speed = 6 +export(float) var c_friction = 4.0 +export(float) var _airspeed_cap = 1.0 +export(float) var air_control = 1.0 + +# Called when the node enters the scene tree +func _ready() -> void: + weapon = preload("res://scenes/weapons/w_Rockets.tscn").instance() + add_child(weapon) + world = get_tree().get_root().get_node("GAMEWORLD") + if is_player: + cam.current = true + + $Head/Camera/UseRay.add_exception(self) + $Head/Camera/MeleeRay.add_exception(self) + $Head/Camera/UseRay.add_exception($AreaDetect) + $Head/Camera/MeleeRay.add_exception($AreaDetect) + Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) + cam.fov = FOV + +func get_init_info(): + return {"linear_velocity" : linear_velocity, "angular_velocity" : angular_velocity, "controlling_machine" : controlling_machine, "team" : team, "health" : health, "nametag" : $Nametag.text} + +func mp_init(init_info): + for variable in init_info.keys(): + set(variable, init_info[variable]) + $Nametag.text = init_info["nametag"] + +remote func set_phys_transform(trfrm, lvel): + transform = trfrm + linear_velocity = lvel + +# Called every frame. 'delta' is the elapsed time since the previous frame +func _process(_delta: float) -> void: + if is_player and !world.is_chatting: + if Input.is_action_just_pressed("use"): + initiate_use() + + if controlling_machine: + if Input.is_action_just_pressed("fire"): + machine.attack1() + if Input.is_action_just_pressed("fire2"): + machine.attack2() + machine.direction_input(Input.get_action_strength("move_forward"),Input.get_action_strength("move_backward"), Input.get_action_strength("move_right"),Input.get_action_strength("move_left"), Input.get_action_strength("alt_right"),Input.get_action_strength("alt_left")) + else: + jumping = Input.get_action_strength("move_jump") + if Input.is_action_just_pressed("fire"): + weapon.attack1() + move_axis.x = Input.get_action_strength("move_forward") - Input.get_action_strength("move_backward") + move_axis.y = Input.get_action_strength("move_right") - Input.get_action_strength("move_left") + + +func initiate_use(): + if controlling_machine: + machine.relinquish_control() + return + if ladder_m != null: + leave_ladder() + return + if $Head/Camera/UseRay.is_colliding(): + var area_c = $Head/Camera/UseRay.get_collider() + match area_c.name: + "SteerArea": + world.rpc_id(1, "_call_on_server", "_client_request_control_vehicle", {"id" : world.client_id, "machine_path" : area_c.get_parent().get_path(), "char_name" : name}) + "LadderArea": + mount_ladder(area_c.get_parent()) + "TugArea": + pass + "PickupArea": + pass + _: + pass + +remotesync func set_net_owner(owner_id): + $Nametag.text = "" + set_network_master(owner_id) + if owner_id != 1: + $Nametag.text = world.players_info[owner_id][0] + if get_tree().get_network_unique_id() != 1: + if owner_id == world.client_id: + $Nametag.visible = false + world.player_char = self + is_player = true + cam.current = true + else: + $Nametag.visible = true + cam.current = false + is_player = false + world.get_node("HUD").update_characters() + +func deselect_character(): + if is_network_master(): + world.player_char = null + world.get_node("DEFAULTCAM").current = true + rpc("set_net_owner", 1) + +func take_control_of_machine(slave_machine): + machine = slave_machine + controlling_machine = true + +func lose_machine(): + if is_player: + cam.current = true + controlling_machine = false + machine = null + +# Called every physics tick. 'delta' is constant +func _physics_process(delta: float) -> void: + if is_network_master(): + if ladder_m != null: + climb_ladder(delta) + elif !on_floor_test() and in_water: + swim(delta) + else: + walk(delta) + rpc("set_phys_transform", transform, linear_velocity) + is_on_floor = false #reset whether is on floor in between frames + +# called each physics frame +func on_floor_test() -> bool: + if $Feet.is_colliding(): + is_on_floor = true + floorspeed = $Feet.get_collider().get_linear_velocity() if $Feet.get_collider().has_method("get_linear_velocity") else Vector3.ZERO + return true + if player_state: + for i in range(player_state.get_contact_count()): + var contact_angle_from_up : float = Vector3.UP.angle_to(player_state.get_contact_local_normal(i)) + if contact_angle_from_up < FLOOR_MAX_ANGLE: + is_on_floor = true + return true + return false + +#modify simulated physics results +func _integrate_forces(state) -> void: + player_state = state + velocity = state.get_linear_velocity() + if should_move: + nav.set_velocity(velocity) + if nav.is_target_reached(): + should_move = false + +# on input event +func _input(event: InputEvent) -> void: + if is_player: + if event is InputEventMouseMotion: + mouse_axis = event.relative + camera_rotation() + +func walk(_delta:float) -> void: + # Input + direction = Vector3() + var aim: Basis = head.get_global_transform().basis + direction += -move_axis.x * aim.z + move_axis.y * aim.x + if !is_player and should_move: + direction = nav.get_next_location() - global_transform.origin + if nav.get_next_location().y - global_transform.origin.y > 0.05 and is_on_floor: + apply_central_impulse(Vector3.UP*jump_height) + direction.y = 0 + direction = direction.normalized() + + # Jump + if is_on_floor and is_player and jumping: + apply_central_impulse(Vector3.UP*jump_height) + + #max walk speed + var _speed = walk_speed + var _temp_accel: float = acceleration + var _cspeed = sqrt(pow(velocity.x-floorspeed.x,2)+pow(velocity.z-floorspeed.z,2)) + + if not is_on_floor: + _temp_accel *= air_control + + var projVel = Vector2(velocity.x-floorspeed.x,velocity.z-floorspeed.z).dot(Vector2(direction.x,direction.z)) + + if is_on_floor: + if _speed - _cspeed > 0: + add_central_force (mass*Vector3(direction.x*_temp_accel, 0, direction.z*_temp_accel))#velocity.x += direction.x*_temp_accel + else: + add_central_force(mass*Vector3(direction.x*(_speed-projVel), 0, direction.z*(_speed-projVel))) + elif _airspeed_cap - projVel > 0: + add_central_force (mass*Vector3(direction.x*_temp_accel, 0, direction.z*_temp_accel)) + +func swim(_delta): + #drag and buoyancy + add_central_force(Vector3.UP*weight*1.0) + add_central_force(-1*linear_velocity*75) + #controls + var dir: Basis = cam.get_global_transform().basis + var m_dir: Vector3 = -move_axis.x * dir.z + move_axis.y * dir.x + m_dir = m_dir.normalized() + add_central_force(swim_speed*m_dir) + if jumping: + add_central_force(Vector3.UP*weight*0.5) + +func enter_water(): + in_water = true + +func exit_water(): + in_water = false + +func mount_ladder(target_ladder): + var ladder_tracker = Spatial.new() + ladder_tracker.name = name + target_ladder.add_child(ladder_tracker) + ladder_tracker.transform = target_ladder.bottom.transform + + ladder_tracker.global_transform.origin = target_ladder.get_nearest_point_to_route(global_transform.origin) + look_at(global_transform.origin + target_ladder.global_transform.basis.x, target_ladder.global_transform.basis.y) + + ladder_m = ladder_tracker + global_transform.origin = ladder_m.global_transform.origin + global_transform.basis = ladder_m.global_transform.basis.orthonormalized() + linear_velocity = Vector3.ZERO + set_gravity_scale(0.0) + +#called each frame while climbing ladder +func climb_ladder(delta): + var new_ladder_pos = ladder_m.global_transform.origin + ladder_m.global_transform.basis.y.normalized() * move_axis.x * delta + var prog = ladder_m.get_parent().get_climb_scalar(new_ladder_pos) + if prog >= 0.0 and prog <= 1.0: + ladder_m.global_transform.origin = new_ladder_pos + global_transform.origin = ladder_m.global_transform.origin + global_transform.basis = ladder_m.global_transform.basis.orthonormalized() + +func leave_ladder(): + if (ladder_m.get_parent().top.global_transform.origin - global_transform.origin).length_squared() < 0.01: + apply_central_impulse(-400*ladder_m.global_transform.basis.z) + global_transform.basis = world.global_transform.basis + set_gravity_scale(1.0) + ladder_m.queue_free() + ladder_m = null + +remotesync func damage(dmg_amt, _type, shooter, extra = ""): + health -= dmg_amt + if health <= 0 and is_network_master(): + if shooter[0] != get_network_master(): world.rpc_id(shooter[0], "game_killsound") + if get_network_master() == 1: + world._call_on_server("_character_death", {"killer_id" : shooter[0], "killer" : shooter[1], "victim_mp_id" : get_network_master(), "victim" : name, "extra" : extra}) + else: + world.rpc_id(1, "_call_on_server", "_character_death", {"killer_id" : shooter[0], "killer" : shooter[1], "victim_mp_id" : get_network_master(), "victim" : name, "extra" : extra}) + elif is_network_master(): + if shooter[0] != get_network_master(): world.rpc_id(shooter[0], "game_hitsound") + +remotesync func remove_dead_character(): + if is_network_master() and machine != null: + machine.relinquish_control() + deselect_character() + queue_free() + +func camera_rotation() -> void: + if Input.get_mouse_mode() != Input.MOUSE_MODE_CAPTURED: + return + if mouse_axis.length() > 0: + var horizontal: float = -mouse_axis.x * (mouse_sensitivity / 100) + var vertical: float = -mouse_axis.y * (mouse_sensitivity / 100) + + mouse_axis = Vector2() + + head.rotate_y(deg2rad(horizontal)) + cam.rotate_x(deg2rad(vertical)) + + # Clamp mouse rotation + var temp_rot: Vector3 = cam.rotation_degrees + temp_rot.x = clamp(temp_rot.x, -90, 90) + cam.rotation_degrees = temp_rot + +remotesync func net_apply_impulse(impulse_v): + apply_central_impulse(impulse_v) diff --git a/godot/scripts/machines/Cannon.gd b/godot/scripts/machines/Cannon.gd new file mode 100644 index 0000000..6b636c5 --- /dev/null +++ b/godot/scripts/machines/Cannon.gd @@ -0,0 +1,79 @@ +extends "res://scripts/machines/NetworkedMachine.gd" + +var world_ballistics = null + +var cooldown = 0 +export var fire_rate = 1 #shot/s +export var ball_speed = 400 #m/s + +var pitch :float = 0.0 +var turn :float = 0.0 + +export var turn_speed = 7.5 #deg/s +export var pitch_speed = 10 + +export var max_pitch = 50 +export var min_pitch = -10 +export var min_yaw = -15 +export var max_yaw = 15 + +onready var muzzle = get_node("YawJoint/PitchJoint/Muzzle") + +remote func update_aim(pitch_z, yaw_y): + $YawJoint/PitchJoint.rotation_degrees.z = pitch_z + $YawJoint.rotation_degrees.y = yaw_y + +func get_init_info(): + return {"pitch_rot" : $YawJoint/PitchJoint.rotation_degrees.z, "turn_rot" : $YawJoint.rotation_degrees.y, "in_use" : in_use} + +func mp_init(init_info): + $YawJoint/PitchJoint.rotation_degrees.z = init_info["pitch_rot"] + $YawJoint.rotation_degrees.y = init_info["turn_rot"] + in_use = init_info["in_use"] + +# Called when the node enters the scene tree for the first time. +func _ready(): + if get_parent().name != "MACHINES": + add_collision_exception_with(get_parent()) + mode = RigidBody.MODE_STATIC + world_ballistics = world.get_node("BALLISTICS") + +func on_new_control(): + $YawJoint/PitchJoint/Camera.current = true + +func _physics_process(delta): + if cooldown > 0: + cooldown -= delta + if in_use and is_network_master(): #aim + $YawJoint/PitchJoint.rotation_degrees.z += pitch*pitch_speed*delta + $YawJoint.rotation_degrees.y += turn*turn_speed*delta + $YawJoint.rotation_degrees.y = clamp($YawJoint.rotation_degrees.y, min_yaw, max_yaw) + $YawJoint/PitchJoint.rotation_degrees.z = clamp($YawJoint/PitchJoint.rotation_degrees.z, min_pitch, max_pitch) + + rpc("update_aim", $YawJoint/PitchJoint.rotation_degrees.z, $YawJoint.rotation_degrees.y) + +func direction_input(fwd,bwd,left,right,_left,_right): + pitch = fwd - bwd + turn = right - left + +func attack1(): + if cooldown > 0: + return + rpc("fire") + +remotesync func fire(): + $YawJoint/PitchJoint/Muzzle/explosion_sound.play() + var expl = preload("res://particles/p_Explosion.tscn").instance() + var cball = preload("res://scenes/ballistics/Cannonball.tscn").instance() + world_ballistics.add_child(cball, true) + world.add_child(expl) + expl.scale = Vector3(0.25,0.25,0.25) + expl.init(muzzle.global_transform.origin, Vector3.ZERO) + add_collision_exception_with(cball) + cball.global_transform.origin = muzzle.global_transform.origin + cball.linear_velocity = muzzle.global_transform.basis.x*ball_speed + cooldown = fire_rate + if mode == RigidBody.MODE_STATIC: + get_parent().apply_impulse($YawJoint/PitchJoint.global_transform.origin - get_parent().global_transform.origin, -5*cball.mass*ball_speed*muzzle.global_transform.basis.x) + else: + apply_impulse($YawJoint/PitchJoint.global_transform.origin - global_transform.origin, -1*cball.mass*ball_speed*muzzle.global_transform.basis.x) diff --git a/godot/scripts/machines/NetworkedMachine.gd b/godot/scripts/machines/NetworkedMachine.gd new file mode 100644 index 0000000..5ce5cbe --- /dev/null +++ b/godot/scripts/machines/NetworkedMachine.gd @@ -0,0 +1,54 @@ +extends RigidBody + +var in_use : bool = false +var user = null +var world = null + +func _ready(): + world = get_tree().get_root().find_node("GAMEWORLD", true, false) + +remote func update_phys_transform(t, lv, av): + transform = t + linear_velocity = lv + angular_velocity = av + +remotesync func net_apply_impulse(impulse_v): + apply_central_impulse(impulse_v) + +func _integrate_forces(_state): + if is_network_master() and mode == MODE_RIGID: + rpc("update_phys_transform", transform, linear_velocity, angular_velocity) + +remotesync func set_net_owner(id, char_name): + set_network_master(id) + if id == 1 and char_name == "NONE": #not under control + on_no_control() + if user != null: + user.lose_machine() + user = null + in_use = false + else: + in_use = true + user = world.get_node("PLAYERS/"+char_name) + user.take_control_of_machine(self) + if is_network_master(): + on_new_control() + +func relinquish_control(): + rpc("set_net_owner", 1, "NONE") + +#TO BE OVERRIDDEN BY CHILDREN +func on_new_control(): + pass + +func on_no_control(): + pass + +func attack1(): + pass + +func attack2(): + pass + +func direction_input(_fwd,_bwd,_left,_right,_left2,_right2): + pass diff --git a/godot/scripts/vehicles/Airplane.gd b/godot/scripts/vehicles/Airplane.gd new file mode 100644 index 0000000..cf3c21a --- /dev/null +++ b/godot/scripts/vehicles/Airplane.gd @@ -0,0 +1,106 @@ +extends VehicleBody + +var countdown +var boosting = false +const booster_force = 2500 +const brake_force = 50 +const boost_length = 8 +const turn_constant = 0.45 +const roll_constant = 0.5 +const pitch_constant = 0.9 +const drag_constant = 0.4 +const v_angle_max = 1.0472 #60 deg, also top and bottom of regular steering +const v_angle_min = -1.45626 #-85 deg +const roll_angle_max = 1.22173 #70 deg +var roll_dir = 0 #right = 1, left = -1 +var pitch_dir = 0 #up = 1, down = -1 +var v_angle +var roll_angle +var vel_slow +var parentvel = [Vector3.ZERO, Vector3.ZERO] +export var roll_curve : CurveTexture + + + +func _ready(): + countdown = boost_length + brake = 5 + set_network_master(-1) + set_physics_process(true) + #mode = RigidBody.MODE_STATIC + +func trigger_boost(): + if not boosting: + print("boosting") + boosting = true + +func _process(delta): + if is_network_master(): + if boosting and countdown <= 0: + print("stopped boosting") + boosting = false + countdown = boost_length + + if Input.is_action_just_pressed("boost"): + trigger_boost() + roll_dir = Input.get_action_strength("roll_left") - Input.get_action_strength("roll_right") + pitch_dir = Input.get_action_strength("pitch_up") - Input.get_action_strength("pitch_down") + $rearwheel.brake = lerp($rearwheel.brake, Input.get_action_strength("brake")*brake_force, 0.05) + +func _physics_process(delta): + if is_network_master(): + v_angle = atan2(global_transform.basis.x.y,sqrt(global_transform.basis.x.z*global_transform.basis.x.z + global_transform.basis.x.x*global_transform.basis.x.x)) + roll_angle = global_transform.basis.x.cross(Vector3.UP).angle_to($rightaileron.global_transform.origin-$wingcenter.global_transform.origin) + if $rightaileron.global_transform.origin.y < $leftaileron.global_transform.origin.y: + roll_angle *= -1 + if boosting: + add_force(global_transform.basis.x*booster_force,Vector3.ZERO) + countdown -= delta + + #print(linear_velocity.length()) + #slow plane by drag and gravity + if linear_velocity.length() > 16 and linear_velocity.angle_to(global_transform.basis.x) < 0.25: + var v_dir = 1 if linear_velocity.y > 0 else -1 + vel_slow = v_dir*sqrt(abs(2*9.8*linear_velocity.y*delta)) + drag_constant + Input.get_action_strength("brake")*5 + add_force(-1*linear_velocity.normalized()*vel_slow*mass,Vector3.ZERO) + + + + #pass #"inherit" linear and angular velocity of what plane is landed on + +func _integrate_forces(state): + if is_network_master(): + #linear_velocity -= parentvel[0]; angular_velocity -= parentvel[1] +# if $rightwheel.get_rpm() < 60 and $rightwheel.is_in_contact(): +# if $rightwheel/Area.get_overlapping_bodies()[0].has_method("get_linear_velocity"): +# var ulv = $rightwheel/Area.get_overlapping_bodies()[0].linear_velocity +# var uav = $rightwheel/Area.get_overlapping_bodies()[0].angular_velocity +# var upos = $rightwheel/Area.get_overlapping_bodies()[0].global_transform.origin +# ulv += (global_transform.origin - upos).rotated(uav.normalized(),uav.length()*state.get_step()) + global_transform.origin +# parentvel = [ulv,uav] +# else: +# parentvel = [Vector3.ZERO, Vector3.ZERO] + if linear_velocity.length() > 25: + #linear_velocity = linear_velocity.normalized()*(linear_velocity.length()-vel_slow) + var ang_vel_target = Vector3.ZERO + #pitch plane + if pitch_dir == 1: #up, rotate toward maximum vertical angle + ang_vel_target += pitch_constant*(v_angle_max-v_angle)*global_transform.basis.z + elif pitch_dir == -1: #down, rotate toward minimum vertical angle + ang_vel_target += pitch_constant*(v_angle_min-v_angle)*global_transform.basis.z + + #roll (rotate around lengthwise axis) + var is_returning = 3 if roll_dir == 0 else 1 #return to flat quicker + ang_vel_target -= roll_constant*is_returning*(roll_dir*roll_curve.curve.interpolate(roll_angle_max-roll_angle))*global_transform.basis.x + + #turn (based on how much the plane is rolled (need to add + ang_vel_target += turn_constant*Vector3.UP*roll_angle + + #apply angular velocity + angular_velocity = ang_vel_target#lerp(angular_velocity,ang_vel_target,0.1) + if linear_velocity.angle_to(global_transform.basis.x) < 0.25: + set_linear_velocity(get_linear_velocity().slerp(transform.basis.x*linear_velocity.length(),0.1)) + #linear_velocity += parentvel[0]; angular_velocity += parentvel[1] + rpc("_set_position", global_transform, linear_velocity) + elif get_network_master() == -1 and get_tree().get_network_unique_id() == 1: + rpc("_set_position", global_transform, linear_velocity) diff --git a/godot/scripts/vehicles/Gunboat.gd b/godot/scripts/vehicles/Gunboat.gd new file mode 100644 index 0000000..e1a61c5 --- /dev/null +++ b/godot/scripts/vehicles/Gunboat.gd @@ -0,0 +1,100 @@ +extends "res://scripts/machines/NetworkedMachine.gd" + +export var team = 0 + +const accel = 50000 +const turn_accel = 50000 + +export(float, 0.0, 1.0) var sail_out = 0.0 +export var sail_speed : float = 0.5 +var sail_turn = 0 +export var sail_turn_speed = 30 +const SAIL_MAX = 90 + +var rudder_turn : float = 0.0 +export var rudder_speed = 25 +export var rudder_constant = 1800 +const RUDDER_MAX = 60 + +const health_max = 1000 +var health = health_max +const damage_threshold = 20 +const max_depth = 2 + +#controls +var throttle: float = 0.0 +var rudder: float = 0.0 +var mainsheet: float = 0.0 + +func get_init_info(): + return {"sail_out" : sail_out, "rudder_turn" : rudder_turn, "sail_turn" : sail_turn, "health" : health, "in_use" : in_use} + +func mp_init(init_info): + for variable in init_info.keys(): + set(variable, init_info[variable]) + +# Called when the node enters the scene tree for the first time. +func _ready(): + world = get_tree().get_root().find_node("GAMEWORLD", true, false) + mass = 13500 + weight = mass * 9.8 + +func on_no_control(): + rudder = 0.0 + throttle = 0.0 + mainsheet = 0.0 + +func auto_sail(delta): + var in_range = global_transform.basis.x.dot(world.winddir) >= 0 + if in_range: + if Vector2(world.winddir.x, world.winddir.z).angle_to(Vector2($Mast.global_transform.basis.x.x,$Mast.global_transform.basis.x.z)) < 0: + sail_turn -= sail_turn_speed*delta + else: + sail_turn += sail_turn_speed*delta + else: + if abs(Vector2(world.winddir.x, world.winddir.z).angle_to(Vector2(global_transform.basis.z.x,global_transform.basis.z.z))) < PI/2: + sail_turn -= sail_turn_speed*delta + else: + sail_turn += sail_turn_speed*delta + +func direction_input(fwd,bwd,left,right,_left,_right): + throttle = fwd - bwd + rudder = left - right + mainsheet = _left - _right + +remotesync func damage(amount, _type, _shooter, _extra = ""): + if amount >= damage_threshold: + health -= amount + print(health) + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _physics_process(delta): + if is_network_master(): + $Mast/Sail.scale.y = sail_out + $Rudder.rotation_degrees.y = rudder_turn + $Mast.rotation_degrees.y = sail_turn + var push_force = accel*sail_out*world.winddir.dot($Mast.global_transform.basis.x) + if world.winddir.angle_to($Mast.global_transform.basis.x) < PI/2: + add_force(global_transform.basis.x*push_force, Vector3.ZERO) + add_torque(Vector3(0,-rudder_turn*rudder_constant*(0.5+linear_velocity.dot(global_transform.basis.x)),0)) + add_torque(Vector3(-1000000*angular_velocity.x,0,0)) + add_torque(Vector3(0,0,-1000000*angular_velocity.z)) + rudder_turn += rudder_speed*delta*(-0.25 if rudder_turn > 0 else 0.25) + for point in $FloatPoints.get_children(): + for area in point.get_overlapping_areas(): + if area.name == "WaterArea": + var depth = area.global_transform.origin.y-point.global_transform.origin.y + var floatiness = 0.275 if health <= 0 else 1.0 + if floatiness == 0.275: + depth = 0.05 + add_force(Vector3.UP*weight*depth*floatiness, point.global_transform.origin-global_transform.origin) + if in_use: + rudder_turn += rudder_speed*delta*(rudder) + sail_out += sail_speed*delta*(throttle) + sail_turn += sail_turn_speed*delta*(mainsheet) + auto_sail(delta) + sail_out = clamp(sail_out, 0, 1) + rudder_turn = clamp(rudder_turn, -RUDDER_MAX, RUDDER_MAX) + sail_turn = clamp(sail_turn, -SAIL_MAX, SAIL_MAX) + + #add_force(transform.basis.x*accel*Input.get_action_strength("move_forward"), Vector3.ZERO) diff --git a/godot/scripts/world_tools.gd b/godot/scripts/world_tools.gd new file mode 100644 index 0000000..bb3b336 --- /dev/null +++ b/godot/scripts/world_tools.gd @@ -0,0 +1,65 @@ +tool +extends Node + +export(String) var terrain1path = "" setget t1update +export(String) var terrain2path = "" setget t2update +export(bool) var seam = 0 setget makeseam +export(Vector3) var winddir = Vector3.ZERO + +var terr1 +var terr2 + +func t1update(p): + terrain1path = p + terr1 = get_node(terrain1path) +func t2update(p): + terrain2path = p + terr2 = get_node(terrain2path) + +#makes adjacent terrain (terr2) snap to given terrain (terr1) on their border +func makeseam(_p): +# var t1pos: Vector3 = terr1.transform.origin +# var t2pos: Vector3 = terr2.transform.origin +# var t1data = terr1.get_data() +# var t2data = terr2.get_data() +# var heightmap1: Image = t1data.get_image(t1data.CHANNEL_HEIGHT) +# var heightmap2: Image = t2data.get_image(t2data.CHANNEL_HEIGHT) +# +# if heightmap1.get_height() != heightmap2.get_height(): +# return +# var sidelength = heightmap1.get_height() +# heightmap2.lock() +# heightmap1.lock() + + #if t1pos.distance_squared_to(t2pos) != heightmap1.get_height()*heightmap1.get_height(): + # return + pass +# var side : int = 0 +# var is_x : bool +# if t1pos.x == t2pos.x: +# is_x = false +# if t1pos.z > t2pos.z: +# side = sidelength-1 +# elif t2pos.z == t2pos.z: +# is_x = true +# if t1pos.x > t2pos.x: +# side = sidelength-1 +# else: +# return +# +# var oside = sidelength-1-side +# +# for i in sidelength: +# if is_x: +# heightmap2.set_pixel(side, i, heightmap1.get_pixel(oside, i)) +# else: +# heightmap2.set_pixel(i, side, heightmap1.get_pixel(i, oside)) +# +# heightmap2.unlock() +# heightmap1.unlock() +# +# var modified_region = Rect2(Vector2(), heightmap2.get_size()) +# t2data.notify_region_change(modified_region, t2data.CHANNEL_HEIGHT) +# Called every frame. 'delta' is the elapsed time since the previous frame. +#func _process(delta): +# pass -- cgit v1.2.3