Fix multiplayer menu workshop build

This commit is contained in:
Kroonk
2026-05-23 12:44:40 +02:00
parent 5dee1a8e7d
commit f57c606ec9
67 changed files with 9812 additions and 1352 deletions

Binary file not shown.

View File

@@ -0,0 +1,19 @@
extends Node
# TODO rename options to globals
var joining_multiplayer_lobby : bool = false
var is_solo_test : bool = false
var in_multiplayer_game : bool = false
var current_network_id : int = 0
var batched_enemy_deaths = {}
var batched_unit_flashes = {}
var batched_floating_text = []
var batched_hit_particles = []
var batched_explosions = []
var batched_hit_effects = []
var batched_sounds = []
var batched_2d_sounds = []

View File

@@ -0,0 +1,17 @@
extends AttackBehavior
signal shot
signal finished_shooting
signal entered_long_cooldown
signal started_shooting
signal move_unlocked
signal wanted_to_spawn_an_enemy(enemy_scene, position)
# check out all the nothing this enemy will do!
var max_range
var charge_duration
var charge_speed
var long_cooldown = 1.0
var cooldown = 1.0
var _current_cd = 1.0
var number_projectiles

View File

@@ -0,0 +1,504 @@
extends Node
var ClientMovementBehavior = load("res://mods-unpacked/Brogether-Brogether/client/client_movement_behavior.gd")
signal consumable_to_process_added(consumable_data)
signal upgrade_to_process_added(consumable_data)
export (PackedScene) var gold_bag_scene:PackedScene
export (PackedScene) var gold_scene:PackedScene
export (PackedScene) var consumable_scene:PackedScene
export (Array) var gold_sprites:Array
export (Array, Resource) var gold_pickup_sounds:Array
export (Array, Resource) var gold_alt_pickup_sounds:Array
export (Resource) var level_up_sound:Resource
export (Array, Resource) var run_won_sounds:Array
export (Array, Resource) var run_lost_sounds:Array
export (Array, Resource) var end_wave_sounds:Array
const EDGE_SIZE = 96
const MAX_GOLDS = 50
const MIN_MAP_SIZE = 12
const MOUSE_DIST_FROM_PLAYER_MANUAL_AIM = 200
var _cleaning_up: = false
var _golds: = []
var _consumables: = []
var _upgrades_to_process: = []
var _consumables_to_process: = []
var _end_wave_timer_timedout: = false
var _player:Player
var _is_run_lost:bool
var _is_run_won:bool
var _gold_bag:Node
var _landmine_timer:Timer = null
var _is_chal_ui_displayed = false
var _last_rjoy_input:Vector2 = Vector2.ZERO
var _proj_on_death_stat_cache:Dictionary = {}
var _items_spawned_this_wave: = 0
var _nb_bosses_killed_this_wave: = 0
var _update_stats_on_gold_changed: = false
var _update_stats_on_enemies_changed: = false
var _update_stats_on_enemies_changed_timer:Timer = null
var _last_gold_amount_used_to_reload_stats: = 0
var _elite_killed_bonus: = 0
var _elite_killed: = false
var _convert_stats_half_wave_proced: = false
onready var _entities_container = $Entities
onready var _effects_manager = $EffectsManager
onready var _wave_manager = $WaveManager
onready var _floating_text_manager = $FloatingTextManager
onready var _camera = $Camera
onready var _screenshaker = $Camera / Screenshaker
onready var _items_container = $Items
onready var _consumables_container = $Consumables
onready var _births_container = $Births
onready var _pause_menu = $UI / PauseMenu
onready var _end_wave_timer = $EndWaveTimer
onready var _upgrades_ui = $UI / UpgradesUI as UpgradesUI
onready var _item_box_ui = $UI / ItemBoxUI as ItemBoxUI
onready var _wave_timer = $WaveTimer
onready var _player_life_bar = $"%PlayerLifeBar"
onready var _player_life_bar_container = $PlayerLifeBarContainer
onready var _wave_cleared_label = $UI / WaveClearedLabel
onready var _life_label = $UI / HUD / LifeContainer / UILifeBar / MarginContainer / LifeLabel
onready var _level_label = $UI / HUD / LifeContainer / UIXPBar / MarginContainer / LevelLabel
onready var _hud = $UI / HUD
onready var _life_bar = $UI / HUD / LifeContainer / UILifeBar
onready var _xp_bar = $UI / HUD / LifeContainer / UIXPBar
onready var _ui_gold = $UI / HUD / LifeContainer / UIGold
onready var _ui_bonus_gold = $UI / HUD / LifeContainer / UIBonusGold
onready var _ui_bonus_gold_pos = $UI / HUD / LifeContainer / UIBonusGold / Position2D
onready var _current_wave_label = $UI / HUD / WaveContainer / CurrentWaveLabel
onready var _wave_timer_label = $UI / HUD / WaveContainer / WaveTimerLabel
onready var _ui_wave_container = $UI / HUD / WaveContainer
onready var _ui_upgrades_to_process = $UI / HUD / ThingsToProcessContainer / Upgrades
onready var _ui_consumables_to_process = $UI / HUD / ThingsToProcessContainer / Consumables
onready var _ui_dim_screen = $UI / DimScreen
onready var _tile_map = $TileMap
onready var _tile_map_limits = $TileMapLimits
onready var _background = $CanvasLayer / Background
onready var _harvesting_timer = $HarvestingTimer
onready var _challenge_completed_ui = $UI / ChallengeCompletedUI
onready var _damage_vignette = $UI / DamageVignette
onready var _info_popup = $UI / InfoPopup
onready var game_controller = $"/root/GameController"
const refresh_time = 1.0 / 100.0
var update_timer = refresh_time
var health_tracker
const HealthTracker = preload("res://mods-unpacked/Brogether-Brogether/ui/health_tracker/health_tracker.tscn")
func _physics_process(delta:float)->void :
update_timer -= delta
if update_timer <= 0 and not _cleaning_up:
send_player_position()
update_timer = refresh_time
func send_player_position():
game_controller.send_client_position()
func _ready()->void :
_convert_stats_half_wave_proced = false
_background.texture.gradient.colors[1] = RunData.get_background_gradient_color()
_tile_map.tile_set.tile_set_texture(0, RunData.get_background().tiles_sprite)
_tile_map.outline.modulate = RunData.get_background().outline_color
TempStats.reset()
var current_zone = ZoneService.get_zone_data(RunData.current_zone).duplicate()
var current_wave_data = ZoneService.get_wave_data(RunData.current_zone, RunData.current_wave)
var map_size_coef = (1 + (RunData.effects["map_size"] / 100.0))
current_zone.width = max(MIN_MAP_SIZE, (current_zone.width * map_size_coef)) as int
current_zone.height = max(MIN_MAP_SIZE, (current_zone.height * map_size_coef)) as int
_tile_map.init(current_zone)
_tile_map_limits.init(current_zone)
ZoneService.current_zone_max_position = Vector2(current_zone.width * Utils.TILE_SIZE, current_zone.height * Utils.TILE_SIZE)
_current_wave_label.text = Text.text("WAVE", [str(RunData.current_wave)]).to_upper()
# _wave_timer.wait_time = 15
_wave_timer.wait_time = 1 if RunData.instant_waves else current_wave_data.wave_duration
_wave_timer.start()
_wave_timer_label.wave_timer = _wave_timer
var _error_wave_timer = _wave_timer.connect("tick_started", self, "on_tick_started")
_wave_manager.init(_wave_timer, current_wave_data)
on_bonus_gold_changed(RunData.bonus_gold)
init_camera()
set_level_label()
_ui_dim_screen.color.a = 0
DebugService.log_run_info()
RunData.reset_weapons_dmg_dealt()
RunData.reset_cache()
game_controller = $"/root/GameController"
health_tracker = HealthTracker.instance()
health_tracker.set_name("HealthTracker")
$UI.add_child(health_tracker)
health_tracker.init(game_controller.tracked_players)
var run_data = game_controller.tracked_players[game_controller.self_peer_id].run_data
var current_level = run_data.current_level
var current_xp = run_data.current_xp
var next_level_xp = RunData.get_xp_needed(current_level + 1)
_xp_bar.update_value(current_xp, next_level_xp)
var _error_gold_ui = RunData.connect("gold_changed", _ui_gold, "on_gold_changed")
var _error_upg_select = _upgrades_ui.connect("upgrade_selected", self, "on_upgrade_selected")
var _error_consumable_added = connect("consumable_to_process_added", _ui_consumables_to_process, "add_element")
var _error_take_button = _item_box_ui.connect("item_take_button_pressed", self, "on_item_box_take_button_pressed")
var _error_discard_button = _item_box_ui.connect("item_discard_button_pressed", self, "on_item_box_discard_button_pressed")
game_controller.send_client_started()
game_controller.send_client_started()
for player_id in game_controller.tracked_players:
game_controller.tracked_players[player_id].erase("player")
func _input(event:InputEvent)->void :
var is_right_stick_motion = event is InputEventJoypadMotion and (event.axis == JOY_AXIS_2 or event.axis == JOY_AXIS_3) and abs(event.axis_value) > 0.5
var is_mouse_press = event is InputEventMouseButton
if (is_mouse_press or is_right_stick_motion) and not _cleaning_up:
if ProgressData.settings.manual_aim_on_mouse_press and not ProgressData.settings.manual_aim:
ProgressData.update_mouse_cursor()
if is_mouse_press:
if event.pressed:
InputService.hide_mouse = false
else :
InputService.hide_mouse = true
else :
InputService.hide_mouse = false
if InputService.using_gamepad and not InputService.hide_mouse and not ProgressData.is_manual_aim():
InputService.hide_mouse = true
func add_timer(cooldown:int)->Timer:
var timer = Timer.new()
timer.wait_time = cooldown
add_child(timer)
timer.start()
return timer
func on_stat_updated(_stat_name:String, _value:int, _db_mod:float = 0.0)->void :
reload_stats()
func reload_stats()->void :
_player.update_player_stats()
LinkedStats.reset()
for weapon in _player.current_weapons:
if is_instance_valid(weapon):
weapon.init_stats(false)
_proj_on_death_stat_cache.clear()
func on_tick_started()->void :
_wave_timer_label.modulate = Color.red
func on_bonus_gold_changed(value:int)->void :
if value == 0:
_ui_bonus_gold.hide()
func init_camera()->void :
var max_pos = ZoneService.current_zone_max_position
var min_pos = ZoneService.current_zone_min_position
var zone_w = max_pos.x - min_pos.x
var zone_h = max_pos.y - min_pos.y
_camera.center_horizontal_pos = zone_w / 2
_camera.center_vertical_pos = zone_h / 2
if zone_w + EDGE_SIZE * 2 <= Utils.project_width:
_camera.center_horizontal = true
else :
_camera.limit_right = max_pos.x + EDGE_SIZE
_camera.limit_left = min_pos.x - EDGE_SIZE
if zone_h + EDGE_SIZE * 2 <= Utils.project_height:
_camera.center_vertical = true
else :
_camera.limit_bottom = max_pos.y + EDGE_SIZE
_camera.limit_top = min_pos.y - EDGE_SIZE * 2
func _on_player_died(_p_player:Player)->void :
_player_life_bar.hide()
if RunData.current_wave <= RunData.nb_of_waves:
_is_run_lost = true
else :
_is_run_won = true
clean_up_room(false, _is_run_lost, _is_run_won)
_end_wave_timer.start()
ProgressData.reset_run_state()
ChallengeService.complete_challenge("chal_rookie")
func on_levelled_up()->void :
SoundManager.play(level_up_sound, 0, 0, true)
var level = RunData.current_level
emit_signal("upgrade_to_process_added", ItemService.upgrade_to_process_icon, level)
_upgrades_to_process.push_back(level)
set_level_label()
RunData.add_stat("stat_max_hp", 1)
RunData.emit_signal("healing_effect", 1)
func set_level_label()->void :
_level_label.text = "LV." + str(RunData.current_level)
func set_life_label(current_health:int, max_health:int)->void :
_life_label.text = str(max(current_health, 0.0)) + " / " + str(max_health)
func connect_visual_effects(unit:Unit)->void :
var _error_effects = unit.connect("took_damage", _effects_manager, "_on_unit_took_damage")
var _error_floating_text = unit.connect("took_damage", _floating_text_manager, "_on_unit_took_damage")
func clean_up_room(is_last_wave:bool = false, is_run_lost:bool = false, is_run_won:bool = false)->void :
if is_run_lost:
DebugService.log_data("is_run_lost")
_wave_timer_label.is_run_lost = true
_wave_timer.stop()
_wave_timer.tick_timer.stop()
_end_wave_timer.wait_time = 2.5
SoundManager.play(Utils.get_rand_element(run_lost_sounds), - 5, 0, true)
_ui_dim_screen.dim()
MusicManager.tween( - 20)
elif is_run_won:
DebugService.log_data("is_run_won")
apply_run_won()
if RunData.is_endless_run:
if RunData.current_wave % 10 == 0 and RunData.current_wave >= 20:
RunData.init_elites_spawn(RunData.current_wave, 0.0)
DebugService.log_data("is_endless_run")
if RunData.current_wave >= 20:
var character_difficulty = ProgressData.get_character_difficulty_info(RunData.current_character.my_id, RunData.current_zone)
character_difficulty.max_endless_wave_beaten.set_info(
RunData.get_current_difficulty(),
RunData.current_wave,
RunData.current_run_accessibility_settings.health,
RunData.current_run_accessibility_settings.damage,
RunData.current_run_accessibility_settings.speed,
true
)
# TODO maybe replace
# SoundManager.play(Utils.get_rand_element(end_wave_sounds))
_cleaning_up = true
_effects_manager.clean_up_room()
_floating_text_manager.clean_up_room()
for entity in _entities_container.get_children():
if not is_instance_valid(entity):
continue
if entity.get("_boost_zone") and is_instance_valid(entity._boost_zone):
continue
if not entity is EntityBirth and not entity is Player:
entity.die()
if entity.has_node("MovementBehavior"):
var movemenent_node = entity.get_node("MovementBehavior")
entity.remove_child(movemenent_node)
movemenent_node = ClientMovementBehavior.new()
movemenent_node.set_name("MovementBehavior")
entity.add_child(movemenent_node)
_entities_container.remove_child(entity)
health_tracker.hide()
_wave_cleared_label.hide()
_wave_timer_label.hide()
_wave_manager.clean_up_room()
if is_instance_valid(_player):
var movemenent_node = _player.get_node("MovementBehavior")
_player.remove_child(movemenent_node)
_player.clean_up()
DebugService.log_data("start wave_cleared_label...")
_wave_cleared_label.start(is_last_wave, is_run_lost, is_run_won)
DebugService.log_data("wave_cleared_label started...")
func get_gold_bag_pos()->Vector2:
return get_viewport().get_canvas_transform().affine_inverse().xform(_ui_bonus_gold_pos.global_position)
func on_focus_lost()->void :
if _end_wave_timer_timedout:
if _item_box_ui.visible:
_item_box_ui.focus()
elif _upgrades_ui.visible:
_upgrades_ui.focus()
func on_upgrade_selected(upgrade_data:UpgradeData)->void :
game_controller.on_upgrade_selected(upgrade_data)
func on_item_box_take_button_pressed(item_data:ItemParentData)->void :
game_controller.on_item_box_take_button_pressed(item_data)
func on_item_box_discard_button_pressed(item_data:ItemParentData) -> void:
game_controller.discard_item_box(item_data)
func _on_PauseMenu_paused()->void :
reload_stats()
func _on_PauseMenu_unpaused()->void :
on_focus_lost()
func apply_run_won()->void :
_wave_timer.stop()
_wave_timer.tick_timer.stop()
_end_wave_timer.wait_time = 3
_ui_dim_screen.dim()
SoundManager.play(Utils.get_rand_element(run_won_sounds), - 5, 0, true)
var character_chal_name = "chal_" + RunData.current_character.name.to_lower().replace("character_", "")
ChallengeService.complete_challenge(character_chal_name)
var character_difficulty = ProgressData.get_character_difficulty_info(RunData.current_character.my_id, RunData.current_zone)
if character_difficulty.max_selectable_difficulty < RunData.get_current_difficulty() + 1 and RunData.get_current_difficulty() + 1 <= ProgressData.MAX_DIFFICULTY:
RunData.difficulty_unlocked = RunData.get_current_difficulty() + 1
ChallengeService.complete_challenge("chal_difficulty_" + str(RunData.get_current_difficulty()))
for char_diff in ProgressData.difficulties_unlocked:
for zone_difficulty_info in char_diff.zones_difficulty_info:
zone_difficulty_info.max_selectable_difficulty = clamp(RunData.get_current_difficulty() + 1, zone_difficulty_info.max_selectable_difficulty, ProgressData.MAX_DIFFICULTY)
character_difficulty.max_difficulty_beaten.set_info(
RunData.get_current_difficulty(),
RunData.current_wave,
RunData.current_run_accessibility_settings.health,
RunData.current_run_accessibility_settings.damage,
RunData.current_run_accessibility_settings.speed,
false
)
ProgressData.reset_run_state()
func is_last_wave()->bool:
var is_last_wave = RunData.current_wave == ZoneService.get_zone_data(RunData.current_zone).waves_data.size()
if RunData.is_endless_run:is_last_wave = false
return is_last_wave
func manage_harvesting()->void :
pass
func _on_UIBonusGold_mouse_entered()->void :
if _cleaning_up:
_info_popup.display(_ui_bonus_gold, Text.text("INFO_BONUS_GOLD", [str(RunData.bonus_gold)]))
func _on_UIBonusGold_mouse_exited()->void :
_info_popup.hide()
func _on_HarvestingTimer_timeout()->void :
if RunData.current_wave > RunData.nb_of_waves:
var val = ceil(Utils.get_stat("stat_harvesting") * (RunData.ENDLESS_HARVESTING_DECREASE / 100.0))
RunData.remove_stat("stat_harvesting", val)
else :
var val = ceil(Utils.get_stat("stat_harvesting") * (RunData.effects["harvesting_growth"] / 100.0))
var has_crown = false
var crown_value = 0
for item in RunData.items:
if item.my_id == "item_crown":
has_crown = true
crown_value = item.effects[0].value
break
if has_crown:
RunData.tracked_item_effects["item_crown"] += (Utils.get_stat("stat_harvesting") * (crown_value / 100.0)) as int
if val > 0:
RunData.add_stat("stat_harvesting", val)
func on_gold_changed(gold:int)->void :
if _update_stats_on_gold_changed:
if (gold / 30.0) as int != _last_gold_amount_used_to_reload_stats:
reload_stats()
_last_gold_amount_used_to_reload_stats = (gold / 30.0) as int
func _on_WaveTimer_timeout() -> void:
clean_up_room(false, false, false)
func _on_EndWaveTimer_timeout() -> void:
game_controller.send_complete_player_request()
yield(game_controller, "complete_player_update")
if _is_run_won:
apply_run_won()
RunData.run_won = true
var _error = get_tree().change_scene("res://ui/menus/run/end_run.tscn")
return
InputService.hide_mouse = true
var consumables = game_controller.tracked_players[game_controller.self_peer_id].consumables_to_process
if consumables.size() > 0:
for consumable in consumables:
var fixed_tier = - 1
if consumable.my_id == "consumable_legendary_item_box":
fixed_tier = Tier.LEGENDARY
var item_data = ItemService.process_item_box(RunData.current_wave, consumable, fixed_tier)
_item_box_ui.set_item_data(item_data)
yield (_item_box_ui, "item_box_processed")
game_controller.send_complete_player_request()
yield(game_controller, "complete_player_update")
_ui_consumables_to_process.remove_element(consumable)
game_controller.tracked_players[game_controller.self_peer_id].consumables_to_process = []
if _upgrades_to_process.size() > 0:
for upgrade_to_process in _upgrades_to_process:
_upgrades_ui.show_upgrade_options(upgrade_to_process)
yield (_upgrades_ui, "upgrade_selected")
game_controller.send_complete_player_request()
yield(game_controller, "complete_player_update")
_ui_upgrades_to_process.remove_element(upgrade_to_process)
var _error = get_tree().change_scene("res://mods-unpacked/Brogether-Brogether/ui/shop/multiplayer_shop.tscn")

View File

