diff --git a/scenes/lobby.tscn b/scenes/lobby.tscn index 1e98712..deb5ee5 100644 --- a/scenes/lobby.tscn +++ b/scenes/lobby.tscn @@ -60,6 +60,7 @@ align = 1 [node name="ServerStart" type="Button" parent="." index="1"] +visible = false anchor_left = 0.0 anchor_top = 0.0 anchor_right = 0.0 @@ -104,7 +105,7 @@ action_mode = 0 enabled_focus_mode = 2 shortcut = null group = null -text = "Platforms" +text = "Platform map" flat = false align = 0 selected = 0 diff --git a/scripts/args.gd b/scripts/args.gd new file mode 100644 index 0000000..1b3943b --- /dev/null +++ b/scripts/args.gd @@ -0,0 +1,181 @@ +# Adapted from: https://gist.github.com/bitwes/fe1e2aef8da0940104c8 +# Example of a script that can be run from the command line and parses out options. This is adapted +# from the Gut command line interface. The first 2 classes are not to be used directly, they are used +# by the Options class and can be ignored. Start reading after the Options class to get a feel for +# how it is used, then work backwards. +# +# This could be easily extracted out into a class itself, but in the interest of how it is being used +# I wanted it all in one file. It is yours to do with what you please, but if you make something out +# of it, I'd love to hear about it. I'm bitwes on godot forums, github, and bitbucket. +extends Control + +#------------------------------------------------------------------------------- +# Parses the command line arguments supplied into an array that can then be +# examined and parsed based on how the gut options work. +#------------------------------------------------------------------------------- +class CmdLineParser: + var _opts = [] + + func _init(): + for i in range(OS.get_cmdline_args().size()): + _opts.append(OS.get_cmdline_args()[i]) + + # Search _opts for an element that starts with the option name + # specified. + func find_option(name): + var found = false + var idx = 0 + + while(idx < _opts.size() and !found): + if(_opts[idx].find(name) == 0): + found = true + else: + idx += 1 + + if(found): + return idx + else: + return -1 + + # Parse out the value of an option. Values are seperated from + # the option name with "=" + func get_option_value(full_option): + var split = full_option.split('=') + + if(split.size() > 1): + return split[1] + else: + return null + + # Parse out multiple comma delimited values from a command line + # option. Values are separated from option name with "=" and + # additional values are comma separated. + func get_option_array_value(full_option): + var value = get_option_value(full_option) + var split = value.split(',') + return split + + func get_array_value(option): + var to_return = [] + var opt_loc = find_option(option) + if(opt_loc != -1): + to_return = get_option_array_value(_opts[opt_loc]) + _opts.remove(opt_loc) + + return to_return + + # returns the value of an option if it was specfied, otherwise + # it returns the default. + func get_value(option, default): + var to_return = default + var opt_loc = find_option(option) + if(opt_loc != -1): + to_return = get_option_value(_opts[opt_loc]) + _opts.remove(opt_loc) + + return to_return + + # returns true if it finds the option, false if not. + func was_specified(option): + var opt_loc = find_option(option) + if(opt_loc != -1): + _opts.remove(opt_loc) + + return opt_loc != -1 + +#------------------------------------------------------------------------------- +# Simple class to hold a command line option +#------------------------------------------------------------------------------- +class Option: + var value = null + var option_name = '' + var default = null + var description = '' + + func _init(name, default_value, desc=''): + option_name = name + default = default_value + description = desc + value = default_value + + func pad(value, size, pad_with=' '): + var to_return = value + for i in range(value.length(), size): + to_return += pad_with + + return to_return + + func to_s(min_space=0): + var subbed_desc = description + if(subbed_desc.find('[default]') != -1): + subbed_desc = subbed_desc.replace('[default]', str(default)) + return pad(option_name, min_space) + subbed_desc + + +#------------------------------------------------------------------------------- +# The high level interface between this script and the command line options +# supplied. Uses Option class and CmdLineParser to extract information from +# the command line and make it easily accessible. +#------------------------------------------------------------------------------- +class Options: + var options = [] + var _opts = [] + var _banner = '' + + func add(name, default, desc): + options.append(Option.new(name, default, desc)) + + func get_value(name): + var found = false + var idx = 0 + + while(idx < options.size() and !found): + if(options[idx].option_name == name): + found = true + else: + idx += 1 + + if(found): + return options[idx].value + else: + print("COULD NOT FIND OPTION " + name) + return null + + func set_banner(banner): + _banner = banner + + func print_help(): + var longest = 0 + for i in range(options.size()): + if(options[i].option_name.length() > longest): + longest = options[i].option_name.length() + + print('---------------------------------------------------------') + print(_banner) + + print("\nOptions\n-------") + for i in range(options.size()): + print(' ' + options[i].to_s(longest + 2)) + print('---------------------------------------------------------') + + func print_options(): + for i in range(options.size()): + print(options[i].option_name + '=' + str(options[i].value)) + + func parse(): + var parser = CmdLineParser.new() + + for i in range(options.size()): + var t = typeof(options[i].default) + if(t == TYPE_INT): + options[i].value = int(parser.get_value(options[i].option_name, options[i].default)) + elif(t == TYPE_STRING): + options[i].value = parser.get_value(options[i].option_name, options[i].default) + elif(t == TYPE_ARRAY): + options[i].value = parser.get_array_value(options[i].option_name) + elif(t == TYPE_BOOL): + options[i].value = parser.was_specified(options[i].option_name) + elif(t == TYPE_NIL): + print(options[i].option_name + ' cannot be processed, it has a nil datatype') + else: + print(options[i].option_name + ' cannot be processsed, it has unknown datatype:' + str(t)) diff --git a/scripts/lobby.gd b/scripts/lobby.gd index 6f274f3..f486e9d 100644 --- a/scripts/lobby.gd +++ b/scripts/lobby.gd @@ -1,4 +1,4 @@ -extends Control +extends "res://scripts/args.gd" # class member variables go here, for example: # var a = 2 @@ -10,7 +10,56 @@ var SERVER_PLAYING = true var player_info = {} var my_info = {} +func setup_options(): + var opts = Options.new() + opts.set_banner(('A non-violent MOBA inspired by Overwatch and Zineth')) + opts.add('-singleplayer', false, 'Whether to run singeplayer, starting immediately') + opts.add('-server', false, 'Whether to run as server') + opts.add('-client', false, 'Immediately connect as client') + opts.add('-silent-server', false, 'If the server is not playing, merely serving') + opts.add('-hero', 'r', 'Your choice of hero (index)') + opts.add('-level', 'r', 'Your choice of level (index) - server only!') + opts.add('-start-game', false, 'Join as a client and immediately start the game') + opts.add('-h', false, "Print help") + return opts + +func option_sel(button_name, option): + var button = get_node(button_name) + print("-->") + if option == "r": + option = randi() % button.get_item_count() + print(randi() % 3) + else: + option = int(option) + button.select(option) + func _ready(): + + var o = setup_options() + o.parse() + + randomize() + + if o.get_value("-silent-server"): + SERVER_PLAYING = false # TODO: Uncaps :( + if o.get_value("-hero"): + option_sel("HeroSelect", o.get_value("-hero")) + print(get_node("HeroSelect").get_selected_id()) + if o.get_value("-level"): + option_sel("ServerStart/LevelSelect", o.get_value("-level")) + if o.get_value("-server"): + call_deferred("_server_init") + if o.get_value("-client"): + call_deferred("_client_init") + if o.get_value("-start-game"): + my_info.start_game = true + call_deferred("_client_init") + if o.get_value("-singleplayer"): + call_deferred("_singleplayer_init") + if o.get_value('-h'): + o.print_help() + quit() + # Called every time the node is added to the scene. # Initialization here get_node("Server").connect("pressed", self, "_server_init") @@ -54,11 +103,16 @@ func _player_connected(id): func _connected_ok(): rpc("register_player", get_tree().get_network_unique_id(), my_info) + if "start_game" in my_info: + rpc_id(1, "start_game") func collect_info(): - my_info.username = get_node("Username").get_text() - my_info.hero = get_node("HeroSelect").get_selected_id() - my_info.is_right_team = false # Server assigns team, wait for that + if not "username" in my_info: + my_info.username = get_node("Username").get_text() + if not "hero" in my_info: + my_info.hero = get_node("HeroSelect").get_selected_id() + if not "is_right_team" in my_info: + my_info.is_right_team = false # Server assigns team, wait for that remote func register_player(new_peer, info): player_info[new_peer] = info @@ -113,7 +167,7 @@ func render_player_list(): list += "\n" get_node("PlayerList").set_text(list) -func start_game(): +sync func start_game(): var level = get_node("ServerStart/LevelSelect").get_selected_id() rpc("pre_configure_game", level) diff --git a/util/start-multiple.sh b/util/start-multiple.sh new file mode 100644 index 0000000..e9470a7 --- /dev/null +++ b/util/start-multiple.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +count=$1 +if [[ $count ]]; then + shift # Only do this if arg exists (fixes error) +else + count=2 # Default 2 +fi + +godot -server "$@" & +for i in `seq 3 $count` # 3, for 1 + the server + the starter +do + godot -client "$@" & +done +sleep 1 +godot -start-game "$@" & +