summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/ClientUI.gd27
-rw-r--r--scripts/CmdPrompt.gd26
-rw-r--r--scripts/Console.gd14
-rw-r--r--scripts/GameBase.gd77
-rw-r--r--scripts/Server.gd110
-rw-r--r--scripts/ServerUI.gd38
-rw-r--r--scripts/World.gd51
-rw-r--r--scripts/ballistics/Cannonball.gd24
-rw-r--r--scripts/boats/Gunboat.gd121
-rw-r--r--scripts/characters/player_controller_new.gd236
-rw-r--r--scripts/machines/Cannon.gd92
-rw-r--r--scripts/world_tools.gd65
12 files changed, 881 insertions, 0 deletions
diff --git a/scripts/ClientUI.gd b/scripts/ClientUI.gd
new file mode 100644
index 0000000..6a3eb41
--- /dev/null
+++ b/scripts/ClientUI.gd
@@ -0,0 +1,27 @@
+extends Node2D
+
+var client : NetworkedMultiplayerENet
+var world
+
+func _ready():
+ world = preload("res://scenes/GameBase.tscn").instance()
+ get_tree().connect("network_peer_connected", self, "_player_connected")
+ get_tree().connect("network_peer_disconnected", self, "_player_disconnected")
+ get_tree().connect("connected_to_server", self, "_connection_successful")
+ get_tree().connect("connection_failed", self, "_connected_fail")
+ get_tree().connect("server_disconnected", world, "_server_disconnected")
+
+func _connect_btn():
+ client = NetworkedMultiplayerENet.new()
+ client.create_client($IP.text, $Port.value)
+
+ get_tree().set_network_peer(client)
+
+
+func _connection_successful():
+ get_tree().get_root().add_child(world)
+ world.client = client
+ world.client_id = get_tree().get_network_unique_id()
+ world.player_name = $Name.text
+ print("Connected.")
+ queue_free()
diff --git a/scripts/CmdPrompt.gd b/scripts/CmdPrompt.gd
new file mode 100644
index 0000000..965631b
--- /dev/null
+++ b/scripts/CmdPrompt.gd
@@ -0,0 +1,26 @@
+extends LineEdit
+
+export var console_path := @""; onready var console := get_node(console_path) as ScrollContainer
+var server_ref = null
+var ui_ref = null
+
+func enter_cmd(new_text: String):
+ console.add_line(new_text)
+ text = ""
+ if new_text.substr(0,1) == "/": #PROCESS COMMAND
+ var end_ind = new_text.find(" ")-1
+ if end_ind < -1: end_ind = -1
+ var cmd : String = new_text.substr(1, end_ind)
+ var args = new_text.substr(end_ind+1, -1).split(" ", false)
+
+ if cmd == "say":
+ if server_ref != null:
+ var m_args = {"name" : "SERVER", "msg" : args.join(" ")}
+ server_ref._send_chat(m_args)
+ elif cmd == "start":
+ if server_ref == null:
+ ui_ref.start_server()
+ elif cmd == "stop":
+ if server_ref != null:
+ ui_ref.stop_server()
+
diff --git a/scripts/Console.gd b/scripts/Console.gd
new file mode 100644
index 0000000..ac73525
--- /dev/null
+++ b/scripts/Console.gd
@@ -0,0 +1,14 @@
+extends ScrollContainer
+
+# Called when the node enters the scene tree for the first time.
+func _ready():
+ pass
+
+func add_line(text):
+ var lbl = Label.new()
+ lbl.autowrap = true
+ lbl.text = text
+ text = ""
+ $ConsoleLines.add_child(lbl)
+ yield(get_tree().create_timer(0.05), "timeout")
+ ensure_control_visible(lbl)
diff --git a/scripts/GameBase.gd b/scripts/GameBase.gd
new file mode 100644
index 0000000..ee26531
--- /dev/null
+++ b/scripts/GameBase.gd
@@ -0,0 +1,77 @@
+extends Spatial
+
+var client
+var client_id
+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)
+
+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()
+ $WORLDGEO.add_child(prop)
+ prop.transform = geo["transform"]
+
+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)
+
+func select_character(dest):
+ print(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()
+ get_tree().quit()
+
+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/scripts/Server.gd b/scripts/Server.gd
new file mode 100644
index 0000000..bd24911
--- /dev/null
+++ b/scripts/Server.gd
@@ -0,0 +1,110 @@
+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):
+ output.call(output_func, line)
+
+func _ready():
+ get_tree().connect("network_peer_connected", self, "_client_connect")
+ get_tree().connect("network_peer_disconnected", self, "_client_disconnect")
+
+func start_server(_server_name: String, _motd: String, max_players: int, map_path: String, ip: String, port: int, root, 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)
+ root.add_child(world)
+ world.client_id = 1
+
+ server_enet = NetworkedMultiplayerENet.new()
+ server_enet.create_server(port, max_players)
+ get_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.get_network_master() == 1:
+ print_line(connected_players[arguments["id"]][0] + " selected character " + arguments["char_name"])
+ dest.rpc("set_owner", arguments["id"])
+ if arguments["current_char_name"] != "NULL":
+ var old = world.get_node("PLAYERS/"+arguments["current_char_name"])
+ old.rpc("set_owner", 1)
+
+func _character_death(arguments):
+ print(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")
+ 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/scripts/ServerUI.gd b/scripts/ServerUI.gd
new file mode 100644
index 0000000..1acf9eb
--- /dev/null
+++ b/scripts/ServerUI.gd
@@ -0,0 +1,38 @@
+extends Node2D
+
+
+var server
+onready var console = get_node("Console")
+
+var is_running :bool = false
+
+func _ready():
+ $CmdPrompt.ui_ref = self
+
+func hide_init_fields():
+ for node in get_tree().get_nodes_in_group("init_fields"):
+ node.visible = false
+ $StopButton.visible = true
+
+func show_init_fields():
+ for node in get_tree().get_nodes_in_group("init_fields"):
+ node.visible = true
+ $StopButton.visible = false
+
+func start_server():
+ console.add_line("Server starting...")
+ is_running = true
+
+ server = preload("res://scenes/Server.tscn").instance()
+ $CmdPrompt.server_ref = server
+ server.start_server($ServerName.text, $MOTD.text, $PlayerCount.value, $MapPath.text, $IP.text, $Port.value, get_tree().get_root(), console, "add_line")
+
+ Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
+ hide_init_fields()
+
+func stop_server():
+ server.stop_server()
+ is_running = false
+ $CmdPrompt.server_ref = null
+
+ show_init_fields()
diff --git a/scripts/World.gd b/scripts/World.gd
new file mode 100644
index 0000000..dd5b177
--- /dev/null
+++ b/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()
+ print(map_rid)
+ 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/scripts/ballistics/Cannonball.gd b/scripts/ballistics/Cannonball.gd
new file mode 100644
index 0000000..e59ca6c
--- /dev/null
+++ b/scripts/ballistics/Cannonball.gd
@@ -0,0 +1,24 @@
+extends RigidBody
+
+export var drag_constant = 0.3
+var damage_exceptions = []
+var oldvel
+
+var shooter = "WORLD"
+
+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}
+
+func mp_init(init_info):
+ for variable in init_info.keys():
+ set(variable, init_info[variable])
+
+
+func _on_collision(body):
+ if linear_velocity.length() > 20 and !damage_exceptions.has(body) and body.has_method("damage"):
+ body.damage(oldvel.length(), "blunt")
+ damage_exceptions.append(body)
diff --git a/scripts/boats/Gunboat.gd b/scripts/boats/Gunboat.gd
new file mode 100644
index 0000000..1e8dd49
--- /dev/null
+++ b/scripts/boats/Gunboat.gd
@@ -0,0 +1,121 @@
+extends RigidBody
+
+
+# Declare member variables here. Examples:
+# var a = 2
+# var b = "text"
+var in_use : bool = false
+var user = null
+var world = null
+
+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 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 is_in_use():
+ return in_use
+
+func take_control(controller):
+ user = controller
+ in_use = true
+ return self
+
+func relinquish_control():
+ in_use = false
+ rudder = 0.0
+ throttle = 0.0
+ mainsheet = 0.0
+ user = null
+
+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 attack1():
+ pass
+
+func attack2():
+ pass
+
+func direction_input(fwd,bwd,left,right,_left,_right):
+ throttle = fwd - bwd
+ rudder = left - right
+ mainsheet = _left - _right
+
+func damage(amount, type, shooter, extra = ""):
+ health -= amount
+ print(health)
+
+# Called every frame. 'delta' is the elapsed time since the previous frame.
+func _physics_process(delta):
+ $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
+ 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/scripts/characters/player_controller_new.gd b/scripts/characters/player_controller_new.gd
new file mode 100644
index 0000000..37f8562
--- /dev/null
+++ b/scripts/characters/player_controller_new.gd
@@ -0,0 +1,236 @@
+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()
+onready var nav = $NavigationAgent
+
+# Walk
+const FLOOR_MAX_ANGLE: float = deg2rad(46.0)
+export(float) var jump_height = 400
+
+# 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
+
+#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/LongRay.add_exception(self)
+ 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:
+ 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()
+ machine = null
+ controlling_machine = false
+ if is_player:
+ cam.current = true
+ return
+ if $Head/Camera/UseRay.is_colliding():
+ var type = $Head/Camera/UseRay.get_collider().name
+ match type:
+ "SteerArea":
+ controlling_machine = true
+ machine = $Head/Camera/UseRay.get_collider().get_parent().take_control(self)
+ var gt = global_transform.origin
+ velocity = Vector3.ZERO
+ global_transform.origin = gt
+ _:
+ pass
+
+remotesync func set_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:
+ world.player_char = self
+ is_player = true
+ cam.current = true
+ else:
+ 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_owner", 1)
+
+func regain_control(_gt):
+ controlling_machine = false
+ machine = null
+
+# Called every physics tick. 'delta' is constant
+func _physics_process(delta: float) -> void:
+ walk(delta)
+ if is_network_master():
+ rpc("set_phys_transform", transform, linear_velocity)
+
+# called by signal when character is collided with
+func on_floor_test() -> void:
+ if $Feet.is_colliding():
+ is_on_floor = true
+ if $Feet.get_collider().has_method("get_linear_velocity"):
+ floorspeed = $Feet.get_collider().get_linear_velocity()
+ else:
+ floorspeed = Vector3.ZERO
+ return
+ 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
+
+#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:
+ on_floor_test()
+ # 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:
+ if Input.is_action_just_pressed("move_jump"):
+ 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))
+
+ is_on_floor = false #reset whether is on floor in between frames
+
+remotesync func damage(dmg_amt, type, shooter, extra = ""):
+ health -= dmg_amt
+ if health <= 0 and is_network_master():
+ if get_network_master() == 1:
+ world._call_on_server("_character_death", {"killer" : shooter, "victim_mp_id" : get_network_master(), "victim" : name, "extra" : extra})
+ else:
+ world.rpc_id(1, "_call_on_server", "_character_death", {"killer" : shooter, "victim_mp_id" : get_network_master(), "victim" : name, "extra" : extra})
+
+
+remotesync func remove_dead():
+ 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
+
diff --git a/scripts/machines/Cannon.gd b/scripts/machines/Cannon.gd
new file mode 100644
index 0000000..d7c9a18
--- /dev/null
+++ b/scripts/machines/Cannon.gd
@@ -0,0 +1,92 @@
+extends RigidBody
+#Basis
+var in_use : bool = false
+var user = null
+var world = null
+
+var cooldown = 0
+export var fire_rate = 1 #shot/s
+export var ball_speed = 500 #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")
+
+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 = get_tree().get_root().find_node("BALLISTICS", true, false)
+
+
+func is_in_use():
+ return in_use
+
+func take_control(controller):
+ $YawJoint/PitchJoint/Camera.current = true
+ user = controller
+ in_use = true
+ return self
+
+func relinquish_control():
+ in_use = false
+ user = null
+
+func _physics_process(delta):
+ if cooldown > 0:
+ cooldown -= delta
+ if in_use:
+ #aim
+ $YawJoint/PitchJoint.rotation_degrees.z += pitch*pitch_speed*delta
+ $YawJoint.rotation_degrees.y += turn*turn_speed*delta
+ if $YawJoint.rotation_degrees.y > max_yaw:
+ $YawJoint.rotation_degrees.y = max_yaw
+ elif $YawJoint.rotation_degrees.y < min_yaw:
+ $YawJoint.rotation_degrees.y = min_yaw
+ if $YawJoint/PitchJoint.rotation_degrees.z > max_pitch:
+ $YawJoint/PitchJoint.rotation_degrees.z = max_pitch
+ elif $YawJoint/PitchJoint.rotation_degrees.z < min_pitch:
+ $YawJoint/PitchJoint.rotation_degrees.z = min_pitch
+
+func direction_input(fwd,bwd,left,right,_left,_right):
+ pitch = fwd - bwd
+ turn = left - right
+
+func attack1():
+ if cooldown > 0:
+ return
+ $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.add_child(cball)
+ world.add_child(expl)
+ 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, -1*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)
+
+func attack2():
+ pass
diff --git a/scripts/world_tools.gd b/scripts/world_tools.gd
new file mode 100644
index 0000000..bb3b336
--- /dev/null
+++ b/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