@@ -0,0 +1,460 @@
[gd_scene load_steps=81 format=2]
[ext_resource path="res://particles/heal_particles.tscn" type="PackedScene" id=1]
[ext_resource path="res://particles/directional_hit_particles.tscn" type="PackedScene" id=2]
[ext_resource path="res://visual_effects/floating_text/floating_text.tscn" type="PackedScene" id=3]
[ext_resource path="res://visual_effects/hit_effect/hit_effect.tscn" type="PackedScene" id=4]
[ext_resource path="res://particles/pickup_gold_particles.tscn" type="PackedScene" id=5]
[ext_resource path="res://particles/stats_boost_particles.tscn" type="PackedScene" id=6]
[ext_resource path="res://ui/hud/ui_upgrade_to_process_element.tscn" type="PackedScene" id=7]
[ext_resource path="res://particles/speed_removed_particles.tscn" type="PackedScene" id=8]
[ext_resource path="res://particles/hit_particles.tscn" type="PackedScene" id=9]
[ext_resource path="res://resources/themes/base_theme.tres" type="Theme" id=11]
[ext_resource path="res://weapons/melee/dagger/dagger_icon.png" type="Texture" id=12]
[ext_resource path="res://ui/hud/ui_progress_bar.tscn" type="PackedScene" id=13]
[ext_resource path="res://items/materials/harvesting_icon.png" type="Texture" id=14]
[ext_resource path="res://ui/hud/ui_wave_timer.gd" type="Script" id=15]
[ext_resource path="res://zones/zone_1/000_all/group_2.tres" type="Resource" id=16]
[ext_resource path="res://zones/zone_1/000_elites/group_1.tres" type="Resource" id=17]
[ext_resource path="res://zones/zone_1/000_hordes/group_1.tres" type="Resource" id=18]
[ext_resource path="res://zones/zone_1/000_hordes/group_3.tres" type="Resource" id=19]
[ext_resource path="res://zones/zone_1/000_all/group_1.tres" type="Resource" id=20]
[ext_resource path="res://zones/zone_1/000_hordes/group_2.tres" type="Resource" id=21]
[ext_resource path="res://zones/zone_1/000_extra/group_1.tres" type="Resource" id=22]
[ext_resource path="res://zones/zone_1/000_hordes/group_4.tres" type="Resource" id=23]
[ext_resource path="res://zones/zone_1/000_hordes/group_5.tres" type="Resource" id=24]
[ext_resource path="res://items/materials/alt_sounds/coin_bag_ring_gemstone_item_03.wav" type="AudioStream" id=28]
[ext_resource path="res://resources/sounds/harvest/cartoon_boing_jump_01.wav" type="AudioStream" id=29]
[ext_resource path="res://resources/sounds/Madness 10.mp3" type="AudioStream" id=30]
[ext_resource path="res://resources/sounds/Madness 9.mp3" type="AudioStream" id=31]
[ext_resource path="res://resources/sounds/Madness 7.mp3" type="AudioStream" id=32]
[ext_resource path="res://resources/sounds/Madness 3.mp3" type="AudioStream" id=33]
[ext_resource path="res://resources/sounds/harvest/cartoon_boing_jump_07.wav" type="AudioStream" id=34]
[ext_resource path="res://resources/sounds/harvest/cartoon_boing_jump_09.wav" type="AudioStream" id=35]
[ext_resource path="res://resources/sounds/harvest/cartoon_boing_jump_06.wav" type="AudioStream" id=36]
[ext_resource path="res://ui/hud/ui_consumable_to_process_list.gd" type="Script" id=37]
[ext_resource path="res://global/my_tile_map_limits.gd" type="Script" id=38]
[ext_resource path="res://zones/backgrounds/background_gradient.tres" type="Gradient" id=39]
[ext_resource path="res://resources/tiles/ground_tiles.tres" type="TileSet" id=40]
[ext_resource path="res://global/my_camera.gd" type="Script" id=41]
[ext_resource path="res://resources/sounds/Madness 1.mp3" type="AudioStream" id=42]
[ext_resource path="res://items/materials/alt_sounds/coin_bag_ring_gemstone_item_01.wav" type="AudioStream" id=43]
[ext_resource path="res://resources/sounds/level_up.wav" type="AudioStream" id=44]
[ext_resource path="res://resources/sounds/Madness 8.mp3" type="AudioStream" id=45]
[ext_resource path="res://items/materials/water_drop_drip_single_02.wav" type="AudioStream" id=46]
[ext_resource path="res://items/materials/alt_sounds/coin_bag_ring_gemstone_item_05.wav" type="AudioStream" id=47]
[ext_resource path="res://resources/sounds/Madness 6.mp3" type="AudioStream" id=48]
[ext_resource path="res://items/materials/alt_sounds/coin_bag_ring_gemstone_item_02.wav" type="AudioStream" id=49]
[ext_resource path="res://items/materials/water_drop_drip_single_04.wav" type="AudioStream" id=50]
[ext_resource path="res://ui/sounds/end_wave.wav" type="AudioStream" id=51]
[ext_resource path="res://items/materials/water_drop_drip_single_05.wav" type="AudioStream" id=52]
[ext_resource path="res://resources/sounds/Madness 4.mp3" type="AudioStream" id=53]
[ext_resource path="res://items/materials/water_drop_drip_single_01.wav" type="AudioStream" id=54]
[ext_resource path="res://ui/sounds/CGM3_Bubble_Button_01_4.wav" type="AudioStream" id=55]
[ext_resource path="res://resources/sounds/Fear 10.mp3" type="AudioStream" id=56]
[ext_resource path="res://resources/tiles/tiles_outline.png" type="Texture" id=57]
[ext_resource path="res://items/materials/alt_sounds/coin_bag_ring_gemstone_item_04.wav" type="AudioStream" id=58]
[ext_resource path="res://items/materials/water_drop_drip_single_03.wav" type="AudioStream" id=59]
[ext_resource path="res://ui/sounds/clock_tick_01.wav" type="AudioStream" id=60]
[ext_resource path="res://resources/sounds/Madness 2.mp3" type="AudioStream" id=61]
[ext_resource path="res://ui/sounds/CGM3_Bubble_Button_01_3.wav" type="AudioStream" id=62]
[ext_resource path="res://global/screenshaker.gd" type="Script" id=63]
[ext_resource path="res://mods-unpacked/Brogether-Brogether/client/client_main.gd" type="Script" id=64]
[ext_resource path="res://visual_effects/damage_vignette.gd" type="Script" id=65]
[ext_resource path="res://ui/hud/ui_dim_screen.gd" type="Script" id=67]
[ext_resource path="res://ui/hud/ui_upgrade_to_process_list.gd" type="Script" id=68]
[ext_resource path="res://global/effects_manager.gd" type="Script" id=69]
[ext_resource path="res://global/my_tile_map.gd" type="Script" id=70]
[ext_resource path="res://ui/hud/wave_timer.gd" type="Script" id=71]
[ext_resource path="res://zones/wave_manager.gd" type="Script" id=72]
[ext_resource path="res://visual_effects/floating_text/floating_text_manager.gd" type="Script" id=73]
[ext_resource path="res://ui/menus/ingame/wave_cleared_label.gd" type="Script" id=74]
[ext_resource path="res://ui/hud/ui_bonus_gold.tscn" type="PackedScene" id=75]
[ext_resource path="res://ui/hud/ui_item_list.tscn" type="PackedScene" id=76]
[ext_resource path="res://ui/hud/ui_gold.tscn" type="PackedScene" id=77]
[ext_resource path="res://ui/menus/ingame/pause_menu.tscn" type="PackedScene" id=78]
[ext_resource path="res://ui/menus/ingame/upgrades_ui.tscn" type="PackedScene" id=79]
[ext_resource path="res://ui/menus/ingame/challenge_completed_ui.tscn" type="PackedScene" id=80]
[ext_resource path="res://ui/menus/ingame/info_popup.tscn" type="PackedScene" id=81]
[ext_resource path="res://ui/menus/upgrades/item_box_ui.tscn" type="PackedScene" id=82]
[sub_resource type="GradientTexture" id=3]
gradient = ExtResource( 39 )
width = 1920
[sub_resource type="Shader" id=10]
code = "shader_type canvas_item;
uniform vec4 color: hint_color;
uniform float multiplier = 0.2;
uniform float softness = 0.2;
void fragment() {
float value = distance(UV, vec2(0.5));
COLOR = vec4(color.rgb, smoothstep(multiplier, softness, value));
}"
[sub_resource type="ShaderMaterial" id=11]
shader = SubResource( 10 )
shader_param/color = Color( 0, 0, 0, 1 )
shader_param/multiplier = 0.8
shader_param/softness = 0.8
[node name="ClientMain" type="Node"]
script = ExtResource( 64 )
gold_pickup_sounds = [ ExtResource( 54 ), ExtResource( 46 ), ExtResource( 59 ), ExtResource( 50 ), ExtResource( 52 ) ]
gold_alt_pickup_sounds = [ ExtResource( 43 ), ExtResource( 49 ), ExtResource( 28 ), ExtResource( 58 ), ExtResource( 47 ) ]
level_up_sound = ExtResource( 44 )
run_won_sounds = [ ExtResource( 56 ) ]
run_lost_sounds = [ ExtResource( 42 ), ExtResource( 61 ), ExtResource( 33 ), ExtResource( 53 ), ExtResource( 48 ), ExtResource( 32 ), ExtResource( 45 ), ExtResource( 31 ), ExtResource( 30 ) ]
end_wave_sounds = [ ExtResource( 51 ) ]
[node name="CanvasLayer" type="CanvasLayer" parent="."]
layer = -10
[node name="Background" type="TextureRect" parent="CanvasLayer"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 1920.0
margin_right = 1080.0
margin_bottom = 840.0
rect_rotation = 89.9906
texture = SubResource( 3 )
expand = true
[node name="WaveManager" type="Node2D" parent="."]
script = ExtResource( 72 )
extra_groups = [ ExtResource( 22 ) ]
groups_data_in_all_waves = [ ExtResource( 20 ), ExtResource( 16 ) ]
horde_groups = [ ExtResource( 18 ), ExtResource( 21 ), ExtResource( 19 ), ExtResource( 23 ), ExtResource( 24 ) ]
elite_group = ExtResource( 17 )
[node name="EffectsManager" type="Node2D" parent="."]
script = ExtResource( 69 )
heal_particles = ExtResource( 1 )
stats_boost_particles = ExtResource( 6 )
speed_removed_particles = ExtResource( 8 )
hit_particles = ExtResource( 9 )
directional_hit_particles = ExtResource( 2 )
hit_effect = ExtResource( 4 )
gold_pickup_particles = ExtResource( 5 )
[node name="FloatingTextManager" type="Node2D" parent="."]
script = ExtResource( 73 )
_floating_text = ExtResource( 3 )
gold_on_crit_kill_icon = ExtResource( 12 )
harvesting_icon = ExtResource( 14 )
stat_pos_sounds = [ ExtResource( 35 ) ]
stat_neg_sounds = [ ExtResource( 34 ) ]
harvest_pos_sounds = [ ExtResource( 29 ) ]
harvest_neg_sounds = [ ExtResource( 36 ) ]
[node name="Camera" type="Camera2D" parent="."]
current = true
script = ExtResource( 41 )
__meta__ = {
"_edit_lock_": true
}
[node name="Screenshaker" type="Node" parent="Camera"]
script = ExtResource( 63 )
[node name="TileMap" type="TileMap" parent="."]
tile_set = ExtResource( 40 )
cell_quadrant_size = 64
collision_layer = 128
collision_mask = 0
format = 1
tile_data = PoolIntArray( 7, 0, 65538, 65536, 0, 65538, 65537, 0, 65538 )
script = ExtResource( 70 )
[node name="Outline" type="NinePatchRect" parent="TileMap"]
modulate = Color( 0.470588, 0.403922, 0.345098, 1 )
show_behind_parent = true
margin_left = -64.0
margin_top = -64.0
margin_right = 640.0
margin_bottom = 392.0
texture = ExtResource( 57 )
patch_margin_left = 64
patch_margin_top = 64
patch_margin_right = 64
patch_margin_bottom = 64
axis_stretch_horizontal = 1
axis_stretch_vertical = 1
__meta__ = {
"_edit_lock_": true,
"_edit_use_anchors_": false
}
[node name="TileMapLimits" type="StaticBody2D" parent="."]
position = Vector2( -64, -64 )
collision_layer = 128
collision_mask = 0
script = ExtResource( 38 )
[node name="Items" type="Node2D" parent="."]
[node name="Consumables" type="Node2D" parent="."]
[node name="Births" type="Node2D" parent="."]
[node name="Entities" type="YSort" parent="."]
[node name="Projectiles" type="Node2D" parent="."]
[node name="PlayerLifeBarContainer" type="Node2D" parent="."]
[node name="PlayerLifeBar" parent="PlayerLifeBarContainer" instance=ExtResource( 13 )]
unique_name_in_owner = true
visible = false
margin_left = -32.0
margin_top = -72.0
margin_right = 32.0
margin_bottom = -56.0
nine_patch_stretch = true
progress_color = Color( 0.721569, 0, 0, 1 )
[node name="UI" type="CanvasLayer" parent="."]
[node name="DamageVignette" type="ColorRect" parent="UI"]
material = SubResource( 11 )
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 65 )
[node name="DimScreen" type="ColorRect" parent="UI"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 40.0
margin_right = 40.0
color = Color( 0, 0, 0, 0 )
script = ExtResource( 67 )
[node name="Tween" type="Tween" parent="UI/DimScreen"]
[node name="WaveClearedLabel" type="Label" parent="UI"]
anchor_left = 0.5
anchor_right = 0.5
margin_left = -958.0
margin_right = 960.0
margin_bottom = 400.0
theme = ExtResource( 11 )
text = "WAVE_COMPLETED"
align = 1
valign = 1
uppercase = true
script = ExtResource( 74 )
font_end_run = ExtResource( 26 )
character_appearing_sounds = [ ExtResource( 62 ), ExtResource( 55 ) ]
[node name="Timer" type="Timer" parent="UI/WaveClearedLabel"]
wait_time = 0.1
one_shot = true
[node name="ItemBoxUI" parent="UI" instance=ExtResource( 82 )]
visible = false
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 0.0
margin_top = 0.0
margin_right = 0.0
margin_bottom = 0.0
[node name="UpgradesUI" parent="UI" instance=ExtResource( 79 )]
visible = false
[node name="HUD" type="MarginContainer" parent="UI"]
anchor_right = 1.0
margin_bottom = 284.0
size_flags_horizontal = 0
size_flags_vertical = 0
custom_constants/margin_right = 24
custom_constants/margin_top = 24
custom_constants/margin_left = 24
custom_constants/margin_bottom = 24
[node name="LifeContainer" type="VBoxContainer" parent="UI/HUD"]
margin_left = 24.0
margin_top = 24.0
margin_right = 344.0
margin_bottom = 260.0
size_flags_horizontal = 0
size_flags_vertical = 0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="UILifeBar" parent="UI/HUD/LifeContainer" instance=ExtResource( 13 )]
margin_bottom = 48.0
size_flags_horizontal = 0
tint_progress = Color( 0.721569, 0, 0, 1 )
progress_color = Color( 0.721569, 0, 0, 1 )
[node name="MarginContainer" type="MarginContainer" parent="UI/HUD/LifeContainer/UILifeBar"]
anchor_right = 1.0
anchor_bottom = 1.0
custom_constants/margin_right = 12
custom_constants/margin_top = 7
custom_constants/margin_left = 12
custom_constants/margin_bottom = 7
__meta__ = {
"_edit_use_anchors_": false
}
[node name="LifeLabel" type="Label" parent="UI/HUD/LifeContainer/UILifeBar/MarginContainer"]
margin_left = 12.0
margin_top = 8.0
margin_right = 308.0
margin_bottom = 39.0
grow_horizontal = 0
text = "8 / 8"
align = 1
valign = 1
uppercase = true
__meta__ = {
"_edit_use_anchors_": false
}
[node name="UIXPBar" parent="UI/HUD/LifeContainer" instance=ExtResource( 13 )]
margin_top = 52.0
margin_bottom = 100.0
size_flags_horizontal = 0
value = 50.0
[node name="MarginContainer" type="MarginContainer" parent="UI/HUD/LifeContainer/UIXPBar"]
anchor_right = 1.0
anchor_bottom = 1.0
custom_constants/margin_right = 12
custom_constants/margin_top = 7
custom_constants/margin_left = 12
custom_constants/margin_bottom = 7
__meta__ = {
"_edit_use_anchors_": false
}
[node name="LevelLabel" type="Label" parent="UI/HUD/LifeContainer/UIXPBar/MarginContainer"]
margin_left = 12.0
margin_top = 8.0
margin_right = 308.0
margin_bottom = 39.0
grow_horizontal = 0
text = "LV. 1"
align = 2
valign = 1
uppercase = true
__meta__ = {
"_edit_use_anchors_": false
}
[node name="UIGold" parent="UI/HUD/LifeContainer" instance=ExtResource( 77 )]
anchor_left = 0.0
anchor_right = 0.0
margin_left = 0.0
margin_top = 104.0
margin_right = 320.0
margin_bottom = 168.0
size_flags_horizontal = 3
alignment = 0
[node name="UIBonusGold" parent="UI/HUD/LifeContainer" instance=ExtResource( 75 )]
margin_top = 172.0
margin_bottom = 236.0
size_flags_horizontal = 0
[node name="WaveContainer" type="VBoxContainer" parent="UI/HUD"]
margin_left = 873.0
margin_top = 24.0
margin_right = 1046.0
margin_bottom = 146.0
size_flags_horizontal = 4
size_flags_vertical = 0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="CurrentWaveLabel" type="Label" parent="UI/HUD/WaveContainer"]
margin_right = 173.0
margin_bottom = 45.0
theme = ExtResource( 11 )
text = "WAVE {0}"
align = 1
__meta__ = {
"_edit_use_anchors_": false
}
[node name="WaveTimerLabel" type="Label" parent="UI/HUD/WaveContainer"]
margin_top = 49.0
margin_right = 173.0
margin_bottom = 122.0
theme = ExtResource( 11 )
text = "60"
align = 1
script = ExtResource( 15 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ThingsToProcessContainer" type="VBoxContainer" parent="UI/HUD"]
margin_left = 1896.0
margin_top = 24.0
margin_right = 1896.0
margin_bottom = 36.0
size_flags_horizontal = 8
size_flags_vertical = 0
custom_constants/separation = 12
[node name="Upgrades" parent="UI/HUD/ThingsToProcessContainer" instance=ExtResource( 76 )]
modulate = Color( 1, 1, 1, 0.501961 )
script = ExtResource( 68 )
element_scene = ExtResource( 7 )
[node name="Consumables" parent="UI/HUD/ThingsToProcessContainer" instance=ExtResource( 76 )]
modulate = Color( 1, 1, 1, 0.501961 )
margin_top = 12.0
margin_bottom = 12.0
script = ExtResource( 37 )
[node name="PauseMenu" parent="UI" instance=ExtResource( 78 )]
pause_mode = 2
visible = false
[node name="ChallengeCompletedUI" parent="UI" instance=ExtResource( 80 )]
visible = false
[node name="InfoPopup" parent="UI" instance=ExtResource( 81 )]
visible = false
[node name="EndWaveTimer" type="Timer" parent="."]
wait_time = 0.75
one_shot = true
[node name="HarvestingTimer" type="Timer" parent="."]
wait_time = 0.66
one_shot = true
[node name="WaveTimer" type="Timer" parent="."]
one_shot = true
script = ExtResource( 71 )
tick_sounds = [ ExtResource( 60 ) ]
[node name="TickTimer" type="Timer" parent="WaveTimer"]
[node name="CanvasLayer2" type="CanvasLayer" parent="."]
[connection signal="timeout" from="UI/WaveClearedLabel/Timer" to="UI/WaveClearedLabel" method="_on_Timer_timeout"]
[connection signal="mouse_entered" from="UI/HUD/LifeContainer/UIBonusGold" to="." method="_on_UIBonusGold_mouse_entered"]
[connection signal="mouse_exited" from="UI/HUD/LifeContainer/UIBonusGold" to="." method="_on_UIBonusGold_mouse_exited"]
[connection signal="paused" from="UI/PauseMenu" to="." method="_on_PauseMenu_paused"]
[connection signal="unpaused" from="UI/PauseMenu" to="." method="_on_PauseMenu_unpaused"]
[connection signal="timeout" from="EndWaveTimer" to="." method="_on_EndWaveTimer_timeout"]
[connection signal="timeout" from="HarvestingTimer" to="." method="_on_HarvestingTimer_timeout"]
[connection signal="timeout" from="WaveTimer" to="." method="_on_WaveTimer_timeout"]
[connection signal="timeout" from="WaveTimer" to="WaveTimer" method="_on_WaveTimer_timeout"]
[connection signal="timeout" from="WaveTimer/TickTimer" to="WaveTimer" method="_on_TickTimer_timeout"]
[editable path="UI/HUD/LifeContainer/UILifeBar"]
[editable path="UI/HUD/LifeContainer/UIXPBar"]
[editable path="UI/ChallengeCompletedUI"]

View File

@@ -0,0 +1,11 @@
extends MovementBehavior
signal detected_player
func init(parent:Node)->Node:
var _init = .init(parent)
return self
func get_movement()->Vector2:
return Vector2.ZERO

View File

@@ -0,0 +1,4 @@
extends TargetBehavior
func update_target():
pass

View File

@@ -0,0 +1,21 @@
extends Node2D
onready var message_label = $Message
# Called when the node enters the scene tree for the first time.
func _ready():
var messages = []
messages.push_back("THESE PRETZELS ARE MAKING ME THIRSTY")
messages.push_back("support the dev -> get a better screen here")
messages.push_back("SERENITY NOW")
messages.push_back("(1/3) We must go forward not backward")
messages.push_back("(2/3) Upward not forward")
messages.push_back("(3/3) And always twirling, twirling, twirling towards freedom")
messages.push_back("So you're telling me there's a chance")
messages.push_back("Great Success!")
messages.push_back("Bueller ... Bueller ... Bueller")
messages.push_back("The dude abides")
message_label.text = Utils.get_rand_element(messages)

View File

@@ -0,0 +1,25 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://resources/themes/inventory_button_theme.tres" type="Theme" id=1]
[ext_resource path="res://mods-unpacked/Brogether-Brogether/client/waiting.gd" type="Script" id=2]
[node name="Node2D" type="Node2D"]
script = ExtResource( 2 )
[node name="Label" type="Label" parent="."]
margin_left = 2.0
margin_top = 6.0
margin_right = 1918.0
margin_bottom = 192.0
size_flags_horizontal = 3
theme = ExtResource( 1 )
text = "Please wait for the host to shop and resume the game"
[node name="Message" type="Label" parent="."]
margin_left = -1.0
margin_top = 285.0
margin_right = 1915.0
margin_bottom = 471.0
size_flags_horizontal = 3
theme = ExtResource( 1 )
text = "These Pretzels are making me Thirsty!"

View File

@@ -0,0 +1,3 @@
extends Node
var data = {}

View File

@@ -0,0 +1,17 @@
extends "res://entities/birth/entity_birth.gd"
var steam_connection
var brotatogether_options
var in_multiplayer_game = false
var network_id = 0
var is_host = false
func _ready():
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
in_multiplayer_game = brotatogether_options.in_multiplayer_game
if in_multiplayer_game:
is_host = steam_connection.is_host()
network_id = brotatogether_options.current_network_id
brotatogether_options.current_network_id = brotatogether_options.current_network_id + 1

View File

@@ -0,0 +1,18 @@
extends "res://entities/entity.gd"
var steam_connection
var brotatogether_options
var in_multiplayer_game = false
var network_id = 0
var is_host = false
func _ready():
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
in_multiplayer_game = brotatogether_options.in_multiplayer_game
if in_multiplayer_game:
is_host = steam_connection.is_host()
network_id = brotatogether_options.current_network_id
brotatogether_options.current_network_id = brotatogether_options.current_network_id + 1

View File

@@ -0,0 +1,12 @@
extends "res://entities/structures/turret/turret.gd"
func _on_AnimationPlayer_animation_finished(anim_name: String) -> void :
if self.in_multiplayer_game and not self.is_host:
return
._on_AnimationPlayer_animation_finished(anim_name)
func shoot() -> void :
if self.in_multiplayer_game and not self.is_host:
return
.shoot()

View File

@@ -0,0 +1,8 @@
extends "res://entities/units/movement_behaviors/player_movement_behavior.gd"
func get_movement()->Vector2:
if device >= 50:
return get_parent()._current_movement
return .get_movement()

View File

@@ -0,0 +1,123 @@
extends "res://entities/units/unit/unit.gd"
# THIS IS CURRENTLY A DUPE, hopefully that won't be a problem
enum EntityState {
ENTITY_STATE_NETWORK_ID,
ENTITY_STATE_X_POS,
ENTITY_STATE_Y_POS,
ENTITY_STATE_X_MOVE,
ENTITY_STATE_Y_MOVE,
ENTITY_STATE_CURRENT_HP,
ENTITY_STATE_MAX_HP,
ENTITY_STATE_SPRITE_SCALE,
# Player-specific entity state.
ENTITY_STATE_PLAYER_GOLD,
ENTITY_STATE_PLAYER_CURRENT_XP,
ENTITY_STATE_PLAYER_NEXT_LEVEL_XP,
ENTITY_STATE_PLAYER_NUM_UPGRADES,
ENTITY_STATE_PLAYER_WEAPONS,
ENTITY_STATE_PLAYER_LEVEL,
}
enum WeaponState {
WEAPON_STATE_X_POS,
WEAPON_STATE_Y_POS,
WEAPON_STATE_ROTATION,
WEAPON_STATE_SPRITE_ROTATION,
WEAPON_STATE_IS_SHOOTING,
}
func take_damage(value: int, args: TakeDamageArgs) -> Array:
if self.in_multiplayer_game and not self.is_host:
return []
return .take_damage(value, args)
func _on_Hurtbox_area_entered(hitbox: Area2D) -> void :
if self.in_multiplayer_game and not self.is_host:
return
._on_Hurtbox_area_entered(hitbox)
func flash()->void :
if self.in_multiplayer_game and self.is_host:
self.brotatogether_options.batched_unit_flashes[self.network_id] = true
.flash()
func die(args: = Entity.DieArgs.new())->void :
if self.in_multiplayer_game and self.is_host:
self.brotatogether_options.batched_enemy_deaths[self.network_id] = true
.die(args)
func update_client_enemy(unit_dict : Dictionary) -> void:
set_deferred("position", Vector2(unit_dict["X_POS"], unit_dict["Y_POS"]))
_current_movement.x = unit_dict["MOVE_X"]
_current_movement.y = unit_dict["MOVE_Y"]
var modulate_a = unit_dict["MODULATE_A"]
var modulate_r = unit_dict["MODULATE_R"]
var modulate_g = unit_dict["MODULATE_G"]
var modulate_b = unit_dict["MODULATE_B"]
sprite.self_modulate = Color8(modulate_r, modulate_g, modulate_b, modulate_a)
call_deferred("update_animation", _current_movement)
func update_external_player_position(player_dict : Dictionary) -> void:
self.position.x = player_dict[EntityState.ENTITY_STATE_X_POS]
self.position.y = player_dict[EntityState.ENTITY_STATE_Y_POS]
self.sprite.scale.x = player_dict[EntityState.ENTITY_STATE_SPRITE_SCALE]
self._current_movement.x = player_dict[EntityState.ENTITY_STATE_X_MOVE]
self._current_movement.y = player_dict[EntityState.ENTITY_STATE_Y_MOVE]
if not dead:
self.update_animation(_current_movement)
func update_client_player(player_dict : Dictionary, player_index : int) -> void:
var current_xp = player_dict[EntityState.ENTITY_STATE_PLAYER_CURRENT_XP]
var next_level_xp = player_dict[EntityState.ENTITY_STATE_PLAYER_NEXT_LEVEL_XP]
RunData.players_data[player_index].current_xp = current_xp
RunData.emit_signal("xp_added", current_xp, next_level_xp, player_index)
var current_hp = player_dict[EntityState.ENTITY_STATE_CURRENT_HP]
var max_hp = player_dict[EntityState.ENTITY_STATE_MAX_HP]
var should_send_hp_signal = false
if current_hp != self.current_stats.health:
should_send_hp_signal = true
self.current_stats.health = current_hp
if max_hp != self.max_stats.health:
should_send_hp_signal = true
self.max_stats.health = max_hp
if should_send_hp_signal:
self.emit_signal("health_updated", self, self.current_stats.health, self.max_stats.health)
var current_gold = player_dict[EntityState.ENTITY_STATE_PLAYER_GOLD]
var current_level = player_dict[EntityState.ENTITY_STATE_PLAYER_LEVEL]
RunData.players_data[player_index].gold = current_gold
RunData.players_data[player_index].current_level = current_level
RunData.emit_signal("gold_changed", current_gold, player_index)
var weapons_array = player_dict[EntityState.ENTITY_STATE_PLAYER_WEAPONS]
for weapon_index in weapons_array.size():
var weapon_dict = weapons_array[weapon_index]
var weapon = self.current_weapons[weapon_index]
weapon.sprite.position.x = weapon_dict[WeaponState.WEAPON_STATE_X_POS]
weapon.sprite.position.y = weapon_dict[WeaponState.WEAPON_STATE_Y_POS]
weapon.sprite.rotation = weapon_dict[WeaponState.WEAPON_STATE_SPRITE_ROTATION]
weapon.rotation = weapon_dict[WeaponState.WEAPON_STATE_ROTATION]
weapon._is_shooting = weapon_dict[WeaponState.WEAPON_STATE_IS_SHOOTING]
weapon._current_cooldown = 9999

View File

@@ -0,0 +1,39 @@
extends "res://global/effects_manager.gd"
var steam_connection
var brotatogether_options
var in_multiplayer_game = false
var is_host = false
func _ready():
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
in_multiplayer_game = brotatogether_options.in_multiplayer_game
if in_multiplayer_game:
is_host = steam_connection.is_host()
func play_hit_particles(effect_pos: Vector2, direction: Vector2, effect_scale: float) -> void:
.play_hit_particles(effect_pos, direction, effect_scale)
if in_multiplayer_game and self.is_host:
var hit_particle_dict = {}
hit_particle_dict["X_POS"] = effect_pos.x
hit_particle_dict["Y_POS"] = effect_pos.y
hit_particle_dict["X_DIR"] = direction.x
hit_particle_dict["Y_DIR"] = direction.y
hit_particle_dict["SCALE"] = effect_scale
brotatogether_options.batched_hit_particles.push_back(hit_particle_dict)
func play_hit_effect(effect_pos: Vector2, _direction: Vector2, effect_scale: float)->void :
.play_hit_effect(effect_pos, _direction, effect_scale)
if in_multiplayer_game and self.is_host:
var hit_effect_dict = {}
hit_effect_dict["X_POS"] = effect_pos.x
hit_effect_dict["Y_POS"] = effect_pos.y
hit_effect_dict["SCALE"] = effect_scale
brotatogether_options.batched_hit_effects.push_back(hit_effect_dict)

View File

@@ -0,0 +1,21 @@
extends "res://global/entity_spawner.gd"
var steam_connection
var brotatogether_options
func _ready():
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
func spawn(queue_from: Array, player_index: = - 1) -> void:
if not brotatogether_options.in_multiplayer_game:
.spawn(queue_from, player_index)
return
if not steam_connection.is_host():
queue_from.pop_back()
return
.spawn(queue_from, player_index)

View File

@@ -0,0 +1,18 @@
extends "res://items/global/item.gd"
var steam_connection
var brotatogether_options
var in_multiplayer_game = false
var network_id = 0
var is_host = false
func _ready():
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
in_multiplayer_game = brotatogether_options.in_multiplayer_game
if in_multiplayer_game:
is_host = steam_connection.is_host()
network_id = brotatogether_options.current_network_id
brotatogether_options.current_network_id = brotatogether_options.current_network_id + 1

File diff suppressed because it is too large Load Diff

View File

@@ -1,44 +0,0 @@
extends "res://main.gd"
var _brogether_snapshot_elapsed := 0.0
func _ready() -> void:
._ready()
var brogether = _get_brogether()
if brogether != null:
brogether.on_run_scene_ready(self)
func _physics_process(delta: float) -> void:
._physics_process(delta)
_brogether_snapshot_elapsed += delta
if _brogether_snapshot_elapsed < 0.25:
return
_brogether_snapshot_elapsed = 0.0
var brogether = _get_brogether()
if brogether != null:
brogether.on_run_snapshot_tick(_players)
func _on_EntitySpawner_players_spawned(players: Array) -> void:
._on_EntitySpawner_players_spawned(players)
var brogether = _get_brogether()
if brogether != null:
brogether.on_run_players_spawned(players)
func _exit_tree() -> void:
var brogether = _get_brogether()
if brogether != null:
brogether.on_run_scene_exiting()
func _get_brogether():
var root: Viewport = get_tree().get_root()
if root == null:
return null
var mod_loader: Node = root.get_node_or_null("ModLoader")
if mod_loader == null:
return null
return mod_loader.get_node_or_null("Brogether-Brogether")

View File

@@ -1,41 +0,0 @@
extends "res://ui/menus/pages/main_menu.gd"
var _brogether_button: Button = null
func setup_menu() -> void:
.setup_menu()
_add_brogether_button()
func _add_brogether_button() -> void:
if _brogether_button != null and is_instance_valid(_brogether_button):
return
if right_container == null:
return
_brogether_button = Button.new()
_brogether_button.name = "BrogetherButton"
_brogether_button.text = "BROGETHER"
_brogether_button.rect_min_size = mods_button.rect_min_size
_brogether_button.size_flags_horizontal = mods_button.size_flags_horizontal
_brogether_button.size_flags_vertical = mods_button.size_flags_vertical
_brogether_button.theme = mods_button.theme
_brogether_button.connect("pressed", self, "_on_BrogetherButton_pressed")
right_container.add_child(_brogether_button)
right_container.move_child(_brogether_button, right_container.get_child_count() - 1)
func _on_BrogetherButton_pressed() -> void:
var brogether = _get_brogether()
if brogether != null:
brogether.open_debug_panel()
func _get_brogether():
var root: Viewport = get_tree().get_root()
if root == null:
return null
var mod_loader: Node = root.get_node_or_null("ModLoader")
if mod_loader == null:
return null
return mod_loader.get_node_or_null("Brogether-Brogether")

View File

@@ -0,0 +1,18 @@
extends "res://projectiles/player_explosion.gd"
var is_ghost_explosion = false
func start_explosion() -> void:
var steam_connection = $"/root/SteamConnection"
var brotatogether_options = $"/root/BrotogetherOptions"
if brotatogether_options.in_multiplayer_game and not is_ghost_explosion:
if steam_connection.is_host():
var explosion_dict = {
"X_POS" : global_position.x,
"Y_POS" : global_position.y,
"SCALE" : scale.x,
}
brotatogether_options.batched_explosions.push_back(explosion_dict)
.start_explosion()
else:
.start_explosion()

View File

@@ -0,0 +1,8 @@
extends "res://singletons/coop_service.gd"
func is_player_using_gamepad(player_index) -> bool:
if get_remapped_player_device(player_index) >= 50:
return false
return .is_player_using_gamepad(player_index)

View File

@@ -0,0 +1,31 @@
extends "res://singletons/run_data.gd"
var steam_connection
var brotatogether_options
var is_multiplayer_lobby = false
func init_multiplayer() -> void:
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
steam_connection.leave_game_lobby()
func lock_player_shop_item(item_data: ItemParentData, wave_value: int, player_index: int)->void :
if brotatogether_options.in_multiplayer_game:
if steam_connection.is_host():
.lock_player_shop_item(item_data, wave_value, player_index)
steam_connection.shop_lock_item(item_data.my_id, wave_value)
else:
.lock_player_shop_item(item_data, wave_value, player_index)
func unlock_player_shop_item(item_data: ItemParentData, player_index: int)->void :
if brotatogether_options.in_multiplayer_game:
if steam_connection.is_host():
.unlock_player_shop_item(item_data, player_index)
steam_connection.shop_unlock_item(item_data.my_id)
else:
.unlock_player_shop_item(item_data, player_index)

View File

@@ -0,0 +1,22 @@
extends "res://singletons/sound_manager.gd"
var steam_connection
var brotatogether_options
var is_multiplayer_lobby = false
func init_multiplayer() -> void:
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
func play(sound: Resource, volume_mod: float = 0.0, pitch_rand: float = 0.0, always_play: bool = false)->void :
if steam_connection and brotatogether_options:
if brotatogether_options.in_multiplayer_game:
if steam_connection.is_host():
var sound_dict = {
"RESOURCE_PATH" : sound.resource_path,
}
brotatogether_options.batched_sounds.push_back(sound_dict)
.play(sound, volume_mod, pitch_rand, always_play)

View File

@@ -0,0 +1,24 @@
extends "res://singletons/sound_manager_2d.gd"
var steam_connection
var brotatogether_options
var is_multiplayer_lobby = false
func init_multiplayer() -> void:
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
func play(sound: Resource, pos: Vector2, volume_mod: float = 0.0, pitch_rand: float = 0.0, always_play: bool = false)->void :
if steam_connection and brotatogether_options:
if brotatogether_options.in_multiplayer_game:
if steam_connection.is_host():
var sound_dict = {
"RESOURCE_PATH" : sound.resource_path,
"X_POS" : pos.x,
"Y_POS" : pos.y
}
brotatogether_options.batched_2d_sounds.push_back(sound_dict)
.play(sound, pos, volume_mod, pitch_rand, always_play)

View File

@@ -0,0 +1,8 @@
extends "res://singletons/utils.gd"
func is_player_action_released(event: InputEvent, player_index: int, action: String)->bool:
if CoopService.get_remapped_player_device(player_index) >= 50:
return false
return .is_player_action_released(event, player_index, action)

View File

@@ -0,0 +1,46 @@
extends "res://ui/menus/global/focus_emulator.gd"
var global_focused_control
var steam_connection
var brotatogether_options
func ready() -> void:
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
var _err = get_viewport().connect("gui_focus_changed", self, "_on_focus_changed_multiplayer")
func _on_focus_changed_multiplayer(control:Control) -> void:
if control != null:
global_focused_control = control
# Have line edit eat inputs so that you can send messages
func _handle_input(event:InputEvent) -> bool:
if not brotatogether_options:
brotatogether_options = $"/root/BrotogetherOptions"
if not steam_connection:
steam_connection = $"/root/SteamConnection"
if get_tree().current_scene.name == "CoopShop" and brotatogether_options.in_multiplayer_game:
if event is InputEventKey:
if event.pressed:
var carousel = get_tree().current_scene._get_coop_player_container(steam_connection.get_my_index()).carousel
if event.scancode == KEY_R:
carousel._on_ArrowRight_pressed()
if event.scancode == KEY_L:
carousel._on_ArrowLeft_pressed()
if global_focused_control != null and global_focused_control is LineEdit:
return false
return ._handle_input(event)
func _get_focus_neighbour_for_event(event: InputEvent, target: Control)->GetFocusNeighbourForEventResult:
if _device >= 50:
return GetFocusNeighbourForEventResult.new()
return ._get_focus_neighbour_for_event(event,target)

View File

@@ -0,0 +1,54 @@
extends "res://ui/menus/ingame/coop_upgrades_ui_player_container.gd"
var steam_connection
var brotatogether_options
var in_multiplayer_game = false
func _ready():
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
in_multiplayer_game = brotatogether_options.in_multiplayer_game
func _on_RerollButton_pressed() -> void:
if in_multiplayer_game:
if steam_connection.is_host():
._on_RerollButton_pressed()
else:
steam_connection.send_main_scene_client_reroll_button_ressed()
else:
._on_RerollButton_pressed()
func _on_choose_button_pressed(upgrade: UpgradeData) -> void:
if in_multiplayer_game:
if steam_connection.is_host():
._on_choose_button_pressed(upgrade)
else:
var upgrade_dict = {
"UPGRADE_ID" : upgrade.my_id
}
steam_connection.send_main_scene_client_choose_upgrade_pressed(upgrade_dict)
else:
._on_choose_button_pressed(upgrade)
func _on_TakeButton_pressed():
if in_multiplayer_game:
if steam_connection.is_host():
._on_TakeButton_pressed()
else:
steam_connection.send_main_scene_client_take_button_pressed()
else:
._on_TakeButton_pressed()
func _on_DiscardButton_pressed():
if in_multiplayer_game:
if steam_connection.is_host():
._on_DiscardButton_pressed()
else:
steam_connection.send_main_scene_client_discard_button_pressed()
else:
._on_DiscardButton_pressed()

View File

@@ -0,0 +1,18 @@
extends "res://ui/menus/ingame/pause_menu.gd"
var steam_connection
var brotatogether_options
var is_multiplayer_lobby = false
# Called when the node enters the scene tree for the first time.
func _ready():
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
func on_game_lost_focus()->void :
if steam_connection.game_lobby_id > 0:
return
.on_game_lost_focus()

View File

@@ -0,0 +1,45 @@
extends "res://ui/menus/pages/main_menu.gd"
var multiplayer_button: Button
# Add a multiplayer button to the main menu
func _ready():
RunData.init_multiplayer()
SoundManager.init_multiplayer()
SoundManager2D.init_multiplayer()
var buttons_node = $"MarginContainer/VBoxContainer/HBoxContainer/ButtonsLeft"
# Duplicate a Button to get the styling
multiplayer_button = start_button.duplicate()
multiplayer_button.text = "Multiplayer"
multiplayer_button.name = "MultiplayerButton"
var _unused = multiplayer_button.connect("pressed", self, "_on_MultiplayerButton_pressed")
multiplayer_button.disconnect("pressed", self, "_on_StartButton_pressed")
buttons_node.add_child_below_node(buttons_node.get_children()[0], multiplayer_button)
buttons_node.move_child(multiplayer_button, 0)
remove_game_controller()
$"/root/BrotogetherOptions".joining_multiplayer_lobby = false
$"/root/BrotogetherOptions".is_solo_test = false
func init() -> void:
.init()
if continue_button.visible:
continue_button.focus_neighbour_top = multiplayer_button.get_path()
else:
start_button.focus_neighbour_top = multiplayer_button.get_path()
func _on_MultiplayerButton_pressed():
var _error = get_tree().change_scene("res://mods-unpacked/Brogether-Brogether/ui/multiplayer_menu.tscn")
func remove_game_controller():
if $"/root".has_node("GameController"):
var game_controller = $"/root/GameController"
$"/root".remove_child(game_controller)

View File

@@ -0,0 +1,342 @@
extends "res://ui/menus/run/character_selection.gd"
const ChatPanel = preload("res://mods-unpacked/Brogether-Brogether/ui/chat_panel.tscn")
const ChatMessage = preload("res://mods-unpacked/Brogether-Brogether/ui/chat/chat_message.tscn")
const UsernameLabel = preload("res://mods-unpacked/Brogether-Brogether/ui/username_label.tscn")
const MULTIPLAYER_CLIENT_PLAYER_TYPE = 10
var steam_connection
var brotatogether_options
var is_multiplayer_lobby = false
var global_chat_panel
var global_chat_messsages
var global_chat_input
var lobby_chat_panel
var lobby_chat_messsages
var lobby_chat_input
var panel_parent_container
var panels_array
var visible_panel_index : int = 0
# Mapped inventory item by item keys. Used to reset focus based on external
# calls.
var inventory_by_string_key : Dictionary
# Mapped Selections by item keys. Kept independent of itnentory items to
# avoid complications around randoms, locked items, etc.
var selections_by_string_key : Dictionary
var username_labels = []
var external_focus = false
func _ready():
steam_connection = $"/root/SteamConnection"
steam_connection.connect("global_chat_received", self, "_received_global_chat")
steam_connection.connect("game_lobby_chat_received", self, "_received_lobby_chat")
steam_connection.connect("player_focused_character", self, "_player_focused_character")
steam_connection.connect("player_selected_character", self, "_player_selected_character")
steam_connection.connect("character_lobby_update", self, "_lobby_characters_updated")
steam_connection.connect("request_character_lobby_update", self, "_character_lobby_update_requested")
steam_connection.connect("character_selection_complete", self, "_host_character_selection_complete")
# TODO gracefully add new players
steam_connection.connect("lobby_players_updated", self, "reload_scene")
brotatogether_options = $"/root/BrotogetherOptions"
is_multiplayer_lobby = brotatogether_options.joining_multiplayer_lobby
if is_multiplayer_lobby:
RunData.is_coop_run = true
_coop_button.init()
_coop_button.hide()
var run_options_top_panel = _run_options_panel.get_node("MarginContainer/VBoxContainer/HBoxContainer")
run_options_top_panel.remove_child(run_options_top_panel.get_node("Icon"))
# Add left and right buttons to the heading
var run_options_left_arrow : TextureButton = TextureButton.new()
run_options_left_arrow.texture_normal = load("res://ui/menus/global/arrow_left.png")
run_options_top_panel.add_child(run_options_left_arrow)
run_options_top_panel.move_child(run_options_left_arrow, 0)
var _err = run_options_left_arrow.connect("pressed", self, "_on_pressed_left_menu_arrow")
var run_options_right_arrow : TextureButton = TextureButton.new()
run_options_right_arrow.texture_normal = load("res://ui/menus/global/arrow_right.png")
run_options_top_panel.add_child(run_options_right_arrow)
_err = run_options_right_arrow.connect("pressed", self, "_on_pressed_right_menu_arrow")
panel_parent_container = _run_options_panel.get_parent()
var username_label_player_1 : Label = UsernameLabel.instance()
$"MarginContainer/VBoxContainer/DescriptionContainer/HBoxContainer/Panel1/vboxContainer".add_child(username_label_player_1)
$"MarginContainer/VBoxContainer/DescriptionContainer/HBoxContainer/Panel1/vboxContainer".move_child(username_label_player_1, 0)
username_labels.push_back(username_label_player_1)
var username_label_player_2 : Label = UsernameLabel.instance()
$"MarginContainer/VBoxContainer/DescriptionContainer/HBoxContainer/Panel2/vboxContainer".add_child(username_label_player_2)
$"MarginContainer/VBoxContainer/DescriptionContainer/HBoxContainer/Panel2/vboxContainer".move_child(username_label_player_2, 0)
username_labels.push_back(username_label_player_2)
var username_label_player_3 : Label = UsernameLabel.instance()
$"MarginContainer/VBoxContainer/DescriptionContainer/HBoxContainer/Panel3/vboxContainer".add_child(username_label_player_3)
$"MarginContainer/VBoxContainer/DescriptionContainer/HBoxContainer/Panel3/vboxContainer".move_child(username_label_player_3, 0)
username_labels.push_back(username_label_player_3)
var username_label_player_4 : Label = UsernameLabel.instance()
$"MarginContainer/VBoxContainer/DescriptionContainer/HBoxContainer/Panel4/vboxContainer".add_child(username_label_player_4)
$"MarginContainer/VBoxContainer/DescriptionContainer/HBoxContainer/Panel4/vboxContainer".move_child(username_label_player_4, 0)
username_labels.push_back(username_label_player_4)
for index in steam_connection.lobby_member_names.size():
username_labels[index].text = steam_connection.lobby_member_names[index]
global_chat_panel = ChatPanel.instance()
global_chat_panel.get_node("MarginContainer/VBoxContainer/HBoxContainer/ChatTitle").text = "Global Chat"
var global_chat_left_arrow = global_chat_panel.get_node("MarginContainer/VBoxContainer/HBoxContainer/LeftArrow")
_err = global_chat_left_arrow.connect("pressed", self, "_on_pressed_left_menu_arrow")
var global_chat_right_arrow = global_chat_panel.get_node("MarginContainer/VBoxContainer/HBoxContainer/RightArrow")
_err = global_chat_right_arrow.connect("pressed", self, "_on_pressed_right_menu_arrow")
panel_parent_container.add_child_below_node(_run_options_panel, global_chat_panel)
global_chat_messsages = global_chat_panel.get_node("MarginContainer/VBoxContainer/ScrollContainer/ChatMessages")
global_chat_input = global_chat_panel.get_node("MarginContainer/VBoxContainer/ChatInput")
global_chat_input.connect("text_entered", self, "_on_global_chat_text_entered")
lobby_chat_panel = ChatPanel.instance()
lobby_chat_panel.get_node("MarginContainer/VBoxContainer/HBoxContainer/ChatTitle").text = "Lobby Chat"
var lobby_chat_left_arrow = lobby_chat_panel.get_node("MarginContainer/VBoxContainer/HBoxContainer/LeftArrow")
_err = lobby_chat_left_arrow.connect("pressed", self, "_on_pressed_left_menu_arrow")
var lobby_chat_right_arrow = lobby_chat_panel.get_node("MarginContainer/VBoxContainer/HBoxContainer/RightArrow")
_err = lobby_chat_right_arrow.connect("pressed", self, "_on_pressed_right_menu_arrow")
panel_parent_container.add_child_below_node(_run_options_panel, lobby_chat_panel)
lobby_chat_messsages = lobby_chat_panel.get_node("MarginContainer/VBoxContainer/ScrollContainer/ChatMessages")
lobby_chat_input = lobby_chat_panel.get_node("MarginContainer/VBoxContainer/ChatInput")
lobby_chat_input.connect("text_entered", self, "_on_lobby_chat_text_entered")
panels_array = [_run_options_panel, global_chat_panel, lobby_chat_panel]
update_panel_visiblility()
for member_index in steam_connection.lobby_members.size():
var member_id = steam_connection.lobby_members[member_index]
if member_id == steam_connection.steam_id:
CoopService._add_player(0, MULTIPLAYER_CLIENT_PLAYER_TYPE)
else:
CoopService._add_player(100 + member_index, MULTIPLAYER_CLIENT_PLAYER_TYPE)
for character_data in _get_all_possible_elements(0):
selections_by_string_key[character_item_to_string(character_data)] = character_data
# Find the random element in the inventory
for character_data in _get_inventories()[0].get_children():
if character_data.is_random:
inventory_by_string_key[character_item_to_string(character_data)] = character_data
selections_by_string_key[character_item_to_string(character_data)] = character_data
else:
inventory_by_string_key[character_item_to_string(character_data.item)] = character_data
for panel in _get_panels():
panel._small(true)
func _on_pressed_left_menu_arrow() -> void:
visible_panel_index = (visible_panel_index - 1 + panels_array.size()) % panels_array.size()
update_panel_visiblility()
func _on_pressed_right_menu_arrow() -> void:
visible_panel_index = (visible_panel_index + 1) % panels_array.size()
update_panel_visiblility()
func update_panel_visiblility() -> void:
for index in panels_array.size():
if index == visible_panel_index:
panels_array[index].visible = true
else:
panels_array[index].visible = false
func _received_global_chat(user, message) -> void:
var new_message_node = ChatMessage.instance()
new_message_node.message = message
new_message_node.username = user
global_chat_messsages.add_child(new_message_node)
func _received_lobby_chat(user, message) -> void:
var new_message_node = ChatMessage.instance()
new_message_node.message = message
new_message_node.username = user
lobby_chat_messsages.add_child(new_message_node)
func _on_global_chat_text_entered(message):
steam_connection.send_global_chat_message(message)
global_chat_input.clear()
func _on_lobby_chat_text_entered(message):
steam_connection.send_lobby_chat_message(message)
lobby_chat_input.clear()
func _on_element_focused(element:InventoryElement, inventory_player_index:int, _displayPanelData: bool = true) -> void:
._on_element_focused(element, inventory_player_index)
if is_multiplayer_lobby:
var element_string = ""
if element.item != null:
element_string = element.item.name
elif element.is_random:
element_string = "RANDOM"
if not external_focus:
steam_connection.character_focused(element_string)
else:
external_focus = false
func character_item_to_string(item : Resource) -> String:
if item == null:
return "RANDOM"
return item.name
func _player_focused_character(player_index : int , character : String) -> void:
var selected_item = null
var focused_element = null
if selections_by_string_key.has(character):
selected_item = selections_by_string_key[character]
if inventory_by_string_key.has(character):
focused_element = inventory_by_string_key[character]
_clear_selected_element(player_index)
_player_characters[player_index] = selected_item
if player_index < RunData.get_player_count() and player_index != steam_connection.get_my_index():
if focused_element != null:
if Utils.get_focus_emulator(player_index).focused_control != focused_element:
external_focus = true
Utils.get_focus_emulator(player_index).focused_control = focused_element
var panel = _get_panels()[player_index]
if panel.visible:
if selected_item != null:
if character == "RANDOM":
panel.set_custom_data("RANDOM", selected_item.get_inventory_icon())
else:
panel.set_data(selected_item, player_index)
func _player_selected_character(player_index : int) -> void:
_set_selected_element(player_index)
func reload_scene() -> void:
$"/root/BrotogetherOptions".joining_multiplayer_lobby = true
var _error = get_tree().change_scene(MenuData.character_selection_scene)
func _lobby_characters_updated(player_characters : Array, has_player_selected : Array) -> void:
for player_index in RunData.get_player_count():
if player_characters[player_index] != null:
_player_focused_character(player_index, player_characters[player_index])
var all_selected = true
for player_index in RunData.get_player_count():
if has_player_selected[player_index]:
_set_selected_element(player_index)
else:
all_selected = false
_clear_selected_element(player_index)
if all_selected and steam_connection.is_host():
_selections_completed_timer.start()
func _set_selected_element(player_index:int) -> void:
if _has_player_selected[player_index]:
return
._set_selected_element(player_index)
if brotatogether_options.is_solo_test:
var all_selected = true
for i in RunData.get_player_count():
if not _has_player_selected[i]:
all_selected = false
break
if all_selected:
CoopService.listening_for_inputs = false
_selections_completed_timer.start()
if steam_connection.get_lobby_index_for_player(steam_connection.steam_id) == player_index:
steam_connection.character_selected()
func _character_lobby_update_requested() -> void:
var currently_focused_characters = []
for panel in _get_panels():
var selected_item = panel.item_data
if selected_item == null:
currently_focused_characters.push_back("RANDOM")
else:
currently_focused_characters.push_back(character_item_to_string(selected_item))
steam_connection.send_character_lobby_update(currently_focused_characters, _has_player_selected)
func _on_selections_completed() -> void:
if is_multiplayer_lobby:
if not steam_connection.is_host():
return
else:
._on_selections_completed()
return
var currently_focused_characters = []
# Sad dupe of inner logic to make things simpler
for player_index in RunData.get_player_count():
var chosen_item = _get_panels()[player_index]
var character = _player_characters[player_index]
if chosen_item.item_data == null:
var available_elements: = []
for element in ItemService.characters:
if not element.is_locked:
available_elements.push_back(element)
character = Utils.get_rand_element(available_elements)
_player_characters[player_index] = character
RunData.add_character(character, player_index)
currently_focused_characters.push_back(character_item_to_string(character))
steam_connection.send_character_selection_completed(RunData.some_player_has_weapon_slots(), currently_focused_characters)
if RunData.some_player_has_weapon_slots():
_change_scene(MenuData.weapon_selection_scene)
else:
_change_scene(MenuData.difficulty_selection_scene)
func _host_character_selection_complete(some_player_has_weapon_slots : bool, selected_characters : Array) -> void:
# Sad dupe of inner logic to make things simpler
for player_index in selected_characters.size():
if selected_characters[player_index] != null:
_player_focused_character(player_index, selected_characters[player_index])
for player_index in RunData.get_player_count():
var character = _player_characters[player_index]
RunData.add_character(character, player_index)
if some_player_has_weapon_slots:
_change_scene(MenuData.weapon_selection_scene)
else:
_change_scene(MenuData.difficulty_selection_scene)

View File

@@ -0,0 +1,73 @@
extends "res://ui/menus/run/difficulty_selection/difficulty_selection.gd"
var steam_connection
var brotatogether_options
var is_multiplayer_lobby = false
var inventory_by_string_key : Dictionary
var selection_by_string_key : Dictionary
func _ready():
steam_connection = $"/root/SteamConnection"
steam_connection.connect("difficulty_focused", self, "_difficulty_focused")
steam_connection.connect("difficulty_selected", self, "_difficulty_selected")
brotatogether_options = $"/root/BrotogetherOptions"
is_multiplayer_lobby = brotatogether_options.joining_multiplayer_lobby
if is_multiplayer_lobby:
brotatogether_options.joining_multiplayer_lobby = false
brotatogether_options.in_multiplayer_game = true
for difficulty_data in ItemService.difficulties:
selection_by_string_key[difficulty_data.value] = difficulty_data
for inventory_item in _get_inventories()[0].get_children():
inventory_by_string_key[inventory_item.item.value] = inventory_item
func _on_element_focused(element:InventoryElement, inventory_player_index:int, _displayPanelData: bool = true) -> void:
# Disregard difficulty updates from clients
if is_multiplayer_lobby:
if not steam_connection.is_host():
return
._on_element_focused(element, inventory_player_index)
if is_multiplayer_lobby:
steam_connection.difficulty_focused()
func _on_element_pressed(element: InventoryElement, _inventory_player_index: int) -> void:
# Disregard difficulty updates from clients
if is_multiplayer_lobby:
if not steam_connection.is_host():
return
._on_element_pressed(element, _inventory_player_index)
if is_multiplayer_lobby:
steam_connection.difficulty_pressed()
func _difficulty_selected(_difficutly : int) -> void:
var _error = get_tree().change_scene(MenuData.game_scene)
func _difficulty_focused(difficutly : int) -> void:
# Hosts don't respect update calls
if is_multiplayer_lobby:
if steam_connection.is_host():
return
var selected_item = null
if selection_by_string_key.has(difficutly):
selected_item = selection_by_string_key[difficutly]
Utils.get_focus_emulator(0).focused_control = inventory_by_string_key[difficutly]
if selected_item != null:
_get_panels()[0].visible = true
_get_panels()[0].set_data(selected_item, 0)

View File

@@ -0,0 +1,172 @@
extends "res://ui/menus/run/weapon_selection.gd"
var steam_connection
var brotatogether_options
var is_multiplayer_lobby = false
var selections_by_string_key : Dictionary
var external_focus = false
var inventory_maps : Array
func _ready():
steam_connection = $"/root/SteamConnection"
steam_connection.connect("player_focused_weapon", self, "_player_focused_weapon")
steam_connection.connect("player_selected_weapon", self, "_player_selected_weapon")
steam_connection.connect("weapon_lobby_update", self, "_lobby_weapons_updated")
steam_connection.connect("weapon_selection_completed", self, "_weapon_selection_completed")
brotatogether_options = $"/root/BrotogetherOptions"
is_multiplayer_lobby = brotatogether_options.joining_multiplayer_lobby
if is_multiplayer_lobby:
for weapon_data in ItemService.weapons:
selections_by_string_key[weapon_item_to_string(weapon_data)] = weapon_data
inventory_maps = []
for player_index in RunData.get_player_count():
var inventory_map = {}
for inventory_item in _get_inventories()[player_index].get_children():
inventory_map[weapon_item_to_string(inventory_item.item)] = inventory_item
inventory_maps.push_back(inventory_map)
func weapon_item_to_string(item : Resource) -> String:
if item == null:
return "RANDOM"
return item.my_id
func _lobby_weapons_updated(player_weapons : Array, has_player_selected : Array) -> void:
for player_index in player_weapons.size():
if player_weapons[player_index] != null:
_player_focused_weapon(player_index, player_weapons[player_index])
var all_selected = true
for player_index in RunData.get_player_count():
if has_player_selected[player_index]:
_set_selected_element(player_index)
else:
all_selected = false
_clear_selected_element(player_index)
if all_selected:
_selections_completed_timer.start()
func _on_element_focused(element:InventoryElement, inventory_player_index:int, _displayPanelData: bool = true) -> void:
._on_element_focused(element, inventory_player_index)
if is_multiplayer_lobby:
var element_string = ""
if element.item != null:
element_string = element.item.my_id
elif element.is_random:
element_string = "RANDOM"
if not external_focus:
steam_connection.weapon_focused(element_string)
else:
external_focus = false
func _player_focused_weapon(player_index : int , weapon : String) -> void:
var selected_item = null
if selections_by_string_key.has(weapon):
selected_item = selections_by_string_key[weapon]
var focused_weapon = null
if inventory_maps[player_index].has(weapon):
focused_weapon = inventory_maps[player_index][weapon]
if focused_weapon != null:
if Utils.get_focus_emulator(player_index).focused_control != focused_weapon:
external_focus = true
Utils.get_focus_emulator(player_index).focused_control = focused_weapon
_player_weapons[player_index] = selected_item
_clear_selected_element(player_index)
var panel = _get_panels()[player_index]
if panel.visible:
# TODO handle randoms
if selected_item != null:
panel.set_data(selected_item, player_index)
func _player_selected_weapon(player_index : int) -> void:
_set_selected_element(player_index)
func _set_selected_element(p_player_index:int) -> void:
if _has_player_selected[p_player_index]:
return
# This happens during the base _ready for characters with no weapon slots.
if not steam_connection:
_has_player_selected[p_player_index] = true
return
._set_selected_element(p_player_index)
if brotatogether_options.is_solo_test:
var all_selected = true
for i in RunData.get_player_count():
if not _has_player_selected[i]:
all_selected = false
break
if all_selected:
CoopService.listening_for_inputs = false
_selections_completed_timer.start()
if steam_connection.get_lobby_index_for_player(steam_connection.steam_id) == p_player_index:
steam_connection.weapon_selected()
func _on_selections_completed() -> void:
if is_multiplayer_lobby:
if not steam_connection.is_host():
return
else:
._on_selections_completed()
return
for player_index in RunData.get_player_count():
var chosen_item = _get_panels()[player_index]
var weapon = _player_weapons[player_index]
if chosen_item.item_data == null or weapon == null:
var available_elements: = []
for element in displayed_elements[player_index]:
if not element.is_locked:
available_elements.push_back(element)
weapon = Utils.get_rand_element(available_elements)
_player_weapons[player_index] = weapon
if weapon:
var _weapon = RunData.add_weapon(weapon, player_index, true)
RunData.add_starting_items_and_weapons()
var all_selected_weapons = []
for player_index in RunData.get_player_count():
var selected_weapons = []
for owned_weapon in RunData.players_data[player_index].weapons:
selected_weapons.push_back(weapon_item_to_string(owned_weapon))
all_selected_weapons.push_back(selected_weapons)
steam_connection.send_weapon_selection_completed(all_selected_weapons)
_change_scene(MenuData.difficulty_selection_scene)
func _weapon_selection_completed(selected_weapons : Array) -> void:
for player_index in RunData.get_player_count():
RunData.players_data[player_index].weapons.clear()
for weapon_index in selected_weapons[player_index].size():
RunData.players_data[player_index].weapons.push_back(selections_by_string_key[selected_weapons[player_index][weapon_index]])
_change_scene(MenuData.difficulty_selection_scene)

View File

@@ -0,0 +1,643 @@
extends "res://ui/menus/shop/coop_shop.gd"
var steam_connection
var brotatogether_options
var in_multiplayer_game = false
# If true, the steam logic will be skipped to avoid duplicate rpc chains.
var is_self_call = false
var player_in_scene = [true, false, false, false]
var waiting_to_start_shop = false
var focusing_reroll_button = false
var focusing_go_button = false
# Force clients to update their focus, this syncs up focus after inventory
# changes (buys, discards, etc) and prevents the client focus from disappearing
var pending_force_focus = []
func _ready():
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
in_multiplayer_game = brotatogether_options.in_multiplayer_game
if in_multiplayer_game:
waiting_to_start_shop = true
steam_connection.connect("client_status_received", self, "_client_status_received")
steam_connection.connect("shop_lobby_update", self, "_update_shop")
steam_connection.connect("client_shop_focus_updated", self, "_client_shop_focus_updated")
steam_connection.connect("client_shop_go_button_pressed", self, "_client_shop_go_button_pressed")
steam_connection.connect("client_shop_discard_weapon", self, "_client_shop_discard_weapon")
steam_connection.connect("client_shop_combine_weapon", self, "_client_shop_combine_weapon")
steam_connection.connect("client_shop_buy_item", self, "_client_shop_buy_item")
steam_connection.connect("client_shop_reroll", self, "_client_shop_reroll")
steam_connection.connect("client_shop_lock_item", self, "_client_shop_lock_item")
steam_connection.connect("client_shop_unlock_item", self, "_client_shop_unlock_item")
steam_connection.connect("client_shop_focus_inventory_element", self, "_client_focused_inventory_element")
steam_connection.connect("client_shop_requested", self, "send_shop_state")
steam_connection.connect("close_popup", self, "_remote_close_client_popup")
steam_connection.connect("receive_leave_shop", self, "_receive_leave_shop")
if steam_connection.is_host():
steam_connection.send_host_entered_shop()
func _process(_delta):
if in_multiplayer_game:
if waiting_to_start_shop:
if steam_connection.is_host():
var all_players_entered = true
for player_index in RunData.get_player_count():
if not player_in_scene[player_index]:
all_players_entered = false
break
if all_players_entered:
waiting_to_start_shop = false
# Initial shop state, all players can break focus
send_shop_state([0,1,2,3])
_check_for_focus_change()
func _client_status_received(client_data : Dictionary, player_index : int) -> void:
if waiting_to_start_shop:
if client_data["CURRENT_SCENE"] == get_tree().current_scene.name:
player_in_scene[player_index] = true
func _on_element_focused(element: InventoryElement, player_index: int)->void :
._on_element_focused(element,player_index)
if in_multiplayer_game:
if is_self_call:
is_self_call = false
else:
var focus_string = _dictionary_for_focus_inventory_element(element, player_index)
steam_connection.shop_focus_inventory_element(focus_string)
func _client_shop_focus_updated(shop_item_string : String, player_index : int) -> void:
is_self_call = true
var shop_item : ShopItem = _shop_item_for_string(shop_item_string, player_index)
if shop_item:
Utils.get_focus_emulator(player_index).focused_control = shop_item._button
func _on_shop_item_focused(shop_item: ShopItem, player_index : int) -> void:
if in_multiplayer_game:
var is_me = player_index == steam_connection.get_my_index()
if waiting_to_start_shop and not is_me:
return
._on_shop_item_focused(shop_item, player_index)
if is_self_call:
is_self_call = false
elif is_me or steam_connection.is_host():
var players_to_force = pending_force_focus.duplicate()
pending_force_focus.clear()
steam_connection.shop_item_focused(_string_for_shop_item(shop_item), players_to_force)
else:
._on_shop_item_focused(shop_item, player_index)
func _on_item_discard_button_pressed(weapon_data: WeaponData, player_index: int)->void :
if in_multiplayer_game:
if steam_connection.is_host():
._on_item_discard_button_pressed(weapon_data, player_index)
pending_force_focus.push_back(player_index)
steam_connection.shop_weapon_discard(_string_for_weapon(weapon_data), player_index)
else:
._on_item_discard_button_pressed(weapon_data, player_index)
func _client_shop_discard_weapon(weapon_string : String, player_index : int) -> void:
_on_item_discard_button_pressed(_weapon_for_string(weapon_string, player_index), player_index)
steam_connection.request_close_client_shop_popup(player_index)
func _remote_close_client_popup() -> void:
_get_coop_player_container(steam_connection.get_my_index()).item_popup.cancel()
func _on_GoButton_pressed(player_index: int) -> void:
if in_multiplayer_game:
if steam_connection.is_host():
_on_go_button_pressed_brotatogether(player_index)
steam_connection.shop_go_button_pressed(not _player_pressed_go_button[player_index])
else:
._on_GoButton_pressed(player_index)
func _on_go_button_pressed_brotatogether(player_index: int)->void :
if get_tree().paused:
return
if _player_pressed_go_button[player_index]:
_clear_go_button_pressed(player_index)
return
_player_pressed_go_button[player_index] = true
var checkmark = _get_checkmark(player_index)
if checkmark != null:
checkmark.show()
for other_player_index in RunData.get_player_count():
if not _player_pressed_go_button[other_player_index]:
return
RunData.current_wave += 1
steam_connection.leave_shop()
var _error = get_tree().change_scene(MenuData.game_scene)
func _receive_leave_shop() -> void:
RunData.current_wave += 1
var _error = get_tree().change_scene(MenuData.game_scene)
func _clear_go_button_pressed(player_index: int) -> void :
if in_multiplayer_game:
if steam_connection.is_host():
._clear_go_button_pressed(player_index)
steam_connection.shop_go_button_pressed(false)
else:
._clear_go_button_pressed(player_index)
func _client_shop_go_button_pressed(latest_go_state : bool, player_index : int) -> void:
# Make the go function change the state to the new state by setting to the
# opposite
_player_pressed_go_button[player_index] = not latest_go_state
_on_GoButton_pressed(player_index)
func on_shop_item_bought(shop_item: ShopItem, player_index: int) -> void:
if in_multiplayer_game:
if steam_connection.is_host():
.on_shop_item_bought(shop_item, player_index)
pending_force_focus.push_back(player_index)
steam_connection.shop_buy_item(_string_for_shop_item(shop_item), player_index)
else:
.on_shop_item_bought(shop_item, player_index)
func _client_shop_buy_item(item_string : String, player_index : int) -> void:
_get_shop_items_container(player_index).on_shop_item_buy_button_pressed(_shop_item_for_string(item_string, player_index))
func _on_item_combine_button_pressed(weapon_data: WeaponData, player_index: int)->void :
if in_multiplayer_game:
if steam_connection.is_host():
pending_force_focus.push_back(player_index)
._on_item_combine_button_pressed(weapon_data, player_index)
steam_connection.shop_combine_weapon(_string_for_weapon(weapon_data), false, player_index)
else:
._on_item_combine_button_pressed(weapon_data, player_index)
func _client_shop_combine_weapon(weapon_string : String, _is_upgrade: bool, player_index : int) -> void:
_on_item_combine_button_pressed(_weapon_for_string(weapon_string, player_index), player_index)
steam_connection.request_close_client_shop_popup(player_index)
func _client_shop_lock_item(item_string : String, _wave_value: int, player_index: int) -> void:
_shop_item_for_string(item_string, player_index).change_lock_status(true)
func _client_shop_unlock_item(item_string : String, player_index : int) -> void:
_shop_item_for_string(item_string, player_index).change_lock_status(false)
func send_shop_state(changed_shop_player_indeces : Array = []) -> void:
var result_dict : Dictionary = {}
var abyssal_dlc = ProgressData.get_dlc_data("abyssal_terrors")
var players_array = []
var player_count: int = RunData.get_player_count()
for player_index in player_count:
var player_data = RunData.players_data[player_index]
var player_dict = {}
var shop_items = []
for item in _shop_items[player_index]:
var item_dict : Dictionary = {}
item_dict["ID"] = item[0].my_id
item_dict["WAVE_VALUE"] = item[1]
if abyssal_dlc:
if item[0].is_cursed:
item_dict["CURSED"] = true
shop_items.push_back(item_dict)
player_dict["SHOP_ITEMS"] = shop_items
player_dict["EFFECTS"] = player_data._serialize_effects(player_data.effects)
var active_sets = {}
for set in player_data.active_sets:
active_sets[set] = player_data.active_sets[set]
player_dict["ACTIVE_SETS"] = active_sets
var locked_items : Array = []
for item in RunData.locked_shop_items[player_index]:
var item_dict : Dictionary = {}
item_dict["ID"] = item[0].my_id
item_dict["WAVE_VALUE"] = item[1]
locked_items.push_back(item_dict)
player_dict["LOCKED_ITEMS"] = locked_items
var weapons_array = []
for weapon in RunData.get_player_weapons(player_index):
weapons_array.push_back(_dictionary_for_weapon(weapon))
player_dict["WEAPONS"] = weapons_array
player_dict["GO_PRESSED"] = _player_pressed_go_button[player_index]
var gear_container : PlayerGearContainer = _get_gear_container(player_index)
var items_array = []
for element in gear_container.items_container._elements.get_children():
items_array.push_back(_dictionary_for_inventory_item(element))
# print_debug("shop container has ", element.item.my_id)
player_dict["ITEMS"] = items_array
player_dict["GOLD"] = RunData.get_player_gold(player_index)
player_dict["FOCUS"] = _focus_dictionary_for_player(player_index)
player_dict["REROLL_PRICE"] = _reroll_price[player_index]
player_dict["REROLL_DICSOUNT"] = _reroll_discount[player_index]
player_dict["HAS_BONUS_FREE_REROLL"] = _has_bonus_free_reroll[player_index]
players_array.push_back(player_dict)
result_dict["PLAYERS"] = players_array
result_dict["CHANGED_PLAYERS"] = changed_shop_player_indeces
# print_debug("sending shop state with dict ", result_dict)
steam_connection.send_shop_update(result_dict)
func _update_shop(shop_dictionary : Dictionary) -> void:
if not shop_dictionary.has("PLAYERS"):
print("WARNING - received shop update without PLAYERS element, ignoring; data:", shop_dictionary)
return
if steam_connection.is_host():
print("WARNING - host shouldn't be updating for remote, returning; data:", shop_dictionary)
return
# print_debug("updating shop for dictionary - ", shop_dictionary)
for player_index in shop_dictionary["PLAYERS"].size():
var player_dict = shop_dictionary["PLAYERS"][player_index]
var is_me = steam_connection.get_my_index() == player_index
var can_update = not is_me or shop_dictionary["CHANGED_PLAYERS"].has(player_index)
var go_pressed = player_dict["GO_PRESSED"]
_player_pressed_go_button[player_index] = go_pressed
var checkmark = _get_checkmark(player_index)
if checkmark != null:
if go_pressed:
checkmark.show()
else:
checkmark.hide()
if not can_update:
# print_debug("skipping update for player ", player_index)
continue
Utils.get_focus_emulator(player_index)._clear_focused_control()
_shop_items[player_index].clear()
Utils.reset_stat_cache(player_index)
var player_data = RunData.players_data[player_index]
var reserilaized_effects = {}
for key in player_dict["EFFECTS"]:
reserilaized_effects[str(key)] = player_dict["EFFECTS"][key]
player_data.effects = player_data._deserialize_effects(reserilaized_effects, {})
player_data.active_sets.clear()
var active_sets_dict = player_dict["ACTIVE_SETS"]
for active_set in player_dict["ACTIVE_SETS"]:
player_data.active_sets[active_set] = active_sets_dict[active_set]
for shop_item_dict in player_dict["SHOP_ITEMS"]:
_shop_items[player_index].push_back(_shop_item_for_dictionary(shop_item_dict))
_get_shop_items_container(player_index).set_shop_items(_shop_items[player_index])
var player_gear_container = _get_gear_container(player_index)
RunData.players_data[player_index].weapons.clear()
for weapon in player_dict["WEAPONS"]:
RunData.players_data[player_index].weapons.push_back(_weapon_for_dictionary(weapon))
player_gear_container.set_weapons_data(RunData.players_data[player_index].weapons)
RunData.players_data[player_index].items.clear()
for item in player_dict["ITEMS"]:
for _i in item["NUMBER"]:
RunData.players_data[player_index].items.push_back(_inventory_item_for_dictionary(item))
player_gear_container.set_items_data(RunData.players_data[player_index].items)
if player_dict.has("FOCUS"):
_set_client_focus_for_player(player_dict["FOCUS"], player_index)
else:
print_debug("WARN - missing focus for player ", player_index, " ", player_dict)
_reroll_price[player_index] = player_dict["REROLL_PRICE"]
_reroll_discount[player_index] = player_dict["REROLL_DICSOUNT"]
_has_bonus_free_reroll[player_index] = player_dict["HAS_BONUS_FREE_REROLL"]
set_reroll_button_price(player_index)
var locked_items : Array = player_dict["LOCKED_ITEMS"]
for item in _get_shop_items_container(player_index).get_children():
if not item is ShopItem:
continue
var is_locked = false
for locked_item in locked_items:
if locked_item["ID"] == item.item_data.my_id:
is_locked = true
break
if is_locked:
item.lock_visually()
else:
item.unlock_visually()
RunData.players_data[player_index].gold = player_dict["GOLD"]
_get_gold_label(player_index).update_value(RunData.players_data[player_index].gold)
waiting_to_start_shop = false
func _dictionary_for_inventory_item(inventory_element : InventoryElement) -> Dictionary:
var item_data : ItemData = inventory_element.item
var item_dict = {
"ID" : item_data.my_id,
"NUMBER" : inventory_element.current_number,
}
var dlc = ProgressData.get_dlc_data("abyssal_terrors")
if dlc:
if item_data.is_cursed:
item_dict["CURSED"] = true
return item_dict
func _client_focused_inventory_element(data : Dictionary, player_index : int) -> void:
var focused_element = _focus_inventory_item_for_dictionary(data, player_index)
is_self_call = true
Utils.get_focus_emulator(player_index).focused_control = focused_element
func _dictionary_for_focus_inventory_element(element: InventoryElement, player_index : int) -> Dictionary:
var gear_container : PlayerGearContainer = _get_gear_container(player_index)
var weapons : Array = gear_container.weapons_container._elements.get_children()
for weapon_index in weapons.size():
if element == weapons[weapon_index]:
return {
"TYPE" : "WEAPON",
"INDEX" : weapon_index
}
var items : Array = gear_container.items_container._elements.get_children()
for item_index in items.size():
if element == items[item_index]:
return {
"TYPE" : "ITEM",
"INDEX" : item_index
}
return {}
func _focus_inventory_item_for_dictionary(item_dict : Dictionary, player_index : int) -> Control:
var gear_container : PlayerGearContainer = _get_gear_container(player_index)
var focus_type = item_dict["TYPE"]
if focus_type == "WEAPON":
var weapon_index = item_dict["INDEX"]
if weapon_index < gear_container.weapons_container._elements.get_children().size():
return gear_container.weapons_container._elements.get_children()[weapon_index]
elif focus_type == "ITEM":
return gear_container.items_container._elements.get_children()[item_dict["INDEX"]]
elif focus_type == "GO":
return _get_go_button(player_index)
elif focus_type == "REROLL":
return _get_reroll_button(player_index)
elif focus_type == "SHOP_ITEM":
var shop_item : ShopItem = _shop_item_for_string(item_dict["ID"], player_index)
if shop_item:
return shop_item._button
else:
print("ERR - Focusing inventgory element of unknown type ", item_dict)
print("ERR - failed to found focus for dict ", item_dict, " ", player_index)
return null
func _inventory_item_for_dictionary(item_dict : Dictionary) -> ItemData:
var query_id = item_dict["ID"]
var result
for item in ItemService.items:
if item.my_id == query_id:
result = item.duplicate()
for character in ItemService.characters:
if character.my_id == query_id:
result = character.duplicate()
var dlc = ProgressData.get_dlc_data("abyssal_terrors")
if dlc:
if item_dict.has("CURSED") and item_dict["CURSED"]:
result.is_cursed = true
return result
func _dictionary_for_shop_item(shop_item : Array) -> Dictionary:
return {
"ID" : shop_item[0].my_id,
"WAVE_VALUE": shop_item[1],
}
func _shop_item_for_dictionary(shop_item_dict : Dictionary) -> Array:
var item_element
var query_id = shop_item_dict["ID"]
for item in ItemService.items:
if item.my_id == query_id:
item_element = item.duplicate()
for weapon in ItemService.weapons:
if weapon.my_id == query_id:
item_element = weapon.duplicate()
var abyssal_dlc = ProgressData.get_dlc_data("abyssal_terrors")
if abyssal_dlc:
if shop_item_dict.has("CURSED") and shop_item_dict["CURSED"]:
item_element.is_cursed = true
return [item_element, shop_item_dict["WAVE_VALUE"]]
func _dictionary_for_weapon(weapon_data : WeaponData) -> Dictionary:
var weapon_dict = {
"ID" : weapon_data.my_id
}
var dlc = ProgressData.get_dlc_data("abyssal_terrors")
if dlc:
if weapon_data.is_cursed:
weapon_dict["CURSED"] = true
return weapon_dict
func _weapon_for_dictionary(weapon_dict : Dictionary) -> WeaponData:
var query_id = weapon_dict["ID"]
var result
for weapon in ItemService.weapons:
if weapon.my_id == query_id:
result = weapon.duplicate()
var dlc = ProgressData.get_dlc_data("abyssal_terrors")
if dlc and result:
if weapon_dict.has("CURSED") and weapon_dict["CURSED"]:
result.is_cursed = true
return result
func _string_for_weapon(weapon_data : WeaponData) -> String:
return weapon_data.my_id
func _weapon_for_string(weapon_string : String, player_index : int) -> WeaponData:
for element in _get_gear_container(player_index).weapons_container._elements.get_children():
if _string_for_weapon(element.item) == weapon_string:
return element.item
return null
func _string_for_shop_item(shop_item : ShopItem) -> String:
return shop_item.item_data.my_id
func _shop_item_for_string(shop_item_string : String, player_index : int) -> ShopItem:
for item in _get_shop_items_container(player_index).get_children():
if not item is ShopItem:
continue
if item.item_data.my_id == shop_item_string:
return item
return null
func fill_shop_items(player_locked_items: Array, player_index: int, just_entered_shop: bool = false) -> void:
if $"/root/BrotogetherOptions".in_multiplayer_game:
if $"/root/SteamConnection".is_host():
.fill_shop_items(player_locked_items, player_index, just_entered_shop)
else:
.fill_shop_items(player_locked_items, player_index, just_entered_shop)
func _focus_dictionary_for_player(player_index : int) -> Dictionary:
var focused_control : Control = Utils.get_focus_emulator(player_index).focused_control
for shop_item in _get_shop_items_container(player_index).get_children():
if not shop_item is ShopItem:
continue
if shop_item._button == focused_control:
return {
"TYPE" : "SHOP_ITEM",
"ID" : _string_for_shop_item(shop_item)
}
var gear_container : PlayerGearContainer = _get_gear_container(player_index)
var weapons : Array = gear_container.weapons_container._elements.get_children()
for weapon_index in weapons.size():
if focused_control == weapons[weapon_index]:
return {
"TYPE" : "WEAPON",
"INDEX" : weapon_index
}
var items : Array = gear_container.items_container._elements.get_children()
for item_index in items.size():
if focused_control == items[item_index]:
return {
"TYPE" : "ITEM",
"INDEX" : item_index
}
if focused_control == _get_go_button(player_index):
return {
"TYPE" : "GO",
}
if focused_control == _get_reroll_button(player_index):
return {
"TYPE" : "REROLL",
}
return {}
func _set_client_focus_for_player(focus_dict : Dictionary, player_index : int) -> void:
if not focus_dict.has("TYPE"):
print("ERR - unkown client focus ", player_index, " ", focus_dict)
return
var focus_control = _focus_inventory_item_for_dictionary(focus_dict, player_index)
if focus_control != null and is_instance_valid(focus_control):
var focus_type : String = focus_dict["TYPE"]
if focus_type in ["SHOP_ITEM", "ITEM", "WEAPON"]:
is_self_call = true
Utils.get_focus_emulator(player_index).focused_control = focus_control
else:
print("ERR - Invalid focus dict", focus_dict, " ", player_index)
# Process time check for focusing reroll or go buttons which don't have
# exisiting connected functions
func _check_for_focus_change() -> void:
var player_index : int = steam_connection.get_my_index()
var focused_control = Utils.get_focus_emulator(player_index).focused_control
if focused_control == _get_reroll_button(player_index):
if not focusing_reroll_button and not waiting_to_start_shop:
steam_connection.shop_focus_inventory_element(_focus_dictionary_for_player(player_index))
focusing_reroll_button = true
else:
focusing_reroll_button = false
if focused_control == _get_go_button(player_index):
if not focusing_go_button and not waiting_to_start_shop:
steam_connection.shop_focus_inventory_element(_focus_dictionary_for_player(player_index))
focusing_go_button = true
else:
focusing_go_button = false
func _client_shop_reroll(player_index : int) -> void:
_on_RerollButton_pressed(player_index)
func _on_RerollButton_pressed(player_index: int)->void :
if in_multiplayer_game:
if steam_connection.is_host():
._on_RerollButton_pressed(player_index)
steam_connection.shop_reroll(player_index)
else:
._on_RerollButton_pressed( player_index)

View File

@@ -0,0 +1,19 @@
extends "res://ui/menus/shop/player_gear_container.gd"
var steam_connection
var brotatogether_options
var in_multiplayer_game = false
func _ready():
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
in_multiplayer_game = brotatogether_options.in_multiplayer_game
func set_items_data(items: Array)->void :
if in_multiplayer_game:
items_container.set_data("ITEMS", Category.ITEM, items, false, true)
else:
.set_items_data(items)

View File

@@ -0,0 +1,3 @@
extends "res://ui/menus/shop/shop.gd"
# Empty extension to help some extension chaos

View File

@@ -0,0 +1,28 @@
extends "res://ui/menus/shop/shop_item.gd"
var steam_connection
var brotatogether_options
var in_multiplayer_game = false
func _ready():
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
in_multiplayer_game = brotatogether_options.in_multiplayer_game
func deactivate()->void :
if in_multiplayer_game:
if steam_connection.is_host():
.deactivate()
return
else:
modulate = Color(1, 1, 1, 0)
_button.disable()
_lock_icon.hide()
locked = false
active = false
emit_signal("shop_item_deactivated", self)
else:
.deactivate()

View File

@@ -0,0 +1,33 @@
extends "res://visual_effects/floating_text/floating_text_manager.gd"
var steam_connection
var brotatogether_options
var in_multiplayer_game = false
var is_host = false
func _ready():
steam_connection = $"/root/SteamConnection"
brotatogether_options = $"/root/BrotogetherOptions"
in_multiplayer_game = brotatogether_options.in_multiplayer_game
if in_multiplayer_game:
is_host = steam_connection.is_host()
func display(value: String, text_pos: Vector2, color: Color = Color.white, icon: Resource = null, p_duration: float = duration, always_display: bool = false, p_direction: Vector2 = direction, need_translate: bool = true, icon_scale: Vector2 = Vector2(0.5, 0.5))->void :
.display(value, text_pos, color, icon, p_duration, always_display, p_direction, need_translate, icon_scale)
if in_multiplayer_game and self.is_host:
var text_dict = {}
text_dict["X_POS"] = text_pos.x
text_dict["Y_POS"] = text_pos.y
text_dict["R_COLOR"] = color.r8
text_dict["G_COLOR"] = color.g8
text_dict["B_COLOR"] = color.b8
text_dict["A_COLOR"] = color.a8
text_dict["VALUE"] = value
text_dict["DURATION"] = p_duration
# brotatogether_options.batched_floating_text.push_back(text_dict)

View File

@@ -1,7 +1,7 @@
{
"name": "Brogether",
"namespace": "Brogether",
"version_number": "0.1.0",
"version_number": "0.1.6",
"description": "Experimental 2-player online multiplayer foundation for Brotato using Steam lobbies and P2P packets.",
"website_url": "https://git.mischlabs.de/MrDiderot/Brogether",
"dependencies": [],

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
extends Node
# Connection interface
func send_start_game(_game_info:Dictionary) -> void:
pass
func send_display_floating_text(_text_info:Dictionary) -> void:
pass
func send_display_hit_effect(_effect_info:Dictionary) -> void:
pass
func send_enemy_death(_enemy_id:int) -> void:
pass
func send_end_wave() -> void:
pass
func send_flash_enemy(_enemy_id:int) -> void:
pass
func send_flash_neutral(_neutral_id:int) -> void:
pass
func send_client_position(_client_position: Dictionary) -> void:
pass

View File

@@ -0,0 +1,3 @@
extends Node
var network_id

View File

@@ -0,0 +1,117 @@
extends "res://mods-unpacked/Brogether-Brogether/networking/connection.gd"
var parent
func _ready():
var _error = get_tree().connect("network_peer_connected", self, "_player_connected")
_error = get_tree().connect("connected_to_server", self, "_connected_ok")
_error = get_tree().connect("connection_failed", self, "_connected_fail")
_error = get_tree().connect("server_disconnected", self, "_server_disconnected")
func _player_connected(_id):
rpc("register_player")
func _connected_ok():
var current_scene_name = get_tree().get_current_scene().get_name()
if current_scene_name == "MultiplayerMenu":
$"/root/MultiplayerMenu/HBoxContainer/InfoBox/Label".text = "connected"
parent.self_peer_id = get_tree().get_network_unique_id()
func _server_disconnected():
pass # Server kicked us; show error and abort.
func _connected_fail():
pass # Could not even connect to server; abort.
remotesync func register_player():
var id = get_tree().get_rpc_sender_id()
if parent.is_host and not parent.tracked_players.has(id):
parent.tracked_players[id] = {}
func send_state(game_state:Dictionary) -> void:
rpc("update_game_state", game_state)
remote func update_game_state(game_state:Dictionary) -> void:
parent.update_game_state(game_state)
func send_start_game(game_info:Dictionary) -> void:
rpc("start_game", game_info)
remote func start_game(game_info: Dictionary) -> void:
parent.start_game(game_info)
func send_display_floating_text(text_info:Dictionary) -> void:
rpc("display_floating_text", text_info)
remote func display_floating_text(text_info:Dictionary) -> void:
parent.display_floating_text(text_info)
func send_display_hit_effect(effect_info: Dictionary) -> void:
rpc("display_hit_effect", effect_info)
remote func display_hit_effect(effect_info: Dictionary) -> void:
parent.display_hit_effect(effect_info)
func send_enemy_death(enemy_id:int) -> void:
rpc("enemy_death", enemy_id)
remote func enemy_death(enemy_id:int) -> void:
parent.enemy_death(enemy_id)
func send_end_wave() -> void:
rpc("end_wave")
remote func end_wave() -> void:
parent.end_wave()
func send_more_enemies() -> void:
rpc("receive_more_enemies")
remote func receive_more_enemies() -> void:
parent.received_more_enemies(get_tree().get_rpc_sender_id())
func send_bought_item(shop_item:Resource) -> void:
rpc("receive_bought_item", shop_item.get_path())
remote func receive_bought_item(resource_path: String) -> void:
var sender_id = get_tree().get_rpc_sender_id()
var resource = load(resource_path)
parent.receive_bought_item(resource, sender_id)
func send_flash_enemy(enemy_id:int) -> void:
rpc("flash_enemy", enemy_id)
remote func flash_enemy(enemy_id:int) -> void:
parent.flash_enemy(enemy_id)
func send_flash_neutral(neutral_id:int) -> void:
rpc("flash_neutral", neutral_id)
remote func flash_neutral(neutral_id:int) -> void:
parent.flash_neutral(neutral_id)
func send_client_position(client_position:Dictionary) -> void:
rpc("update_client_position", client_position)
remote func update_client_position(client_position:Dictionary) -> void:
parent.update_client_position(client_position)
func send_ready(is_ready:bool) -> void:
rpc("ready", is_ready)
remote func ready(is_ready:bool) -> void:
var sender_id = get_tree().get_rpc_sender_id()
parent.update_ready_state(sender_id, is_ready)
func send_tracked_players(tracked_players:Dictionary) -> void:
rpc("update_tracked_players", tracked_players)
remote func update_tracked_players(tracked_players:Dictionary) -> void:
parent.update_tracked_players(tracked_players)
func send_health_update(current_health:int, max_health:int) -> void:
rpc("health_update", current_health, max_health)
remote func health_update(current_health:int, max_health:int) -> void:
var sender_id = get_tree().get_rpc_sender_id()
parent.receive_health_update(current_health, max_health, sender_id)

View File

@@ -0,0 +1,946 @@
extends Node
# here they'll be keyed by steam user ids
var tracked_players = {}
var connection
# tracked player info to be used in the multiplayer lobby
var lobby_data = {}
# The id of this player, in direct ip connections this will be 1 for the host.
# in steam connections this will be a steam id against which a username can
# be queried
var self_peer_id
# True iff the user hosted the lobby
var is_host = false
var is_source_of_truth = true
var game_mode = GameMode.VERSUS
enum GameMode {
VERSUS,
COOP,
}
# A counter user to assign ids for game components
var id_count = 0
var GameStateController = load("res://mods-unpacked/Brogether-Brogether/networking/game_state_controller.gd")
const toggle_scene = preload("res://mods-unpacked/Brogether-Brogether/ui/toggle.tscn")
const button_scene = preload("res://mods-unpacked/Brogether-Brogether/ui/button.tscn")
const explosion_scene = preload("res://projectiles/explosion.tscn")
const MOD_NAME = "Brogether-Brogether"
const CONFIG_FILENAME = "user://Brogether-Brogether-options.cfg"
const CONFIG_SECTION = "latest-mp-lobby"
var current_scene_name = ""
var run_updates = false
var disable_pause = false
var back_to_lobby = false
var all_players_ready = true
var batched_deaths = []
var batched_enemy_damage = []
var batched_flash_enemy = []
var batched_floating_text = []
var batched_hit_effects = []
var ready_toggle
var extra_enemies_next_wave = {}
var effects_next_wave = {}
var game_state_controller
var waiting_for_client_starts = false
signal complete_player_update
signal lobby_info_updated
signal danger_selected(danger_level)
signal character_selected(character_item_data)
signal weapon_selected(weapon_data)
func _init():
game_state_controller = GameStateController.new()
game_state_controller.parent = self
add_child(game_state_controller)
func _ready():
init_lobby_info()
func _process(_delta):
var scene_name = get_tree().get_current_scene().get_name()
# TODO: NOT THIS
if scene_name != current_scene_name:
if scene_name == "Shop":
enter_async_shop()
current_scene_name = scene_name
func init_client_starts_wait() -> void:
waiting_for_client_starts = true
for player in tracked_players:
if not player != self_peer_id:
tracked_players[player].in_game = false
receive_client_start(self_peer_id)
func init_lobby_info() -> void:
lobby_data = {}
lobby_data["players"] = {}
lobby_data["game_mode"] = 0
lobby_data["copy_host"] = false
lobby_data["first_death_loss"] = false
lobby_data["shared_gold"] = false
lobby_data["material_count"] = 1
lobby_data["enemy_count"] = 1
lobby_data["enemy_hp"] = 1
lobby_data["enemy_damage"] = 1
lobby_data["enemy_speed"] = 1
load_config()
func save_config() -> void:
var config := ConfigFile.new()
for key in lobby_data:
if key == "players":
continue
config.set_value(CONFIG_SECTION, key, lobby_data[key])
var _unused_save_result = config.save(CONFIG_FILENAME)
func load_config() -> void:
var config := ConfigFile.new()
var err = config.load(CONFIG_FILENAME)
if err != OK:
return
for key in config.get_section_keys(CONFIG_SECTION):
lobby_data[key] = config.get_value(CONFIG_SECTION, key)
func send_client_started() -> void:
connection.send_client_started()
func receive_client_start(player_id) -> void:
if not is_host:
return
if not waiting_for_client_starts:
return
tracked_players[player_id].in_game = true
var done_waiting = true
for player in tracked_players:
if not player != self_peer_id:
if not tracked_players[player].in_game:
done_waiting = false
break
if done_waiting:
waiting_for_client_starts = false
$"/root/Main"._wave_timer.set_paused(false)
$"/root/Main".send_updates = true
func enter_async_shop() -> void:
if $"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/StatsContainer":
$"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/StatsContainer".update_bought_items(tracked_players)
if is_host:
init_shop_go_button()
else:
$"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/GoButton".hide()
$"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2".add_child(create_ready_toggle())
func create_extra_creatures_map() -> Dictionary:
var extra_creatures_map = {}
for player_id in tracked_players:
extra_creatures_map[player_id] = tracked_players[player_id]["extra_enemies_next_wave"]
return extra_creatures_map
func create_effects_map() -> Dictionary:
var effects_map = {}
for player_id in tracked_players:
effects_map[player_id] = tracked_players[player_id]["effects"]
return effects_map
func init_shop_go_button() -> void:
var shop = get_tree().get_current_scene()
var button = $"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/GoButton"
button.disconnect("pressed", shop, "_on_GoButton_pressed")
button.connect("pressed", self, "_on_GoButton_pressed")
update_go_button()
func _on_GoButton_pressed()-> void:
var shop = get_tree().get_current_scene()
if shop._go_button_pressed:
return
shop._go_button_pressed = true
extra_enemies_next_wave = create_extra_creatures_map()
effects_next_wave = create_effects_map()
RunData.current_wave += 1
MusicManager.tween(0)
var wave_data = {"current_wave":RunData.current_wave, "mode":game_mode}
var extra_creatures_map = extra_enemies_next_wave
wave_data["extra_enemies_next_wave"] = extra_creatures_map
wave_data["effects"] = effects_next_wave
send_start_game(wave_data)
reset_extra_creatures()
reset_ready_map()
# RunData.effects["extra_enemies_next_wave"] = tracked_players[self_peer_id]["extra_enemies_next_wave"]
var _error = get_tree().change_scene(MenuData.game_scene)
func init_all_player_data():
for player_id in tracked_players:
init_player_data(tracked_players[player_id], player_id)
func init_player_data(player:Dictionary, player_id) -> void:
var run_data = {}
# Randomly useful in places
run_data["player_id"] = player_id
run_data["effects"] = RunData.init_effects()
run_data["items"] = []
run_data["weapons"] = []
run_data["additional_weapon_effects"] = []
run_data["active_set_effects"] = []
run_data["gold"] = DebugService.starting_gold
run_data["current_xp"] = 0
run_data["current_level"] = 0
run_data["tier_i_weapon_effects"] = []
run_data["tier_iv_weapon_effects"] = []
run_data["unique_effects"] = []
run_data["appearances_displayed"] = []
run_data["tracked_item_effects"] = RunData.init_tracked_effects()
player["linked_stats"] = {}
player["consumables_to_process"] = []
player["linked_stats"]["stats"] = RunData.init_stats(true)
player["linked_stats"]["update_on_gold_chance"] = false
player["temp_stats"] = {}
player["temp_stats"]["stats"] = RunData.init_stats(true)
player["run_data"] = run_data
func start_game(_game_info: Dictionary):
print_debug("game controller game would have started here but we had to remove all the logic")
func send_death() -> void:
disable_pause = false
run_updates = false
if is_host:
receive_death(self_peer_id)
connection.send_death()
func receive_death(source_player_id:int) -> void:
tracked_players[source_player_id]["dead"] = true
if is_host:
connection.send_tracked_players(tracked_players)
if source_player_id != self_peer_id:
check_win()
func check_win() -> void:
var all_others_dead = true
var anyone_dead = false
var all_dead = true
for tracked_player_id in tracked_players:
if tracked_player_id == self_peer_id:
continue
var tracked_player = tracked_players[tracked_player_id]
if not tracked_player.has("dead") or not tracked_player.dead:
all_others_dead = false
all_dead = false
else:
anyone_dead = true
# TODO this doesn't actually work yet
var should_end_coop = false
if lobby_data["first_death_loss"]:
should_end_coop = anyone_dead
else:
should_end_coop = all_dead
if all_others_dead or (should_end_coop):
disable_pause = false
var main = get_tree().get_current_scene()
var did_win = not is_coop()
main._is_run_won = did_win
main._is_run_lost = not did_win
RunData.run_won = did_win
main.clean_up_room(false, main._is_run_lost, main._is_run_won)
if not is_host:
send_complete_player_request()
yield(self, "complete_player_update")
var _error = get_tree().change_scene("res://ui/menus/run/end_run.tscn")
func on_consumable_to_process_added(player_id, consumable_data) -> void:
if player_id == self_peer_id:
receive_add_consumable_to_process(player_id, consumable_data.get_path())
else:
connection.send_add_consumable_to_process(player_id, consumable_data.get_path())
func receive_add_consumable_to_process(player_id, consumable_data_path) -> void:
if player_id == self_peer_id:
var consumable_data = load(consumable_data_path)
tracked_players[self_peer_id].consumables_to_process.push_back(consumable_data)
get_tree().get_current_scene().emit_signal("consumable_to_process_added", consumable_data)
func on_upgrade_selected(upgrade_data:UpgradeData)->void :
if is_host:
receive_uprade_selected(upgrade_data.my_id, self_peer_id)
else:
connection.send_upgrade_selection(upgrade_data.my_id)
func send_complete_player_request() -> void:
connection.send_complete_player_request()
func send_complete_player(player_id:int) -> void:
if not is_host:
return
var player_dict = tracked_players[player_id].duplicate()
if not player_dict.has("run_data"):
player_dict.run_data = {}
player_dict.run_data = player_dict.run_data.duplicate()
player_dict.run_data.effects = player_dict.run_data.effects.duplicate()
var new_items = []
if player_dict.run_data.has("items"):
for item in player_dict.run_data.items:
var to_add = {}
to_add["my_id"] = item.my_id
new_items.push_back(to_add)
player_dict.run_data["items"] = new_items
if player_dict.run_data.has("current_character"):
player_dict.run_data.current_character = player_dict.run_data.current_character.my_id
var new_weapons = []
if player_dict.run_data.has("weapons"):
for weapon in player_dict.run_data.weapons:
var to_add = {}
to_add["my_id"] = weapon.my_id
new_weapons.push_back(to_add)
player_dict.run_data["weapons"] = new_weapons
var burn_chance = {}
if player_dict.run_data.has("effects"):
burn_chance.chance = player_dict.run_data.effects.burn_chance.chance
burn_chance.damage = player_dict.run_data.effects.burn_chance.damage
burn_chance.duration = player_dict.run_data.effects.burn_chance.duration
player_dict.run_data.effects.burn_chance = burn_chance
var elites = []
for elite in RunData.elites_spawn:
elites.push_back([elite[0], elite[1]])
player_dict["elites"] = elites
connection.send_complete_player(player_id, player_dict)
func receive_complete_player(player_id:int, player_dict: Dictionary) -> void:
if player_id == self_peer_id:
tracked_players[player_id]["run_data"] = player_dict.run_data
var new_items = []
for item in player_dict.run_data.items:
var found : bool = false
for query_item in ItemService.items:
if query_item.my_id == item.my_id:
new_items.push_back(query_item.duplicate())
found = true
break
if not found:
for query_item in ItemService.characters:
if query_item.my_id == item.my_id:
new_items.push_back(query_item.duplicate())
found = true
break
tracked_players[player_id]["run_data"]["items"] = new_items
for query_character in ItemService.characters:
if player_dict.run_data.has("current_character"):
if query_character.my_id == player_dict.run_data.current_character:
player_dict.run_data.current_character = query_character
break
var new_weapons = []
for weapon in player_dict.run_data.weapons:
for query_weapon in ItemService.weapons:
if query_weapon.my_id == weapon.my_id:
new_weapons.push_back(query_weapon.duplicate())
tracked_players[player_id]["run_data"]["weapons"] = new_weapons
if tracked_players[player_id]["run_data"].has("gold"):
RunData.emit_signal("gold_changed", tracked_players[player_id]["run_data"].gold)
var elites_spawn = []
for elite in player_dict["elites"]:
elites_spawn.push_back([elite[0], elite[1], "some_boss_id"])
RunData.elites_spawn = elites_spawn
emit_signal("complete_player_update")
func on_item_box_take_button_pressed(item_data:ItemParentData) -> void:
if is_host:
receive_item_box_take(item_data.my_id, self_peer_id)
else:
connection.send_take_item_box(self_peer_id, item_data.my_id)
func receive_item_box_take(item_id, player_id) -> void:
var run_data_node = $"/root/MultiplayerRunData"
for item in ItemService.items:
if item.my_id == item_id:
run_data_node.add_item(player_id, item)
return
func reroll_upgrades() -> void:
var upgrades_ui = get_tree().get_current_scene()._upgrades_ui
var reroll_price = upgrades_ui._reroll_price
if is_host:
receive_reroll_upgrades(self_peer_id, reroll_price)
else:
connection.send_reroll_upgrades(reroll_price)
func receive_reroll_upgrades(_player_id:int, _reroll_price:int) -> void:
print_debug("receive_reroll_upgrades")
func on_item_discard_button_pressed(weapon_data:WeaponData) -> void:
if is_host:
receive_item_discard(weapon_data.my_id, self_peer_id)
else:
connection.send_weapon_discard(weapon_data.my_id)
func receive_item_discard(weapon_id, player_id) -> void:
var run_data_node = $"/root/MultiplayerRunData"
var run_data = tracked_players[player_id].run_data
var weapon_data = null
for weapon in run_data.weapons:
if weapon.my_id == weapon_id:
weapon_data = weapon
var _weapon = run_data_node.remove_weapon(player_id, weapon_data)
run_data_node.add_gold(player_id, ItemService.get_recycling_value(RunData.current_wave, weapon_data.value, true))
if player_id != self_peer_id:
send_complete_player(player_id)
func receive_player_enter_shop(player_id:int) -> void:
var run_data = tracked_players[player_id].run_data
var run_data_node = $"/root/MultiplayerRunData"
if run_data.effects["destroy_weapons"]:
run_data_node.remove_all_weapons(player_id)
if run_data.effects["upgrade_random_weapon"].size() > 0:
for effect in run_data.effects["upgrade_random_weapon"]:
var possible_upgrades = []
for weapon in run_data.weapons:
if weapon.upgrades_into != null and weapon.tier < run_data.effects["max_weapon_tier"]:
possible_upgrades.push_back(weapon)
if possible_upgrades.size() > 0:
var weapon_to_upgrade = Utils.get_rand_element(possible_upgrades)
receive_item_combine(weapon_to_upgrade.my_id, true, player_id)
else :
run_data_node.add_stat(player_id, effect[0], effect[1])
if player_id != self_peer_id:
send_complete_player(player_id)
func on_item_combine_button_pressed(weapon_data:WeaponData, is_upgrade:bool = false) -> void:
if is_host:
receive_item_combine(weapon_data.my_id, is_upgrade, self_peer_id)
else:
connection.send_combine_item(weapon_data.my_id, is_upgrade, self_peer_id)
func receive_item_combine(weapon_id, is_upgrade, player_id) -> void:
var run_data_node = $"/root/MultiplayerRunData"
var nb_to_remove = 2
var removed_weapons_tracked_value = 0
if is_upgrade:
nb_to_remove = 1
var run_data = tracked_players[player_id].run_data
var weapon_data = null
for weapon in run_data.weapons:
if weapon.my_id == weapon_id:
weapon_data = weapon
removed_weapons_tracked_value += run_data_node.remove_weapon(player_id, weapon_data)
if not is_upgrade:
removed_weapons_tracked_value += run_data_node.remove_weapon(player_id, weapon_data)
var new_weapon = run_data_node.add_weapon(player_id, weapon_data.upgrades_into)
new_weapon.tracked_value = removed_weapons_tracked_value
if is_upgrade:
new_weapon.dmg_dealt_last_wave = weapon_data.dmg_dealt_last_wave
if player_id == self_peer_id:
var shop = $"/root/Shop"
shop._weapons_container._elements.remove_element(weapon_data, nb_to_remove)
shop.reset_item_popup_focus()
shop._stats_container.update_stats()
shop._weapons_container._elements.add_element(new_weapon)
SoundManager.play(Utils.get_rand_element(shop.combine_sounds), 0, 0.1, true)
shop._weapons_container.set_label(shop.get_weapons_label_text())
if Input.get_mouse_mode() == Input.MOUSE_MODE_HIDDEN:
shop._weapons_container._elements.focus_element(new_weapon)
func receive_uprade_selected(upgrade_data_id, player_id):
for upgrade in ItemService.upgrades:
if upgrade.my_id == upgrade_data_id:
var run_data = tracked_players[player_id].run_data
var run_data_node = $"/root/MultiplayerRunData"
run_data_node.apply_item_effects(player_id, upgrade, run_data)
# Send normal item
func send_bought_item_by_id(item_id:String, value:int) -> void:
if is_host:
receive_bought_item_by_id(item_id, self_peer_id, value)
else:
connection.send_bought_item_by_id(item_id, value)
func receive_bought_item_by_id(item_id:String, player_id:int, value:int) -> void:
var run_data_node = $"/root/MultiplayerRunData"
var run_data = tracked_players[player_id].run_data
run_data_node.remove_currency(player_id, value)
var nb_coupons = run_data_node.get_nb_item(player_id, "item_coupon")
var shop_item_data = null
for weapon in ItemService.weapons:
if weapon.my_id == item_id:
shop_item_data = weapon
for item in ItemService.items:
if item.my_id == item_id:
shop_item_data = item
if nb_coupons > 0:
# var coupon_value = get_coupon_value() reimplemented in place
var coupon_value = 0
for item in run_data.items:
if item.my_id == "item_coupon":
coupon_value = abs(item.effects[0].value)
break
var coupon_effect = nb_coupons * (coupon_value / 100.0)
var base_value = ItemService.get_value(RunData.current_wave, shop_item_data.value, false, shop_item_data is WeaponData, shop_item_data.my_id)
run_data.tracked_item_effects["item_coupon"] += (base_value * coupon_effect) as int
if shop_item_data.get_category() == Category.ITEM:
run_data_node.add_item(player_id, shop_item_data)
elif shop_item_data.get_category() == Category.WEAPON:
if not run_data_node.has_weapon_slot_available(player_id, shop_item_data.type):
for weapon in run_data.weapons:
if weapon.my_id == shop_item_data.my_id and weapon.upgrades_into != null:
var _weapon = run_data_node.add_weapon(player_id, shop_item_data)
receive_item_combine(weapon.my_id, false, player_id)
break
else :
var _weapon = run_data_node.add_weapon(player_id, shop_item_data)
func send_reroll(price) -> void:
if is_host:
receive_reroll(price, self_peer_id)
else:
connection.send_reroll(price)
func receive_reroll(price, player_id):
var run_data = tracked_players[player_id].run_data
run_data.gold -= price
func send_bought_item(shop_item:Resource) -> void:
if is_host:
receive_bought_item(shop_item, self_peer_id)
else:
connection.send_bought_item(shop_item)
func send_client_entered_shop() -> void:
connection.send_client_entered_shop()
func receive_bought_item(shop_item:Resource, source_player_id:int) -> void:
var effect_path = shop_item.effect.get_path()
for player_id in tracked_players:
if player_id != source_player_id:
var effect = shop_item.effect
if effect is WaveGroupData:
if not tracked_players[player_id]["extra_enemies_next_wave"].has(effect_path):
tracked_players[player_id]["extra_enemies_next_wave"][effect_path] = 0
tracked_players[player_id]["extra_enemies_next_wave"][effect_path] = tracked_players[player_id]["extra_enemies_next_wave"][effect_path] + 1
elif effect is Effect:
if not tracked_players[player_id]["effects"].has(effect_path):
tracked_players[player_id]["effects"][effect_path] = 0
tracked_players[player_id]["effects"][effect_path] = tracked_players[player_id]["effects"][effect_path] + 1
if is_host:
connection.send_tracked_players(tracked_players)
if $"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/StatsContainer":
$"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/StatsContainer".update_bought_items(tracked_players)
func update_tracked_players(updated_tracked_players: Dictionary) -> void:
# Actual Player state is updated elsewhere
for player_id in updated_tracked_players:
if tracked_players.has(player_id) and tracked_players[player_id].has("player"):
updated_tracked_players[player_id]["player"] = tracked_players[player_id]["player"]
else:
updated_tracked_players[player_id].erase("player")
var scene_name = get_tree().get_current_scene().get_name()
tracked_players = updated_tracked_players
if scene_name == "Shop":
$"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/StatsContainer".update_bought_items(tracked_players)
elif scene_name == "Main":
check_win()
func create_ready_toggle() -> Node:
ready_toggle = toggle_scene.instance()
ready_toggle.connect("pressed", self, "_on_ready_toggle")
return ready_toggle
func _on_ready_toggle() -> void:
connection.send_ready(ready_toggle.pressed)
func discard_item_box(item_data:ItemParentData) -> void:
if is_host:
receive_discard_item_box(self_peer_id, item_data.my_id)
else:
connection.send_discard_item_box(item_data.my_id)
func receive_discard_item_box(_player_id:int, _item_id:String) -> void:
print_debug("receive_discard_item_box")
func end_wave():
run_updates = false
game_state_controller.reset_client_items()
if current_scene_name == "ClientMain":
var wave_timer = $"/root/ClientMain"._wave_timer
wave_timer.wait_time = 0.1
wave_timer.one_shot = true
wave_timer.start()
var end_wave_timer = $"/root/ClientMain"._end_wave_timer
end_wave_timer.one_shot = true
end_wave_timer.start()
for player_id in tracked_players:
tracked_players[player_id].erase("player")
func send_ready(is_ready:bool) -> void:
connection.send_ready(is_ready)
func send_game_state() -> void:
if run_updates:
connection.send_state(game_state_controller.get_game_state())
func send_start_game(game_info:Dictionary) -> void:
connection.send_start_game(game_info)
func send_display_floating_text(value:String, text_pos:Vector2, color:Color = Color.white) -> void:
batched_floating_text.push_back([value,text_pos, color])
func send_display_hit_effect(effect_pos, direction, effect_scale) -> void:
batched_hit_effects.push_back([effect_pos, direction, effect_scale])
func send_enemy_death(enemy_id:int) -> void:
batched_deaths.push_back(enemy_id)
# connection.send_enemy_death(enemy_id)
func send_end_wave() -> void:
connection.send_end_wave()
func send_flash_enemy(enemy_id:int) -> void:
batched_flash_enemy.push_back(enemy_id)
func send_shot(player_id:int, weapon_index:int) -> void:
connection.send_shot(player_id, weapon_index)
func receive_shot(player_id:int, weapon_index:int) -> void:
if tracked_players.has(player_id):
if tracked_players[player_id].has("player"):
var player = tracked_players[player_id]["player"]
if is_instance_valid(player):
var weapon = player.current_weapons[weapon_index]
if is_instance_valid(weapon):
SoundManager.play(Utils.get_rand_element(weapon.current_stats.shooting_sounds), weapon.current_stats.sound_db_mod, 0.2)
func send_explosion(pos: Vector2, scale: float) -> void:
connection.send_explosion(pos, scale)
func receive_explosion(pos: Vector2, scale: float) -> void:
var main = get_tree().current_scene
var instance = explosion_scene.instance()
instance.set_deferred("global_position", pos)
main.call_deferred("add_child", instance)
instance.call_deferred("set_area", scale)
func send_enemy_take_damage(enemy_id:int, is_dodge: bool) -> void:
batched_enemy_damage.push_back([enemy_id, is_dodge])
func send_flash_neutral(neutral_id:int) -> void:
connection.send_flash_neutral(neutral_id)
func send_lobby_update(lobby_info:Dictionary) -> void:
connection.send_lobby_update(lobby_info)
func receive_lobby_update(lobby_info:Dictionary) -> void:
if current_scene_name == "MultiplayerLobby":
lobby_data = lobby_info.duplicate()
$"/root/MultiplayerLobby".remote_update_lobby(lobby_info)
func update_multiplayer_lobby() -> void:
if current_scene_name == "MultiplayerLobby":
$"/root/MultiplayerLobby".update_selections()
func send_client_position() -> void:
if not tracked_players.has(self_peer_id) or not tracked_players[self_peer_id].has("player"):
return
var my_player = tracked_players[self_peer_id]["player"]
if not is_instance_valid(my_player):
return
var client_position = {}
client_position["player"] = my_player.position
client_position["id"] = self_peer_id
client_position["movement"] = my_player._current_movement
var weapons = []
for weapon in my_player.current_weapons:
var weapon_data = {}
weapon_data["weapon_id"] = weapon.weapon_id
weapon_data["position"] = weapon.sprite.position
weapon_data["rotation"] = weapon.sprite.rotation
weapon_data["hitbox_disabled"] = weapon._hitbox._collision.disabled
weapons.push_back(weapon_data)
client_position["weapons"] = weapons
connection.send_client_position(client_position)
func update_client_position(client_position:Dictionary) -> void:
if is_source_of_truth:
var id = client_position.id
if tracked_players.has(id):
if tracked_players[id].has("player"):
var player = tracked_players[id]["player"]
if not is_instance_valid(player):
return
player.position = client_position.player
player.maybe_update_animation(client_position.movement, true)
func update_ready_state(sender_id, is_ready):
if is_host:
tracked_players[sender_id]["is_ready"] = is_ready
if current_scene_name == "Shop":
update_go_button()
func reset_ready_map():
for player_id in tracked_players:
tracked_players[player_id]["is_ready"] = false
func reset_extra_creatures():
for player_id in tracked_players:
tracked_players[player_id]["extra_enemies_next_wave"] = {}
tracked_players[player_id]["effects"] = {}
func update_go_button():
var should_enable = true
for player_id in tracked_players:
if player_id != self_peer_id and not tracked_players[player_id]["is_ready"]:
should_enable = false
break
var shop_button = $"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/GoButton"
if not should_enable:
shop_button.disabled = true
else:
shop_button.disabled = false
func get_items_state() -> Dictionary:
var main = $"/root/Main"
var items = []
for item in main._items_container.get_children():
var item_data = {}
item_data["id"] = item.id
item_data["scale_x"] = item.scale.x
item_data["scale_y"] = item.scale.y
item_data["position"] = item.global_position
item_data["rotation"] = item.rotation
item_data["push_back_destination"] = item.push_back_destination
# TODO we may want textures propagated
items.push_back(item_data)
return items
func get_projectiles_state() -> Dictionary:
var main = $"/root/Main"
var projectiles = []
for child in main.get_children():
if child is PlayerProjectile:
var projectile_data = {}
projectile_data["id"] = child.id
projectile_data["filename"] = child.filename
projectile_data["position"] = child.position
projectile_data["global_position"] = child.global_position
projectile_data["rotation"] = child.rotation
projectiles.push_back(projectile_data)
return projectiles
func send_player_level_up(player_id: int, level:int) -> void:
connection.send_player_level_up(player_id, level)
func receive_player_level_up(player_id: int, level:int) -> void:
if player_id == self_peer_id:
var main = $"/root/ClientMain"
main._ui_upgrades_to_process.add_element(ItemService.upgrade_to_process_icon, level)
main._upgrades_to_process.push_back(level)
func receive_health_update(current_health:int, max_health:int, source_player_id:int) -> void:
tracked_players[source_player_id]["max_health"] = max_health
tracked_players[source_player_id]["current_health"] = current_health
# if is_host:
# connection.send_tracked_players(tracked_players)
# update_health_ui()
func update_health_ui() -> void:
if current_scene_name == "Main":
if $"/root/Main/UI/HealthTracker":
$"/root/Main/UI/HealthTracker".update_health_bars(tracked_players)
if current_scene_name == "ClientMain":
if $"/root/ClientMain/UI/HealthTracker":
$"/root/ClientMain/UI/HealthTracker".update_health_bars(tracked_players)
func update_health(current_health:int, max_health:int) -> void:
# If this player owns all the players, all the updates will come through
# here.
if is_coop() and is_host:
for player_id in tracked_players:
var player_dict = tracked_players[player_id]
if player_dict.has("player") and is_instance_valid(player_dict.player):
var player = player_dict.player
tracked_players[player_id]["max_health"] = player.max_stats.health
tracked_players[player_id]["current_health"] = player.current_stats.health
receive_health_update(current_health, max_health, self_peer_id)
connection.send_health_update(current_health, max_health)
func update_game_state(data: PoolByteArray) -> void:
if get_tree().get_current_scene().get_name() != "ClientMain":
return
game_state_controller.update_game_state(data)
update_health_ui()
func enemy_death(enemy_id):
game_state_controller.enemy_death(enemy_id)
func flash_neutral(neutral_id):
game_state_controller.flash_neutral(neutral_id)
func is_coop() -> bool:
return game_mode == GameMode.COOP
func on_danger_selected(danger) -> void:
if is_host:
received_danger_selected(self_peer_id, danger)
else:
connection.send_danger_selected(danger)
func received_danger_selected(player_id, danger) -> void:
lobby_data["players"][player_id]["danger"] = danger
emit_signal("lobby_info_updated")
func on_character_selected(character) -> void:
if is_host:
received_character_selected(self_peer_id, character)
else:
connection.send_character_selected(character)
func received_character_selected(player_id, character) -> void:
lobby_data["players"][player_id]["character"] = character
lobby_data["players"][player_id].erase("weapon")
emit_signal("lobby_info_updated")
func on_weapon_selected(weapon) -> void:
if is_host:
received_weapon_selected(self_peer_id, weapon)
else:
connection.send_weapon_selected(weapon)
func received_weapon_selected(player_id, weapon) -> void:
lobby_data["players"][player_id]["weapon"] = weapon
emit_signal("lobby_info_updated")
func on_mp_lobby_ready_changed(is_ready:bool) -> void:
if is_host:
receive_mp_lobby_ready_changed(self_peer_id, is_ready)
else:
connection.send_mp_lobby_readied(is_ready)
func receive_mp_lobby_ready_changed(player_id, is_ready:bool) -> void:
lobby_data["players"][player_id]["ready"] = is_ready
emit_signal("lobby_info_updated")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,453 @@
extends "res://mods-unpacked/Brogether-Brogether/networking/connection.gd"
var lobby_id = 0
var parent
var established_p2p_connections = {}
var rpcs_by_type = {}
var prev_sec = 0
func _ready():
print_debug("steam connection ready?")
lobby_id = 0
var _connect_error = Steam.connect("lobby_created", self, "_on_Lobby_Created")
print_debug("connect error? ", _connect_error)
_connect_error = Steam.connect("lobby_match_list", self, "_on_Lobby_Match_List")
_connect_error = Steam.connect("lobby_joined", self, "_on_Lobby_Joined")
_connect_error = Steam.connect("lobby_chat_update", self, "_on_Lobby_Chat_Update")
_connect_error = Steam.connect("p2p_session_request", self, "_on_P2P_Session_Request")
func _process(_delta):
if lobby_id > 0:
while read_p2p_packet():
pass
func read_p2p_packet() -> bool:
var packet_size = Steam.getAvailableP2PPacketSize(0)
var sec = Time.get_time_dict_from_system()["second"]
if sec != prev_sec:
prev_sec = sec
if packet_size > 0:
var packet = Steam.readP2PPacket(packet_size, 0)
var sender = packet["steam_id_remote"]
var data = bytes2var(packet["data"].decompress_dynamic(-1, File.COMPRESSION_GZIP))
var type = data.type
if not rpcs_by_type.has(type):
rpcs_by_type[type] = 0
rpcs_by_type[type] += 1
if type == "start_game":
parent.start_game(data.game_info)
elif type == "reroll_upgrades":
parent.receive_reroll_upgrades(sender, data.reroll_price)
elif type == "weapon_discard":
parent.receive_item_discard(data.weapon_id, sender)
elif type == "send_combine_item":
parent.receive_item_combine(data.weapon_data_id, data.is_upgrade, data.player_id)
elif type == "discard_item_box":
parent.receive_discard_item_box(sender, data.item_id)
elif type == "send_take_item_box":
parent.receive_item_box_take(data.item_id, data.player_id)
elif type == "client_entered_shop":
parent.receive_player_enter_shop(data.player_id)
elif type == "client_started":
parent.receive_client_start(data.player_id)
elif type == "add_consumable_to_process":
parent.receive_add_consumable_to_process(data.player_id, data.consumable_data_path)
elif type == "end_wave":
parent.end_wave()
elif type == "level_up":
parent.receive_player_level_up(data.player_id, data.level)
elif type == "flash_neutral":
parent.flash_neutral(data.neutral_id)
elif type == "client_position_update":
parent.update_client_position(data.client_position)
elif type == "send_bought_item":
parent.receive_bought_item(load(data.shop_item), sender)
elif type == "send_bought_item_by_id":
parent.receive_bought_item_by_id(data.item_id, sender, data.value)
elif type == "send_ready":
parent.update_ready_state(sender, data.is_ready)
elif type == "request_complete_player":
parent.send_complete_player(sender)
elif type == "complete_player":
parent.receive_complete_player(data.player_id, data.player_dict)
elif type == "handshake":
# completes the handshake?
established_p2p_connections[sender] = true
update_tracked_players()
send_welcomes()
elif type == "update_tracked_players":
parent.update_tracked_players(data.tracked_players)
elif type == "lobby_update":
print_debug("received lobby update")
parent.receive_lobby_update(data.lobby_info)
elif type == "reroll":
parent.receive_reroll(data.price, sender)
elif type == "health_update":
parent.receive_health_update(data.current_health, data.max_health, sender)
elif type == "death":
parent.receive_death(sender)
elif type == "select_upgrade":
parent.receive_uprade_selected(data.upgrade_data_id, sender)
elif type == "shot":
parent.receive_shot(data.player_id, data.weapon_index)
elif type == "explosion":
parent.receive_explosion(Vector2(data.pos_x, data.pos_y), data.scale)
elif type == "character_selected":
parent.received_character_selected(data.player_id, data.character)
elif type == "weapon_selected":
parent.received_weapon_selected(data.player_id, data.weapon)
elif type == "danger_selected":
parent.received_danger_selected(data.player_id, data.danger)
elif type == "mp_lobby_readied":
parent.receive_mp_lobby_ready_changed(data.player_id, data.is_ready)
elif type == "request_lobby_update":
print_debug("received updated request")
if get_tree().get_current_scene().get_name() == "MultiplayerLobby":
send_lobby_update(parent.lobby_data)
else:
print_debug("unhandled type " , type)
return true
return false
func _on_Lobby_Match_List(lobbies: Array):
var scene_name = get_tree().get_current_scene().get_name()
if scene_name == "MultiplayerMenu":
$"/root/MultiplayerMenu".update_lobbies(lobbies)
pass
# if lobbies.size() == 1:
# Steam.joinLobby(lobbies[0])
# pass
func _on_Lobby_Created(connect: int, connected_lobby_id: int) -> void:
print_debug("lobby created")
if connect == 1:
lobby_id = connected_lobby_id
var _set_error = Steam.setLobbyData(lobby_id, "game", "Brotatogether")
_set_error = Steam.setLobbyData(lobby_id, "host", Steam.getPersonaName())
var _error = Steam.allowP2PPacketRelay(false)
func close_lobby() -> void:
# TODO this probably isn't the correct way to close a lobby but at least no one will find it
# for now.
var _set_error = Steam.setLobbyData(lobby_id, "game", "Not At All Brotatogether, Go Away")
func _on_Lobby_Joined(joined_lobby_id: int, _permissions: int, _locked: bool, response: int) -> void:
if response == 1:
print_debug("on lobby joined")
lobby_id = joined_lobby_id
parent.self_peer_id = Steam.getSteamID()
var _scene_error = get_tree().change_scene("res://mods-unpacked/Brogether-Brogether/ui/multiplayer_lobby.tscn")
update_tracked_players()
send_handshakes()
if not parent.is_host:
request_lobby_update()
func _on_Lobby_Chat_Update(_update_lobby_id: int, change_id: int, _making_change_id: int, chat_state: int) -> void:
var username = Steam.getFriendPersonaName(change_id)
update_tracked_players()
if chat_state == 1:
print_debug(username, " joined the lobby ", change_id)
pass
# Else if a player has left the lobby
elif chat_state == 2:
print_debug(username, " has left the lobby.")
# Else if a player has been kicked
elif chat_state == 8:
print_debug(username, " has been kicked from the lobby.")
# Else if a player has been banned
elif chat_state == 16:
print_debug(username, " has been banned from the lobby.")
# clears tracked_players and resets tracked players based
func update_tracked_players() -> void:
# Clear your previous lobby list
parent.tracked_players.clear()
# Get the number of members from this lobby from Steam
var num_members = Steam.getNumLobbyMembers(lobby_id)
var all_players_ready = true
# Get the data of these players from Steam
for member_index in range(num_members):
var member_steam_id = Steam.getLobbyMemberByIndex(lobby_id, member_index)
var member_username: String = Steam.getFriendPersonaName(member_steam_id)
if not established_p2p_connections.has(member_steam_id) or not established_p2p_connections[member_steam_id]:
if member_steam_id != parent.self_peer_id:
all_players_ready = false
if parent.is_host:
member_username = member_username
parent.tracked_players[member_steam_id] = {"username": member_username}
parent.all_players_ready = all_players_ready
parent.update_multiplayer_lobby()
func send_state(game_state:PoolByteArray) -> void:
var send_data = {}
send_data["game_state"] = game_state
send_data["type"] = "game_state"
for player_id in parent.tracked_players:
if player_id == parent.self_peer_id:
continue
var compressed_data = var2bytes(send_data).compress(File.COMPRESSION_GZIP)
# State updates get their own channel
var _error = Steam.sendP2PPacket(player_id, compressed_data, Steam.P2P_SEND_RELIABLE, 1)
func send_start_game(game_info:Dictionary) -> void:
var send_data = {}
send_data["type"] = "start_game"
send_data["game_info"] = game_info
send_data_to_all(send_data)
func send_end_wave():
var send_data = {}
send_data["type"] = "end_wave"
send_data_to_all(send_data)
func send_shot(player_id:int, weapon_index:int) -> void:
var send_data = {}
send_data["type"] = "shot"
send_data["player_id"] = player_id
send_data["weapon_index"] = weapon_index
send_data_to_all(send_data)
func send_flash_neutral(neutral_id):
var send_data = {}
send_data["type"] = "flash_neutral"
send_data["neutral_id"] = neutral_id
send_data_to_all(send_data)
func send_data_to_all(packet_data: Dictionary):
for player_id in parent.tracked_players:
if player_id == parent.self_peer_id:
continue
send_data(packet_data, player_id)
func send_data(packet_data: Dictionary, target: int):
# TODO actually compress
var compressed_data = var2bytes(packet_data).compress(File.COMPRESSION_GZIP)
# Just use channel 0 for everything for now
var _error = Steam.sendP2PPacket(target, compressed_data, Steam.P2P_SEND_RELIABLE, 0)
# Done to trigger p2p session requests
func send_handshakes() -> void:
var send_data = {}
send_data["type"] = "handshake"
send_data_to_all(send_data)
func send_welcomes() -> void:
var send_data = {}
send_data["type"] = "welcome"
send_data_to_all(send_data)
func _on_P2P_Session_Request(remote_id: int) -> void:
var _error = Steam.acceptP2PSessionWithUser(remote_id)
# Make the initial handshake
send_handshakes()
func send_client_position(client_position:Dictionary) -> void:
var send_data = {}
send_data["type"] = "client_position_update"
send_data["client_position"] = client_position
send_data_to_all(send_data)
func send_bought_item(shop_item:Resource) -> void:
var send_data = {}
send_data["type"] = "send_bought_item"
send_data["shop_item"] = shop_item.get_path()
send_data_to_all(send_data)
func send_bought_item_by_id(item_id:String, value:int) -> void:
var send_data = {}
send_data["type"] = "send_bought_item_by_id"
send_data["item_id"] = item_id
send_data["value"] = value
send_data_to_all(send_data)
func send_ready(is_ready:bool) -> void:
print_debug("sending ready")
var send_data = {}
send_data["type"] = "send_ready"
send_data["is_ready"] = is_ready
send_data_to_all(send_data)
func send_tracked_players(tracked_players:Dictionary) -> void:
var send_data = {}
send_data["type"] = "update_tracked_players"
send_data["tracked_players"] = tracked_players
send_data_to_all(send_data)
func send_lobby_update(lobby_info:Dictionary) -> void:
var send_data = {}
send_data["type"] = "lobby_update"
send_data["lobby_info"] = lobby_info
send_data_to_all(send_data)
func request_lobby_update() -> void:
var send_data = {}
send_data["type"] = "request_lobby_update"
send_data_to_all(send_data)
func send_health_update(current_health:int, max_health:int) -> void:
var send_data = {}
send_data["type"] = "health_update"
send_data["current_health"] = current_health
send_data["max_health"] = max_health
send_data_to_all(send_data)
func send_death() -> void:
var send_data = {}
send_data["type"] = "death"
send_data_to_all(send_data)
func send_explosion(pos: Vector2, scale: float) -> void:
var send_data = {}
send_data["type"] = "explosion"
send_data["pos_x"] = pos.x
send_data["pos_y"] = pos.y
send_data["scale"] = scale
send_data_to_all(send_data)
func get_lobby_host() -> String:
return Steam.getLobbyData(lobby_id,"host")
func send_upgrade_selection(upgrade_data_id) -> void:
var send_data = {}
send_data["type"] = "select_upgrade"
send_data["upgrade_data_id"] = upgrade_data_id
send_data_to_all(send_data)
func send_player_level_up(player_id:int, level:int) -> void:
var send_data = {}
send_data["type"] = "level_up"
send_data["player_id"] = player_id
send_data["level"] = level
send_data_to_all(send_data)
func send_complete_player_request() -> void:
var send_data = {}
send_data["type"] = "request_complete_player"
send_data_to_all(send_data)
func send_complete_player(player_id:int, player_dict:Dictionary) -> void:
var send_data = {}
send_data["type"] = "complete_player"
send_data["player_id"] = player_id
send_data["player_dict"] = player_dict
send_data_to_all(send_data)
func send_add_consumable_to_process(player_id:int, consumable_data_path) -> void:
var send_data = {}
send_data["type"] = "add_consumable_to_process"
send_data["player_id"] = player_id
send_data["consumable_data_path"] = consumable_data_path
send_data_to_all(send_data)
func send_take_item_box(player_id:int, item_id) -> void:
var send_data = {}
send_data["type"] = "send_take_item_box"
send_data["player_id"] = player_id
send_data["item_id"] = item_id
send_data_to_all(send_data)
func send_combine_item(weapon_data_id, is_upgrade, player_id) -> void:
var send_data = {}
send_data["type"] = "send_combine_item"
send_data["player_id"] = player_id
send_data["is_upgrade"] = is_upgrade
send_data["weapon_data_id"] = weapon_data_id
send_data_to_all(send_data)
func send_discard_item_box(item_id) -> void:
var send_data = {}
send_data["type"] = "discard_item_box"
send_data["item_id"] = item_id
send_data_to_all(send_data)
func send_reroll_upgrades(reroll_price) -> void:
var send_data = {}
send_data["type"] = "reroll_upgrades"
send_data["reroll_price"] = reroll_price
send_data_to_all(send_data)
func send_weapon_discard(weapon_id) -> void:
var send_data = {}
send_data["type"] = "weapon_discard"
send_data["weapon_id"] = weapon_id
send_data_to_all(send_data)
func send_client_entered_shop() -> void:
var send_data = {}
send_data["type"] = "client_entered_shop"
send_data["player_id"] = parent.self_peer_id
send_data_to_all(send_data)
func send_client_started() -> void:
var send_data = {}
send_data["type"] = "client_started"
send_data["player_id"] = parent.self_peer_id
send_data_to_all(send_data)
func send_danger_selected(danger) -> void:
var send_data = {}
send_data["type"] = "danger_selected"
send_data["player_id"] = parent.self_peer_id
send_data["danger"] = danger
send_data_to_all(send_data)
func send_character_selected(character) -> void:
var send_data = {}
send_data["type"] = "character_selected"
send_data["player_id"] = parent.self_peer_id
send_data["character"] = character
send_data_to_all(send_data)
func send_weapon_selected(weapon) -> void:
var send_data = {}
send_data["type"] = "weapon_selected"
send_data["player_id"] = parent.self_peer_id
send_data["weapon"] = weapon
send_data_to_all(send_data)
func send_mp_lobby_readied(is_ready) -> void:
var send_data = {}
send_data["type"] = "mp_lobby_readied"
send_data["player_id"] = parent.self_peer_id
send_data["is_ready"] = is_ready
send_data_to_all(send_data)
func send_reroll(price) -> void:
var send_data = {}
send_data["type"] = "reroll"
send_data["player_id"] = parent.self_peer_id
send_data["price"] = price
send_data_to_all(send_data)
func send_game_state() -> void:
pass

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://ui/menus/global/my_menu_button.gd" type="Script" id=1]
[node name="GoButton" type="Button"]
margin_top = 1522.0
margin_right = 411.0
margin_bottom = 1587.0
focus_neighbour_top = NodePath("../../VBoxContainer/HBoxContainer2/ShopItemsContainer/ShopItem4/PanelContainer/MarginContainer/VBoxContainer/BuyButton")
size_flags_horizontal = 3
size_flags_vertical = 8
text = "MORE_BADDIES"
script = ExtResource( 1 )

View File

@@ -0,0 +1,12 @@
extends HBoxContainer
var username := "Test Username"
var message := "Test Message"
onready var username_label = $"%Username"
onready var message_label = $"%Message"
func _ready():
username_label.text = username
message_label.text = message

View File

@@ -0,0 +1,30 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://mods-unpacked/Brogether-Brogether/ui/chat/chat_message.gd" type="Script" id=1]
[ext_resource path="res://resources/themes/panel/stat_panel.tres" type="StyleBox" id=2]
[node name="ChatMessage" type="HBoxContainer"]
margin_right = 710.0
margin_bottom = 45.0
size_flags_horizontal = 3
custom_constants/separation = 35
script = ExtResource( 1 )
[node name="Username" type="Label" parent="."]
unique_name_in_owner = true
margin_top = 45.0
margin_right = 64.0
margin_bottom = 64.0
custom_styles/normal = ExtResource( 2 )
text = "username"
[node name="Message" type="Label" parent="."]
unique_name_in_owner = true
margin_left = 99.0
margin_right = 710.0
margin_bottom = 109.0
size_flags_horizontal = 3
custom_styles/normal = ExtResource( 2 )
text = "Very Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long TextVery Long Text
"
autowrap = true

View File

@@ -0,0 +1,73 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://ui/menus/global/arrow_right.png" type="Texture" id=3]
[ext_resource path="res://ui/menus/global/arrow_left.png" type="Texture" id=4]
[node name="ChatPanel" type="PanelContainer"]
margin_left = 1540.0
margin_right = 1853.0
margin_bottom = 500.0
rect_min_size = Vector2( 0, 500 )
[node name="MarginContainer" type="MarginContainer" parent="."]
margin_left = 7.0
margin_top = 7.0
margin_right = 343.0
margin_bottom = 493.0
custom_constants/margin_right = 25
custom_constants/margin_top = 25
custom_constants/margin_left = 25
custom_constants/margin_bottom = 25
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
margin_left = 25.0
margin_top = 25.0
margin_right = 311.0
margin_bottom = 461.0
custom_constants/separation = 25
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
margin_right = 286.0
margin_bottom = 34.0
custom_constants/separation = 15
alignment = 1
[node name="LeftArrow" type="TextureButton" parent="MarginContainer/VBoxContainer/HBoxContainer"]
margin_right = 33.0
margin_bottom = 34.0
texture_normal = ExtResource( 4 )
[node name="ChatTitle" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer"]
margin_left = 48.0
margin_top = 1.0
margin_right = 238.0
margin_bottom = 32.0
rect_min_size = Vector2( 190, 0 )
text = "Global Chat"
clip_text = true
__meta__ = {
"_edit_use_anchors_": false
}
[node name="RightArrow" type="TextureButton" parent="MarginContainer/VBoxContainer/HBoxContainer"]
margin_left = 253.0
margin_right = 286.0
margin_bottom = 34.0
texture_normal = ExtResource( 3 )
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer"]
margin_top = 59.0
margin_right = 286.0
margin_bottom = 387.0
size_flags_horizontal = 3
size_flags_vertical = 3
scroll_horizontal_enabled = false
[node name="ChatMessages" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer"]
margin_right = 286.0
size_flags_horizontal = 3
[node name="ChatInput" type="LineEdit" parent="MarginContainer/VBoxContainer"]
margin_top = 412.0
margin_right = 286.0
margin_bottom = 436.0

View File

@@ -0,0 +1,40 @@
extends VBoxContainer
var health_bar_map = {}
const HealthBar = preload("res://ui/hud/ui_progress_bar.tscn")
func init(tracked_players:Dictionary) -> void:
var life_bar = $TemplateLifeBar
for tracked_player_id in tracked_players:
var health_bar = life_bar.duplicate()
add_child(health_bar)
health_bar_map[tracked_player_id] = health_bar
remove_child(life_bar)
update_health_bars(tracked_players)
func update_health_bars(tracked_players:Dictionary) -> void:
for tracked_player_id in tracked_players:
var player = tracked_players[tracked_player_id]
var health_bar = health_bar_map[tracked_player_id]
if player.has("username"):
var name_label = health_bar.get_node("NameLabel")
name_label.text = str(player.username)
var current_health = 15
var max_health = 15
if player.has("player") and is_instance_valid(player.player):
current_health = player.player.current_stats.health
max_health = player.player.max_stats.health
if max_health != 0:
health_bar.update_value(current_health, max_health)
var life_label = health_bar.get_node("MarginContainer/LifeLabel")
life_label.text = str(max(current_health, 0.0)) + " / " + str(max_health)

View File

@@ -0,0 +1,53 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://ui/hud/ui_progress_bar.tscn" type="PackedScene" id=1]
[ext_resource path="res://mods-unpacked/Brogether-Brogether/ui/health_tracker/health_tracker.gd" type="Script" id=3]
[node name="HealthTracker" type="VBoxContainer"]
margin_left = 30.0
margin_top = 300.0
margin_right = 350.0
margin_bottom = 452.0
script = ExtResource( 3 )
[node name="TemplateLifeBar" parent="." instance=ExtResource( 1 )]
margin_bottom = 48.0
size_flags_horizontal = 0
tint_progress = Color( 0.721569, 0, 0, 1 )
progress_color = Color( 0.721569, 0, 0, 1 )
[node name="NameLabel" type="Label" parent="TemplateLifeBar"]
anchor_left = 1.0
anchor_right = 1.0
margin_left = 12.0
margin_top = 8.0
margin_right = 308.0
margin_bottom = 39.0
grow_horizontal = 0
text = "8 / 8"
valign = 1
uppercase = true
[node name="MarginContainer" type="MarginContainer" parent="TemplateLifeBar"]
anchor_right = 1.0
anchor_bottom = 1.0
custom_constants/margin_right = 12
custom_constants/margin_top = 7
custom_constants/margin_left = 12
custom_constants/margin_bottom = 7
[node name="LifeLabel" type="Label" parent="TemplateLifeBar/MarginContainer"]
margin_left = 12.0
margin_top = 8.0
margin_right = 308.0
margin_bottom = 39.0
grow_horizontal = 0
text = "8 / 8"
align = 1
valign = 1
uppercase = true
__meta__ = {
"_edit_use_anchors_": false
}
[editable path="TemplateLifeBar"]

View File

@@ -0,0 +1,17 @@
extends HBoxContainer
var lobby_id
var lobby_name
var steam_connection
onready var lobby_name_label = $"%LobbyName"
func _ready():
steam_connection = $"/root/SteamConnection"
lobby_name_label.text = lobby_name
func _on_join_button_pressed():
Steam.joinLobby(lobby_id)

View File

@@ -0,0 +1,25 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://mods-unpacked/Brogether-Brogether/ui/lobby_entry.gd" type="Script" id=1]
[node name="HBoxContainer" type="HBoxContainer"]
margin_right = 789.0
margin_bottom = 65.0
custom_constants/separation = 30
script = ExtResource( 1 )
[node name="JoinButton" type="Button" parent="."]
margin_right = 35.0
margin_bottom = 65.0
text = "Join"
[node name="LobbyName" type="Label" parent="."]
unique_name_in_owner = true
margin_left = 65.0
margin_top = 25.0
margin_right = 145.0
margin_bottom = 39.0
text = "Lobby Name"
align = 1
[connection signal="pressed" from="JoinButton" to="." method="_on_join_button_pressed"]

View File

@@ -0,0 +1,103 @@
extends Control
# must be greater than 1024
var SERVER_PORT = 11111
var MAX_PLAYERS = 5
const ChatMessage = preload("res://mods-unpacked/Brogether-Brogether/ui/chat/chat_message.tscn")
const LobbyEntry = preload("res://mods-unpacked/Brogether-Brogether/ui/lobby_entry.tscn")
const DirectConnection = preload("res://mods-unpacked/Brogether-Brogether/networking/direct_connection.gd")
var GameController = load("res://mods-unpacked/Brogether-Brogether/networking/game_controller.gd")
var direct_connection
var game_controller
var DEBUG = false
onready var chat_messages = $"%ChatMessages"
onready var lobbies_list = $"%Lobbies"
onready var chat_input : LineEdit = $"%ChatInput"
onready var create_lobby_button : Button = $"%CreateLobbyButton"
# Manual on ready vars
var steam_connection
var brotatogether_options
# Called when the node enters the scene tree for the first time.
func _ready():
steam_connection = $"/root/SteamConnection"
steam_connection.connect("global_chat_received", self, "_received_global_chat")
steam_connection.connect("game_lobby_found", self, "_game_lobby_found")
brotatogether_options = $"/root/BrotogetherOptions"
for message in steam_connection.pending_system_messages:
var new_message_node = ChatMessage.instance()
new_message_node.message = message
new_message_node.username = "SYSTEM"
chat_messages.add_child(new_message_node)
steam_connection.pending_system_messages.clear()
create_lobby_button.grab_focus()
CoopService.clear_coop_players()
# var solo_test_btn = Button.new()
# solo_test_btn.text = "Solo Test"
# create_lobby_button.get_parent().add_child(solo_test_btn)
# solo_test_btn.connect("pressed", self, "_on_solo_test_button_pressed")
func _input(event:InputEvent)->void :
manage_back(event)
func manage_back(event:InputEvent)->void :
if event.is_action_pressed("ui_cancel"):
RunData.current_zone = 0
RunData.reload_music = false
var _error = get_tree().change_scene(MenuData.title_screen_scene)
func _on_back_button_pressed():
RunData.reload_music = false
var _error = get_tree().change_scene(MenuData.title_screen_scene)
func _on_chat_input_text_entered(message):
steam_connection.send_global_chat_message(message)
chat_input.clear()
func _received_global_chat(user, message) -> void:
var new_message_node = ChatMessage.instance()
new_message_node.message = message
new_message_node.username = user
chat_messages.add_child(new_message_node)
func _on_solo_test_button_pressed() -> void:
steam_connection.game_lobby_id = 1
steam_connection.game_lobby_owner_id = steam_connection.steam_id
steam_connection.lobby_members = [steam_connection.steam_id]
steam_connection.lobby_member_names = [Steam.getFriendPersonaName(steam_connection.steam_id)]
brotatogether_options.is_solo_test = true
brotatogether_options.joining_multiplayer_lobby = true
var _error = get_tree().change_scene(MenuData.character_selection_scene)
func _on_create_lobby_button_pressed():
steam_connection.create_new_game_lobby()
func _on_refresh_lobbies_button_pressed():
for child in lobbies_list.get_children():
lobbies_list.remove_child(child)
steam_connection.request_lobby_search()
func _game_lobby_found(lobby_id, lobby_name) -> void:
var new_lobby_entry = LobbyEntry.instance()
new_lobby_entry.lobby_id = lobby_id
new_lobby_entry.lobby_name = lobby_name
lobbies_list.add_child(new_lobby_entry)

View File

@@ -0,0 +1,107 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://resources/themes/base_theme.tres" type="Theme" id=2]
[ext_resource path="res://mods-unpacked/Brogether-Brogether/ui/multiplayer_menu.gd" type="Script" id=3]
[ext_resource path="res://ui/menus/global/my_menu_button.gd" type="Script" id=4]
[node name="MultiplayerMenu" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = -3.0
margin_bottom = 10.0
theme = ExtResource( 2 )
script = ExtResource( 3 )
[node name="TitleLabel" type="Label" parent="."]
self_modulate = Color( 1, 0.6, 0.1, 1 )
margin_left = 300.0
margin_top = 48.0
margin_right = 1000.0
margin_bottom = 148.0
text = "Brogether"
[node name="BackButton" type="Button" parent="."]
unique_name_in_owner = true
self_modulate = Color( 1, 1, 1, 0.862745 )
margin_left = 48.0
margin_top = 48.0
margin_right = 233.0
margin_bottom = 99.0
size_flags_horizontal = 4
size_flags_vertical = 4
text = "MENU_BACK"
script = ExtResource( 4 )
[node name="HBoxContainer" type="HBoxContainer" parent="."]
margin_left = 300.0
margin_top = 173.0
margin_right = 1883.0
margin_bottom = 1066.0
[node name="ChatContainer2" type="VBoxContainer" parent="HBoxContainer"]
margin_right = 789.0
margin_bottom = 893.0
size_flags_horizontal = 3
[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer/ChatContainer2"]
margin_right = 789.0
margin_bottom = 893.0
custom_constants/separation = 28
[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer/ChatContainer2/VBoxContainer"]
margin_right = 789.0
margin_bottom = 65.0
[node name="CreateLobbyButton" type="Button" parent="HBoxContainer/ChatContainer2/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
margin_right = 303.0
margin_bottom = 65.0
text = "Create Lobby"
[node name="RefreshLobbiesButton" type="Button" parent="HBoxContainer/ChatContainer2/VBoxContainer/HBoxContainer"]
margin_left = 307.0
margin_right = 666.0
margin_bottom = 65.0
text = "Refresh Lobbies"
[node name="ScrollContainer" type="ScrollContainer" parent="HBoxContainer/ChatContainer2/VBoxContainer"]
margin_top = 93.0
margin_right = 789.0
margin_bottom = 893.0
rect_min_size = Vector2( 0, 800 )
[node name="Lobbies" type="VBoxContainer" parent="HBoxContainer/ChatContainer2/VBoxContainer/ScrollContainer"]
unique_name_in_owner = true
margin_right = 789.0
size_flags_horizontal = 3
custom_constants/separation = 15
[node name="ChatContainer" type="VBoxContainer" parent="HBoxContainer"]
margin_left = 793.0
margin_right = 1583.0
margin_bottom = 893.0
size_flags_horizontal = 3
[node name="ScrollContainer" type="ScrollContainer" parent="HBoxContainer/ChatContainer"]
margin_right = 790.0
margin_bottom = 800.0
rect_min_size = Vector2( 0, 800 )
[node name="ChatMessages" type="VBoxContainer" parent="HBoxContainer/ChatContainer/ScrollContainer"]
unique_name_in_owner = true
margin_right = 790.0
size_flags_horizontal = 3
custom_constants/separation = 15
[node name="ChatInput" type="LineEdit" parent="HBoxContainer/ChatContainer"]
unique_name_in_owner = true
margin_top = 804.0
margin_right = 790.0
margin_bottom = 858.0
placeholder_text = "Type to Chat..."
[connection signal="pressed" from="BackButton" to="." method="_on_back_button_pressed"]
[connection signal="pressed" from="HBoxContainer/ChatContainer2/VBoxContainer/HBoxContainer/CreateLobbyButton" to="." method="_on_create_lobby_button_pressed"]
[connection signal="pressed" from="HBoxContainer/ChatContainer2/VBoxContainer/HBoxContainer/RefreshLobbiesButton" to="." method="_on_refresh_lobbies_button_pressed"]
[connection signal="text_entered" from="HBoxContainer/ChatContainer/ChatInput" to="." method="_on_chat_input_text_entered"]

View File

@@ -0,0 +1,102 @@
extends HBoxContainer
signal ready_toggled(is_ready)
onready var selected_character_element := get_node("%SelectedCharacter")
onready var selected_weapon_element := get_node("%SelectedWeapon")
onready var selected_danger_element := get_node("%SelectedDanger")
onready var username_label := get_node("%Username")
onready var ready_toggle := get_node("%ReadyToggle")
var unpicked_icon = load("res://items/global/random_icon.png")
func set_player_name(name:String) -> void:
username_label.text = name
func disable_selections() -> void:
selected_character_element.disabled = true
selected_weapon_element.disabled = true
selected_danger_element.disabled = true
func hide_ready_toggle() -> void:
ready_toggle.hide()
func disable_ready_toggle() -> void:
ready_toggle.disabled = true
func _on_SelectedCharacter_element_pressed(_element):
$"/root/GameController".back_to_lobby = true
var _error = get_tree().change_scene(MenuData.character_selection_scene)
func _on_SelectedWeapon_element_pressed(_element):
$"/root/GameController".back_to_lobby = true
var _error = get_tree().change_scene(MenuData.weapon_selection_scene)
func set_player_selections(selections_dict : Dictionary, is_me:bool = false, lock_danger: bool = false) -> void:
var weapon_required = true
if selections_dict.has("character"):
var character_id = selections_dict["character"]
for character in ItemService.characters:
if character.my_id == character_id:
selected_character_element.set_element(character)
if character.starting_weapons.empty():
weapon_required = false
break
if is_me:
if weapon_required:
selected_weapon_element.disabled = false
else:
selected_weapon_element.disabled = true
selected_danger_element.disabled = false
else:
selected_character_element.icon = unpicked_icon
selected_weapon_element.disabled = true
selected_danger_element.disabled = true
if selections_dict.has("weapon"):
var weapon_id = selections_dict["weapon"]
for weapon in ItemService.weapons:
if weapon.my_id == weapon_id:
selected_weapon_element.set_element(weapon)
break
if is_me:
selected_danger_element.disabled = false
else:
selected_weapon_element.icon = unpicked_icon
selected_danger_element.disabled = weapon_required
if selections_dict.has("danger"):
var danger_id = selections_dict["danger"]
for difficulty in ItemService.difficulties:
if difficulty.my_id == danger_id:
selected_danger_element.set_element(difficulty)
break
else:
selected_danger_element.icon = unpicked_icon
if selections_dict.has("ready"):
ready_toggle.pressed = selections_dict["ready"]
if lock_danger:
selected_danger_element.disabled = true
if is_me:
selected_character_element.disabled = false
if not is_me:
disable_selections()
func _on_select_danger_element_pressed(_element):
$"/root/GameController".back_to_lobby = true
var _error = get_tree().change_scene(MenuData.difficulty_selection_scene)
func _on_ReadyToggle_toggled(button_pressed):
emit_signal("ready_toggled", button_pressed)

View File

@@ -0,0 +1,67 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://mods-unpacked/Brogether-Brogether/ui/player_selections.gd" type="Script" id=1]
[ext_resource path="res://items/global/inventory_element.tscn" type="PackedScene" id=2]
[ext_resource path="res://items/global/random_icon.png" type="Texture" id=5]
[node name="PlayerSelections" type="HBoxContainer"]
margin_right = 1120.0
margin_bottom = 105.0
custom_constants/separation = 5
script = ExtResource( 1 )
[node name="SelectedCharacter" parent="." instance=ExtResource( 2 )]
unique_name_in_owner = true
margin_right = 108.0
margin_bottom = 105.0
rect_min_size = Vector2( 105, 105 )
size_flags_horizontal = 0
size_flags_vertical = 0
icon = ExtResource( 5 )
expand_icon = false
[node name="SelectedWeapon" parent="." instance=ExtResource( 2 )]
unique_name_in_owner = true
margin_left = 113.0
margin_right = 218.0
margin_bottom = 105.0
rect_min_size = Vector2( 105, 105 )
size_flags_horizontal = 0
size_flags_vertical = 0
icon = ExtResource( 5 )
[node name="SelectedDanger" parent="." instance=ExtResource( 2 )]
unique_name_in_owner = true
margin_left = 223.0
margin_right = 328.0
margin_bottom = 105.0
rect_min_size = Vector2( 105, 105 )
size_flags_horizontal = 0
size_flags_vertical = 0
icon = ExtResource( 5 )
[node name="Username" type="Label" parent="."]
unique_name_in_owner = true
margin_left = 333.0
margin_top = 30.0
margin_right = 970.0
margin_bottom = 75.0
rect_min_size = Vector2( 500, 0 )
size_flags_horizontal = 3
text = "USERNAME"
clip_text = true
[node name="ReadyToggle" type="CheckButton" parent="."]
unique_name_in_owner = true
margin_left = 975.0
margin_top = 32.0
margin_right = 1120.0
margin_bottom = 72.0
size_flags_vertical = 4
text = "Ready"
align = 1
[connection signal="element_pressed" from="SelectedCharacter" to="." method="_on_SelectedCharacter_element_pressed"]
[connection signal="element_pressed" from="SelectedWeapon" to="." method="_on_SelectedWeapon_element_pressed"]
[connection signal="element_pressed" from="SelectedDanger" to="." method="_on_select_danger_element_pressed"]
[connection signal="toggled" from="ReadyToggle" to="." method="_on_ReadyToggle_toggled"]

View File

@@ -0,0 +1,25 @@
[gd_scene load_steps=8 format=2]
[ext_resource path="res://resources/fonts/raw/NotoSansSC-Medium.otf" type="DynamicFontData" id=1]
[ext_resource path="res://resources/themes/base_theme.tres" type="Theme" id=2]
[ext_resource path="res://resources/fonts/raw/Anybody-Medium.ttf" type="DynamicFontData" id=3]
[ext_resource path="res://resources/fonts/raw/NotoSansJP-Medium.otf" type="DynamicFontData" id=4]
[ext_resource path="res://resources/fonts/raw/NotoSansTC-Medium.otf" type="DynamicFontData" id=5]
[ext_resource path="res://resources/fonts/raw/NotoSansKR-Medium.otf" type="DynamicFontData" id=6]
[sub_resource type="DynamicFont" id=1]
size = 18
extra_spacing_top = 10
font_data = ExtResource( 3 )
fallback/0 = ExtResource( 4 )
fallback/1 = ExtResource( 6 )
fallback/2 = ExtResource( 1 )
fallback/3 = ExtResource( 5 )
[node name="Label" type="Label"]
margin_left = 136.0
margin_right = 191.0
margin_bottom = 23.0
theme = ExtResource( 2 )
custom_fonts/font = SubResource( 1 )
text = "User 1 - 1"

View File

@@ -0,0 +1,63 @@
extends StatsContainer
onready var game_controller = $"/root/GameController"
const shop_options_resource = preload("res://mods-unpacked/Brogether-Brogether/opponents_shop/data/opponents_shop_options.tres")
const shop_monster_container_scene = preload("res://mods-unpacked/Brogether-Brogether/ui/shop/shop_monster_container.tscn")
func _ready():
if not $"/root".has_node("GameController") or $"/root/GameController".is_coop():
$"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/StatsContainer/MarginContainer/VBoxContainer2/OpponentsButton".hide()
var opponents_shop = $MarginContainer/VBoxContainer2/OpponentsShop
for opponents_shop_option in shop_options_resource.shop_options:
var shop_option = shop_monster_container_scene.instance()
shop_option.init(opponents_shop_option, game_controller)
opponents_shop.add_child(shop_option)
func update_tab(tab:int)->void :
var has_opponents_tab = $"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/StatsContainer/MarginContainer/VBoxContainer2".has_node("OpponentsButton")
if tab == 3:
if has_opponents_tab:
var opponents_button = $"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/StatsContainer/MarginContainer/VBoxContainer2/OpponentsButton"
var opponents_shop = $"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/StatsContainer/MarginContainer/VBoxContainer2/OpponentsShop"
opponents_button.flat = true
opponents_shop.show()
_primary_tab.flat = false
_secondary_tab.flat = false
_general_stats.hide()
_primary_stats.hide()
_secondary_stats.hide()
pass
else:
if has_opponents_tab:
var opponents_button = $"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/StatsContainer/MarginContainer/VBoxContainer2/OpponentsButton"
var opponents_shop = $"/root/Shop/Content/MarginContainer/HBoxContainer/VBoxContainer2/StatsContainer/MarginContainer/VBoxContainer2/OpponentsShop"
opponents_button.flat = false
opponents_shop.hide()
.update_tab(tab)
func _on_OpponentsButton_pressed():
update_tab(3)
func update_bought_items(tracked_players:Dictionary) -> void:
var opponents_shop = $MarginContainer/VBoxContainer2/OpponentsShop
for shop_item_container in opponents_shop.get_children():
shop_item_container.clear_player_counts()
for tracked_player_id in tracked_players:
var all_effects = {}
if tracked_players[tracked_player_id].has("extra_enemies_next_wave"):
all_effects.merge(tracked_players[tracked_player_id]["extra_enemies_next_wave"].duplicate())
if tracked_players[tracked_player_id].has("effects"):
all_effects.merge(tracked_players[tracked_player_id]["effects"])
for effect_path in all_effects:
if effect_path == shop_item_container._shop_option.effect.get_path():
var tracked_player = tracked_players[tracked_player_id]
var count = all_effects[effect_path]
var display_name = str(tracked_player_id)
if tracked_player.has("username"):
display_name = tracked_player["username"]
shop_item_container.add_player_count(display_name, count)

View File

@@ -0,0 +1,368 @@
[gd_scene load_steps=8 format=2]
[ext_resource path="res://ui/menus/shop/secondary_stats_container.gd" type="Script" id=2]
[ext_resource path="res://resources/themes/base_theme.tres" type="Theme" id=3]
[ext_resource path="res://ui/menus/shop/stat_container.tscn" type="PackedScene" id=4]
[ext_resource path="res://ui/menus/shop/general_stats_container.tscn" type="PackedScene" id=5]
[ext_resource path="res://ui/menus/shop/secondary_stat_container.tscn" type="PackedScene" id=6]
[ext_resource path="res://ui/menus/global/my_menu_button.gd" type="Script" id=7]
[ext_resource path="res://mods-unpacked/Brogether-Brogether/ui/shop/multiplayer_stats_container.gd" type="Script" id=8]
[node name="MultiplayerStats" type="PanelContainer"]
self_modulate = Color( 1, 1, 1, 0.478431 )
margin_top = 132.0
margin_right = 384.0
margin_bottom = 368.0
rect_min_size = Vector2( 411, 782 )
theme = ExtResource( 3 )
script = ExtResource( 8 )
[node name="MarginContainer" type="MarginContainer" parent="."]
margin_left = 5.0
margin_top = 5.0
margin_right = 406.0
margin_bottom = 1332.0
custom_constants/margin_right = 10
custom_constants/margin_top = 10
custom_constants/margin_left = 10
custom_constants/margin_bottom = 10
[node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer"]
margin_left = 10.0
margin_top = 10.0
margin_right = 391.0
margin_bottom = 1317.0
custom_constants/separation = 20
[node name="OpponentsButton" type="Button" parent="MarginContainer/VBoxContainer2"]
margin_right = 381.0
margin_bottom = 51.0
rect_min_size = Vector2( 150, 0 )
text = "Opponents Shop"
clip_text = true
script = ExtResource( 7 )
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer2"]
margin_top = 71.0
margin_right = 381.0
margin_bottom = 122.0
custom_constants/separation = 10
alignment = 1
[node name="Primary" type="Button" parent="MarginContainer/VBoxContainer2/HBoxContainer"]
unique_name_in_owner = true
margin_left = 20.0
margin_right = 170.0
margin_bottom = 51.0
rect_min_size = Vector2( 150, 0 )
text = "PRIMARY"
clip_text = true
script = ExtResource( 7 )
[node name="Secondary" type="Button" parent="MarginContainer/VBoxContainer2/HBoxContainer"]
unique_name_in_owner = true
margin_left = 180.0
margin_right = 360.0
margin_bottom = 51.0
rect_min_size = Vector2( 180, 0 )
text = "SECONDARY"
clip_text = true
script = ExtResource( 7 )
[node name="GeneralStats" parent="MarginContainer/VBoxContainer2" instance=ExtResource( 5 )]
margin_top = 142.0
margin_right = 381.0
margin_bottom = 172.0
[node name="PrimaryStats" type="VBoxContainer" parent="MarginContainer/VBoxContainer2"]
unique_name_in_owner = true
margin_top = 192.0
margin_right = 381.0
margin_bottom = 687.0
custom_constants/separation = 1
[node name="MaxHPContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_right = 381.0
margin_bottom = 30.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_MAX_HP"
[node name="HPRegenerationContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 31.0
margin_right = 381.0
margin_bottom = 61.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_HP_REGENERATION"
[node name="LifeStealContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 62.0
margin_right = 381.0
margin_bottom = 92.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_LIFESTEAL"
[node name="PercentDamageContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 93.0
margin_right = 381.0
margin_bottom = 123.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_PERCENT_DAMAGE"
[node name="MeleeDamageContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 124.0
margin_right = 381.0
margin_bottom = 154.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_MELEE_DAMAGE"
[node name="RangedDamageContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 155.0
margin_right = 381.0
margin_bottom = 185.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_RANGED_DAMAGE"
[node name="ElementalDamageContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 186.0
margin_right = 381.0
margin_bottom = 216.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_ELEMENTAL_DAMAGE"
[node name="AttackSpeedContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 217.0
margin_right = 381.0
margin_bottom = 247.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_ATTACK_SPEED"
[node name="CritChanceContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 248.0
margin_right = 381.0
margin_bottom = 278.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_CRIT_CHANCE"
[node name="EngineeringContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 279.0
margin_right = 381.0
margin_bottom = 309.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_ENGINEERING"
[node name="RangeContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 310.0
margin_right = 381.0
margin_bottom = 340.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_RANGE"
[node name="ArmorContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 341.0
margin_right = 381.0
margin_bottom = 371.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_ARMOR"
[node name="DodgeContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 372.0
margin_right = 381.0
margin_bottom = 402.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_DODGE"
[node name="SpeedContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 403.0
margin_right = 381.0
margin_bottom = 433.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_SPEED"
[node name="LuckContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 434.0
margin_right = 381.0
margin_bottom = 464.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_LUCK"
[node name="HarvestingContainer" parent="MarginContainer/VBoxContainer2/PrimaryStats" instance=ExtResource( 4 )]
margin_top = 465.0
margin_right = 381.0
margin_bottom = 495.0
__meta__ = {
"_edit_use_anchors_": false
}
key = "STAT_HARVESTING"
[node name="SecondaryStats" type="VBoxContainer" parent="MarginContainer/VBoxContainer2"]
unique_name_in_owner = true
margin_top = 707.0
margin_right = 381.0
margin_bottom = 1307.0
custom_constants/separation = 0
script = ExtResource( 2 )
[node name="ConsumableHealContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_right = 381.0
margin_bottom = 30.0
key = "CONSUMABLE_HEAL"
[node name="MaterialsHealContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 30.0
margin_right = 381.0
margin_bottom = 60.0
key = "heal_when_pickup_gold"
custom_text_key = "CHANCE_HEAL_ON_GOLD"
[node name="XPGainContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 60.0
margin_right = 381.0
margin_bottom = 90.0
key = "XP_GAIN"
[node name="PickupRangeContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 90.0
margin_right = 381.0
margin_bottom = 120.0
key = "PICKUP_RANGE"
[node name="ItemPriceContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 120.0
margin_right = 381.0
margin_bottom = 150.0
key = "ITEMS_PRICE"
reverse = true
[node name="ExplosionDamageContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 150.0
margin_right = 381.0
margin_bottom = 180.0
key = "EXPLOSION_DAMAGE"
[node name="ExplosionSizeContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 180.0
margin_right = 381.0
margin_bottom = 210.0
key = "EXPLOSION_SIZE"
[node name="BounceContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 210.0
margin_right = 381.0
margin_bottom = 240.0
key = "BOUNCE"
[node name="PiercingContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 240.0
margin_right = 381.0
margin_bottom = 270.0
key = "PIERCING"
[node name="PiercingDamageContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 270.0
margin_right = 381.0
margin_bottom = 300.0
key = "PIERCING_DAMAGE"
[node name="DamageAgainstBossesContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 300.0
margin_right = 381.0
margin_bottom = 330.0
key = "DAMAGE_AGAINST_BOSSES"
[node name="BurningCooldownReductionContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 330.0
margin_right = 381.0
margin_bottom = 360.0
key = "BURNING_COOLDOWN_REDUCTION"
[node name="BurningSpreadContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 360.0
margin_right = 381.0
margin_bottom = 390.0
key = "BURNING_SPREAD"
[node name="KnockbackContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 390.0
margin_right = 381.0
margin_bottom = 420.0
key = "KNOCKBACK"
[node name="ChanceDoubleGoldContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 420.0
margin_right = 381.0
margin_bottom = 450.0
key = "CHANCE_DOUBLE_GOLD"
[node name="ItemBoxGoldContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 450.0
margin_right = 381.0
margin_bottom = 480.0
key = "ITEM_BOX_GOLD"
[node name="FreeRerollsContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 480.0
margin_right = 381.0
margin_bottom = 510.0
key = "FREE_REROLLS"
[node name="TreesContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 510.0
margin_right = 381.0
margin_bottom = 540.0
key = "TREES"
[node name="NumberOfEnemiesContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 540.0
margin_right = 381.0
margin_bottom = 570.0
key = "NUMBER_OF_ENEMIES"
custom_text_key = "PCT_NUMBER_OF_ENEMIES"
[node name="EnemySpeedContainer" parent="MarginContainer/VBoxContainer2/SecondaryStats" instance=ExtResource( 6 )]
margin_top = 570.0
margin_right = 381.0
margin_bottom = 600.0
key = "ENEMY_SPEED"
custom_text_key = "PCT_ENEMY_SPEED"
reverse = true
[node name="OpponentsShop" type="VBoxContainer" parent="MarginContainer/VBoxContainer2"]
visible = false
margin_top = 1327.0
margin_right = 354.0
margin_bottom = 1327.0
custom_constants/separation = 10
[connection signal="pressed" from="MarginContainer/VBoxContainer2/OpponentsButton" to="." method="_on_OpponentsButton_pressed"]
[connection signal="pressed" from="MarginContainer/VBoxContainer2/HBoxContainer/Primary" to="." method="_on_Primary_pressed"]
[connection signal="pressed" from="MarginContainer/VBoxContainer2/HBoxContainer/Secondary" to="." method="_on_Secondary_pressed"]

View File

@@ -0,0 +1,48 @@
extends VBoxContainer
class_name ShopMonsterContainer
var _shop_option
var game_controller
const count_label_scene = preload("res://mods-unpacked/Brogether-Brogether/ui/shop/count_label.tscn")
# TODO rename to work for all types
func init(shop_option: Resource, parent_game_controller) -> void:
game_controller = parent_game_controller
_shop_option = shop_option
var price: int = shop_option.price
var effect: Resource = shop_option.effect
var display_text: String = shop_option.display_text
get_node("ShopItem/Title").text = display_text
var _connection_error = get_node("ShopItem/VBoxContainer/BuyButton").connect("pressed", self, "_on_buyButton_pressed", [shop_option])
if effect is WaveGroupData:
for wave_unit_data in effect.wave_units_data:
var enemy = wave_unit_data.unit_scene.instance()
get_node("ShopItem/Icon").texture = enemy.get_node("Animation/Sprite").texture
elif effect is Effect:
if effect.key.begins_with("stat_"):
get_node("ShopItem/Icon").texture = ItemService.get_stat_icon(effect.key)
var adjusted_price = ItemService.get_value(RunData.current_wave, price, true, true)
get_node("ShopItem/VBoxContainer/BuyButton").text = str(adjusted_price)
func _on_buyButton_pressed(shop_option: Resource) -> void:
var adjusted_price = ItemService.get_value(RunData.current_wave, shop_option.price, true, true)
if RunData.gold >= adjusted_price:
RunData.remove_gold(adjusted_price)
game_controller.send_bought_item(shop_option)
$"/root/Shop"._shop_items_container.update_buttons_color()
func add_player_count(username:String, count: int) -> void:
var label = count_label_scene.instance()
label.text = str(username, "-", count)
get_node("PurchaseTracker").add_child(label)
func clear_player_counts() -> void:
var purchase_tracker = get_node("PurchaseTracker")
for child in purchase_tracker.get_children():
purchase_tracker.remove_child(child)

View File

@@ -0,0 +1,71 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://items/materials/material_ui.png" type="Texture" id=1]
[ext_resource path="res://entities/units/enemies/001/1.png" type="Texture" id=4]
[ext_resource path="res://mods-unpacked/Brogether-Brogether/ui/shop/shop_monster_container.gd" type="Script" id=5]
[node name="ShopMonsterContainer" type="VBoxContainer"]
margin_left = -212.5
margin_right = 114.5
margin_bottom = 116.0
grow_horizontal = 2
size_flags_vertical = 0
alignment = 1
script = ExtResource( 5 )
[node name="ShopItem" type="HBoxContainer" parent="."]
margin_top = 18.0
margin_right = 327.0
margin_bottom = 93.0
custom_constants/separation = 5
alignment = 2
[node name="Icon" type="TextureRect" parent="ShopItem"]
margin_left = 30.0
margin_right = 105.0
margin_bottom = 75.0
rect_min_size = Vector2( 75, 75 )
texture = ExtResource( 4 )
expand = true
stretch_mode = 6
[node name="Title" type="Label" parent="ShopItem"]
margin_left = 110.0
margin_top = 25.0
margin_right = 222.0
margin_bottom = 49.0
text = "Baby Alien"
align = 2
[node name="VBoxContainer" type="VBoxContainer" parent="ShopItem"]
margin_left = 227.0
margin_right = 327.0
margin_bottom = 75.0
alignment = 1
[node name="BuyButton" type="Button" parent="ShopItem/VBoxContainer"]
margin_top = 19.0
margin_right = 100.0
margin_bottom = 56.0
grow_horizontal = 0
grow_vertical = 0
rect_min_size = Vector2( 100, 0 )
focus_neighbour_left = NodePath("../../../ShopItem/HBoxContainer/LockButton")
focus_neighbour_right = NodePath("../../../ShopItem3/HBoxContainer/LockButton")
size_flags_horizontal = 12
size_flags_vertical = 0
toggle_mode = true
text = "1500"
icon = ExtResource( 1 )
icon_align = 2
expand_icon = true
__meta__ = {
"_edit_use_anchors_": false
}
[node name="PurchaseTracker" type="HBoxContainer" parent="."]
margin_top = 97.0
margin_right = 327.0
margin_bottom = 97.0
custom_constants/separation = 10
alignment = 1

View File

@@ -0,0 +1,10 @@
[gd_scene load_steps=1 format=2]
[node name="DamageDisplayButton" type="CheckButton"]
unique_name_in_owner = true
margin_top = 365.0
margin_right = 780.0
margin_bottom = 435.0
focus_neighbour_right = NodePath("../../../../Column2/SoundContainer/PauseOnFocusLostButton")
text = "Ready"

View File

@@ -0,0 +1,6 @@
[gd_scene format=2]
[node name="username_label" type="Label"]
size_flags_vertical = 1
text = "Username"
align = 1