StreamOverlay/UI/floating_menu.gd
Mario Steele 5f8df9bf7b Updated FloatingMenu
Added Testing parameters for Alerts, so we can test alerts being used.
Added Camera variable @onready fetch.
Added OBS and VTuber Process ID, Camera and VTuber Scene ID.
Connected signal to get Scene ID's when we connect to OBS.
Added event handlers for Step Array (Testing Alerts), VTuber launcher,
and Camera toggle to switch between VTuber and Camera.
2026-03-02 02:14:07 -06:00

251 lines
10 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends GDSubMenuButton
class_name GDFloatingMenu
const COL_ON := Color.LIGHT_GREEN
const COL_OFF := Color.LIGHT_SALMON
const SETTINGS_PANEL = preload("res://UI/settings_panel.tscn")
const SCRIPT_EDITOR = preload("res://UI/Controls/script_editor.tscn")
#region Alerts (Testing)
const FOLLOW_ALERT = preload("res://UI/Alerts/follow_alert.tscn")
const CHEER_ALERT = preload("res://UI/Alerts/cheer_alert.tscn")
const RAID_ALERT = preload("res://UI/Alerts/raid_alert.tscn")
const SUBSCRIBER_ALERT = preload("res://UI/Alerts/subscriber_alert.tscn")
var _impl_alerts: Array = [
FOLLOW_ALERT,
CHEER_ALERT,
RAID_ALERT,
SUBSCRIBER_ALERT,
]
const RANDOM_NAMES := [
"Voylin",
"meneldal",
"Pengo",
"Adam",
"Yagich",
"Falinere",
"Eroaxee",
"wolbee",
"JeSuisEmma",
"Temptic404",
"RobitussinMD",
"spimort",
"FuzzyDemonBunny",
"OdatNurd",
]
const RANDOM_MESSAGES := [
"Android was an easy fix, accidentally applied vpx to everything in Scons, Windows need an AUR package, and the arm builds are complaining about not finding vpx (probably no static build available). So not too difficult to fix luckily. But for arm I might need to build vpx myself for those builds, and whilst I'm at it I can implement support for Android as I need to clone and build it anyway for the arm builds ... Well ... that will be for later as I'll probably get stuck on this for the entire day without finishing what I started working on before XD",
"awesome, im going to run the project update tool with 4.6 and PR that so that we don't have 4.6 upgraded files in future PRs",
"I can do any time during the day or night, I can even wake up in the middle of the night if I need to",
"done 🙂 ready when you are",
"you probably been busy lately, but just curious how progress was coming along on the new launcher?",
"A version or two ago they moved the strip properties from the sequencer timeline (in blue) to the generic \"properties\" panel (in red) thats used everywhere else.",
"thanks, I realised my models were all subdivided triangle riddled messes!",
"I still don't know how to make a good short - but I can't get over the background music I was able to generate for this one. It just cracks me up. ",
"i think my favourite thing about streaming game dev is just how much of us are just sitting there guessing???",
"148 TEAM MEMBERS AND NOBODY TO WATCH ON A SUNDAY EVENING flips table",
"i'd like to formally apologize to @iRad for how i treated him in my really weird dream last night. we entered a game jam together and i spent the entire time talking to friends. he really carried me.",
"Sometimes thats all it takes, just one person believing youll succeed. Or sometimes a whole bunch of people, who mysteriously growl low if you try and leave the chair. And in no way shape or form require you to send eye blinking messages for outside help, nor throw digital snacks in one direction while sneaking out the other. ",
"That's the reason I come into the communities, to be supportive, positive, try to help when possible... because game dev is already hard enough. Sometimes all you need is just that one little boost on a rough day to keep you going and just offset the grind enough to let you feel okay again. That and giving friendly pushes to devs to get that steam page done, or that demo knocked out, or just to push forward progress.",
"Born to late to explore Earth, born to early to explore the universe",
]
#endregion
# Indicators
@onready var indicators: Control = %indicators
@onready var ind_stream: TextureRect = %ind_stream
@onready var ind_mic: TextureRect = %ind_mic
@onready var ind_pixelate: TextureRect = %ind_pixelate
@onready var ind_browser: TextureRect = %ind_browser
# Buttons
@onready var btn_obs: GDSubMenuButton = %btn_obs
@onready var btn_run_obs: GDSubMenuButton = %btn_run_obs
@onready var btn_step_away: GDSubMenuButton = %btn_step_away
@onready var btn_tuber: GDSubMenuButton = %btn_tuber
@onready var btn_camera: GDSubMenuButton = %btn_camera
@onready var btn_chat: GDSubMenuButton = %btn_chat
@onready var btn_script_editor: GDSubMenuButton = %btn_script_editor
@onready var btn_user_list: GDSubMenuButton = %btn_user_list
@onready var btn_settings: GDSubMenuButton = %btn_settings
@onready var btn_end: GDSubMenuButton = %btn_end
var is_anchored := true
var anchored_position: Vector2
var _set_win: Window
var _edit_win: Window
var _obs_pid: int = -1
var _vtuber_pid: int = -1
var _vtuber_sid: int
var _camera_sid: int
func _ready() -> void:
ObsManager.obs_ready.connect(_handle_obs_connect)
func _handle_obs_connect() -> void:
_vtuber_sid = await ObsManager.get_scene_item_id("~Avatar", "~VTuber")
_camera_sid = await ObsManager.get_scene_item_id("~Avatar", "~WebCam")
func start(_main_menu_button: GDSubMenuButton = null) -> void:
anchored_position = position
var _parent: Control = get_parent()
_parent.resized.connect(func(): calculate_anchored_pos(); is_anchored = false)
generate_panels_buttons()
super(self)
start_indicators()
_connect_signals()
func start_indicators() -> void:
var tot = indicators.get_child_count()
for i in tot:
var ind: TextureRect = indicators.get_child(i)
ind.show()
ind.position = (size/2 - ind.size/2)
ind.position += Vector2.from_angle(TAU*i/tot + PI/2) * (size.x/2 - ind.size.x/2)
func generate_panels_buttons() -> void:
# TODO: Generate Panels?
pass
func _process(d: float) -> void:
super(d)
if not is_anchored and not is_dragged:
position = position.lerp(anchored_position, d*10)
if position.distance_squared_to(anchored_position) < 1:
position = anchored_position
is_anchored = true
func _on_gui_input(event: InputEvent) -> void:
super(event)
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if not event.is_pressed():
is_anchored = false
calculate_anchored_pos()
if event is InputEventMouseMotion and is_mouse_click_down:
if is_open:
expand_menu(false)
func calculate_anchored_pos() -> void:
var m_pos := global_position + size/2.0
var w_size := get_window().size / get_tree().root.content_scale_factor
if min(m_pos.x, abs(w_size.x - m_pos.x)) < min(m_pos.y, abs(w_size.y - m_pos.y)):
closest_edge = Edge.LEFT if m_pos.x < abs(w_size.x - m_pos.x) else Edge.RIGHT
else:
closest_edge = Edge.UP if m_pos.y < abs(w_size.y - m_pos.y) else Edge.BOT
match closest_edge:
Edge.UP:
anchored_position = Vector2(position.x, 0)
parent_dir = Vector2.DOWN
Edge.RIGHT:
anchored_position = Vector2(w_size.x - size.x, position.y)
parent_dir = Vector2.LEFT
Edge.BOT:
anchored_position = Vector2(position.x, w_size.y - size.y)
parent_dir = Vector2.UP
Edge.LEFT:
anchored_position = Vector2(0, position.y)
parent_dir = Vector2.RIGHT
anchored_position = anchored_position.clamp(Vector2(), Vector2(w_size)-size)
#region Hnadle Button Functions
func _connect_signals() -> void:
btn_end.properly_pressed.connect(func(): get_tree().quit())
btn_settings.properly_pressed.connect(_handle_settings)
btn_script_editor.properly_pressed.connect(_handle_script_editor)
btn_run_obs.properly_pressed.connect(_handle_run_obs)
btn_step_away.properly_pressed.connect(_handle_step_away)
btn_tuber.properly_pressed.connect(_handle_run_vtuber)
btn_camera.properly_pressed.connect(_handle_camera)
func _handle_settings() -> void:
if _set_win: return
var win := Window.new()
win.borderless = false
win.always_on_top = false
win.mode = Window.MODE_WINDOWED
var pnl := SETTINGS_PANEL.instantiate()
#Globals.disable_mouse_passthrough()
win.add_child(pnl)
win.size = Vector2i(1000,800)
win.close_requested.connect(func(): win.queue_free(); _set_win = null)
win.name = "SettingsWindow"
win.title = "Settings"
get_tree().root.add_child(win)
win.show()
_set_win = win
func _handle_script_editor() -> void:
if _edit_win: return
var win := Window.new()
win.borderless = false
win.always_on_top = false
win.mode = Window.MODE_WINDOWED
var pnl := SCRIPT_EDITOR.instantiate()
win.name = "ScriptEditorWindow"
win.title = "Script Editor"
win.add_child(pnl)
win.size = Vector2i(800,600)
win.close_requested.connect(func(): win.queue_free(); _edit_win = null)
get_tree().root.add_child(win)
win.show()
_edit_win = win
func _handle_run_obs() -> void:
var tree := ProcessTree.new()
if tree.has_process_name(Globals.settings.obs_name):
print("OBS is running.")
return
else:
OS.create_process(Globals.settings.obs_path, [])
func _handle_step_away() -> void:
var alert = _impl_alerts.pick_random().instantiate()
if alert is FollowAlert:
alert.setup(RANDOM_NAMES.pick_random())
elif alert is CheerAlert:
alert.setup(RANDOM_NAMES.pick_random(), randi_range(10,1000), RANDOM_MESSAGES.pick_random())
elif alert is RaidAlert:
alert.setup(RANDOM_NAMES.pick_random(), randi_range(5,500))
elif alert is SubscriberAlert:
var prime: bool = randf() < 0.25
var months: int = randi_range(0,64) if randf() < 0.25 else 1
var tier: bool = randi_range(1,3)
var msg: String = RANDOM_MESSAGES.pick_random() if randf() < 0.45 else ""
alert.setup(RANDOM_NAMES.pick_random(), months, prime, tier, msg)
EventManager.add_alert(alert)
func _handle_run_vtuber() -> void:
var tree := ProcessTree.new()
if tree.has_process_name(Globals.settings.vtuber_name):
print("VTuber is running.")
return
else:
OS.create_process(Globals.settings.vtuber_path, [Globals.settings.vtuber_model_path])
func _handle_camera() -> void:
if not ObsManager.is_open(): return
var vtuber_vis := await ObsManager.get_item_enabled("~Avatar", _vtuber_sid)
ObsManager.set_item_enabled("~Avatar",_vtuber_sid, !vtuber_vis)
ObsManager.set_item_enabled("~Avatar",_camera_sid, vtuber_vis)
var aid := await ObsManager.get_scene_item_id("Talking", "~Avatar")
var transform := await ObsManager.get_item_transform("Talking", aid)
transform.bounds_width = 1.0
transform.bounds_height = 1.0
if vtuber_vis:
transform.position_x = 70
transform.position_y = 320
else:
transform.position_x = 39
transform.position_y = 492
print("Moving ~Avatar to: %s" % [{"x": transform.position_x, "y": transform.position_y}])
ObsManager.set_item_transform("Talking", aid, transform)
#endregion