main.gd 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. # This code is adapted from work by Joshua Moelans
  2. # https://github.com/JoshuaMoelans/Master-Thesis-Godot-exploration (accessed March 2025)
  3. # Originally developed for his master's thesis at the University of Antwerp
  4. extends Node2D
  5. const GameOverscreen = preload("res://UI/game_over_screen.tscn")
  6. var Game_Instance_Scene = preload("res://instance_small_map.tscn")
  7. #var Game_Instance_Scene = preload("res://small_map_many.tscn")
  8. @export var instance_num = 1;
  9. @export var visible_games = true;
  10. @export var flush_state = false;
  11. @export var timeout:int = 0;
  12. @export var start_minimized = false;
  13. @export var timescale:float = 1.0
  14. @onready var GAMES = $GAMES
  15. @onready var player: Player = $Player
  16. @onready var gui = $GUI
  17. @onready var outputhandler = $outputhandler
  18. @onready var openfile = $OpenFile
  19. func parse_CLA():
  20. var arguments = {}
  21. for argument in OS.get_cmdline_args():
  22. if argument.find("=") > -1:
  23. var key_value = argument.split("=")
  24. arguments[key_value[0].lstrip("-")] = key_value[1]
  25. return arguments
  26. # LIST OF PARAMETERS BELOW!
  27. @export var communication_count : int = 1 # number of units to notify when attacking
  28. @export var communication_delay : float = 1.5 # takes 1.5s before notifying
  29. @export var vision_distance : float = 300 # how far a unit can 'see'
  30. @export var vision_angle : float = 60 # total vision angle (half above horizon, half below)
  31. # READ IN PARAMETERS BELOW
  32. func set_CLA_vars():
  33. var args = parse_CLA()
  34. if args.has("timeout"):
  35. if args["timeout"].is_valid_int():
  36. timeout = int(args["timeout"])
  37. else:
  38. print("ERROR! non-integer timeout specified")
  39. if args.has("ngames"):
  40. if args["ngames"].is_valid_int():
  41. instance_num = int(args["ngames"])
  42. else:
  43. print("ERROR! non-integer amount of ngames parameter")
  44. if args.has("visible"):
  45. if args["visible"] == "true":
  46. visible_games = true
  47. else:
  48. visible_games = false
  49. if args.has("communication_count"):
  50. if args["communication_count"].is_valid_int():
  51. communication_count = int(args["communication_count"])
  52. if args.has("communication_delay"):
  53. if args["communication_delay"].is_valid_float():
  54. communication_delay = float(args["communication_delay"])
  55. if args.has("vision_angle"):
  56. if args["vision_angle"].is_valid_float():
  57. vision_angle = float(args["vision_angle"])
  58. if args.has("vision_distance"):
  59. if args["vision_distance"].is_valid_float():
  60. vision_distance = float(args["vision_distance"])
  61. if args.has("start_minimized"):
  62. if args["start_minimized"] == "true":
  63. start_minimized = true
  64. else:
  65. start_minimized = false
  66. if args.has("timescale"):
  67. if args["timescale"].is_valid_float():
  68. timescale = args["timescale"]
  69. # Called when the node enters the scene tree for the first time.
  70. # using deferred setup due to navigationserver error
  71. # see https://github.com/godotengine/godot/issues/84677
  72. func _ready():
  73. set_CLA_vars()
  74. if start_minimized:
  75. DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED)
  76. if timeout > 0:
  77. $TimeOut.wait_time = timeout
  78. $TimeOut.start()
  79. seed(0) # doesnt 100% work!
  80. Engine.set_time_scale(timescale)
  81. set_physics_process(false)
  82. setup()
  83. func _physics_process(delta):
  84. if Input.is_action_just_pressed("toggle_visibility"):
  85. for game_instance in GAMES.get_children():
  86. game_instance.visible = not game_instance.visible
  87. if Input.is_action_just_pressed("load"):
  88. GAMES.process_mode = Node.PROCESS_MODE_DISABLED
  89. openfile.popup()
  90. if Input.is_action_just_pressed("save"):
  91. for game_instance:GameInstance in GAMES.get_children():
  92. game_instance.game_state.state_update(true)
  93. func setup_instance(i:int):
  94. var grid_dim:int = ceil(sqrt(instance_num))
  95. # instance a Game_Instance from the Game_Instance scene
  96. var new_game_instance:GameInstance = Game_Instance_Scene.instantiate()
  97. new_game_instance.name = "game_instance_" + str(i)
  98. new_game_instance.id = i
  99. GAMES.add_child(new_game_instance)
  100. GAMES.move_child(new_game_instance, i)
  101. new_game_instance.visible = visible_games
  102. # Position the new game_instance
  103. new_game_instance.position = new_game_instance.calculate_instance_position(grid_dim, i)
  104. # Setup the new game_instance by calling it's setup function
  105. new_game_instance.setup()
  106. new_game_instance.assign_parameters(null)
  107. # connect state flush to output handler
  108. new_game_instance.game_state.connect("state_flush",flush_game_instance_state)
  109. new_game_instance.set_flush_state(flush_state)
  110. return new_game_instance
  111. func setup():
  112. await get_tree().physics_frame
  113. set_physics_process(true)
  114. outputhandler.init(instance_num) # initialize file output handler
  115. for i in range(instance_num):
  116. setup_instance(i)
  117. gui.set_player(player)
  118. func flush_game_instance_state(filename, data):
  119. outputhandler.write_to_file(filename, data)
  120. func _notification(what):
  121. if what == NOTIFICATION_WM_CLOSE_REQUEST:
  122. handle_end_of_game()
  123. func handle_end_of_game():
  124. outputhandler.write_buffered_data()
  125. outputhandler.write_to_file("main", "games timed out")
  126. for game_instance:GameInstance in GAMES.get_children():
  127. game_instance.game_state.state_update(true,"", true) # store save on timeout
  128. get_tree().quit()
  129. func _on_time_out_timeout():
  130. handle_end_of_game()
  131. func _on_open_file_files_selected(paths):
  132. for path in paths:
  133. var file = FileAccess.open(path, FileAccess.READ)
  134. var filename:String = path.get_file()
  135. var instance_id = int(filename.split("_")[2])
  136. if instance_id >= instance_num:
  137. print("ERROR! trying to load instance that doesn't exist")
  138. print("\tplease rerun with at least ", instance_id+1, " instances")
  139. continue # in case we try to load a higher instance then exists
  140. remove_game_instance(instance_id)
  141. var content = file.get_as_text()
  142. var contentDict = JSON.parse_string(content)
  143. var g = setup_instance(instance_id) # and generate a new one
  144. g.load_game_state(contentDict) # load from contentDict
  145. GAMES.process_mode = Node.PROCESS_MODE_INHERIT
  146. func remove_game_instance(i):
  147. var all_games = GAMES.get_children()
  148. var game_instance:GameInstance = all_games[i]
  149. game_instance.queue_free() # need to REMOVE old instance!
  150. func _on_open_file_canceled():
  151. GAMES.process_mode = Node.PROCESS_MODE_INHERIT