summaryrefslogtreecommitdiff
path: root/godot/scripts
diff options
context:
space:
mode:
authorAnson Bridges <bridges.anson@gmail.com>2022-09-07 14:07:30 -0400
committerAnson Bridges <bridges.anson@gmail.com>2022-09-07 14:07:30 -0400
commitc232b92e2dde1277324d1f89d0e75ae641e4ac3b (patch)
treee11a5dd52f259c1cc7345baa40b372b304417f00 /godot/scripts
parenta0967ebe815cd229b69fb9578f2288b95b2ddb28 (diff)
reorganized, ladders, vehicle control
Diffstat (limited to 'godot/scripts')
-rw-r--r--godot/scripts/GameBase.gd107
-rw-r--r--godot/scripts/Server.gd127
-rw-r--r--godot/scripts/World.gd51
-rw-r--r--godot/scripts/ballistics/Cannonball.gd17
-rw-r--r--godot/scripts/ballistics/NetworkedProjectile.gd29
-rw-r--r--godot/scripts/cameras/plane_armcam.gd26
-rw-r--r--godot/scripts/cameras/player_firstperson.gd33
-rw-r--r--godot/scripts/characters/player_controller_new.gd308
-rw-r--r--godot/scripts/machines/Cannon.gd79
-rw-r--r--godot/scripts/machines/NetworkedMachine.gd54
-rw-r--r--godot/scripts/vehicles/Airplane.gd106
-rw-r--r--godot/scripts/vehicles/Gunboat.gd100
-rw-r--r--godot/scripts/world_tools.gd65
13 files changed, 1102 insertions, 0 deletions
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