diff --git a/.gitignore b/.gitignore index fe1988a..e202a50 100644 --- a/.gitignore +++ b/.gitignore @@ -359,6 +359,8 @@ HACKING/__pycache__/** .env onboard/.env HACKING/.env +dcu/.env +dcu/output/** # test locations for save data onboard/backend/.save diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..8a3bacc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "godotTools.editorPath.godot4": "/usr/bin/godot-mono-4.4.1" +} \ No newline at end of file diff --git a/HACKING/export-all.sh b/HACKING/export-all.sh new file mode 100755 index 0000000..35f126b --- /dev/null +++ b/HACKING/export-all.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# first arg is --help or -h or # of args != 3 +if [[ $1 == --help ]] || [[ $1 == -h ]] || [[ $# -ne 3 ]]; then + echo "Usage:" $0 " " + exit +fi + +# backend +# cargo build the backend cause yeah + +# godot frontend +$1 --path $2/onboard/godot-frontend --headless --export-release "Linux" $3/godot_frontend + +exit \ No newline at end of file diff --git a/README.md b/README.md index 1fe79f5..4bafffa 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,12 @@ Run the `update_onboard.sh` script located in HACKING ## Building (manual) -To build and run on the DCU, do the following from `./onboard/frontend`: -``` -dotnet publish -c Release -r linux-x64 --no-self-contained +To build and run on the DCU, do the following from `./onboard/godot-frontend`: +```Bash +godot-4.4.1 --path ./ --headless --export-release "Linux" out-path/godot_frontend ``` And the following from `./onboard/backend`: -``` +```Bash cargo build --release --target x86_64-unknown-linux-gnu ``` To put it on the DCU, compress the `publish` folder located at `./onboard/frontend/bin/Release/netcoreapp3.1/linux-x64` and `scp` that to the DCU. @@ -24,26 +24,32 @@ To setup and launch a development environment, you can do the following: ### Env Vars -There is a file called .env.template in the `./onboard` folder. Fill this in with appropriate values for the backend and frontend. - +There is a file called [.env.template](/onboard/.env.template) in the `./onboard` folder. Copy the file to a new file called `.env` in the same directory. Then fill this in with appropriate values for the backend and frontend. ### Running outside a container -In onboard/frontend, run `dotnet run` +In **onboard/backend**, run `cargo run` -In onboard/backend, run `cargo run` +In the godot project manager, select the **onboard/godot-frontend** folder to open the frontend.
+Or run `godot --editor --path onboard/godot-frontend` The frontend will log warnings about not being able to connect until the backend is up and running -### Building and Launching the Container + + +## Further Devolopment + +### Backend -#### `mgcb` +Nothing here yet -The container has `mgcb-editor` installed. To run that, do this: -`dotnet mgcb-editor` +### Frontend +For more information on: +* Creating a new GUI see: [CreatingAGuiREADME.md](/onboard/godot-frontend/GUIs/CreatingAGuiREADME.md) +* Creating a new screensaver animation see: [RecordingREADME.md](/onboard/godot-frontend/guiManager/screensaver/RecordingREADME.md) \ No newline at end of file diff --git a/dcu/.env.template b/dcu/.env.template new file mode 100644 index 0000000..333a8f3 --- /dev/null +++ b/dcu/.env.template @@ -0,0 +1,3 @@ +PASSWORD_VAR=devcade +ROOT_PASSWORD_VAR=devcade +BRANCH_VAR=main \ No newline at end of file diff --git a/dcu/Dockerfile b/dcu/Dockerfile new file mode 100644 index 0000000..31a451b --- /dev/null +++ b/dcu/Dockerfile @@ -0,0 +1,140 @@ +FROM quay.io/fedora/fedora-bootc:43 + +# user and file systemd configs +COPY ./config-files/files.conf /usr/lib/tmpfiles.d/custom_files.conf +COPY ./config-files/users.conf /usr/lib/sysusers.d/custom_users.conf + +# disable sleep/hiberate features +RUN systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target + +### required software +RUN dnf install -y \ + openbox picom awk scrot xterm pamixer xdg-desktop-portal-gtk \ + gcc-c++ git wget unzip dotnet-sdk-10.0 \ + grep openssh-server openssl-devel fail2ban \ + glib2-devel flatpak-devel libnfc-devel \ + make cmake \ + autoconf automake libtool \ + xrandr @base-x \ + plymouth-plugin-script + +# install libfreefare-devel, as it is not in the package manager yet +RUN git clone https://github.com/nfc-tools/libfreefare.git && \ + cd libfreefare && \ + autoreconf -vis && \ + ./configure --prefix=/usr && \ + make && make install && \ + cd .. && rm -r libfreefare + +# required flatpak runtimes +RUN flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo && \ + flatpak install -y flathub org.freedesktop.Platform/x86_64/22.08 + +# root services config file +# ban people from ssh'ing after some number of attempts for some amount of time (see contents of file for specifics) +COPY ./config-files/sshd.local /etc/fail2ban/jail.d/sshd.local +# ssh server configuration file +COPY ./config-files/sshd_config /etc/ssh/sshd_config +RUN systemctl enable fail2ban; \ + systemctl enable sshd + +# # copy boot splash over +# COPY ./boot-splash/devcade /usr/share/plymouth/themes/devcade +# # set theme +# RUN ls -l /usr/share/plymouth/themes && \ +# plymouth-set-default-theme -R devcade + +# install Rust +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y; +ENV PATH "/root/.cargo/bin:${PATH}" + +# install godot 4.7-stable mono +RUN wget "https://github.com/godotengine/godot/releases/download/4.7-stable/Godot_v4.7-stable_mono_linux_x86_64.zip" && \ + unzip Godot_v4.7-stable_mono_linux_x86_64.zip && \ + mv Godot_v4.7-stable_mono_linux_x86_64/Godot_v4.7-stable_mono_linux.x86_64 /usr/bin/Godot_v4.7-stable_mono_linux.x86_64 && \ + mv Godot_v4.7-stable_mono_linux_x86_64/GodotSharp /usr/bin/GodotSharp; \ + rm Godot_v4.7-stable_mono_linux_x86_64.zip + +# file required for xauth +RUN touch .Xauthority + +### copy required files +# NOTE: required symlinks for some config files are created in dcu/config-files/files.conf +# ostree settings +COPY ./config-files/prepare-root.conf /usr/lib/ostree/prepare-root.conf + +# openbox config file +COPY ./config-files/rc.xml /etc/xdg/openbox/rc.xml + +# x11 init file +COPY ./config-files/.xinitrc /etc/X11/xinit/xinitrc + +# bash init file +COPY ./config-files/.bashrc /etc/bashrc + +# autologin service +COPY ./config-files/tty1_service_override.conf /etc/systemd/system/getty@tty1.service.d/override.conf + +# required custom executables +COPY ./config-files/export-all ./config-files/devcade_onboard /usr/bin/ + +# export templates +COPY ./config-files/linux_debug.x86_64 \ + ./config-files/linux_release.x86_64 \ + ./config-files/linux_debug.x86_32 \ + ./config-files/linux_release.x86_32 \ + /usr/share/godot/export_templates/4.7.stable.mono/ + +# symlink required for the export functionality of the godot cli, +# I blame godot for not letting me change the install location of the export templates +RUN mkdir -p /root/.local/share/godot/export_templates && \ + ln -s -f /usr/share/godot/export_templates/4.7.stable.mono /root/.local/share/godot/export_templates/ + +# devcade-onboard git repository +ARG BRANCH_VAR +RUN git clone https://github.com/matthewlefler/devcade-onboard-ui-revamp.git && \ + cd devcade-onboard-ui-revamp && git switch ${BRANCH_VAR} + +RUN mkdir -p /usr/share/devcade/ && \ + cp ./devcade-onboard-ui-revamp/onboard/.env.template /usr/share/devcade/.env + +# export-all +RUN export-all \ + Godot_v4.7-stable_mono_linux.x86_64 \ + /devcade-onboard-ui-revamp \ + /usr/bin + +# delete git repo +RUN rm -r /devcade-onboard-ui-revamp + +# enable autologin +RUN systemctl enable getty@tty1 + +# niceity software +RUN dnf install -y \ + vim fastfetch btop lspci usbutils gdb \ + && dnf clean all + +### cleanup +# remove all log files, runtime only, and temporary files +RUN rm -rf /var; \ + rm -rf /run; \ + rm -rf /tmp; \ + mkdir -p /var/home; \ + mkdir -p /run; \ + mkdir -p /tmp; + +ARG PASSWORD_VAR +ARG ROOT_PASSWORD_VAR +# set passwords and set devcade acc to not expire +RUN chage -E -1 devcade; \ + echo -e "devcade:${PASSWORD_VAR}" | chpasswd; \ + echo -e "root:${ROOT_PASSWORD_VAR}" | chpasswd; + +# make it known as a bootc container +LABEL containers.bootc=1 +LABEL bootc.rootfs=/sysroot + +RUN bootc container lint + +RUN plymouth-set-default-theme --list diff --git a/dcu/Dockerfile.nvidia b/dcu/Dockerfile.nvidia new file mode 100644 index 0000000..67a1d69 --- /dev/null +++ b/dcu/Dockerfile.nvidia @@ -0,0 +1,31 @@ +FROM localhost/dcu-devcade-onboard + +### Nvidia drivers +# enable require repositories +RUN dnf -y install \ + https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm \ + https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm && \ +# Install NVIDIA drivers + dnf install -y --allowerasing \ + akmod-nvidia \ + xorg-x11-drv-nvidia \ + xorg-x11-drv-nvidia-cuda + +# Rebuild akmods +RUN akmods --force --kernels `rpm -q --queryformat '%{VERSION}-%{RELEASE}.%{ARCH}' kernel-devel` +# add kernel arguments +# see: https://bootc.dev/bootc/building/kernel-arguments.html +RUN mkdir -p /usr/lib/bootc/kargs.d +RUN cat <> /usr/lib/bootc/kargs.d/01-nvidia-driver.toml +kargs = ["rd.driver.blacklist=nouveau", "modprobe.blacklist=nouveau", "nvidia-drm.modeset=1"] +match-architectures = ["x86_64", "aarch64"] +EOF + +RUN flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo && \ + flatpak install -y flathub org.freedesktop.Platform.GL.nvidia-580-142 + +# make it known as a bootc container +LABEL containers.bootc=1 +LABEL bootc.rootfs=/sysroot + +RUN bootc container lint diff --git a/dcu/README.md b/dcu/README.md index 0f160d5..29627b5 100644 --- a/dcu/README.md +++ b/dcu/README.md @@ -1 +1,127 @@ Documentation can be found here: https://devcade-docs.csh.rit.edu/#/Hardware/installing-dcu + +# bootc containerization +see https://docs.fedoraproject.org/en-US/bootc/getting-started/ +for image building tool documentation https://github.com/osbuild/bootc-image-builder +**bootc** is a tool for creating bootable containers, basically meaning putting a container on top of a kernel. +In this case Fedora is used as a base image, and the devcade-onboard is built on top of it using a Dockerfile based container. +## Installing +For most use cases this is unnecessary, and you can skip this +### For official installation documentation see: +see https://bootc.dev/bootc/packaging-and-integration.html +### Arch: + +First try: +```bash +paru bootc +``` + +If that fails build the program manually: +```bash +git clone https://github.com/bootc-dev/bootc.git # clone the git repository +cd bootc +sudo make install # build and install the program +``` +Then install dependencies +``` +bootupd, skopo +``` +## Installing to a machine +Requirements: +- USB stick +- This repository +- At most 20 minutes + +***Note:* This wipes whatever drive it is installed to, removing all data already there, do not install this to a machine that contains important data, as it will be wiped** +create the ISO as follows, in `devcade-onboard/dcu/`: +```bash +./create-iso.sh +``` +Then create a bootable USB drive using the tool of your choice (I used a Ventoy where i can just copy the ISO to the USB stick) +Then insert the USB stick into the machine and turn it on, going to the BIOS if needed to boot from the USB stick. +Then select install (the first option) to install the devcade container to the machine. + +# Testing +## Using podman +Refer to: https://github.com/containers/podman/blob/main/docs/tutorials/podman_tutorial.md + +*Quick note:* these images are around 5-6 GiB in size at the moment. +To remove unused images run `podman image purge`. +The reason why this command is not run automatically is that it could remove images that the user wants to keep unintentionally. +## Testing the container +For a quick way to test things like: +- file structure +- file ownership +- package installation +- Dockerfile correctness +build and run the container locally +### Using predefined scripts +First build the container with: +```bash +./build.sh +``` +Then run it with: +```bash +./run.sh +``` +### Manually +First build the container with: +```bash +podman build --build-arg-file=./.env -t dcu-devcade-onboard:latest . +``` +Then run it with: +```bash +podman run -p 2200:22 -it dcu-devcade-onboard:latest /bin/bash +``` +### SSH +to SSH into the container run: +```bash +ssh -p 2200 devcade@localhost +``` +to ssh while forwarding the display server +```bash +ssh -p 2200 -X devcade@localhost +``` +### ISO +The only true way to test the container is to boot is though "real" hardware, e.g. a virtual machine. +#### Requirements +- compatible virtual machine software + - all it needs to be able to do is boot from an **ISO** file +- podman +- sudo +- an internet connection +- 10-15 minutes (may take longer or shorter depending on hardware) +#### Creation +```bash +./create-iso.sh +``` +if prompted for your password enter it, the container to create the ISO requires elevated privileges, and by extension building the dcu-devcade-onboard image requires it. *Note: that copying the container to the root user is possible but it was found to be slower than just having the root user build it.* +#### Virtual Machine +##### Setup +After the script finishes an ISO file will exist as `./output/bootiso/install.iso`. In this example i will use `virtmanager` but other virtual machine software should work. +1. Start `virtmanager`, click on **create a new virtual machine** in the top left (the computer icon with the star). This will open a pop-up window. +2. Next select **local media install** and click **Forward**. +3. Then click **browse**, if the path `/path/to/git/repo/dcu/output/bootiso/install.iso` is there you can select it, if not click **Browse Local** and find it there. +4. Then click **Choose Volume** and if it does not detect the operating system uncheck **Automatically detect from the installation media / source** and manually select it from the selections, currently this is **Fedora 43** but check to make sure +5. Then click **Forward** again and select the number of CPUs and memory to dedicate to this VM, i find the default 2 and 4 GiB work for me, but higher numbers will make it faster. +6. Click **Forward** again and allocate storage to the machine, again the defaults have been fine to me, but if run out of storage, you can increase the number of GiB allocated. +7. Click **Forward** again and give it a name, network selection is also possible here, but the default is fine again. +8. Click **Finish** to create the new virtual machine +At this point the machine will start booting, this does take some time to do so, so take a break and scroll Reddit or something. + +After the machine finishes installing the container and booting, you should be in a WM where the onboard is running +##### Useful info +To **stop** the machine click the **power button icon** at the top of the virtual machine's output display, or shut down the machine from the command line. +To **release the mouse** press `left alt + left ctrl`, or try `left ctrl + left alt + g`. +##### Issues +If you get any issues, such as not being able to select an OS make sure the libvirtd service is started: `systemctl start libvirtd` +##### SSH +Click on **view** then **details** and click on the sub category: **NIC ab:cd:ef:gh:ij:kl** and the IP address to use is listed as **IP address: 192.xxx.xxx.xxx** +Then run: +```bash +ssh devcade@ip-address +``` +If any issues arise, running in verbose mode will reveal more information: +```bash +ssh -v devcade@ip-address +``` diff --git a/dcu/boot-splash/devcade/0150.png b/dcu/boot-splash/devcade/0150.png new file mode 100644 index 0000000..1c241e5 Binary files /dev/null and b/dcu/boot-splash/devcade/0150.png differ diff --git a/dcu/boot-splash/devcade/box.png b/dcu/boot-splash/devcade/box.png new file mode 100644 index 0000000..54876e6 Binary files /dev/null and b/dcu/boot-splash/devcade/box.png differ diff --git a/dcu/boot-splash/devcade/bullet.png b/dcu/boot-splash/devcade/bullet.png new file mode 100644 index 0000000..dd52736 Binary files /dev/null and b/dcu/boot-splash/devcade/bullet.png differ diff --git a/dcu/boot-splash/devcade/devcade.plymouth b/dcu/boot-splash/devcade/devcade.plymouth new file mode 100644 index 0000000..ed1faa5 --- /dev/null +++ b/dcu/boot-splash/devcade/devcade.plymouth @@ -0,0 +1,13 @@ +[Plymouth Theme] +Name=Devcade +Description=Script example plugin. +ModuleName=script + +[script] +ImageDir=/usr/share/plymouth/themes//devcade +ScriptFile=/usr/share/plymouth/themes//devcade/devcade.script +ConsoleLogBackgroundColor=0x000000ff + +[script-env-vars] +example_env_var=example env var value + diff --git a/dcu/boot-splash/devcade/devcade.script b/dcu/boot-splash/devcade/devcade.script new file mode 100644 index 0000000..a67b432 --- /dev/null +++ b/dcu/boot-splash/devcade/devcade.script @@ -0,0 +1,27 @@ + +animation_images = []; + +screen_width = Window.GetWidth(); +screen_height = Window.GetHeight(); + +# Get the original dimensions +image = Image("0150.png"); +image_width = image.GetWidth(); +image_height = image.GetHeight(); + +# Create sprite +animation_sprite = Sprite(); +animation_sprite.SetImage(image); + +# Center it +animation_sprite.SetX((screen_width - image_width) / 2); +animation_sprite.SetY((screen_height - image_height) / 2); +animation_sprite.SetZ(1); + +Window.SetBackgroundTopColor(0, 0, 0); +Window.SetBackgroundBottomColor(0, 0, 0); + +fun on_refresh() { +} + +Plymouth.SetRefreshFunction(on_refresh); diff --git a/dcu/boot-splash/devcade/entry.png b/dcu/boot-splash/devcade/entry.png new file mode 100644 index 0000000..a9f4157 Binary files /dev/null and b/dcu/boot-splash/devcade/entry.png differ diff --git a/dcu/boot-splash/devcade/lock.png b/dcu/boot-splash/devcade/lock.png new file mode 100644 index 0000000..a0f8c12 Binary files /dev/null and b/dcu/boot-splash/devcade/lock.png differ diff --git a/dcu/boot-splash/devcade/progress_bar.png b/dcu/boot-splash/devcade/progress_bar.png new file mode 100644 index 0000000..dd1e747 Binary files /dev/null and b/dcu/boot-splash/devcade/progress_bar.png differ diff --git a/dcu/boot-splash/devcade/progress_box.png b/dcu/boot-splash/devcade/progress_box.png new file mode 100644 index 0000000..c485cfb Binary files /dev/null and b/dcu/boot-splash/devcade/progress_box.png differ diff --git a/dcu/boot-splash/test.sh b/dcu/boot-splash/test.sh new file mode 100755 index 0000000..e06d553 --- /dev/null +++ b/dcu/boot-splash/test.sh @@ -0,0 +1,15 @@ +sudo bash -c " + current_splash=$(plymouth-set-default-theme); + + plymouthd --kernel-command-line=splash && + plymouth-set-default-theme devcade >/dev/null 2>&1 && + plymouth --show-splash; + for ((I=0; I<40; I++)); do + plymouth --update=test$I ; + sleep 0.1; + done; + plymouth quit; + + plymouth-set-default-theme $current_splash >/dev/null 2>&1; + echo \"done, splash reset to $(plymouth-set-default-theme)\" +" \ No newline at end of file diff --git a/dcu/build-nvidia.sh b/dcu/build-nvidia.sh new file mode 100755 index 0000000..b9ba163 --- /dev/null +++ b/dcu/build-nvidia.sh @@ -0,0 +1,3 @@ +podman build --build-arg-file=./.env -t dcu-devcade-onboard:latest . && \ +podman build -f ./Dockerfile.nvidia --build-arg-file=./.env -t dcu-devcade-onboard:latest-nvidia . +# builds the container \ No newline at end of file diff --git a/dcu/build.sh b/dcu/build.sh new file mode 100755 index 0000000..83ce260 --- /dev/null +++ b/dcu/build.sh @@ -0,0 +1,2 @@ +podman build --build-arg-file=./.env -t dcu-devcade-onboard:latest . +# builds the container \ No newline at end of file diff --git a/dcu/config-files/.bashrc b/dcu/config-files/.bashrc new file mode 100644 index 0000000..e91e3b2 --- /dev/null +++ b/dcu/config-files/.bashrc @@ -0,0 +1,127 @@ +# ~/.bashrc: executed by bash(1) for non-login shells. +# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) +# for examples + +# If not running interactively, don't do anything +case $- in + *i*) ;; + *) return;; +esac + +# don't put duplicate lines or lines starting with space in the history. +# See bash(1) for more options +HISTCONTROL=ignoreboth + +# append to the history file, don't overwrite it +shopt -s histappend + +# for setting history length see HISTSIZE and HISTFILESIZE in bash(1) +HISTSIZE=1000 +HISTFILESIZE=2000 + +# check the window size after each command and, if necessary, +# update the values of LINES and COLUMNS. +shopt -s checkwinsize + +# If set, the pattern "**" used in a pathname expansion context will +# match all files and zero or more directories and subdirectories. +#shopt -s globstar + +# make less more friendly for non-text input files, see lesspipe(1) +#[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" + +# set variable identifying the chroot you work in (used in the prompt below) +if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then + debian_chroot=$(cat /etc/debian_chroot) +fi + +# set a fancy prompt (non-color, unless we know we "want" color) +case "$TERM" in + xterm-color|*-256color) color_prompt=yes;; +esac + +# uncomment for a colored prompt, if the terminal has the capability; turned +# off by default to not distract the user: the focus in a terminal window +# should be on the output of commands, not on the prompt +#force_color_prompt=yes + +if [ -n "$force_color_prompt" ]; then + if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then + # We have color support; assume it's compliant with Ecma-48 + # (ISO/IEC-6429). (Lack of such support is extremely rare, and such + # a case would tend to support setf rather than setaf.) + color_prompt=yes + else + color_prompt= + fi +fi + +if [ "$color_prompt" = yes ]; then + PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' +else + PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' +fi +unset color_prompt force_color_prompt + +# If this is an xterm set the title to user@host:dir +case "$TERM" in +xterm*|rxvt*) + PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" + ;; +*) + ;; +esac + +# enable color support of ls and also add handy aliases +if [ -x /usr/bin/dircolors ]; then + test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" + alias ls='ls --color=auto' + alias dir='dir --color=auto' + alias vdir='vdir --color=auto' + + alias grep='grep --color=auto' + alias fgrep='fgrep --color=auto' + alias egrep='egrep --color=auto' +fi + +# colored GCC warnings and errors +export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' + +# some more ls aliases +alias ll='ls -l' +alias la='ls -A' +alias l='ls -CF' + +# Alias definitions. +# You may want to put all your additions into a separate file like +# ~/.bash_aliases, instead of adding them here directly. +# See /usr/share/doc/bash-doc/examples in the bash-doc package. + +if [ -f ~/.bash_aliases ]; then + . ~/.bash_aliases +fi + +# enable programmable completion features (you don't need to enable +# this, if it's already enabled in /etc/bash.bashrc and /etc/profile +# sources /etc/bash.bashrc). +if ! shopt -oq posix; then + if [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion + elif [ -f /etc/bash_completion ]; then + . /etc/bash_completion + fi +fi + +DEVCADE_AUTOLOGIN_INSTALLED=1 +if [[ -z "$DISPLAY" ]] && [[ $(tty) = /dev/tty1 ]]; then + . startx -- -nocursor +# logout +fi + +. "$HOME/.cargo/env" +# rotate terminal +echo 3 | sudo tee /sys/class/graphics/fbcon/rotate +# custom aliases +alias l='ls -lApvh --group-directories-first --color=always' +alias clr='clear && fastfetch' +alias c='clear' \ No newline at end of file diff --git a/dcu/.xinitrc b/dcu/config-files/.xinitrc similarity index 70% rename from dcu/.xinitrc rename to dcu/config-files/.xinitrc index 093fb8f..366e1fd 100644 --- a/dcu/.xinitrc +++ b/dcu/config-files/.xinitrc @@ -11,5 +11,7 @@ configure_display() { . ~/.env configure_display -openbox & compton & /usr/libexec/xdg-desktop-portal-gtk & -~/publish/onboard 2>&1 | systemd-cat -t devcade-onboard +picom & +/usr/libexec/xdg-desktop-portal-gtk & +devcade_onboard 2>&1 | systemd-cat -t devcade-onboard & +exec openbox diff --git a/dcu/bashrc-check.sh b/dcu/config-files/bashrc-check.sh similarity index 100% rename from dcu/bashrc-check.sh rename to dcu/config-files/bashrc-check.sh diff --git a/dcu/config-files/devcade_onboard b/dcu/config-files/devcade_onboard new file mode 100755 index 0000000..a720225 --- /dev/null +++ b/dcu/config-files/devcade_onboard @@ -0,0 +1,47 @@ +#!/bin/bash + +# This script runs and manages the onboard frontend and backend + +backend_pid=-1 +frontend_pid=-1 + +function cleanup() { + if [ -n "$(ps -p $backend_pid -o pid=)" ]; then + kill $backend_pid + echo -e "Killed backend (pid $backend_pid)" + fi + if [ -n "$(ps -p $frontend_pid -o pid=)" ]; then + kill $frontend_pid + echo -e "Killed frontend (pid $frontend_pid)" + fi + exit 1 +} + +godot=Godot_v4.4.1-stable_mono_linux.x86_64 +# compile frontend +# Usage: export_all +# ${HOME}/export-all.sh $godot ${HOME}/devcade-onboard-ui-revamp/ ${HOME}/ + +devcade_backend & +backend_pid=$! + +### run frontend +devcade_frontend & +frontend_pid=$! + +# run 'cleanup' on exit +trap 'cleanup' exit + +# run forever to keep handle on frontend and backend. +# if this script is interrupted it will kill both the +# frontend and backend. +while [ 1 -eq 1 ]; do + sleep 5 + # if backend or frontend are not running, then kill the other and exit. + if [ -z "$(ps -p $backend_pid -o pid=)" ]; then + cleanup + fi + if [ -z "$(ps -p $frontend_pid -o pid=)" ]; then + cleanup + fi +done \ No newline at end of file diff --git a/dcu/config-files/export-all b/dcu/config-files/export-all new file mode 100755 index 0000000..14e84c0 --- /dev/null +++ b/dcu/config-files/export-all @@ -0,0 +1,31 @@ +#!/bin/bash + +# first arg is --help or -h or # of args != 3 +if [[ $1 == --help ]] || [[ $1 == -h ]] || [[ $# -ne 3 ]]; then + echo "Usage:" $0 " " + exit 1 +fi + +# backend +# cargo build the backend cause yeah +cd $2/onboard/backend +cargo build --release +backend_exit_code=$? +if [[ $backend_exit_code -ne 0 ]]; then + echo "backend failed to compile" + exit 2 +fi +cd $2 +cp $2/onboard/backend/target/release/backend $3/devcade_backend + +# godot frontend +/usr/bin/dotnet build "$2/onboard/godot-frontend/godot-frontend.csproj" -c Release + +$1 --path $2/onboard/godot-frontend --headless --export-release "Linux" $3/devcade_frontend +frontend_exit_code=$? +if [[ $frontend_exit_code -ne 0 ]]; then + echo "frontend failed to compile" + exit 3 +fi + +exit 0 \ No newline at end of file diff --git a/dcu/config-files/files.conf b/dcu/config-files/files.conf new file mode 100644 index 0000000..74dd541 --- /dev/null +++ b/dcu/config-files/files.conf @@ -0,0 +1,11 @@ +# see https://www.freedesktop.org/software/systemd/man/latest/tmpfiles.d.html +#Type Path Mode User Group Age Argument +d /var/home/devcade/ 755 devcade devcade - - +d /var/home/devcade/.local/share/xorg/ 755 devcade devcade - - +d /var/home/devcade/.local/ 755 devcade devcade - - +d /var/home/devcade/.local/share/ 755 devcade devcade - - +d /var/home/devcade/.local/share/godot/ 755 devcade devcade - - + +# L+ forces a symlink to be created +#Type Symlink to create Mode User Group Age Target_path +L+ /var/home/devcade/.local/share/godot/export_templates/ - devcade devcade - /usr/share/godot/export_templates/ diff --git a/dcu/config-files/linux_debug.x86_32 b/dcu/config-files/linux_debug.x86_32 new file mode 100755 index 0000000..2d9498b Binary files /dev/null and b/dcu/config-files/linux_debug.x86_32 differ diff --git a/dcu/config-files/linux_debug.x86_64 b/dcu/config-files/linux_debug.x86_64 new file mode 100755 index 0000000..e666b73 Binary files /dev/null and b/dcu/config-files/linux_debug.x86_64 differ diff --git a/dcu/config-files/linux_release.x86_32 b/dcu/config-files/linux_release.x86_32 new file mode 100755 index 0000000..bde1d02 Binary files /dev/null and b/dcu/config-files/linux_release.x86_32 differ diff --git a/dcu/config-files/linux_release.x86_64 b/dcu/config-files/linux_release.x86_64 new file mode 100755 index 0000000..7cd4db5 Binary files /dev/null and b/dcu/config-files/linux_release.x86_64 differ diff --git a/dcu/config-files/prepare-root.conf b/dcu/config-files/prepare-root.conf new file mode 100644 index 0000000..3347154 --- /dev/null +++ b/dcu/config-files/prepare-root.conf @@ -0,0 +1,3 @@ +# see `Enabling transient etc` in https://bootc.dev/bootc//filesystem.html +[etc] +transient = true diff --git a/dcu/rc.xml b/dcu/config-files/rc.xml similarity index 99% rename from dcu/rc.xml rename to dcu/config-files/rc.xml index a994b25..5233ab1 100644 --- a/dcu/rc.xml +++ b/dcu/config-files/rc.xml @@ -630,7 +630,7 @@ - /var/lib/openbox/debian-menu.xml + /etc/xdg/openbox/menu.xml menu.xml 200 - - - - - VT323-Regular.ttf - - - 48 - - - 0 - - - true - - - - - - - - - - - - ~ - - - - diff --git a/onboard/frontend/Content/devcade-menu-title.spritefont b/onboard/frontend/Content/devcade-menu-title.spritefont deleted file mode 100644 index bb93b42..0000000 --- a/onboard/frontend/Content/devcade-menu-title.spritefont +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - VT323-Regular.ttf - - - 96 - - - 0 - - - true - - - - - - - - - - - - ~ - - - - diff --git a/onboard/frontend/Icon.bmp b/onboard/frontend/Icon.bmp deleted file mode 100644 index 2b48165..0000000 Binary files a/onboard/frontend/Icon.bmp and /dev/null differ diff --git a/onboard/frontend/Icon.ico b/onboard/frontend/Icon.ico deleted file mode 100644 index 7d9dec1..0000000 Binary files a/onboard/frontend/Icon.ico and /dev/null differ diff --git a/onboard/frontend/LogConfig.cs b/onboard/frontend/LogConfig.cs deleted file mode 100644 index 7c6ca8c..0000000 --- a/onboard/frontend/LogConfig.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using log4net; -using log4net.Appender; - -namespace onboard; - -public static class LogConfig { - public enum Level { - INHERIT, - TRACE, - VERBOSE, - DEBUG, - INFO, - WARN, - ERROR, - FATAL, - } - - private class NS { - public string fullName { get; init; } - public Level level { get; set; } - private NS[] children { get; init; } - - public NS(string fullName, Level level, params NS[] children) { - this.fullName = fullName; - this.level = level; - this.children = children; - } - - public NS(string fullname, Level level) { - this.fullName = fullname; - this.level = level; - this.children = null; - } - - private void set(Level level) { - this.level = level; - } - - public void cascade() { - if (children == null) return; - foreach (NS ns in children) { - if (ns.level == Level.INHERIT) { - ns.set(level); - } - ns.cascade(); - } - } - - public IEnumerable flatten() { - var list = new List { this }; - if (children == null) return list; - foreach (NS ns in children) { - list.AddRange(ns.flatten()); - } - return list; - } - } - - public static void init(Level rootLevel) { - root.level = rootLevel; - root.cascade(); - var list = root.flatten(); - foreach (NS ns in list) { - ILog logger = LogManager.GetLogger(ns.fullName); - log4net.Core.Level level = ns.level switch { - Level.TRACE => log4net.Core.Level.Trace, - Level.VERBOSE => log4net.Core.Level.Verbose, - Level.DEBUG => log4net.Core.Level.Debug, - Level.INFO => log4net.Core.Level.Info, - Level.WARN => log4net.Core.Level.Warn, - Level.ERROR => log4net.Core.Level.Error, - Level.FATAL => log4net.Core.Level.Fatal, - _ => log4net.Core.Level.All, - }; - - // what the fuck is this line? why does it work? - ((log4net.Repository.Hierarchy.Logger)logger.Logger).Level = level; - } - } - - // This is the config for the logger for all namespaces. A log level of INHERIT means that the log level of the parent namespace will be used. - // The log level of the root namespace is the default log level for all namespaces. - private static readonly NS root = new( - "onboard", - Level.DEBUG, - new NS( - "onboard.devcade", - Level.INHERIT, - new NS( - "onboard.devcade.Client", - Level.INHERIT - ), - new NS( - "onboard.devcade.DevcadeAPI", - Level.INHERIT - ) - ), - new NS( - "onboard.ui", - Level.INHERIT, - new NS( - "onboard.ui.Devcade", - Level.INHERIT - ), - new NS( - "onboard.ui.Menu", - Level.INHERIT - ) - ), - new NS( - "onboard.util", - Level.INHERIT, - new NS( - "onboard.util.Cmd", - Level.INHERIT - ), - new NS( - "onboard.util.Container", - Level.INHERIT - ), - new NS( - "onboard.util.Network", - Level.INHERIT - ), - new NS( - "onboard.util.Zip", - Level.INHERIT - ) - ) - ); -} \ No newline at end of file diff --git a/onboard/frontend/Program.cs b/onboard/frontend/Program.cs deleted file mode 100644 index e1d02b0..0000000 --- a/onboard/frontend/Program.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Reflection; -using log4net; -using onboard.devcade; -using onboard.util; - -namespace onboard -{ - public static class Program - { - [STAThread] - private static void Main() { - Env.load("../.env"); - - // Logging setup - GlobalContext.Properties["LogFilePath"] = $"{Env.get("DEVCADE_PATH").unwrap_or("/tmp/devcade")}/logs/frontend"; - GlobalContext.Properties["LogFileName"] = ".log"; - log4net.Config.XmlConfigurator.Configure(); - LogManager.GetLogger(MethodBase.GetCurrentMethod()?.DeclaringType?.FullName).Info("Starting application"); - - LogConfig.Level level = Env.get("FRONTEND_LOG").unwrap_or_else(() => Env.get("RUST_LOG").unwrap_or("INFO")).ToUpper() switch { - "TRACE" => LogConfig.Level.TRACE, - "VERBOSE" => LogConfig.Level.VERBOSE, - "DEBUG" => LogConfig.Level.DEBUG, - "INFO" => LogConfig.Level.INFO, - "WARN" => LogConfig.Level.WARN, - "ERROR" => LogConfig.Level.ERROR, - "FATAL" => LogConfig.Level.FATAL, - _ => LogConfig.Level.INFO, - }; - - // Set namespace log levels - LogConfig.init(level); - - // Application setup - Client.init(); - - using var game = new ui.Devcade(); - game.Run(); - } - } -} diff --git a/onboard/frontend/app.manifest b/onboard/frontend/app.manifest deleted file mode 100644 index 6101db0..0000000 --- a/onboard/frontend/app.manifest +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true/pm - permonitorv2,permonitor - - - - diff --git a/onboard/frontend/onboard.csproj b/onboard/frontend/onboard.csproj deleted file mode 100644 index 15b70f1..0000000 --- a/onboard/frontend/onboard.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - WinExe - net6.0 - Major - false - false - - - app.manifest - Icon.ico - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/onboard/frontend/onboard.sln b/onboard/frontend/onboard.sln deleted file mode 100644 index e007f3a..0000000 --- a/onboard/frontend/onboard.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.3.32929.385 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "onboard", "onboard.csproj", "{28C58787-620E-403F-90E7-DD7B11E98DD2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {28C58787-620E-403F-90E7-DD7B11E98DD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {28C58787-620E-403F-90E7-DD7B11E98DD2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {28C58787-620E-403F-90E7-DD7B11E98DD2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {28C58787-620E-403F-90E7-DD7B11E98DD2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {CB204B11-6D06-4E67-8976-EB5C1808E0FD} - EndGlobalSection -EndGlobal diff --git a/onboard/frontend/release.sh b/onboard/frontend/release.sh deleted file mode 100755 index 9edad1f..0000000 --- a/onboard/frontend/release.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -dotnet publish -c Release -r linux-x64 --no-self-contained && \ -scp -r /Devcade-onboard/onboard/bin/Release/netcoreapp3.1/linux-x64/publish/ devcade@devcade.csh.rit.edu:~/onboard-new - diff --git a/onboard/frontend/to_linux.ps1 b/onboard/frontend/to_linux.ps1 deleted file mode 100644 index 6d5259c..0000000 --- a/onboard/frontend/to_linux.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -dotnet publish -c Release -r linux-x64 --no-self-contained -rm -r -force C:\Users\dingus\Documents\devcade-shared\publish\ -cp C:\Users\dingus\Code\Devcade-onboard\onboard\bin\Release\netcoreapp3.1\linux-x64\publish\ C:\Users\dingus\Documents\devcade-shared\ -r \ No newline at end of file diff --git a/onboard/frontend/ui/Devcade.cs b/onboard/frontend/ui/Devcade.cs deleted file mode 100644 index e5db9be..0000000 --- a/onboard/frontend/ui/Devcade.cs +++ /dev/null @@ -1,502 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Devcade; -using log4net; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; -using onboard.devcade; -using onboard.util; - -namespace onboard.ui; - -public class Devcade : Game { - private static ILog logger = LogManager.GetLogger("onboard.ui.Devcade"); - - public static Devcade instance { get; set; } - - private readonly GraphicsDeviceManager graphics; - private SpriteBatch spriteBatch; - - private Menu menu; - private bool demo; - - private SpriteFont _devcadeMenuBig; - private SpriteFont _devcadeMenuTitle; - - public bool _loading; - - public MenuState state = MenuState.Launch; - private float fadeColor; - - private KeyboardState lastState; - - public enum MenuState { - Launch, - Loading, - Input, - Descritpion, - Tags, - LaunchingGame - } - - private Texture2D cardTexture; - private Texture2D loadingSpin; - private Texture2D BGgradient; - private Texture2D icon; - private Texture2D titleTexture; - private Texture2D titleDevTexture; - private Texture2D titleTextureWhite; - private Texture2D descriptionTexture; - - // If we can't fetch the game list (like if the API is down) - private bool _cantFetch; - - private double? _holdingSupervisor = null; - - public Devcade() { - this.graphics = new GraphicsDeviceManager(this); - } - - protected override void Initialize() { - var sWidth = Env.get("VIEW_WIDTH"); - var sHeight = Env.get("VIEW_HEIGHT"); - if (sWidth.is_none()) { - logger.Warn("VIEW_WIDTH not set. Using default 1080"); - } - - if (sHeight.is_none()) { - logger.Warn("VIEW_HEIGHT not set. Using default 2560"); - } - - int width = sWidth.map_or(1080, int.Parse); - int height = sHeight.map_or(2560, int.Parse); - - // if the DEMO_MODE environment variable is true, sorting by tags is disabled, and only curated games are shown - demo = Env.get("DEMO_MODE").map_or(false, bool.Parse); - - this.menu = new Menu(this.graphics); - - graphics.PreferredBackBufferWidth = width; - graphics.PreferredBackBufferHeight = height; - graphics.ApplyChanges(); - - menu.Initialize(); - - instance = this; - - //-------------------------------------------- - // Testing API routes to make sure they work - // There's no code that uses these routes yet - // so I'm just testing them here - //-------------------------------------------- - - // TODO proper tests? how test in C#? - // no cargo test in C# :( - - // Run in thread so we don't block the main thread - // new Thread(() => { - // try { - // List tags = new(); - // var tagResult = Client.getTags(); - // tagResult.ContinueWith(res => { - // if (!res.IsCompletedSuccessfully) { - // logger.Warn("Failed to fetch tags (Task failed)"); - // return; - // } - - // var tagRes = res.Result.into_result>(); - // if (tagRes.is_err()) { - // logger.Warn($"Failed to fetch tags (API error): {tagRes.unwrap_err()}"); - // return; - // } - - // tags = tagRes.unwrap(); - // logger.Debug($"Successfully fetched tags (Got {tags.Count} tags)"); - // }).Wait(); - - // List games = new(); - // var gamesByTagResult = Client.getGamesWithTag(tags[0]); - // gamesByTagResult.ContinueWith(res => { - // if (!res.IsCompletedSuccessfully) { - // logger.Warn("Failed to fetch games by tag (Task failed)"); - // return; - // } - - // var gamesRes = res.Result.into_result>(); - // if (gamesRes.is_err()) { - // logger.Warn($"Failed to fetch games by tag (API error): {gamesRes.unwrap_err()}"); - // return; - // } - - // games = gamesRes.unwrap(); - // logger.Debug($"Successfully fetched games by tag (Got {games.Count} games)"); - // }).Wait(); - - // logger.Debug($"The following games have the tag {tags[0].name}:"); - // foreach (DevcadeGame game in games) { - // logger.Debug($"- {game.name}"); - // } - - // User joe = new(); - // var userResult = Client.getUser("joeneil"); // Who else would I use but myself? - // userResult.ContinueWith(res => { - // if (!res.IsCompletedSuccessfully) { - // logger.Warn("Failed to fetch user (Task failed)"); - // return; - // } - - // var userRes = res.Result.into_result(); - // if (userRes.is_err()) { - // logger.Warn($"Failed to fetch user (API error): {userRes.unwrap_err()}"); - // return; - // } - - // joe = userRes.unwrap(); - // logger.Debug($"Successfully fetched user (Got user {joe.id} aka {joe.first_name} {joe.last_name})"); - // }).Wait(); - // } catch (Exception e) { - // // This is just a test, so we don't want to crash the game - // logger.Warn("Failed to test API routes", e); - // } - // }).Start(); - - // End of testing API routes - - base.Initialize(); - } - - protected override void LoadContent() { - Content.RootDirectory = "Content"; - menu.LoadContent(Content); - - this.spriteBatch = new SpriteBatch(this.graphics.GraphicsDevice); - - _devcadeMenuBig = Content.Load("devcade-menu-big"); - _devcadeMenuTitle = Content.Load("devcade-menu-title"); - - cardTexture = Content.Load("card"); - titleTexture = Content.Load("transparent-logo"); - titleDevTexture = Content.Load("transparent-dev-logo"); - titleTextureWhite = Content.Load("transparent-logo-white"); - - descriptionTexture = Content.Load("description"); - - BGgradient = Content.Load("OnboardBackgroundGradient"); - icon = Content.Load("CSH"); - - loadingSpin = Content.Load("loadingSheet"); - - // TODO: use this.Content to load your game content here - - if (!menu.reloadGames(GraphicsDevice, false)) { - state = MenuState.Loading; - _cantFetch = true; - } else { - _cantFetch = false; - } - - // Create instances related to the tags menu - menu.initializeTagsMenu(cardTexture, _devcadeMenuBig); - - base.LoadContent(); - } - - protected override void Update(GameTime gameTime) { - menu.Update(gameTime); - // Update inputs - KeyboardState myState = Keyboard.GetState(); - Input.Update(); // Controller update - - // Keyboard only to exit menu as it should never exit in prod - if (Keyboard.GetState().IsKeyDown(Keys.Tab)) { - Exit(); - } - - // If the state is loading, it is still taking input as though it is in the input state..? - switch (state) { - // Fade in when the app launches - case MenuState.Launch: - if (fadeColor < 1f) { - fadeColor += (float)(gameTime.ElapsedGameTime.TotalSeconds); - } - else { - // Once the animation completes, begin tracking input - state = MenuState.Input; - } - - break; - - case MenuState.LaunchingGame: - if (fadeColor < 1f) { - fadeColor += (float)(gameTime.ElapsedGameTime.TotalSeconds); - } - - if (myState.IsKeyDown(Keys.Space) || - (Input.GetButton(1, Input.ArcadeButtons.Menu) && - Input.GetButton(2, Input.ArcadeButtons.Menu))) { - if (_holdingSupervisor == null) { - _holdingSupervisor = gameTime.TotalGameTime.TotalMilliseconds + 3000; - logger.Info("Starting a supervisor button timer!"); - } - if (_holdingSupervisor <= gameTime.TotalGameTime.TotalMilliseconds) { - logger.Info("Requesting game death!"); - Client.killGame(); - _holdingSupervisor = null; - } - } else if (_holdingSupervisor != null) { - logger.Info("Cancelling a supervisor button timer!"); - _holdingSupervisor = null; - } - - break; - - - case MenuState.Loading: - - if (_cantFetch && (myState.IsKeyDown(Keys.Space) || - (Input.GetButton(1, Input.ArcadeButtons.Menu) && - Input.GetButtonDown(2, Input.ArcadeButtons.Menu)) || - (Input.GetButtonDown(1, Input.ArcadeButtons.Menu) && - Input.GetButton(2, Input.ArcadeButtons.Menu)))) { - try { - menu.reloadGames(GraphicsDevice); - _cantFetch = false; - state = MenuState.Input; - } catch (AggregateException e) { - logger.Error($"Failed to fetch games: {e}"); - state = MenuState.Loading; - _cantFetch = true; - } - } - - // TODO - Fix this to work with new client - // if (_client.DownloadFailed) - // { - // _loading = false; - // _client.DownloadFailed = false; - // } - - if (fadeColor < 1f) { - fadeColor += (float)(gameTime.ElapsedGameTime.TotalSeconds); - } - - if (!_loading) { - fadeColor = 0f; - state = MenuState.Launch; - } - - break; - - // In this state, the user is able to scroll through the menu and launch games - case MenuState.Input: - menu.descFadeOut(gameTime); - menu.cardFadeIn(gameTime); - menu.updateTagsMenu(myState, lastState, gameTime); - - if ((myState.IsKeyDown(Keys.Space) || (Input.GetButton(1, Input.ArcadeButtons.Menu) && - Input.GetButtonDown(2, Input.ArcadeButtons.Menu)) || - (Input.GetButtonDown(1, Input.ArcadeButtons.Menu) && - Input.GetButton(2, Input.ArcadeButtons.Menu))) && - !menu.reloadGames(GraphicsDevice)) { - state = MenuState.Loading; - _cantFetch = true; - } - - - if (myState.IsKeyDown(Keys.Z) || (Input.GetButtonDown(1, Input.ArcadeButtons.B4) && - Input.GetButton(2, Input.ArcadeButtons.B4)) || - (Input.GetButton(1, Input.ArcadeButtons.B4) && - Input.GetButtonDown(2, Input.ArcadeButtons.B4))) { - // Switch to dev/prod - Client.setProduction(!Client.isProduction).Wait(); - - // And reload - if (!menu.reloadGames(GraphicsDevice)) { - state = MenuState.Loading; - _cantFetch = true; - } - } - - if (((myState.IsKeyDown(Keys.Down)) || // Keyboard down - Input.GetButton(1, Input.ArcadeButtons.StickDown) || // or joystick down - Input.GetButton(2, Input.ArcadeButtons.StickDown))) // of either player - { - menu.beginAnimUp(); - } - - if (((myState.IsKeyDown(Keys.Up)) || // Keyboard up - Input.GetButton(1, Input.ArcadeButtons.StickUp) || // or joystick up - Input.GetButton(2, - Input.ArcadeButtons.StickUp))) // of either player // and not at top of list - { - menu.beginAnimDown(); - } - - if ((myState.IsKeyDown(Keys.Enter) && lastState.IsKeyUp(Keys.Enter)) || // Keyboard Enter - Input.GetButtonDown(1, Input.ArcadeButtons.A1) || // or A1 button - Input.GetButtonDown(2, Input.ArcadeButtons.A1)) // of either player - { - state = MenuState.Descritpion; - } - - if ((myState.IsKeyDown(Keys.R) && lastState.IsKeyUp(Keys.R)) || // Keyboard R - (Input.GetButton(1, Input.ArcadeButtons.Menu) && - Input.GetButton(2, Input.ArcadeButtons.Menu) && // OR Both Menu Buttons - Input.GetButton(1, Input.ArcadeButtons.B4))) // and Player 1 B4 - { - menu.reloadGames(GraphicsDevice); - - state = MenuState.Input; - } - - if (((myState.IsKeyDown(Keys.Right) && lastState.IsKeyUp(Keys.Right)) || // Keyboard Right - Input.GetButtonDown(1, Input.ArcadeButtons.StickRight) || - Input.GetButtonDown(2, Input.ArcadeButtons.StickRight)) && // OR either right stick - !demo) // AND demo mode is off - { - menu.showTags(); - state = MenuState.Tags; - } - - menu.animate(gameTime); - break; - - case MenuState.Descritpion: - menu.descFadeIn(gameTime); - menu.cardFadeOut(gameTime); - - if ((myState.IsKeyDown(Keys.Enter) && lastState.IsKeyUp(Keys.Enter)) || // Keyboard Enter - Input.GetButtonDown(1, Input.ArcadeButtons.A1) || // or A1 button - Input.GetButtonDown(2, Input.ArcadeButtons.A1)) // of either player - { - if (menu.gameSelected().id == "error") { - // Don't launch the default error game - logger.Info("Someone tried to launch the placeholder error game"); - break; - } - logger.Info("Launching game: " + menu.gameSelected().id + " - " + menu.gameSelected().name); - Client.launchGame( - menu.gameSelected().id - ).ContinueWith(res => { - if (res.IsCompletedSuccessfully) { - state = MenuState.Input; - } - else { - logger.Error("Failed to launch game: " + res.Exception); - state = MenuState.Input; - } - }); - - fadeColor = 0f; - _loading = true; - state = MenuState.LaunchingGame; - } - else if ((myState.IsKeyDown(Keys.RightShift) && - lastState.IsKeyUp(Keys.RightShift)) || // Keyboard Rshift - Input.GetButtonDown(1, Input.ArcadeButtons.A2) || // or A2 button - Input.GetButtonDown(2, Input.ArcadeButtons.A2)) // of either player - { - state = MenuState.Input; - } - - break; - - case MenuState.Tags: - menu.cardFadeOut(gameTime); - - if( ((myState.IsKeyDown(Keys.Left) && lastState.IsKeyUp(Keys.Left)) || // Keyboard Left - Input.GetButtonDown(1, Input.ArcadeButtons.StickLeft) || // OR Stick Left - Input.GetButtonDown(2, Input.ArcadeButtons.StickLeft)) && // of either player - menu.getTagCol() == 0 ) // AND if we are already on the left column of tags - { - menu.hideTags(); - state = MenuState.Input; - } - - if((myState.IsKeyDown(Keys.Enter) && lastState.IsKeyUp(Keys.Enter)) || // Keyboard Enter - Input.GetButtonDown(1, Input.ArcadeButtons.A1) || // OR A1 - Input.GetButtonDown(2, Input.ArcadeButtons.A1)) // of either player - { - menu.updateTag(); - menu.hideTags(); - state = MenuState.Input; - } - - menu.updateTagsMenu(myState, lastState, gameTime); - - break; - } - - lastState = Keyboard.GetState(); - - base.Update(gameTime); - } - - protected override void Draw(GameTime gameTime) { - this.spriteBatch.Begin(); - GraphicsDevice.Clear(Color.Black); - - switch (state) { - case MenuState.Launch: - case MenuState.Input: - case MenuState.Descritpion: - menu.drawBackground(this.spriteBatch, BGgradient, icon, fadeColor, gameTime); - menu.drawTitle(this.spriteBatch, Client.isProduction ? titleTexture : titleDevTexture, fadeColor); - menu.drawCards(this.spriteBatch, cardTexture, _devcadeMenuBig); - menu.drawDescription(this.spriteBatch, descriptionTexture, _devcadeMenuTitle, _devcadeMenuBig); - menu.drawInstructions(this.spriteBatch, _devcadeMenuBig); - menu.drawTagsMenu(this.spriteBatch, _devcadeMenuBig); - break; - - case MenuState.Loading: - case MenuState.LaunchingGame: - menu.drawLoading(this.spriteBatch, loadingSpin, fadeColor); - menu.drawTitle(this.spriteBatch, titleTextureWhite, fadeColor); - if (_cantFetch) - menu.drawError(this.spriteBatch, _devcadeMenuBig); - break; - - case MenuState.Tags: - menu.drawBackground(this.spriteBatch, BGgradient, icon, fadeColor, gameTime); - menu.drawTitle(this.spriteBatch, Client.isProduction ? titleTexture : titleDevTexture, fadeColor); - menu.drawTagsMenu(this.spriteBatch, _devcadeMenuBig); - break; - } - - // Draw a string in the top left showing the current state. Used for debugging. TODO: Use debug tags - //this.spriteBatch.DrawString(_devcadeMenuBig, state, new Vector2(0, 0), Color.White); - - // TODO - Fix this to work with the new Client - // if (_client.DownloadFailed) - // this.spriteBatch.DrawString( - // _devcadeMenuBig, - // "There was a problem running the game.", - // new Vector2(10, 400), - // Color.Red - // ); - - this.spriteBatch.End(); - - base.Draw(gameTime); - } - - public Task> loadTextureFromFile(string path) { - if (!System.IO.File.Exists(path)) { - return Task.FromResult( - Result.Err(new System.IO.FileNotFoundException("File not found", path))); - } - - return Task.Run(() => { - try { - Texture2D tex = Texture2D.FromFile(this.graphics.GraphicsDevice, path); - return Result.Ok(tex); - } - catch (Exception e) { - return Result.Err(e); - } - }); - } -} diff --git a/onboard/frontend/ui/IMenu.cs b/onboard/frontend/ui/IMenu.cs deleted file mode 100644 index 90ec68b..0000000 --- a/onboard/frontend/ui/IMenu.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Content; -using Microsoft.Xna.Framework.Graphics; - -namespace onboard.ui; - -public interface IMenu { - public void Initialize(); - - public void LoadContent(ContentManager contentManager); - - public void Update(GameTime gameTime); - - public void Draw(SpriteBatch spriteBatch, GameTime gameTime); - - public void Unload(); -} \ No newline at end of file diff --git a/onboard/frontend/ui/Menu.cs b/onboard/frontend/ui/Menu.cs deleted file mode 100644 index 416ff27..0000000 --- a/onboard/frontend/ui/Menu.cs +++ /dev/null @@ -1,640 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using log4net; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Content; -using Microsoft.Xna.Framework.Graphics; -using onboard.devcade; -using onboard.util; - -using Microsoft.Xna.Framework.Input; - -namespace onboard.ui; - -public class Menu : IMenu { - private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod()?.DeclaringType?.FullName); - - public static Menu instance { get; private set; } - - // The instance of TagsMenu that will be used to draw the area where the user can sort by tag - private static TagsMenu tagsMenu; - private static devcade.Tag allTag = new devcade.Tag("All Games", "View all available games"); - private string currentTag = allTag.name; - // A list of all the tags - private List tags; - // A dictionary that will map the current tag to a list of the cards that have that tag - private Dictionary> tagLists = new Dictionary>(); - - private readonly GraphicsDeviceManager _device; - - public List gameTitles { get; private set; } - private DevcadeGame defaultGame; - public int itemSelected { get; set; } - private Dictionary cards { get; } = new(); - - private int loadingCol; - private int loadingRow; - private float offset; - - private int _sWidth; - private int _sHeight; - private double scalingAmount; - - private const float moveTime = 0.15f; - private float timeRemaining; - - private float descX; - private float descOpacity; - private const float descFadeTime = 0.4f; - - private bool movingUp; - private bool movingDown; - - private string devcadePath; - - //Determines how far apart each line of text is drawn vertically - private float yscaleInstructions; - - //Both descriptions and error messages - private float yscaleDesc; - - public Menu(GraphicsDeviceManager _device) { - instance = this; - this._device = _device; - } - - public void Initialize() { - // Container.OnContainerBuilt += (_, args) => { - // logger.Info("Running game"); - // Container.runContainer(args); - // }; - - yscaleInstructions = Env.get("Y_SCALE_INSTRUCTIONS").map_or(0.4f, float.Parse); - yscaleDesc = Env.get("Y_SCALE_DESC").map_or(0.4f, float.Parse); - devcadePath = Env.get("DEVCADE_PATH").unwrap_or("/tmp/devcade"); - defaultGame = new DevcadeGame { - name = "Error", - description = "There was a problem loading games from the API. Please check the logs for more information.", - id = "error", - author = "None", - }; - updateDims(_device); - } - - public void LoadContent(ContentManager contentManager) { - // Setup banner finished callback - Client.onBannerFinished += (_, game) => { - Devcade.instance.loadTextureFromFile($"{devcadePath}/{game.id}/banner.png").ContinueWith(t => { - if (t.IsCompletedSuccessfully && t.Result.is_ok() && cards.ContainsKey(game.id)) { - cards[game.name].setTexture(t.Result.unwrap()); - return; - } - - if (!t.IsCompletedSuccessfully) { - logger.Error($"Download thread failed: {t.Exception}"); - return; - } - - if (!t.Result.is_ok()) { - logger.Error($"Download returned error: {t.Result.unwrap_err()}"); - return; - } - - logger.Warn($"Attempted to load banner for non-existent game {game.name}"); - }); - }; - } - - public void Update(GameTime gameTime) { - // Comment to make the linter happy - } - - public void Draw(SpriteBatch spriteBatch, GameTime gameTime) { - // Comment to make the linter happy - } - - public void Unload() { - // Comment to make the linter happy - } - - public void updateDims(GraphicsDeviceManager _graphics) { - // Get the screen width and height. If none are set, set the to the default values - _sWidth = Env.get("VIEW_WIDTH").map_or_else(() => 1920, int.Parse); - _sHeight = Env.get("VIEW_HEIGHT").map_or_else(() => 1080, int.Parse); - - - // This is a constant value that is used to scale the UI elements if the resolution is smaller than 2560x1080. Results may vary if the same aspect ratio is not kept - scalingAmount = Math.Sqrt(_sHeight * _sWidth / (double)(1080 * 2560)); - - - _graphics.PreferredBackBufferHeight = _sHeight; - _graphics.PreferredBackBufferWidth = _sWidth; - _graphics.ApplyChanges(); - } - - // Empties the gameTitles and cards lists. Called when the reload buttons are pressed - public void clearGames() { - gameTitles?.Clear(); - cards?.Clear(); - tagLists.Clear(); - itemSelected = 0; - } - - public bool reloadGames(GraphicsDevice device, bool clear = true) { - if (clear) - clearGames(); - // Reload the .env file every time the games are reloaded to make sure that the demo mode is up to date - Env.load("../.env"); - itemSelected = 0; - - var errorList = new List { defaultGame }; - - setTags(); - - // Public access to state is definitely a good idea (this whole thing needs a refactor) - Devcade.instance.state = Devcade.MenuState.Loading; - Devcade.instance._loading = true; - - // gameTask is 'never used' but tasks in C# are eager, so it doesn't need to be awaited to run. - Task gameTask = Client.getGameList() - .ContinueWith(t => { - if (!t.IsCompletedSuccessfully) { - logger.Error($"Failed to fetch game list: {t.Exception}"); - gameTitles = errorList; - return; - } - - var res = t.Result.into_result>(); - if (!res.is_ok()) { - logger.Error($"Failed to fetch game list: {res.err().unwrap()}"); - gameTitles = errorList; - return; - } - - logger.Info("Got game list, setting titles"); - gameTitles = res.unwrap(); - }) - .ContinueWith(_ => { - logger.Info("Setting cards"); - setCards(device); - Devcade.instance.state = Devcade.MenuState.Input; - Devcade.instance._loading = false; - }) - .WaitAsync(TimeSpan.FromSeconds(10)) - .ContinueWith(t => { - if (t.IsCompletedSuccessfully) return; - // Take timed out, so we need to set the state back to input and game titles to the error list - Devcade.instance.state = Devcade.MenuState.Input; - Devcade.instance._loading = false; - gameTitles = errorList; - setCards(device); - }); - - // Since this is now done asynchronously, the return means nothing. - return true; - } - - public void setTags() { - if (tags == null || tags.Count == 0) { - logger.Info("Getting tags from API (this should be only once, but maybe every reload of the game list?)"); - tags = Client.getTags().Result.into_result>().unwrap_or(new List()); - tags.Insert(0, allTag); // Make all tag appear at the top of the list - } - - if (tagLists.Keys.Count != 0) return; - - // tagLists gets cleared every time the games are reloaded?! - foreach (Tag tag in tags) { - tagLists.Add(tag.name, new List()); - } - } - - public void setCards(GraphicsDevice graphics) { - for (int i = 0; i < gameTitles.Count; i++) { - devcade.DevcadeGame game = gameTitles[i]; - - MenuCard newCard; - - // Start downloading the textures - if (game.id != "error") { - // don't download the banner for the default game - Client.downloadBanner(game.id); - } // check if /tmp/ has the banner - - string bannerPath = $"{Env.get("DEVCADE_PATH").unwrap_or_else(() => Env.get("HOME").unwrap() + "/.devcade")}/{game.id}/banner.png"; - if (File.Exists(bannerPath)) { - try { - Texture2D banner = Texture2D.FromStream(graphics, File.OpenRead(bannerPath)); - newCard = new MenuCard(i * -1, banner, game); - } - catch (InvalidOperationException e) { - logger.Warn($"Unable to set card.{e}"); - newCard = new MenuCard(i * -1, null, game); - } - } - else { - // If the banner doesn't exist, use a placeholder until it can be downloaded later. - newCard = new MenuCard(i * -1, null, game); - } - - cards.Add(game.id, newCard); - - // Add the reference to the card to the proper lists within the tag dictionary - foreach(devcade.Tag tag in game.tags) { - tagLists[tag.name].Add(newCard); - } - - tagLists[allTag.name].Add(newCard); - } - - // shuffle lists - Random rand = new Random(); - foreach(string key in tagLists.Keys) { - tagLists[key] = tagLists[key].OrderBy(a => rand.Next()).ToList(); - } - - // If demo mode is on, then set the tag to be curated instead of all - if (Env.get("DEMO_MODE").map_or(false, bool.Parse)) { - updateTag("Curated"); - } else { - updateTag(currentTag); - } - - MenuCard.cardX = 0; - descX = _sWidth * 1.5f; - } - - public devcade.DevcadeGame gameSelected() { - return tagLists[currentTag].ElementAt(itemSelected).game; - } - - /* - * Tags Menu Related Functions - */ - - // MAKE FONTS, TEXTURES, AND DIMS FIELDS WITHIN TAGS MENU - public void initializeTagsMenu(Texture2D cardTexture, SpriteFont font) { - tagsMenu = new TagsMenu(tags.ToArray(), cardTexture, font, new Vector2(_sWidth, _sHeight), scalingAmount); - } - - public void drawTagsMenu(SpriteBatch spriteBatch, SpriteFont font) { - tagsMenu.Draw(spriteBatch, font, new Vector2(_sWidth, _sHeight)); - } - - public void updateTagsMenu(KeyboardState currentState, KeyboardState lastState, GameTime gameTime) { - tagsMenu.Update(currentState, lastState, gameTime); - } - - public int getTagCol() { return tagsMenu.getCurrentCol(); } - - public void updateTag(string tag) { - this.currentTag = tag; - - // Reset the listPos of each card within the list of currently visible cards - List visibleCards = tagLists[currentTag]; - for (int i=0; i 150) { - offset = 0; - } - - int numColumns = _sWidth / 150 + 1; - int numRows = _sHeight / 150 + 1; - - for (int row = -150; row <= numRows * 150; row += 150) // Starts at -150 to draw an extra row above the screen - { - for (int column = 0; column <= numColumns * 150; column += 150) { - _spriteBatch.Draw( - icon, - new Vector2(column - offset, row + offset), - null, - new Color(col, col, col), - 0f, - new Vector2(0, 0), - 1f, - SpriteEffects.None, - 0f - ); - } - } - } - - public void drawTitle(SpriteBatch _spriteBatch, Texture2D titleTexture, float col) { - // The title will always be scaled to fit the width of the screen. The height follows scaling based on how - // much the title was stretched horizontally - float scaling = (float)_sWidth / titleTexture.Width; - _spriteBatch.Draw( - titleTexture, - new Rectangle(0, 0, _sWidth, (int)(titleTexture.Height * scaling)), - null, - new Color(col, col, col), - 0f, - new Vector2(0, 0), - SpriteEffects.None, - 0f - ); - } - - public void drawInstructions(SpriteBatch _spriteBatch, SpriteFont font) { - List instructions = wrapText("Press the Red button to play! Press both Black Buttons to refresh", 35); - float instructSize = font.MeasureString(instructions[0]).Y; - float yPos = (float)(500 * scalingAmount); - for (int i = 0; i < instructions.Count; i++) { - writeString(_spriteBatch, font, instructions[i], new Vector2(_sWidth / 2.0f, yPos + instructSize * i * yscaleInstructions), 1f); - } - } - - - public void drawError(SpriteBatch _spriteBatch, SpriteFont font) { - const string error = "Error: Could not get game list. Is API Down? Press both black buttons to reload."; - var wrappedError = wrapText(error, 35); - float errorSize = font.MeasureString(wrappedError[0]).Y; - float yPos = (float)(500 * scalingAmount); - - for (int i = 0; i < wrappedError.Count; i++) { - writeString(_spriteBatch, font, wrappedError[i], new Vector2(_sWidth / 2.0f, yPos + errorSize * i * yscaleDesc), 1f, Color.Red); - } - } - - - public void drawLoading(SpriteBatch _spriteBatch, Texture2D loadingSpin, float col) { - if (loadingCol > 4) { - loadingCol = 0; - loadingRow++; - if (loadingRow > 4) { - loadingRow = 0; - } - } - - // Creates a boundary to get the right spot on the spritesheet to be drawn - Rectangle spriteBounds = new( - 600 * loadingCol, - 600 * loadingRow, - 600, - 600 - ); - - _spriteBatch.Draw( - loadingSpin, - new Vector2(_sWidth / 2.0f, _sHeight / 2.0f + 150), - spriteBounds, - new Color(col, col, col), - 0f, - new Vector2(300, 300), - 1.5f, - SpriteEffects.None, - 0f - ); - - loadingCol++; - } - - public void descFadeIn(GameTime gameTime) { - // This does the slide in animation, starting off screen and moving to the middle over 0.8 seconds - if (descOpacity >= 1) return; - descX -= _sWidth / descFadeTime * (float)gameTime.ElapsedGameTime.TotalSeconds; - descOpacity += 1 / descFadeTime * (float)gameTime.ElapsedGameTime.TotalSeconds; - } - - public void descFadeOut(GameTime gameTime) { - // This does the slide out animation, starting in the middle of the screen and moving it off over 0.8 seconds - if (descOpacity <= 0) return; - descX += _sWidth / descFadeTime * (float)gameTime.ElapsedGameTime.TotalSeconds; - descOpacity -= 1 / descFadeTime * (float)gameTime.ElapsedGameTime.TotalSeconds; - } - - public void cardFadeIn(GameTime gameTime) { - if (MenuCard.cardOpacity >= 1) return; - MenuCard.cardX += _sWidth / descFadeTime * (float)gameTime.ElapsedGameTime.TotalSeconds; - MenuCard.cardOpacity += 1 / descFadeTime * (float)gameTime.ElapsedGameTime.TotalSeconds; - } - - public void cardFadeOut(GameTime gameTime) { - if (MenuCard.cardOpacity <= 0) return; - MenuCard.cardX -= _sWidth / descFadeTime * (float)gameTime.ElapsedGameTime.TotalSeconds; - MenuCard.cardOpacity -= 1 / descFadeTime * (float)gameTime.ElapsedGameTime.TotalSeconds; - } - - public void drawDescription(SpriteBatch _spriteBatch, Texture2D descTexture, SpriteFont titleFont, - SpriteFont descFont) { - // First, draw the backdrop of the description - Vector2 descPos = new Vector2(descX, _sHeight / 2 + (int)(descTexture.Height * scalingAmount / 6)); - - _spriteBatch.Draw(descTexture, - descPos, - null, - new Color(descOpacity, descOpacity, descOpacity, descOpacity), - 0f, - new Vector2(descTexture.Width / 2.0f, descTexture.Height / 2.0f), - (float)(1f * scalingAmount), - SpriteEffects.None, - 0f - ); - - // Wraps the description text to fit within the box - // Then draws the description - List wrapDesc = wrapText(gameSelected().description, 35); - float descHeight = descFont.MeasureString(gameSelected().description).Y; - - int lineNum = 0; - foreach (string line in wrapDesc) { - writeString(_spriteBatch, - descFont, - line, - new Vector2(descPos.X, (float)(descPos.Y - descTexture.Height * scalingAmount / 5 + - descHeight * lineNum * yscaleDesc)), - descOpacity - ); - lineNum++; - } - - // Write the game's title - writeString(_spriteBatch, - titleFont, - gameSelected().name, - new Vector2(descPos.X, descPos.Y - (int)(descTexture.Height * scalingAmount / 2.5f)), - descOpacity - ); - - // String author = (gameSelected().user.user_type == UserType.CSH) ? gameSelected().user.id : gameSelected().user.email.Remove(gameSelected().user.email.IndexOf('@')); - // Write the game's author - writeString(_spriteBatch, - descFont, - "By: " + gameSelected().author, - new Vector2(descPos.X, descPos.Y - (int)(descTexture.Height * scalingAmount / 3)), - descOpacity - ); - - // Instructions to go back - writeString(_spriteBatch, - descFont, - "Press the Blue button to return", - new Vector2(descPos.X, descPos.Y + (int)(descTexture.Height * scalingAmount / 2 - descHeight)), - descOpacity - ); - } - - public static List wrapText(string text, int lineLimit) { - List lines = new List(); - StringBuilder currentLine = new StringBuilder(); - string[] words = text.Split(' '); - - foreach (string word in words) { - if (currentLine.Length + word.Length + 1 > lineLimit) { - lines.Add(currentLine.ToString()); - currentLine.Clear(); - } - currentLine.Append(word + " "); - } - - if (currentLine.Length > 0) { - lines.Add(currentLine.ToString()); - } - - return lines; - } - - - public void writeString(SpriteBatch _spriteBatch, SpriteFont font, string str, Vector2 pos, float opacity, - Color color) { - Vector2 strSize = font.MeasureString(str); - - _spriteBatch.DrawString(font, - str, - pos, - color, - 0f, - new Vector2(strSize.X / 2, strSize.Y / 2), - (float)(1f * scalingAmount), - SpriteEffects.None, - 0f - ); - } - - public void writeString(SpriteBatch _spriteBatch, SpriteFont font, string str, Vector2 pos, float opacity) { - Vector2 strSize = font.MeasureString(str); - - _spriteBatch.DrawString(font, - str, - pos, - new Color(opacity, opacity, opacity, opacity), - 0f, - new Vector2(strSize.X / 2, strSize.Y / 2), - (float)(1f * scalingAmount), - SpriteEffects.None, - 0f - ); - } - - public void drawCards(SpriteBatch _spriteBatch, Texture2D cardTexture, SpriteFont font) { - // I still have no idea why the layerDepth does not work\ - foreach (MenuCard card in tagLists[currentTag].Where(card => Math.Abs(card.listPos) == 4)) - { - card.DrawSelf(_spriteBatch, cardTexture, _sHeight, scalingAmount); - } - foreach (MenuCard card in tagLists[currentTag].Where(card => Math.Abs(card.listPos) == 3)) - { - card.DrawSelf(_spriteBatch, cardTexture, _sHeight, scalingAmount); - } - foreach (MenuCard card in tagLists[currentTag].Where(card => Math.Abs(card.listPos) == 2)) - { - card.DrawSelf(_spriteBatch, cardTexture, _sHeight, scalingAmount); - } - foreach (MenuCard card in tagLists[currentTag].Where(card => Math.Abs(card.listPos) == 1)) - { - card.DrawSelf(_spriteBatch, cardTexture, _sHeight, scalingAmount); - } - foreach (MenuCard card in tagLists[currentTag].Where(card => Math.Abs(card.listPos) == 0)) - { - card.DrawSelf(_spriteBatch, cardTexture, _sHeight, scalingAmount); - } - } - - public void beginAnimUp() { - // scrolling beginds only if it is not already moving, and not at bottom of list - if (movingUp || movingDown || itemSelected >= tagLists[currentTag].Count - 1) return; - - foreach (MenuCard card in tagLists[currentTag]) { - card.listPos++; - //card.layer = (float)Math.Abs(card.listPos) / 4; - } - - timeRemaining = moveTime; // Time remaining in the animation begins at the total expected move time - movingUp = true; - itemSelected++; // Update which game is currently selected, so the proper one will be launched - } - - public void beginAnimDown() { - // scrolling begins only if it is not already moving, and not at the top of the list - if (movingDown || movingUp || itemSelected <= 0) return; - foreach (MenuCard card in tagLists[currentTag]) { - card.listPos--; - //card.layer = (float)Math.Abs(card.listPos) / 4; - } - - timeRemaining = moveTime; // Time remaining in the animation begins at the total expected move time - movingDown = true; - itemSelected--; - } - - public void animate(GameTime gameTime) { - if (timeRemaining > - 0) // Continues to execute the following code as long as the animation is playing AND max time isn't reached - { - if (movingUp) { - foreach (MenuCard card in cards.Values) { - card.moveUp(gameTime); - } - } - - else if (movingDown) { - foreach (MenuCard card in cards.Values) { - card.moveDown(gameTime); - } - } - - timeRemaining -= (float)gameTime.ElapsedGameTime.TotalSeconds; // Decrement time until it reaches zero - } - - else // Once timeleft reaches 0, finish anim. - { - movingUp = false; - movingDown = false; - } - } -} \ No newline at end of file diff --git a/onboard/frontend/ui/MenuCard.cs b/onboard/frontend/ui/MenuCard.cs deleted file mode 100644 index 31f0ee7..0000000 --- a/onboard/frontend/ui/MenuCard.cs +++ /dev/null @@ -1,126 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; - -namespace onboard.ui -{ - public class MenuCard - { - private const float moveTime = 0.15f; // Time it takes to finish scrolling anim - - private float rotation; // Initial pos - private static readonly float rotation_amt = MathHelper.ToRadians(25f); // Amount the card moves when scrolling - - private Texture2D texture; - - public int listPos; // Tracks the card's current position on the screen - - // Same as rotation variables, but for scale, color - private float scale = 1f; - private const float scale_amt = 0.05f; - - public static float cardOpacity = 1f; - public static float cardX; - - // Constants that determine the rate at which the rotation, color, scale change. - private static readonly float rotationSpeed = rotation_amt / moveTime; - private const float scaleSpeed = scale_amt / moveTime; - - // I made each card keep a reference to the game it represents - // Because when sorting by tags, the positions of the cards will change, so it is easier to launch the currently selected game by first getting the card - public devcade.DevcadeGame game; - - public MenuCard(int initialPos, Texture2D cardTexture, devcade.DevcadeGame game) - { - this.listPos = initialPos; - this.texture = cardTexture; - this.game = game; - - while(initialPos > 0) - { - rotation -= rotation_amt; - scale -= scale_amt; - - initialPos--; - } - while (initialPos < 0) - { - rotation += rotation_amt; - scale -= scale_amt; - - initialPos++; - } - - } - - public void setListPos(int pos) { - this.listPos = pos; - this.rotation = 0f; - this.scale = 1f; - - while(pos > 0) - { - rotation -= rotation_amt; - scale -= scale_amt; - - pos--; - } - - while (pos < 0) - { - rotation += rotation_amt; - scale -= scale_amt; - - pos++; - } - } - - public void moveUp(GameTime gameTime) - { - // The card scales down moving away from the center, otherwise it scales up as it approaches the center - if (listPos > 0) - { - scale -= scaleSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; - } - else - { - scale += scaleSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; - } - - rotation -= rotationSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; // To rotate counter clockwise (aka up), decrease angle - } - - public void moveDown(GameTime gameTime) - { - // The card scales down moving away from the center, otherwise it scales up as it approaches the center - if (listPos >= 0) - { - scale += scaleSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; - } - else - { - scale -= scaleSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; - } - - rotation += rotationSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; // To rotate counter counterclockwise (aka down), decrease angle - } - - public void DrawSelf(SpriteBatch _spriteBatch, Texture2D cardTexture, int _sHeight, double scalingAmount) - { - _spriteBatch.Draw( - texture ?? cardTexture, - new Vector2(cardX, (int)(_sHeight / 2.0 + (cardTexture.Height * scalingAmount) /2)), - null, - new Color(cardOpacity, cardOpacity, cardOpacity, cardOpacity), - rotation, - new Vector2(0, cardTexture.Height / 2.0f), - (float)(scale * scalingAmount), - SpriteEffects.None, - 0f - ); - } - - public void setTexture(Texture2D texture) { - this.texture = texture; - } - } -} diff --git a/onboard/frontend/ui/TagCard.cs b/onboard/frontend/ui/TagCard.cs deleted file mode 100644 index 5864117..0000000 --- a/onboard/frontend/ui/TagCard.cs +++ /dev/null @@ -1,136 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Content; -using Microsoft.Xna.Framework.Graphics; - -// Change to onboard.ui when changes merge -namespace onboard; - -public class TagCard { - - private static SpriteFont font; - private static Texture2D texture; - - private Vector2 pos; - private static float defaultxVel = 100f; - private float xVel = defaultxVel; - // Because the distance travelled is based of gameTime elapsed, this is a constant value that will increase the amount it moves every frame - // This value is also not affected by the decelleration. Without this the xAccel would make the cards move way too slowly - // Just feels better than using some large number like 10000 for the velocity - private static float xSpeed = 100f; - private float xAccel = 0.85f; - private float xShowing; - private float xHidden; - - private bool isSelected = false; - - private float scale; - private static float unhighlightedScale = 0.6f; - private static float highlightedScale = 0.75f; - private float scaleVel = 0.1f; - private float scaleAccel = 0.5f; - - private static Color color = new Color(150, 0 ,0); - public devcade.Tag tag; - - public TagCard(Texture2D texture, SpriteFont font, Vector2 startPos, devcade.Tag tag, float hiddenOffset) { - TagCard.texture = texture; - TagCard.font = font; - - this.pos = startPos; - this.xHidden = startPos.X; - this.xShowing = startPos.X - hiddenOffset; - this.scale = unhighlightedScale; - this.tag = tag; - } - - public void setSelected(bool selected) { this.isSelected = selected; } - - public void updateScale( GameTime gameTime ) { - // This is a new system for animating on screen elements. I think it's a little bit cleaner and easier to understand than what I previously had - // I will update Menu.cs and MenuCard.cs to do something like this instead - - // The scale will increase or decrease depending on whether it is being selected or deselected - if(isSelected) { - // If the scale has yet to reach it's target - if (scale < highlightedScale) { - // gradually increase scale each frame - scale += scaleVel * (float)gameTime.ElapsedGameTime.TotalSeconds; - - } else { - // Otherwise, it has reached it's target, so just reset the velocity and set scale to what it should be - scale = highlightedScale; - scaleVel = 0.1f; - } - } else { - if (scale > unhighlightedScale) { - scale -= scaleVel * (float)gameTime.ElapsedGameTime.TotalSeconds; - - } else { - scale = unhighlightedScale; - scaleVel = 0.1f; - } - } - - // if scaleAccel is too high, the scale of the button will actually increase so fast that it goes past what it should, and is forced back down. - // It gives the animations a sort of bounce, I like it so I'm keeping it - - // The amount that the scale changes every frame is increasing every frame, - // Gives the animation a less linear look - scaleVel += scaleAccel; - } - - // These two methods are similar to the one above, where the X position of the cards gradually changes each frame when switching between tags and games menu. - public void scrollRight( GameTime gameTime, double scalingAmount ) { - // With these two, I have them slow down as they slide. So, if the xVel reaches zero, just snap them to where they need to be - if (pos.X < xHidden && xVel > 0) { - // If the resolution differs from what's expected, this animation will play much slower/faster than it should - // It still functions, but looks wrong. using scalingAmount more or less makes the animations play at their proper speed - pos.X += xVel * xSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds * (float) scalingAmount; - xVel *= xAccel; - } else { - pos.X = xHidden; - xVel = defaultxVel; - } - } - - public void scrollLeft( GameTime gameTime, double scalingAmount ) { - if (pos.X > xShowing && xVel > 0) { - pos.X -= xVel * xSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds * (float)scalingAmount; - xVel *= xAccel; - } else { - pos.X = xShowing; - xVel = defaultxVel; - } - } - - public void resetxVel() { this.xVel = defaultxVel; } - - public void DrawSelf(SpriteBatch _spriteBatch, double scalingAmount) { - - _spriteBatch.Draw( - texture, - pos, - null, - color, - 0f, - new Vector2(texture.Width/2, texture.Height/2), - (float)(scale * scalingAmount), - SpriteEffects.None, - 0f - ); - - Vector2 strSize = font.MeasureString(this.tag.name); - - _spriteBatch.DrawString(font, - this.tag.name, - pos, - Color.White, - 0f, - new Vector2(strSize.X / 2, strSize.Y / 2), - (float)(scale * 2 * scalingAmount), - SpriteEffects.None, - 0f - ); - } - -} diff --git a/onboard/frontend/ui/TagsMenu.cs b/onboard/frontend/ui/TagsMenu.cs deleted file mode 100644 index 2552e53..0000000 --- a/onboard/frontend/ui/TagsMenu.cs +++ /dev/null @@ -1,207 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Content; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; -using System.Collections.Generic; -using System; -using Devcade; - -namespace onboard.ui; - -public class TagsMenu -{ - - private TagCard[,] cards; - private static int rows; - private static int cols = 2; - private int currentRow; - private int currentCol; - // Jesus this scaling Amount is such a hack, should probably think of a better way to render on non devcade - // It looks like BUNS but the UI is all visible and functional (it looks better on 2560x1080 I swear ;-;) - private double scalingAmount; - private bool isShowing = false; - - public TagsMenu(devcade.Tag[] tags, Texture2D cardTexture, SpriteFont font, Vector2 dims, double scalingAmount) { - - this.scalingAmount = scalingAmount; - - // cards is a 2D array of every tag in the form [row][col] - // The # of rows is tags.length / 2 rounded up - TagsMenu.rows = (int)Math.Ceiling((double)tags.Length/2); - // # of cols is a constant 2 - cards = new TagCard[rows, cols]; // Surprised that math.floor/ceiling dont return ints - - int currentTag = 0; // Int to keep track of our spot within the tags list - - for (int row=0; row 0) ? 1 : 0; - - cards[currentRow, currentCol].setSelected(true); - } - - public void highlightDown() { - cards[currentRow, currentCol].setSelected(false); - - currentRow += (currentRow < rows-1 && cards[currentRow+1, currentCol] != null) ? 1 : 0; - - cards[currentRow, currentCol].setSelected(true); - } - - public void highlightLeft() { - cards[currentRow, currentCol].setSelected(false); - - currentCol -= (currentCol > 0) ? 1 : 0; - - cards[currentRow, currentCol].setSelected(true); - } - - public void highlightRight() { - cards[currentRow, currentCol].setSelected(false); - - currentCol += (currentCol < cols-1 && cards[currentRow, currentCol+1] != null) ? 1 : 0; - - cards[currentRow, currentCol].setSelected(true); - } - -} diff --git a/onboard/frontend/util/Env.cs b/onboard/frontend/util/Env.cs deleted file mode 100644 index 9e57695..0000000 --- a/onboard/frontend/util/Env.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using log4net.Repository.Hierarchy; - -namespace onboard.util; - -public static class Env { - private static readonly Dictionary env = new(); - - static Env() { - foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) { - env.Add((string)entry.Key, (string)entry.Value); - } - } - - public static Option get(string key) { - return env.ContainsKey(key) ? Option.Some(env[key]) : Option.None(); - } - - public static void set(string key, string value) { - env[key] = value; - } - - public static void unset(string key) { - env.Remove(key); - } - - public static void clear() { - env.Clear(); - } - - public static void load(string path) { - if (!File.Exists(path)) { - return; - } - string[] lines = File.ReadAllLines(path); - foreach (string line in lines) { - string[] parts = line.Split('='); - if (parts.Length == 2) { - env[parts[0]] = parts[1]; - } - } - } -} \ No newline at end of file diff --git a/onboard/frontend/util/ILogExtensions.cs b/onboard/frontend/util/ILogExtensions.cs deleted file mode 100644 index ca8fc8a..0000000 --- a/onboard/frontend/util/ILogExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using log4net; - -public static class ILogExtentions { - public static void Trace(this ILog log, string message, Exception exception) { - log.Logger.Log(System.Reflection.MethodBase.GetCurrentMethod()?.DeclaringType, - log4net.Core.Level.Trace, message, exception); - } - - public static void Trace(this ILog log, string message) { - log.Trace(message, null); - } - - public static void Verbose(this ILog log, string message, Exception exception) { - log.Logger.Log(System.Reflection.MethodBase.GetCurrentMethod()?.DeclaringType, - log4net.Core.Level.Verbose, message, exception); - } - - public static void Verbose(this ILog log, string message) { - log.Verbose(message, null); - } - - public static void Log(this ILog log, log4net.Core.Level level, string message, Exception exception) { - log.Logger.Log(System.Reflection.MethodBase.GetCurrentMethod()?.DeclaringType, - level, message, exception); - } - - public static void Log(this ILog log, log4net.Core.Level level, string message) { - log.Log(level, message, null); - } -} \ No newline at end of file diff --git a/onboard/godot-frontend/.editorconfig b/onboard/godot-frontend/.editorconfig new file mode 100644 index 0000000..f28239b --- /dev/null +++ b/onboard/godot-frontend/.editorconfig @@ -0,0 +1,4 @@ +root = true + +[*] +charset = utf-8 diff --git a/onboard/godot-frontend/.gitattributes b/onboard/godot-frontend/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/onboard/godot-frontend/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/onboard/godot-frontend/.gitignore b/onboard/godot-frontend/.gitignore new file mode 100644 index 0000000..a5fa192 --- /dev/null +++ b/onboard/godot-frontend/.gitignore @@ -0,0 +1,4 @@ +# Godot 4+ specific ignores +.godot/ +/android/ +/data_godot-frontend_linuxbsd_x86_64 \ No newline at end of file diff --git a/onboard/godot-frontend/AutoLoad.cs b/onboard/godot-frontend/AutoLoad.cs new file mode 100644 index 0000000..2a171ee --- /dev/null +++ b/onboard/godot-frontend/AutoLoad.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using System.Linq; +using Godot; +using onboard.util; + +namespace onboard; + +public partial class AutoLoad : Node +{ + util.Logger LOG = Log.get(nameof(AutoLoad)); + + /// + /// load the required services as early as possible + /// with set properties + /// + public override void _Ready() + { + // load the .env file (contains the enviorment variables) + LOG.Info("loading env"); + if(File.Exists("./.env")) + { + Env.load("./.env"); + } + else if(File.Exists("../.env")) + { + Env.load("../.env"); + } + else if(File.Exists("/usr/share/devcade/.env")) + { + LOG.Warn("default .env is being used"); + Env.load("../.env"); // note somehow that default .env is being used + } + + string logLocation = Env.LOG_LOCATION(); + try + { + string targetLogPath = ProjectSettings.GetSetting("debug/file_logging/log_path").AsString(); + string[] subStrings = targetLogPath.Split('/'); + + targetLogPath = targetLogPath.Substring(0, targetLogPath.LastIndexOf(subStrings.Last())); // remove file ie "godot.log" from right side + targetLogPath = ProjectSettings.GlobalizePath(targetLogPath); + + // remove link if target locations differ + if(Directory.Exists(logLocation)) + { + FileSystemInfo target = Directory.ResolveLinkTarget(logLocation, true); + if(target.FullName != targetLogPath) + { + LOG.Info("removing link with target: " + target.FullName); + Directory.Delete(logLocation); + + Directory.CreateSymbolicLink(logLocation, targetLogPath); + LOG.Info("created symlink to: " + targetLogPath); + } + } + else + { + Directory.CreateSymbolicLink(logLocation, targetLogPath); + LOG.Info("created symlink to: " + targetLogPath); + } + + } + catch (Exception e) + { + LOG.Error("Unable to create symlink: " + e.Message); + } + + // force initalization of: + + // start client (backend networked communicator) + LOG.Info("starting backend client interface "); + devcade.Client.init(); + } +} diff --git a/onboard/godot-frontend/AutoLoad.cs.uid b/onboard/godot-frontend/AutoLoad.cs.uid new file mode 100644 index 0000000..95f4910 --- /dev/null +++ b/onboard/godot-frontend/AutoLoad.cs.uid @@ -0,0 +1 @@ +uid://n665gxhqf6i3 diff --git a/onboard/frontend/Content/CSH.png b/onboard/godot-frontend/CSHAssets/CSH.png similarity index 100% rename from onboard/frontend/Content/CSH.png rename to onboard/godot-frontend/CSHAssets/CSH.png diff --git a/onboard/godot-frontend/CSHAssets/CSH.png.import b/onboard/godot-frontend/CSHAssets/CSH.png.import new file mode 100644 index 0000000..38cc2e7 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/CSH.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d4nlo4q03fi77" +path="res://.godot/imported/CSH.png-2a0f80cd570ac005f7c1e1fc1f21ede8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/CSH.png" +dest_files=["res://.godot/imported/CSH.png-2a0f80cd570ac005f7c1e1fc1f21ede8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/godot-frontend/CSHAssets/OldloadingAnimation.tres b/onboard/godot-frontend/CSHAssets/OldloadingAnimation.tres new file mode 100644 index 0000000..6af938a --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/OldloadingAnimation.tres @@ -0,0 +1,186 @@ +[gd_resource type="SpriteFrames" format=3 uid="uid://cwsn76nuho5hi"] + +[ext_resource type="Texture2D" uid="uid://cvmfeo0polfva" path="res://CSHAssets/loadingSheet.jpg" id="1_ukx4a"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_h5yho"] +atlas = ExtResource("1_ukx4a") +region = Rect2(0, 0, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_rfbiw"] +atlas = ExtResource("1_ukx4a") +region = Rect2(600, 0, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_j0p0g"] +atlas = ExtResource("1_ukx4a") +region = Rect2(1200, 0, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_so4xy"] +atlas = ExtResource("1_ukx4a") +region = Rect2(1800, 0, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_nrv3j"] +atlas = ExtResource("1_ukx4a") +region = Rect2(2400, 0, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_3l28l"] +atlas = ExtResource("1_ukx4a") +region = Rect2(0, 600, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_iwraf"] +atlas = ExtResource("1_ukx4a") +region = Rect2(600, 600, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ld5lo"] +atlas = ExtResource("1_ukx4a") +region = Rect2(1200, 600, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_dc4qm"] +atlas = ExtResource("1_ukx4a") +region = Rect2(1800, 600, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_6nh8j"] +atlas = ExtResource("1_ukx4a") +region = Rect2(2400, 600, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_jq46g"] +atlas = ExtResource("1_ukx4a") +region = Rect2(0, 1200, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_f2rul"] +atlas = ExtResource("1_ukx4a") +region = Rect2(600, 1200, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_eov7d"] +atlas = ExtResource("1_ukx4a") +region = Rect2(1200, 1200, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_5jh7e"] +atlas = ExtResource("1_ukx4a") +region = Rect2(1800, 1200, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_xqtkm"] +atlas = ExtResource("1_ukx4a") +region = Rect2(2400, 1200, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_85guw"] +atlas = ExtResource("1_ukx4a") +region = Rect2(0, 1800, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_n5xpu"] +atlas = ExtResource("1_ukx4a") +region = Rect2(600, 1800, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_phign"] +atlas = ExtResource("1_ukx4a") +region = Rect2(1200, 1800, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_upaqp"] +atlas = ExtResource("1_ukx4a") +region = Rect2(1800, 1800, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_jkwvv"] +atlas = ExtResource("1_ukx4a") +region = Rect2(2400, 1800, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_hepjo"] +atlas = ExtResource("1_ukx4a") +region = Rect2(0, 2400, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_pqxvb"] +atlas = ExtResource("1_ukx4a") +region = Rect2(600, 2400, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_hhsoa"] +atlas = ExtResource("1_ukx4a") +region = Rect2(1200, 2400, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_0ixky"] +atlas = ExtResource("1_ukx4a") +region = Rect2(1800, 2400, 600, 600) + +[sub_resource type="AtlasTexture" id="AtlasTexture_63fqu"] +atlas = ExtResource("1_ukx4a") +region = Rect2(2400, 2400, 600, 600) + +[resource] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_h5yho") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_rfbiw") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_j0p0g") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_so4xy") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_nrv3j") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_3l28l") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_iwraf") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ld5lo") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_dc4qm") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_6nh8j") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_jq46g") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_f2rul") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_eov7d") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_5jh7e") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_xqtkm") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_85guw") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_n5xpu") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_phign") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_upaqp") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_jkwvv") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_hepjo") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_pqxvb") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_hhsoa") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_0ixky") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_63fqu") +}], +"loop": 1, +"name": &"default", +"speed": 50.0 +}] diff --git a/onboard/frontend/Content/OnboardBackgroundGradient.png b/onboard/godot-frontend/CSHAssets/OnboardBackgroundGradient.png similarity index 100% rename from onboard/frontend/Content/OnboardBackgroundGradient.png rename to onboard/godot-frontend/CSHAssets/OnboardBackgroundGradient.png diff --git a/onboard/godot-frontend/CSHAssets/OnboardBackgroundGradient.png.import b/onboard/godot-frontend/CSHAssets/OnboardBackgroundGradient.png.import new file mode 100644 index 0000000..ea3e0b6 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/OnboardBackgroundGradient.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://gsf5hvgxlwu8" +path="res://.godot/imported/OnboardBackgroundGradient.png-1157aba4a3e565f70f81594edb885942.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/OnboardBackgroundGradient.png" +dest_files=["res://.godot/imported/OnboardBackgroundGradient.png-1157aba4a3e565f70f81594edb885942.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/frontend/Content/VT323-Regular.ttf b/onboard/godot-frontend/CSHAssets/VT323-Regular.ttf similarity index 100% rename from onboard/frontend/Content/VT323-Regular.ttf rename to onboard/godot-frontend/CSHAssets/VT323-Regular.ttf diff --git a/onboard/godot-frontend/CSHAssets/VT323-Regular.ttf.import b/onboard/godot-frontend/CSHAssets/VT323-Regular.ttf.import new file mode 100644 index 0000000..7bb9a3a --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/VT323-Regular.ttf.import @@ -0,0 +1,35 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://cnha1ohwbh3ts" +path="res://.godot/imported/VT323-Regular.ttf-c1c89a3d9637798aa6d4dbae25315f03.fontdata" + +[deps] + +source_file="res://CSHAssets/VT323-Regular.ttf" +dest_files=["res://.godot/imported/VT323-Regular.ttf-c1c89a3d9637798aa6d4dbae25315f03.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/onboard/godot-frontend/CSHAssets/button/DevcadeButton.cs b/onboard/godot-frontend/CSHAssets/button/DevcadeButton.cs new file mode 100644 index 0000000..3b30cf0 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/button/DevcadeButton.cs @@ -0,0 +1,19 @@ +using Godot; + +public partial class DevcadeButton : AnimatedSprite2D +{ + public override void _Notification(int what) + { + if(what == NotificationVisibilityChanged) + { + if(this.Visible) + { + this.Play(); + } + else + { + this.Stop(); + } + } + } +} diff --git a/onboard/godot-frontend/CSHAssets/button/DevcadeButton.cs.uid b/onboard/godot-frontend/CSHAssets/button/DevcadeButton.cs.uid new file mode 100644 index 0000000..b35fd46 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/button/DevcadeButton.cs.uid @@ -0,0 +1 @@ +uid://mk3x5165b4sv diff --git a/onboard/godot-frontend/CSHAssets/button/button_down.png b/onboard/godot-frontend/CSHAssets/button/button_down.png new file mode 100644 index 0000000..c842f10 Binary files /dev/null and b/onboard/godot-frontend/CSHAssets/button/button_down.png differ diff --git a/onboard/godot-frontend/CSHAssets/button/button_down.png.import b/onboard/godot-frontend/CSHAssets/button/button_down.png.import new file mode 100644 index 0000000..90cccf7 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/button/button_down.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1fkjoxsm1ke8" +path="res://.godot/imported/button_down.png-6cb9d9c6e247e8e553d88881efc114e7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/button/button_down.png" +dest_files=["res://.godot/imported/button_down.png-6cb9d9c6e247e8e553d88881efc114e7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/godot-frontend/CSHAssets/button/button_mid.png b/onboard/godot-frontend/CSHAssets/button/button_mid.png new file mode 100644 index 0000000..51566d7 Binary files /dev/null and b/onboard/godot-frontend/CSHAssets/button/button_mid.png differ diff --git a/onboard/godot-frontend/CSHAssets/button/button_mid.png.import b/onboard/godot-frontend/CSHAssets/button/button_mid.png.import new file mode 100644 index 0000000..c2310a0 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/button/button_mid.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c84vahhkvavi6" +path="res://.godot/imported/button_mid.png-c4e5ec8a1ac9ff7349c84cbb5ffadf1d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/button/button_mid.png" +dest_files=["res://.godot/imported/button_mid.png-c4e5ec8a1ac9ff7349c84cbb5ffadf1d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/godot-frontend/CSHAssets/button/button_up.png b/onboard/godot-frontend/CSHAssets/button/button_up.png new file mode 100644 index 0000000..9cf86bc Binary files /dev/null and b/onboard/godot-frontend/CSHAssets/button/button_up.png differ diff --git a/onboard/godot-frontend/CSHAssets/button/button_up.png.import b/onboard/godot-frontend/CSHAssets/button/button_up.png.import new file mode 100644 index 0000000..6c9e1f0 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/button/button_up.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://duav6epeka3wv" +path="res://.godot/imported/button_up.png-0e7350b09075b95c3070ba0b28e8604e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/button/button_up.png" +dest_files=["res://.godot/imported/button_up.png-0e7350b09075b95c3070ba0b28e8604e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/godot-frontend/CSHAssets/button/devcade_button.gdshader b/onboard/godot-frontend/CSHAssets/button/devcade_button.gdshader new file mode 100644 index 0000000..654d5a3 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/button/devcade_button.gdshader @@ -0,0 +1,17 @@ +shader_type canvas_item; + +uniform vec4 color : source_color = vec4(1,1,1,1); + +void vertex() { + // Called for every vertex the material is visible on. +} + +void fragment() { + // Called for every pixel the material is visible on. + COLOR = COLOR * color; +} + +//void light() { +// // Called for every pixel for every light affecting the CanvasItem. +// // Uncomment to replace the default light processing function with this one. +//} diff --git a/onboard/godot-frontend/CSHAssets/button/devcade_button.gdshader.uid b/onboard/godot-frontend/CSHAssets/button/devcade_button.gdshader.uid new file mode 100644 index 0000000..22964da --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/button/devcade_button.gdshader.uid @@ -0,0 +1 @@ +uid://ckw5qtfpbste diff --git a/onboard/godot-frontend/CSHAssets/button/devcade_button.tscn b/onboard/godot-frontend/CSHAssets/button/devcade_button.tscn new file mode 100644 index 0000000..c8a2ec6 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/button/devcade_button.tscn @@ -0,0 +1,39 @@ +[gd_scene load_steps=8 format=3 uid="uid://bglh47iehhwwm"] + +[ext_resource type="Texture2D" uid="uid://duav6epeka3wv" path="res://CSHAssets/button/button_up.png" id="1_2nnuo"] +[ext_resource type="Shader" uid="uid://ckw5qtfpbste" path="res://CSHAssets/button/devcade_button.gdshader" id="1_iwpom"] +[ext_resource type="Texture2D" uid="uid://c84vahhkvavi6" path="res://CSHAssets/button/button_mid.png" id="2_iwpom"] +[ext_resource type="Texture2D" uid="uid://1fkjoxsm1ke8" path="res://CSHAssets/button/button_down.png" id="3_iqb3g"] +[ext_resource type="Script" uid="uid://mk3x5165b4sv" path="res://CSHAssets/button/DevcadeButton.cs" id="5_iqb3g"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_iqb3g"] +shader = ExtResource("1_iwpom") +shader_parameter/color = Color(0.21, 0.21, 0.21, 1) + +[sub_resource type="SpriteFrames" id="SpriteFrames_b4mud"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": ExtResource("1_2nnuo") +}, { +"duration": 1.0, +"texture": ExtResource("2_iwpom") +}, { +"duration": 1.0, +"texture": ExtResource("3_iqb3g") +}, { +"duration": 1.0, +"texture": ExtResource("2_iwpom") +}], +"loop": true, +"name": &"default", +"speed": 5.0 +}] + +[node name="DevcadeButton" type="AnimatedSprite2D"] +texture_filter = 1 +texture_repeat = 1 +material = SubResource("ShaderMaterial_iqb3g") +sprite_frames = SubResource("SpriteFrames_b4mud") +speed_scale = 0.8 +script = ExtResource("5_iqb3g") diff --git a/onboard/frontend/Content/card.png b/onboard/godot-frontend/CSHAssets/card.png similarity index 100% rename from onboard/frontend/Content/card.png rename to onboard/godot-frontend/CSHAssets/card.png diff --git a/onboard/godot-frontend/CSHAssets/card.png.import b/onboard/godot-frontend/CSHAssets/card.png.import new file mode 100644 index 0000000..334629b --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/card.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dfdlp2n60ns8g" +path="res://.godot/imported/card.png-bba1fb411f7699199e0a4793ed76be0d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/card.png" +dest_files=["res://.godot/imported/card.png-bba1fb411f7699199e0a4793ed76be0d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/frontend/Content/description.png b/onboard/godot-frontend/CSHAssets/description.png similarity index 100% rename from onboard/frontend/Content/description.png rename to onboard/godot-frontend/CSHAssets/description.png diff --git a/onboard/godot-frontend/CSHAssets/description.png.import b/onboard/godot-frontend/CSHAssets/description.png.import new file mode 100644 index 0000000..5979e15 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/description.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://f61vcor4mcvi" +path="res://.godot/imported/description.png-e92b567c50dc9778e82fdeb94be4005a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/description.png" +dest_files=["res://.godot/imported/description.png-e92b567c50dc9778e82fdeb94be4005a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/godot-frontend/CSHAssets/loading animation/loadingAnimation.tres b/onboard/godot-frontend/CSHAssets/loading animation/loadingAnimation.tres new file mode 100644 index 0000000..4f9f6c1 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/loading animation/loadingAnimation.tres @@ -0,0 +1,179 @@ +[gd_resource type="SpriteFrames" format=3 uid="uid://bfm4pfog3lttd"] + +[ext_resource type="Texture2D" uid="uid://b8cfwhy0kd75t" path="res://CSHAssets/loading animation/sprite_sheet(2).png" id="1_vjb34"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_j36tv"] +atlas = ExtResource("1_vjb34") +region = Rect2(0, 0, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ushkc"] +atlas = ExtResource("1_vjb34") +region = Rect2(1024, 0, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_h8ot1"] +atlas = ExtResource("1_vjb34") +region = Rect2(2048, 0, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_7t2mm"] +atlas = ExtResource("1_vjb34") +region = Rect2(3072, 0, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_v717p"] +atlas = ExtResource("1_vjb34") +region = Rect2(4096, 0, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_bgmva"] +atlas = ExtResource("1_vjb34") +region = Rect2(0, 1024, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_1jvlp"] +atlas = ExtResource("1_vjb34") +region = Rect2(1024, 1024, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_68mav"] +atlas = ExtResource("1_vjb34") +region = Rect2(2048, 1024, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ofop6"] +atlas = ExtResource("1_vjb34") +region = Rect2(3072, 1024, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_6qgtt"] +atlas = ExtResource("1_vjb34") +region = Rect2(4096, 1024, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_on1o4"] +atlas = ExtResource("1_vjb34") +region = Rect2(0, 2048, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_bht2w"] +atlas = ExtResource("1_vjb34") +region = Rect2(1024, 2048, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_w6nhn"] +atlas = ExtResource("1_vjb34") +region = Rect2(2048, 2048, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_jhhyj"] +atlas = ExtResource("1_vjb34") +region = Rect2(3072, 2048, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_o4djb"] +atlas = ExtResource("1_vjb34") +region = Rect2(4096, 2048, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_yirum"] +atlas = ExtResource("1_vjb34") +region = Rect2(0, 3072, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ptk72"] +atlas = ExtResource("1_vjb34") +region = Rect2(1024, 3072, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_a08of"] +atlas = ExtResource("1_vjb34") +region = Rect2(2048, 3072, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_h0h1g"] +atlas = ExtResource("1_vjb34") +region = Rect2(3072, 3072, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_cdm6x"] +atlas = ExtResource("1_vjb34") +region = Rect2(4096, 3072, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_54pp8"] +atlas = ExtResource("1_vjb34") +region = Rect2(0, 4096, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_2f47b"] +atlas = ExtResource("1_vjb34") +region = Rect2(1024, 4096, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ojbaa"] +atlas = ExtResource("1_vjb34") +region = Rect2(2048, 4096, 1024, 1024) + +[sub_resource type="AtlasTexture" id="AtlasTexture_fmf3s"] +atlas = ExtResource("1_vjb34") +region = Rect2(3072, 4096, 1024, 1024) + +[resource] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_j36tv") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ushkc") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_h8ot1") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_7t2mm") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_v717p") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_bgmva") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_1jvlp") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_68mav") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ofop6") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_6qgtt") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_on1o4") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_bht2w") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_w6nhn") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_jhhyj") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_o4djb") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_yirum") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ptk72") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_a08of") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_h0h1g") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_cdm6x") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_54pp8") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_2f47b") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ojbaa") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_fmf3s") +}], +"loop": 1, +"name": &"default", +"speed": 5.0 +}] diff --git a/onboard/godot-frontend/CSHAssets/loading animation/sprite_sheet(2).png b/onboard/godot-frontend/CSHAssets/loading animation/sprite_sheet(2).png new file mode 100644 index 0000000..d413034 Binary files /dev/null and b/onboard/godot-frontend/CSHAssets/loading animation/sprite_sheet(2).png differ diff --git a/onboard/godot-frontend/CSHAssets/loading animation/sprite_sheet(2).png.import b/onboard/godot-frontend/CSHAssets/loading animation/sprite_sheet(2).png.import new file mode 100644 index 0000000..efcb4fa --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/loading animation/sprite_sheet(2).png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b8cfwhy0kd75t" +path="res://.godot/imported/sprite_sheet(2).png-7367133d750df39cf89de374444cc66b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/loading animation/sprite_sheet(2).png" +dest_files=["res://.godot/imported/sprite_sheet(2).png-7367133d750df39cf89de374444cc66b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/frontend/Content/loadingSheet.jpg b/onboard/godot-frontend/CSHAssets/loadingSheet.jpg similarity index 100% rename from onboard/frontend/Content/loadingSheet.jpg rename to onboard/godot-frontend/CSHAssets/loadingSheet.jpg diff --git a/onboard/godot-frontend/CSHAssets/loadingSheet.jpg.import b/onboard/godot-frontend/CSHAssets/loadingSheet.jpg.import new file mode 100644 index 0000000..f4876f2 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/loadingSheet.jpg.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cvmfeo0polfva" +path="res://.godot/imported/loadingSheet.jpg-6ca8f0bb476a1ba0e705048ce2e46481.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/loadingSheet.jpg" +dest_files=["res://.godot/imported/loadingSheet.jpg-6ca8f0bb476a1ba0e705048ce2e46481.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/godot-frontend/CSHAssets/sprite_sheet(2).png b/onboard/godot-frontend/CSHAssets/sprite_sheet(2).png new file mode 100644 index 0000000..22b1fdc Binary files /dev/null and b/onboard/godot-frontend/CSHAssets/sprite_sheet(2).png differ diff --git a/onboard/godot-frontend/CSHAssets/sprite_sheet(2).png.import b/onboard/godot-frontend/CSHAssets/sprite_sheet(2).png.import new file mode 100644 index 0000000..a846929 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/sprite_sheet(2).png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bvfvpq8dgdo8l" +path="res://.godot/imported/sprite_sheet(2).png-e2c783a6e733759f8c5ee86bccedaadb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/sprite_sheet(2).png" +dest_files=["res://.godot/imported/sprite_sheet(2).png-e2c783a6e733759f8c5ee86bccedaadb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/frontend/Content/transparent-dev-logo.png b/onboard/godot-frontend/CSHAssets/transparent-dev-logo.png similarity index 100% rename from onboard/frontend/Content/transparent-dev-logo.png rename to onboard/godot-frontend/CSHAssets/transparent-dev-logo.png diff --git a/onboard/godot-frontend/CSHAssets/transparent-dev-logo.png.import b/onboard/godot-frontend/CSHAssets/transparent-dev-logo.png.import new file mode 100644 index 0000000..1a62b9b --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/transparent-dev-logo.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bj333tajmq7mt" +path="res://.godot/imported/transparent-dev-logo.png-8674e2307d316e0436eeebff2aaf5080.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/transparent-dev-logo.png" +dest_files=["res://.godot/imported/transparent-dev-logo.png-8674e2307d316e0436eeebff2aaf5080.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/frontend/Content/transparent-logo-white.png b/onboard/godot-frontend/CSHAssets/transparent-logo-white.png similarity index 100% rename from onboard/frontend/Content/transparent-logo-white.png rename to onboard/godot-frontend/CSHAssets/transparent-logo-white.png diff --git a/onboard/godot-frontend/CSHAssets/transparent-logo-white.png.import b/onboard/godot-frontend/CSHAssets/transparent-logo-white.png.import new file mode 100644 index 0000000..e4b253c --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/transparent-logo-white.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cm8hlcb1in1rd" +path="res://.godot/imported/transparent-logo-white.png-73b7c29f7644f61ae52fc3cdada028b8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/transparent-logo-white.png" +dest_files=["res://.godot/imported/transparent-logo-white.png-73b7c29f7644f61ae52fc3cdada028b8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/frontend/Content/transparent-logo.png b/onboard/godot-frontend/CSHAssets/transparent-logo.png similarity index 100% rename from onboard/frontend/Content/transparent-logo.png rename to onboard/godot-frontend/CSHAssets/transparent-logo.png diff --git a/onboard/godot-frontend/CSHAssets/transparent-logo.png.import b/onboard/godot-frontend/CSHAssets/transparent-logo.png.import new file mode 100644 index 0000000..b99d549 --- /dev/null +++ b/onboard/godot-frontend/CSHAssets/transparent-logo.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://lqrfprpd7kh3" +path="res://.godot/imported/transparent-logo.png-579149f39c896152c1b8b1412199dfe7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://CSHAssets/transparent-logo.png" +dest_files=["res://.godot/imported/transparent-logo.png-579149f39c896152c1b8b1412199dfe7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/onboard/godot-frontend/FpsLabel.cs b/onboard/godot-frontend/FpsLabel.cs new file mode 100644 index 0000000..4f16c29 --- /dev/null +++ b/onboard/godot-frontend/FpsLabel.cs @@ -0,0 +1,48 @@ +using Godot; +using onboard.devcade; +using System.Linq; + +public partial class FpsLabel : Label +{ + int[] fpsSave = new int[100]; + int lowFps; + int i = 0; + + [Export] + public bool showRegardless = false; + + public override void _Process(double delta) + { + if(Client.isProduction && !showRegardless) + { + this.Hide(); + return; + } + this.Show(); + + int fps = (int) (1.0 / delta); + fpsSave[i] = fps; + i = (i + 1) % fpsSave.Length; + + fps = (int) fpsSave.Average(); + lowFps = fpsSave.Min(); + + this.Text = $"avg: {fps}\n + low: {lowFps}"; + + Color color; + if(fps < 30) + { + color = Colors.Red; + } + else if( fps < 60 ) + { + color = Colors.Yellow; + } + else + { + color = Colors.Green; + } + + this.Set("theme_override_colors/font_color", color); + } +} diff --git a/onboard/godot-frontend/FpsLabel.cs.uid b/onboard/godot-frontend/FpsLabel.cs.uid new file mode 100644 index 0000000..af12237 --- /dev/null +++ b/onboard/godot-frontend/FpsLabel.cs.uid @@ -0,0 +1 @@ +uid://bft2wse50dh2e diff --git a/onboard/godot-frontend/GUIs/CreatingAGuiREADME.md b/onboard/godot-frontend/GUIs/CreatingAGuiREADME.md new file mode 100644 index 0000000..9e11fbc --- /dev/null +++ b/onboard/godot-frontend/GUIs/CreatingAGuiREADME.md @@ -0,0 +1,26 @@ +## What Happens before a GUI is instantated +* First the [AutoLoad.cs](/onboard/godot-frontend/AutoLoad.cs) script is started by the Godot autoload system +* Then the enviormental values are loaded from the [.env](/onboard/.env) file, from the `load()` function called by [AutoLoad.cs](/onboard/godot-frontend/AutoLoad.cs) +* Then the [Client.cs](/onboard/godot-frontend/devcade/Client.cs) script is started for communication with the backend, again called from [AutoLoad.cs](/onboard/godot-frontend/AutoLoad.cs) +* Then the [GuiManagerGlobal.cs](/onboard/godot-frontend/guiManager/GuiManagerGlobal.cs) script is started by the Godot autoload system +* Then the Scene Tree is loaded, creating the root node that has [GuiManager.cs](/onboard/godot-frontend/guiManager/GuiManager.cs) attached +* Which then finally loads the selected Gui scene + +## Creating a GUI +* First create a new folder under `/onboard/godot-frontend/GUIs` with the name of the new GUI +* Then create a new scene in that folder and start creating +* Use the functions and variables of [GuiManagerGlobal.cs](/onboard/godot-frontend/guiManager/GuiManagerGlobal.cs) to get the list of games, tags, etc. + +## Required Functionality + +### The GUI you create must: +* Have a way to select and set a tag from the list of all the tags +* Have a way to select and lauch a game from the list of the games +* Be able to view the description and author of a game +* Help text, explaining what the buttons do +* Accessiblility is of the upmost importance + +### While not strictly necessary these are things that are nice to have: +* Animations, such as transisions between game 1 and 2 being selected + + diff --git a/onboard/godot-frontend/GUIs/minimal/MinimalGui.cs b/onboard/godot-frontend/GUIs/minimal/MinimalGui.cs new file mode 100644 index 0000000..0482048 --- /dev/null +++ b/onboard/godot-frontend/GUIs/minimal/MinimalGui.cs @@ -0,0 +1,375 @@ +using Godot; +using System; + +using System.Collections.Generic; + +namespace onboard.devcade.GUI.minimal; + +/// +/// this is somewhat simple GUI that implements all the basic functions +/// to show how to create a simple UI script +/// all logic for creating buttons and adding functions to the buttons is done here +/// +public partial class MinimalGui : Control +{ + /// + /// the container that holds the buttons that represent the games + /// which are analogous to the menucards in the old frontend + /// + [Export] + public GridContainer gameContainer; + + /// + /// holds the tag buttons + /// + [Export] + public GridContainer tagContainer; + + /// + /// the overall parent panel + /// that holds all the ui nodes related to the description + /// used to hide/show the description + /// + [Export] + public Panel descriptionPanel; + + /// + /// the label that holds the actual description text (DevcadeGame.description) + /// + [Export] + public Label desriptionLabel; + + /// + /// a label to show the title of the game when the description is shown + /// + [Export] + public Label titleLabel; + + /// + /// the button when showing the description that is connected to lauching the game + /// + [Export] + public BaseButton lauchGameButton; + + + /// + /// The last pressed button's aspectratiocontainer + /// Used for saving focus when the a description of a game is shown. + /// + private AspectRatioContainer lastButtonContainerPressed = null; + + /// + /// a dictionary of the buttons' containers to games, + /// used for accessing a games based on the button, + /// which is useful in this case for when the current tag changes + /// and it is needed to hide all the buttons/games that do not have that tag + /// + private Dictionary gameContainers = new Dictionary(); + + int screenHeight = -1; + int screenWidth = -1; + + + /// + /// runs once after the node is loaded into the scene tree, + /// used in this case to set the monochrome missing texture, + /// and to get the screen width/height + /// + public override void _Ready() + { + descriptionPanel.Hide(); + lauchGameButton.Pressed += lauchCurrentGame; + + Vector2I screenDims = DisplayServer.ScreenGetSize(); + screenHeight = screenDims.Y; + screenWidth = screenDims.X; + + GuiManagerGlobal.instance.gameTitlesUpdated += () => + { + updateGames(GuiManagerGlobal.gameTitles); + }; + + GuiManagerGlobal.instance.tagListUpdated += () => + { + updateTags(GuiManagerGlobal.tagList); + }; + } + + public override void _Input(InputEvent @event) + { + // add any input that happends once per a given keypress here + + // if the back (blue) button is pressed or the Menu (black) button is pressed + if(@event.IsActionPressed("Player1_A2") || @event.IsActionPressed("Player1_Menu") || @event.IsActionPressed("Player2_A2") || @event.IsActionPressed("Player2_Menu")) + { + // and the description panel is visible, hide it + if(descriptionPanel.IsVisibleInTree()) + { + descriptionPanel.Hide(); + + // make the description's game button focused again + // this is a bit cursed and could be done in a better way, + // but as there is only one child of each aspect ratio container + // and it has to be a button of some kind + // this should not fail + (lastButtonContainerPressed.GetChild(0) as BaseButton).GrabFocus(); + } + } + } + + public override void _Process(double delta) + { + // the update loop + // delta refers to delta time in seconds + // some examples of uses are: + // timers based on how long a button is held + // global animations + + } + + public void updateGames(List games) + { + // clear the dict, as the old data is no longer current + gameContainers = new Dictionary(); + + // for each game create a new texture button with the texture set to the banner if it exists + // put it in an aspectRatioContainer so the aspect ratio is saved on scaling, + // sets the size flags so that each aspectRatioContainer takes up the same space + // add the button as a child to the aspectRatioContainer and that as a child to the gameContainer + for(int i = 0; i < games.Count; i++) + { + DevcadeGame game = games[i]; + + BaseButton button; + + if(game.banner != null) + { + // this is slow, save textures some where, so they don't have to be re-calculated every time? + TextureButton textureButton = new TextureButton(); + textureButton.IgnoreTextureSize = true; + textureButton.StretchMode = TextureButton.StretchModeEnum.KeepAspectCentered; + + // make a monochrome variant of the banner that is slightly darker too + Texture2D monochromeBanner = makeMonochrome(game.banner); + monochromeBanner = changeBrightness(monochromeBanner, +0.4f); + + // make a color variant that is darker too + Texture2D darkerBanner = changeBrightness(game.banner, -0.2f); + + textureButton.TextureDisabled = darkerBanner; + textureButton.TextureNormal = monochromeBanner; + textureButton.TextureHover = game.banner; + textureButton.TexturePressed = darkerBanner; + textureButton.TextureFocused = game.banner; + + button = textureButton; + } + else + { + Button textButton = new Button(); + textButton.SizeFlagsHorizontal = SizeFlags.ExpandFill; + textButton.SizeFlagsVertical = SizeFlags.ExpandFill; + + textButton.Text = game.name; + + button = textButton; + } + + + button.CustomMinimumSize = new Vector2(10, 10); + + var aspectRatioContainer = new AspectRatioContainer(); + aspectRatioContainer.SizeFlagsVertical = SizeFlags.ExpandFill; + aspectRatioContainer.SizeFlagsHorizontal = SizeFlags.ExpandFill; + + if(game.name != "Error") + { + // lambda function is required to "bind" the parameter + // to the function called when the button is pressed + button.Pressed += () => { + lastButtonContainerPressed = aspectRatioContainer; + showDescription(game); + }; + } + + aspectRatioContainer.AddChild(button); + + gameContainer.AddChild(aspectRatioContainer); + + // make the first button focused by default, + // makes using the arrow keys and joysticks to navigate easy + if(i == 0) + { + button.CallDeferred("grab_focus"); + lastButtonContainerPressed = aspectRatioContainer; + } + + // add the new button and its corresponding game to the dictionary + gameContainers.Add(aspectRatioContainer, game); + } + } + + public void updateTags(List tags) + { + foreach(Node node in tagContainer.GetChildren()) + { + tagContainer.RemoveChild(node); + } + + for (int i = 0; i < tags.Count; i++) + { + Tag tag = tags[i]; + Button button = new Button(); + + button.Pressed += () => setCurrentTag(tag); + + button.Text = tag.name; + + tagContainer.AddChild(button); + } + } + + /// + /// shows the description for a game + /// the description includes + /// the title, description, and a button to launch the game + /// + /// + private void showDescription(DevcadeGame game) + { + titleLabel.Text = game.name; + desriptionLabel.Text = game.description; + + // set the action to run when the launch button is pressed + // also make this button grab focus + lauchGameButton.GrabFocus(); + + descriptionPanel.Show(); + } + + /// + /// lauches the game that is referenced by the button in the aspect ratio container + /// in th the lastButtonContainerPressed variable, + /// this is used to lauch a game from a description page + /// + private void lauchCurrentGame() + { + DevcadeGame gameToLaunch = gameContainers[lastButtonContainerPressed]; + launchGame(gameToLaunch); + } + + /// + /// launches the given game + /// calls the launchGame function of the model + /// + /// + private void launchGame(DevcadeGame game) + { + // discard result, in this instance it is not requried to await the launched game to close + _ = GuiManagerGlobal.instance.launchGame(game); + } + + /// + /// sets the current tag variable in the model to the given tag + /// + /// the new tag + private void setCurrentTag(Tag tag) + { + GuiManagerGlobal.instance.setTag(tag); + } + + /// + /// kills the currently running game + /// IMPORTANT: does not wait for the game to be killed for the function to return + /// + private void killCurrentlyRunningGame() + { + // discard the result, + // supresses the warning that: + // Because this call is not awaited, execution of the current method continues before the call is completed + _ = GuiManagerGlobal.instance.killGame(); + } + + /// + /// returns a new instance of the texture with all pixels set to the monochrome space + /// does not modify the transparency values, or the original texture + /// + /// + /// a new instance of the texture with all pixels in the monochrome space + private Texture2D makeMonochrome(Texture2D tex) + { + // to modify a texture, it is reqiured that + // we get an image object first. + // this is somewhat expensive as it gets the texture from the gpu, + // so use it sparingly + Image image = tex.GetImage(); + + int height = image.GetHeight(); + int width = image.GetWidth(); + + for(int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + Color pixelColor = image.GetPixel(x, y); + + float avg = (pixelColor.R + pixelColor.G + pixelColor.B) / 3.0f; + + image.SetPixel(x, y, new Color(avg, avg, avg, pixelColor.A)); + } + } + + // translate the image back into a texture object + return ImageTexture.CreateFromImage(image); + } + + /// + /// returns a new instance of the texture with all pixels + the brightness value + /// does not modify the transparency values, or the original texture + /// + /// + /// a new instance of the texture with all pixels + the brightness value + private Texture2D changeBrightness(Texture2D tex, float brightness) + { + // to modify a texture, it is reqiured that + // we get an image object first. + // this is somewhat expensive as it gets the texture from the gpu, + // so use it sparingly + Image image = tex.GetImage(); + + int height = image.GetHeight(); + int width = image.GetWidth(); + + for(int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + Color pixelColor = image.GetPixel(x, y); + + float r = MathF.Min(MathF.Max(pixelColor.R - brightness, 0.0f), 1.0f); + float g = MathF.Min(MathF.Max(pixelColor.G - brightness, 0.0f), 1.0f); + float b = MathF.Min(MathF.Max(pixelColor.B - brightness, 0.0f), 1.0f); + + image.SetPixel(x, y, new Color(r, g, b, pixelColor.A)); + } + } + + // translate the image back into a texture object + return ImageTexture.CreateFromImage(image); + } + + public void setTag(Tag tag) + { + foreach(AspectRatioContainer container in gameContainer.GetChildren()) + { + if(gameContainers[container].tags.Contains(tag)) + { + container.Show(); + } + else + { + container.Hide(); + } + } + } +} diff --git a/onboard/godot-frontend/GUIs/minimal/MinimalGui.cs.uid b/onboard/godot-frontend/GUIs/minimal/MinimalGui.cs.uid new file mode 100644 index 0000000..a137e97 --- /dev/null +++ b/onboard/godot-frontend/GUIs/minimal/MinimalGui.cs.uid @@ -0,0 +1 @@ +uid://dnfisdn7tx8c4 diff --git a/onboard/godot-frontend/GUIs/minimal/minimal_gui.tscn b/onboard/godot-frontend/GUIs/minimal/minimal_gui.tscn new file mode 100644 index 0000000..081b86b --- /dev/null +++ b/onboard/godot-frontend/GUIs/minimal/minimal_gui.tscn @@ -0,0 +1,129 @@ +[gd_scene load_steps=4 format=3 uid="uid://dk7tk0v3fsyu3"] + +[ext_resource type="Script" uid="uid://dnfisdn7tx8c4" path="res://GUIs/template/TemplateGui.cs" id="1_6ar5m"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_yuvhl"] +bg_color = Color(0.196078, 0.196078, 0.196078, 0.741176) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bimps"] +bg_color = Color(0.162066, 0.162066, 0.162065, 1) +corner_radius_top_left = 35 +corner_radius_top_right = 35 +corner_radius_bottom_right = 35 +corner_radius_bottom_left = 35 + +[node name="test_gui" type="Control" node_paths=PackedStringArray("gameContainer", "tagContainer", "descriptionPanel", "desriptionLabel", "titleLabel", "lauchGameButton")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 2.0 +offset_right = 2.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource("1_6ar5m") +gameContainer = NodePath("TabContainer/Games") +tagContainer = NodePath("TabContainer/Tags") +descriptionPanel = NodePath("Panel") +desriptionLabel = NodePath("Panel/MarginContainer/Panel/MarginContainer/VBoxContainer/MarginContainer/description") +titleLabel = NodePath("Panel/MarginContainer/Panel/MarginContainer/VBoxContainer/title") +lauchGameButton = NodePath("Panel/MarginContainer/Panel/MarginContainer/VBoxContainer/MarginContainer2/launchGame") + +[node name="TabContainer" type="TabContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/side_margin = 15 +theme_override_font_sizes/font_size = 56 +current_tab = 0 + +[node name="Games" type="GridContainer" parent="TabContainer"] +layout_mode = 2 +theme_override_constants/h_separation = 5 +theme_override_constants/v_separation = 5 +columns = 3 +metadata/_tab_index = 0 + +[node name="Tags" type="GridContainer" parent="TabContainer"] +visible = false +layout_mode = 2 +columns = 3 +metadata/_tab_index = 1 + +[node name="Panel" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_yuvhl") + +[node name="MarginContainer" type="MarginContainer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 85 +theme_override_constants/margin_top = 30 +theme_override_constants/margin_right = 85 +theme_override_constants/margin_bottom = 30 + +[node name="Panel" type="Panel" parent="Panel/MarginContainer"] +layout_mode = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_bimps") + +[node name="MarginContainer" type="MarginContainer" parent="Panel/MarginContainer/Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_bottom = 20 + +[node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer/Panel/MarginContainer"] +layout_mode = 2 + +[node name="title" type="Label" parent="Panel/MarginContainer/Panel/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 116 +text = "TITLE" +horizontal_alignment = 1 + +[node name="MarginContainer" type="MarginContainer" parent="Panel/MarginContainer/Panel/MarginContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/margin_left = 40 +theme_override_constants/margin_top = 30 + +[node name="description" type="Label" parent="Panel/MarginContainer/Panel/MarginContainer/VBoxContainer/MarginContainer"] +custom_minimum_size = Vector2(200, 20) +layout_mode = 2 +size_flags_vertical = 1 +theme_override_font_sizes/font_size = 41 +text = "Description" +autowrap_mode = 3 + +[node name="MarginContainer2" type="MarginContainer" parent="Panel/MarginContainer/Panel/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_right = 20 + +[node name="launchGame" type="Button" parent="Panel/MarginContainer/Panel/MarginContainer/VBoxContainer/MarginContainer2"] +layout_mode = 2 +focus_neighbor_left = NodePath(".") +focus_neighbor_top = NodePath(".") +focus_neighbor_right = NodePath(".") +focus_neighbor_bottom = NodePath(".") +focus_next = NodePath(".") +focus_previous = NodePath(".") +theme_override_font_sizes/font_size = 61 +text = "Launch" diff --git a/onboard/godot-frontend/GUIs/orignial/Background.cs b/onboard/godot-frontend/GUIs/orignial/Background.cs new file mode 100644 index 0000000..7e567ee --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/Background.cs @@ -0,0 +1,18 @@ +using Godot; +using System; + +public partial class Background : TextureRect +{ + /// + /// camera to get the viewport of + /// + [Export] + Camera2D camera; + + public override void _Ready() + { + this.CustomMinimumSize = camera.GetViewportRect().Size; + + base._Ready(); + } +} diff --git a/onboard/godot-frontend/GUIs/orignial/Background.cs.uid b/onboard/godot-frontend/GUIs/orignial/Background.cs.uid new file mode 100644 index 0000000..99307ae --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/Background.cs.uid @@ -0,0 +1 @@ +uid://cj0skhgydnii2 diff --git a/onboard/godot-frontend/GUIs/orignial/ControlHelpTextTags.cs b/onboard/godot-frontend/GUIs/orignial/ControlHelpTextTags.cs new file mode 100644 index 0000000..4b55742 --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/ControlHelpTextTags.cs @@ -0,0 +1,33 @@ +using Godot; +using System; + +namespace onboard.devcade.GUI.originalGUI; + +public partial class ControlHelpTextTags : Label +{ + [Export] + public TagContainer tagContainer; + + private String initText; + + public override void _Ready() + { + this.initText = this.Text; + base._Ready(); + } + + public override void _Process(double delta) + { + // change text based on currenly seleted tag + if (tagContainer.currentHoveredTag != null) + { + this.Text = initText + "\n" + tagContainer.currentHoveredTag.description; + } + else + { + this.Text = initText + "\n" + "No Tag Selected"; + } + + base._Process(delta); + } +} diff --git a/onboard/godot-frontend/GUIs/orignial/ControlHelpTextTags.cs.uid b/onboard/godot-frontend/GUIs/orignial/ControlHelpTextTags.cs.uid new file mode 100644 index 0000000..e893fde --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/ControlHelpTextTags.cs.uid @@ -0,0 +1 @@ +uid://do70tefvxy3xy diff --git a/onboard/godot-frontend/GUIs/orignial/DevcadeIcon.cs b/onboard/godot-frontend/GUIs/orignial/DevcadeIcon.cs new file mode 100644 index 0000000..f9daf75 --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/DevcadeIcon.cs @@ -0,0 +1,38 @@ +using Godot; +using onboard; +using onboard.devcade; + +public partial class DevcadeIcon : TextureRect +{ + [Export] + public Texture2D prodTexture; + [Export] + public Texture2D devTexture; + + public override void _Ready() + { + GuiManagerGlobal.instance.gameTitlesUpdated += setTexture; + } + + private void setTexture() + { + if(Client.isProduction) + { + setTextureToProd(); + } + else + { + setTextureToDev(); + } + } + + void setTextureToProd() + { + this.Texture = prodTexture; + } + + void setTextureToDev() + { + this.Texture = devTexture; + } +} diff --git a/onboard/godot-frontend/GUIs/orignial/DevcadeIcon.cs.uid b/onboard/godot-frontend/GUIs/orignial/DevcadeIcon.cs.uid new file mode 100644 index 0000000..bd23031 --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/DevcadeIcon.cs.uid @@ -0,0 +1 @@ +uid://62sckdwbwl8r diff --git a/onboard/godot-frontend/GUIs/orignial/Original.tscn b/onboard/godot-frontend/GUIs/orignial/Original.tscn new file mode 100644 index 0000000..5cb9f58 --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/Original.tscn @@ -0,0 +1,258 @@ +[gd_scene format=3 uid="uid://bs2l3u3gpwk3a"] + +[ext_resource type="Script" uid="uid://bytphx3biwmpk" path="res://GUIs/orignial/OriginalGUI.cs" id="1_eo6uj"] +[ext_resource type="Texture2D" uid="uid://gsf5hvgxlwu8" path="res://CSHAssets/OnboardBackgroundGradient.png" id="2_4c0jd"] +[ext_resource type="Texture2D" uid="uid://lqrfprpd7kh3" path="res://CSHAssets/transparent-logo.png" id="2_xc2fw"] +[ext_resource type="Shader" uid="uid://8uidd8pccdyk" path="res://GUIs/orignial/OriginalBackgrounIconRepeat.gdshader" id="3_1otwf"] +[ext_resource type="Texture2D" uid="uid://d4nlo4q03fi77" path="res://CSHAssets/CSH.png" id="3_x7jqg"] +[ext_resource type="FontFile" uid="uid://cnha1ohwbh3ts" path="res://CSHAssets/VT323-Regular.ttf" id="4_mmnpk"] +[ext_resource type="Texture2D" uid="uid://f61vcor4mcvi" path="res://CSHAssets/description.png" id="5_0ghq1"] +[ext_resource type="Script" uid="uid://d15s735p2xr6b" path="res://GUIs/orignial/gamesList/GamesContainer.cs" id="7_s6n7q"] +[ext_resource type="Script" uid="uid://cj0skhgydnii2" path="res://GUIs/orignial/Background.cs" id="8_4asay"] +[ext_resource type="Script" uid="uid://bjnrrsq5svi0h" path="res://GUIs/orignial/tagList/TagContainer.cs" id="8_loec3"] +[ext_resource type="Script" uid="uid://62sckdwbwl8r" path="res://GUIs/orignial/DevcadeIcon.cs" id="10_84p7e"] +[ext_resource type="Script" uid="uid://bnbcabv83bi6o" path="res://GUIs/orignial/SlerpCamera2d.cs" id="10_feq1j"] +[ext_resource type="Texture2D" uid="uid://bj333tajmq7mt" path="res://CSHAssets/transparent-dev-logo.png" id="11_7qoy5"] +[ext_resource type="Theme" uid="uid://cfha8h63q5pfc" path="res://GUIs/orignial/tagList/tagButtonTheme.tres" id="12_vs4r3"] +[ext_resource type="Script" uid="uid://do70tefvxy3xy" path="res://GUIs/orignial/ControlHelpTextTags.cs" id="13_6sm6r"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_loec3"] +shader = ExtResource("3_1otwf") +shader_parameter/scale = 7.27 +shader_parameter/direction = Vector2(-0.4, -0.4) +shader_parameter/alphaScale = 0.72 + +[node name="Original" type="Control" unique_id=1195079720 node_paths=PackedStringArray("camera", "gameContainer", "tagContainer", "description", "descriptionLabel", "titleLabel", "AuthorLabel")] +y_sort_enabled = true +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_eo6uj") +camera = NodePath("SubViewportContainer/SubViewport/Camera2D") +gameContainer = NodePath("SubViewportContainer/SubViewport/gamesContainer") +tagContainer = NodePath("SubViewportContainer/SubViewport/ScrollContainer/tagContainer") +description = NodePath("SubViewportContainer/SubViewport/DescriptionContainer") +descriptionLabel = NodePath("SubViewportContainer/SubViewport/DescriptionContainer/TextureRect2/MarginContainer/VBoxContainer/description") +titleLabel = NodePath("SubViewportContainer/SubViewport/DescriptionContainer/TextureRect2/MarginContainer/VBoxContainer/title") +AuthorLabel = NodePath("SubViewportContainer/SubViewport/DescriptionContainer/TextureRect2/MarginContainer/VBoxContainer/Author") +secBeforeInputEcho = 0.3 + +[node name="SubViewportContainer" type="SubViewportContainer" parent="." unique_id=713276628] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +stretch = true + +[node name="SubViewport" type="SubViewport" parent="SubViewportContainer" unique_id=617728635] +handle_input_locally = false +canvas_item_default_texture_filter = 0 +size = Vector2i(2160, 3840) +render_target_update_mode = 4 + +[node name="DescriptionContainer" type="MarginContainer" parent="SubViewportContainer/SubViewport" unique_id=1243988512] +visible = false +z_as_relative = false +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 400 +theme_override_constants/margin_top = 300 +theme_override_constants/margin_right = 400 +theme_override_constants/margin_bottom = 300 + +[node name="TextureRect2" type="TextureRect" parent="SubViewportContainer/SubViewport/DescriptionContainer" unique_id=1414524657] +z_index = 3000 +z_as_relative = false +y_sort_enabled = true +layout_mode = 2 +texture = ExtResource("5_0ghq1") +expand_mode = 1 + +[node name="MarginContainer" type="MarginContainer" parent="SubViewportContainer/SubViewport/DescriptionContainer/TextureRect2" unique_id=975672172] +z_index = 200 +y_sort_enabled = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 45 +theme_override_constants/margin_top = 25 +theme_override_constants/margin_right = 45 +theme_override_constants/margin_bottom = 25 + +[node name="VBoxContainer" type="VBoxContainer" parent="SubViewportContainer/SubViewport/DescriptionContainer/TextureRect2/MarginContainer" unique_id=328930581] +z_index = 200 +y_sort_enabled = true +layout_mode = 2 +theme_override_constants/separation = 50 + +[node name="title" type="Label" parent="SubViewportContainer/SubViewport/DescriptionContainer/TextureRect2/MarginContainer/VBoxContainer" unique_id=1069058341] +z_index = 200 +y_sort_enabled = true +layout_mode = 2 +theme_override_fonts/font = ExtResource("4_mmnpk") +theme_override_font_sizes/font_size = 180 +text = "Title" +horizontal_alignment = 1 +autowrap_mode = 3 + +[node name="description" type="Label" parent="SubViewportContainer/SubViewport/DescriptionContainer/TextureRect2/MarginContainer/VBoxContainer" unique_id=151021099] +z_index = 3000 +z_as_relative = false +y_sort_enabled = true +layout_mode = 2 +size_flags_vertical = 2 +theme_override_fonts/font = ExtResource("4_mmnpk") +theme_override_font_sizes/font_size = 80 +text = "Description" +autowrap_mode = 2 + +[node name="Author" type="Label" parent="SubViewportContainer/SubViewport/DescriptionContainer/TextureRect2/MarginContainer/VBoxContainer" unique_id=1859417603] +layout_mode = 2 +size_flags_vertical = 8 +theme_override_fonts/font = ExtResource("4_mmnpk") +theme_override_font_sizes/font_size = 80 +text = "Author" +horizontal_alignment = 1 + +[node name="Camera2D" type="Camera2D" parent="SubViewportContainer/SubViewport" unique_id=374544719] +y_sort_enabled = true +anchor_mode = 0 +script = ExtResource("10_feq1j") +easeAmount = 2.0 +animationSpeed = 5.0 + +[node name="background" type="TextureRect" parent="SubViewportContainer/SubViewport/Camera2D" unique_id=1714439144 node_paths=PackedStringArray("camera")] +z_index = -4096 +y_sort_enabled = true +custom_minimum_size = Vector2(900, 0) +offset_right = 2169.0 +offset_bottom = 3842.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +texture = ExtResource("2_4c0jd") +expand_mode = 1 +script = ExtResource("8_4asay") +camera = NodePath("..") + +[node name="backgroundCSHIcons" type="TextureRect" parent="SubViewportContainer/SubViewport/Camera2D/background" unique_id=1685999065] +y_sort_enabled = true +texture_filter = 1 +texture_repeat = 2 +material = SubResource("ShaderMaterial_loec3") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("3_x7jqg") +expand_mode = 1 +stretch_mode = 1 + +[node name="gamesMenu" type="VBoxContainer" parent="SubViewportContainer/SubViewport/Camera2D/background" unique_id=1960973158] +y_sort_enabled = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="MarginContainer" type="MarginContainer" parent="SubViewportContainer/SubViewport/Camera2D/background/gamesMenu" unique_id=355430915] +y_sort_enabled = true +layout_mode = 2 +theme_override_constants/margin_left = 300 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 300 +theme_override_constants/margin_bottom = 10 + +[node name="DevcadeIcon" type="TextureRect" parent="SubViewportContainer/SubViewport/Camera2D/background/gamesMenu/MarginContainer" unique_id=995176626] +y_sort_enabled = true +layout_mode = 2 +texture = ExtResource("2_xc2fw") +expand_mode = 5 +script = ExtResource("10_84p7e") +prodTexture = ExtResource("2_xc2fw") +devTexture = ExtResource("11_7qoy5") + +[node name="ScrollContainer" type="ScrollContainer" parent="SubViewportContainer/SubViewport" unique_id=1424407704] +y_sort_enabled = true +anchors_preset = -1 +anchor_left = 1.0 +anchor_right = 2.0 +anchor_bottom = 1.0 +offset_top = 1548.0 +offset_bottom = -2.0 +grow_horizontal = 0 +grow_vertical = 2 + +[node name="tagContainer" type="GridContainer" parent="SubViewportContainer/SubViewport/ScrollContainer" unique_id=2083496036] +y_sort_enabled = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +columns = 2 +script = ExtResource("8_loec3") +tagButtonTheme = ExtResource("12_vs4r3") + +[node name="gamesContainer" type="Control" parent="SubViewportContainer/SubViewport" unique_id=425249612] +y_sort_enabled = true +layout_mode = 3 +anchors_preset = 0 +offset_top = 2268.0 +offset_right = 2159.0 +offset_bottom = 3855.0 +size_flags_vertical = 3 +script = ExtResource("7_s6n7q") +gameButtonsScale = 1.8 +percentYPositionValue = 60.0 + +[node name="CenterContainer" type="CenterContainer" parent="SubViewportContainer/SubViewport" unique_id=588526480] +y_sort_enabled = true +offset_top = 800.0 +offset_right = 2100.0 +offset_bottom = 1089.0 + +[node name="controlHelpTextGames" type="Label" parent="SubViewportContainer/SubViewport/CenterContainer" unique_id=190039017] +z_index = 100 +z_as_relative = false +y_sort_enabled = true +custom_minimum_size = Vector2(2100, 0) +layout_mode = 2 +theme_override_fonts/font = ExtResource("4_mmnpk") +theme_override_font_sizes/font_size = 142 +text = "Press the Red Button to play! +Press both black buttons to refresh" +horizontal_alignment = 1 +autowrap_mode = 2 + +[node name="CenterContainer2" type="CenterContainer" parent="SubViewportContainer/SubViewport" unique_id=443979358] +y_sort_enabled = true +offset_left = 2160.0 +offset_top = 800.0 +offset_right = 4260.0 +offset_bottom = 943.0 + +[node name="controlHelpTextTags" type="Label" parent="SubViewportContainer/SubViewport/CenterContainer2" unique_id=951840258 node_paths=PackedStringArray("tagContainer")] +y_sort_enabled = true +custom_minimum_size = Vector2(2100, 10.875) +layout_mode = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_fonts/font = ExtResource("4_mmnpk") +theme_override_font_sizes/font_size = 142 +text = "Select A Tag to Search:" +horizontal_alignment = 1 +autowrap_mode = 2 +script = ExtResource("13_6sm6r") +tagContainer = NodePath("../../ScrollContainer/tagContainer") diff --git a/onboard/godot-frontend/GUIs/orignial/OriginalBackgrounIconRepeat.gdshader b/onboard/godot-frontend/GUIs/orignial/OriginalBackgrounIconRepeat.gdshader new file mode 100644 index 0000000..c18943a --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/OriginalBackgrounIconRepeat.gdshader @@ -0,0 +1,20 @@ +shader_type canvas_item; + +uniform float scale = 1.0; +uniform vec2 direction; + +uniform float alphaScale = 1.0; + +void fragment() { + // Called for every pixel the material is visible on. + float screen_width = 1.0 / SCREEN_PIXEL_SIZE.r; + float screen_height = 1.0 / SCREEN_PIXEL_SIZE.g; + + // delta movement value + float dx = -direction.r * mod(TIME, 1.0 / direction.r); + float dy = direction.g * mod(TIME, 1.0 / direction.g); + + COLOR = texture(TEXTURE, fract(SCREEN_UV * vec2(scale, scale * (screen_height / screen_width))) + vec2(dx, dy)); + + COLOR.a *= alphaScale; +} diff --git a/onboard/godot-frontend/GUIs/orignial/OriginalBackgrounIconRepeat.gdshader.uid b/onboard/godot-frontend/GUIs/orignial/OriginalBackgrounIconRepeat.gdshader.uid new file mode 100644 index 0000000..3ebb035 --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/OriginalBackgrounIconRepeat.gdshader.uid @@ -0,0 +1 @@ +uid://8uidd8pccdyk diff --git a/onboard/godot-frontend/GUIs/orignial/OriginalGUI.cs b/onboard/godot-frontend/GUIs/orignial/OriginalGUI.cs new file mode 100644 index 0000000..9e62b6b --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/OriginalGUI.cs @@ -0,0 +1,312 @@ +using System.Collections.Generic; +using System.Dynamic; +using Godot; +using onboard.util; + +namespace onboard.devcade.GUI.originalGUI; +public partial class OriginalGUI : Control +{ + util.Logger LOG = Log.get(nameof(OriginalGUI)); + + /// + /// the current state of the GUI + /// this is used to determine what actions to take based on what is / should be on the screen + /// + private GuiState state = GuiState.ViewGames; + + // the different states of the GUI + public enum GuiState + { + ViewGames, // the game list is shown + Description, // the description of a game is shown + Tags, // the tag list is shown + Theme, // the theme picker is shown + GameLaunched // a game is being run + } + + /// + /// The camera, + /// which is moved around to show the + /// tags, games, theme picker, and similar + /// + [Export] + public SlerpCamera2d camera; + + /// + /// the node that has the game buttons as children + /// + [Export] + public GamesContainer gameContainer; + + /// + /// the node that has the tag buttons as children + /// + [Export] + public TagContainer tagContainer; + + /// + /// the node that is shown/hidden to show/hide the description + /// + [Export] + public CanvasItem description; + + /// + /// the label that holds the description text + /// + [Export] + public Label descriptionLabel; + + /// + /// the label that holds the title text of the game + /// + [Export] + public Label titleLabel; + + /// + /// the label that holds the author text of the game + /// + [Export] + public Label AuthorLabel; + + public override void _Ready() + { + GuiManagerGlobal.instance.gameTitlesUpdated += () => + { + gameContainer.updateGames(GuiManagerGlobal.gameTitles, showDescription); + }; + + GuiManagerGlobal.instance.tagListUpdated += () => + { + tagContainer.updateTags(GuiManagerGlobal.tagList, setCurrentTag); + }; + + GuiManagerGlobal.instance.currentTagUpdated += () => + { + gameContainer.setTag(GuiManagerGlobal.currentTag); + }; + + // hide the description if it is not already hidden + description.Hide(); + + state = GuiState.ViewGames; + + // poll the game list + GuiManagerGlobal.instance.reloadGameList(); + } + + // unhandled to ignore input that the gui manager consumes (aka when a game is launched) + public override void _UnhandledInput(InputEvent @event) + { + if (state != GuiState.Description) + { + // stick right + if (@event.IsActionPressed("Player1_StickRight") || @event.IsActionPressed("Player2_StickRight")) + { + if (state == GuiState.ViewGames) + { + showTagList(); + skipProcessLoop = true; + AcceptEvent(); + return; + } + } + + // stick left + if (@event.IsActionPressed("Player1_StickLeft") || @event.IsActionPressed("Player2_StickLeft")) + { + if (state == GuiState.Tags && tagContainer.currentX == 0) + { + showGameList(); + skipProcessLoop = true; + AcceptEvent(); + return; + } + } + } + + // back button (blue button) + if (@event.IsActionPressed("Player1_A2") || @event.IsActionPressed("Player2_A2")) + { + if (state == GuiState.Description) + { + description.Hide(); + gameContainer.selectLastPressedButton(); + state = GuiState.ViewGames; + } + } + + // enter button (red button) + if (@event.IsActionPressed("Player1_A1") || @event.IsActionPressed("Player2_A1")) + { + if (state == GuiState.Description) + { + lauchCurrentGame(); + } + } + } + + bool skipProcessLoop = false; + public override void _Process(double delta) + { + if(skipProcessLoop) + { + skipProcessLoop = false; + return; + } + + if (state == GuiState.ViewGames) + { + if (isRepeatActionPressed("Player1_StickUp", delta) || isRepeatActionPressed("Player2_StickUp", delta)) + { + gameContainer.previousGame(); + } + if (isRepeatActionPressed("Player1_StickDown", delta) || isRepeatActionPressed("Player2_StickDown", delta)) + { + gameContainer.nextGame(); + } + } + if (state == GuiState.Tags) + { + // stick up + if (isRepeatActionPressed("Player1_StickUp", delta) || isRepeatActionPressed("Player2_StickUp", delta)) + { + tagContainer.selectUp(); + } + // stick down + if (isRepeatActionPressed("Player1_StickDown", delta) || isRepeatActionPressed("Player2_StickDown", delta)) + { + tagContainer.selectDown(); + } + // stick left + if (isRepeatActionPressed("Player1_StickLeft", delta) || isRepeatActionPressed("Player2_StickLeft", delta)) + { + tagContainer.selectLeft(); + } + // stick right + if (isRepeatActionPressed("Player1_StickRight", delta) || isRepeatActionPressed("Player2_StickRight", delta)) + { + tagContainer.selectRight(); + } + } + } + + /// + /// Returns if an action is pressed while taking in to account the handling of the repetition of joystick events + /// + /// The action to check + /// Delta time in seconds + /// true if the action is pressed or should be repeated, false otherwise + private bool isRepeatActionPressed(string actionName, double delta) + { + return isActionRepeated(actionName, delta) || Input.IsActionJustPressed(actionName); + } + + [Export] + double secBeforeInputEcho = 0.2; + [Export] + double secBetweenInputEcho = 0.1; + + Dictionary actionsPressTime = new Dictionary(); + private bool isActionRepeated(string actionName, double delta) + { + if(!actionsPressTime.ContainsKey(actionName)) + { + actionsPressTime.Add(actionName, 0.0); + } + + double actionDt = actionsPressTime[actionName]; + + if(Input.IsActionPressed(actionName)) + { + actionDt += delta; + + if(actionDt > secBeforeInputEcho) + { + actionsPressTime[actionName] -= secBetweenInputEcho; + return true; + } + + actionsPressTime[actionName] = actionDt; + return false; + } + + actionsPressTime[actionName] = 0.0; + return false; + } + + /// + /// lauches the game that is referenced by the button in the aspect ratio container + /// in the lastButtonContainerPressed variable, + /// this is used to lauch a game from a description page + /// + private void lauchCurrentGame() + { + DevcadeGame gameToLaunch = gameContainer.buttonsGames[gameContainer.lastButtonPressed.childButton]; + launchGame(gameToLaunch); + } + + /// + /// launches the given game + /// calls the launchGame function of the model + /// + /// + private void launchGame(DevcadeGame game) + { + state = GuiState.GameLaunched; + // this launches the selected game, and continues when the game closes + GuiManagerGlobal.instance.launchGame(game).ContinueWith(_ => + { + showGameList(); + }); + + description.Hide(); + showGameList(); + } + + /// + /// show the description for an arbitrary game + /// + /// the game's description to show + private void showDescription(DevcadeGame game) + { + state = GuiState.Description; + + titleLabel.Text = game.name; + descriptionLabel.Text = game.description; + LOG.Info(game.author); + LOG.Info(AuthorLabel.Text); + AuthorLabel.Text = $"Author: {game.author}"; + + description.Show(); + } + + /// + /// show the tag list + /// + public void showTagList() + { + state = GuiState.Tags; + tagContainer.grabFocus(); + camera.setRelativeTargetIndex(1); + } + + /// + /// shows the game list + /// + private void showGameList() + { + state = GuiState.ViewGames; + gameContainer.selectCurrentIndexImmediate(); + camera.setRelativeTargetIndex(0); + } + + /// + /// set the current tag to the tag given + /// + /// the new tag + public void setCurrentTag(Tag tag) + { + GuiManagerGlobal.instance.setTag(tag); + showGameList(); + } +} diff --git a/onboard/godot-frontend/GUIs/orignial/OriginalGUI.cs.uid b/onboard/godot-frontend/GUIs/orignial/OriginalGUI.cs.uid new file mode 100644 index 0000000..9d165f6 --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/OriginalGUI.cs.uid @@ -0,0 +1 @@ +uid://bytphx3biwmpk diff --git a/onboard/godot-frontend/GUIs/orignial/SlerpCamera2d.cs b/onboard/godot-frontend/GUIs/orignial/SlerpCamera2d.cs new file mode 100644 index 0000000..aca1b3d --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/SlerpCamera2d.cs @@ -0,0 +1,127 @@ +using Godot; + +namespace onboard.devcade.GUI.originalGUI; + +public partial class SlerpCamera2d : Camera2D +{ + /// + /// the number of valid positions for the camera left of its initial position + /// + [Export] + public int positionsLeft = 1; + + /// + /// the number of valid positions for the camera right of its initial position + /// + [Export] + public int positionsRight = 1; + + /// + /// the amount to ease the animation by from 0.0 to 1.0f + /// + [Export] + public float easeAmount = 1; + + /// + /// the scale of the speed of the animation from 1.0f to inf. + /// + [Export] + public float animationSpeed = 2.0f; + private Vector2[] positions; + + public int targetIndex { get; private set; } + private int previousTargetIndex; + + private float time = 0; + + public override void _Ready() + { + calculatePositions(this.GetViewportRect().Size.X); + + base._Ready(); + } + + private void calculatePositions(float viewportWidth) + { + positions = new Vector2[positionsLeft + positionsRight + 1]; + + for (int i = positionsLeft; i >= 0; i--) + { + positions[i] = new Vector2(viewportWidth * -i, this.Position.Y); + } + + positions[positionsLeft] = this.Position; + + for (int i = 1; i <= positionsRight; i++) + { + positions[positionsLeft + i] = new Vector2(viewportWidth * i, this.Position.Y); + } + + targetIndex = positionsLeft; + previousTargetIndex = positionsLeft; + } + + public override void _Process(double delta) + { + time += (float)delta * animationSpeed; + + if (time > 1) + { + time = 1; + } + + Vector2 startPosition = positions[previousTargetIndex]; + Vector2 endPosition = positions[targetIndex]; + + Vector2 offset = endPosition - startPosition; + + this.Position = CubicBezier(startPosition, startPosition + (offset / easeAmount), endPosition - (offset / easeAmount), endPosition, time); + } + + public void setRelativeTargetIndex(int rel_index) + { + if (rel_index > positionsRight) + { + rel_index = positionsRight; + } + if (rel_index < -positionsLeft) + { + rel_index = -positionsLeft; + } + + previousTargetIndex = targetIndex; + targetIndex = rel_index + positionsLeft; + + time = 0; + } + + public void setAbosluteTargetIndex(int index) + { + if (index > positions.Length - 1) + { + index = positions.Length - 1; + } + if (index < 0) + { + index = 0; + } + + previousTargetIndex = targetIndex; + targetIndex = index; + + time = 0; + } + + private static Vector2 CubicBezier(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) + { + Vector2 q0 = p0.Lerp(p1, t); + Vector2 q1 = p1.Lerp(p2, t); + Vector2 q2 = p2.Lerp(p3, t); + + Vector2 r0 = q0.Lerp(q1, t); + Vector2 r1 = q1.Lerp(q2, t); + + Vector2 s = r0.Lerp(r1, t); + return s; + } +} diff --git a/onboard/godot-frontend/GUIs/orignial/SlerpCamera2d.cs.uid b/onboard/godot-frontend/GUIs/orignial/SlerpCamera2d.cs.uid new file mode 100644 index 0000000..394ac43 --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/SlerpCamera2d.cs.uid @@ -0,0 +1 @@ +uid://bnbcabv83bi6o diff --git a/onboard/godot-frontend/GUIs/orignial/SlerpControl.cs b/onboard/godot-frontend/GUIs/orignial/SlerpControl.cs new file mode 100644 index 0000000..a58bfee --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/SlerpControl.cs @@ -0,0 +1,25 @@ +using Godot; +using System; + +public partial class SlerpControl : Control +{ + public Vector2 targetPosition = new Vector2(DisplayServer.ScreenGetSize().X, 0.0f); + private Vector2 direction = new Vector2(0.0f, 0.0f); + private float velocity = 1.0f; + + [Export] + public float speed = 10f; + + public override void _Process(double delta) + { + if(Position.DistanceTo(targetPosition) < 2.0f) + { + // If the distance to the target position is less than 0.1, snap to the target position + Position = targetPosition; + } + else + { + Position = Position.Slerp(targetPosition, speed * (float) delta); + } + } +} diff --git a/onboard/godot-frontend/GUIs/orignial/SlerpControl.cs.uid b/onboard/godot-frontend/GUIs/orignial/SlerpControl.cs.uid new file mode 100644 index 0000000..274a143 --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/SlerpControl.cs.uid @@ -0,0 +1 @@ +uid://bycjj75k64kbs diff --git a/onboard/godot-frontend/GUIs/orignial/gamesList/GameButton.cs b/onboard/godot-frontend/GUIs/orignial/gamesList/GameButton.cs new file mode 100644 index 0000000..37ba2aa --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/gamesList/GameButton.cs @@ -0,0 +1,64 @@ +using Godot; + +// used for animations +public class GameButton +{ + [Export] + public float minimumRotationSpeed = 3.0f; + [Export] + public float maxRotationSpeed = 20.0f; + + public BaseButton childButton; + + /// + /// how close this button can be to the target rotation before it snaps to the target rotation + /// + const float errorMargin = 0.05f; + public float targetRotation; + + public bool isInsideTree {get { return childButton.IsInsideTree(); } private set {} } + + public int index = -1; + + public GameButton(float targetRotation, BaseButton childButton, int index) + { + this.targetRotation = targetRotation; + this.childButton = childButton; + this.index = index; + } + + public void process(double delta) + { + float deltaRotation = targetRotation - this.childButton.Rotation; + float direction = float.Sign(deltaRotation); + + float d = (float) (1.0 + -1.0 / (1.0 + 0.13 * float.Abs(deltaRotation))); + float rotationSpeed = float.Lerp(minimumRotationSpeed, maxRotationSpeed, d); + float rotation = (float) (direction * rotationSpeed * delta); + + if(rotation > float.Abs(deltaRotation)) + { + this.childButton.Rotation = targetRotation; + } + else if(deltaRotation < errorMargin && deltaRotation > -errorMargin) + { + this.childButton.Rotation = targetRotation; + } else { + this.childButton.Rotation += rotation; + } + + if(this.childButton.Rotation > 3.2f || this.childButton.Rotation < -3.2f) + { + this.childButton.Hide(); + } + else + { + this.childButton.Show(); + } + } + + public void setRotation(float rotation) + { + this.childButton.Rotation = rotation; + } +} diff --git a/onboard/godot-frontend/GUIs/orignial/gamesList/GameButton.cs.uid b/onboard/godot-frontend/GUIs/orignial/gamesList/GameButton.cs.uid new file mode 100644 index 0000000..1bc0cf6 --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/gamesList/GameButton.cs.uid @@ -0,0 +1 @@ +uid://bh3nh7e0qe11w diff --git a/onboard/godot-frontend/GUIs/orignial/gamesList/GamesContainer.cs b/onboard/godot-frontend/GUIs/orignial/gamesList/GamesContainer.cs new file mode 100644 index 0000000..080bebc --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/gamesList/GamesContainer.cs @@ -0,0 +1,362 @@ +using Godot; +using System; +using System.Collections.Generic; + +namespace onboard.devcade.GUI.originalGUI; + +public partial class GamesContainer : Control +{ + /// + /// the last game button that has been pressed + /// + internal GameButton lastButtonPressed = null; + + /// + /// a scalar value for the size of the game buttons + /// + [Export] + public float gameButtonsScale = 1.0f; + + /// + /// the size of the game buttons + /// mulitplied this by the gameButtonsScale + /// + private Vector2 gameButtonsSize = new Vector2(470.0f, 273.0f); + + /// + /// the spacing between the game buttons in radians + /// + [Export] + public float cardSpacing = 0.4f; + + /// + /// the percent of effect that the offset index has on the size of the game cards + /// aka this changes the amount that the closer a card gets to the center, the larger it gets + /// + [Export] + public float cardScaleAmount = 0.3f; + + [Export] + public float percentYPositionValue = 66.0f; + + /// + /// list of game buttons + /// a wrapper class that conatins a BaseButton to animate the rotation of them + /// + private List gameButtons = new List(); + + /// + /// a dictionary of buttons to games, + /// used for accessing a games based on the button, + /// which is useful in this case for when the current tag changes + /// and it is needed to hide all the buttons/games that do not have that tag + /// + internal Dictionary buttonsGames = new Dictionary(); + + public int index {get; private set;} = 0; + public int numberOfGames {get; private set;} = 1; + + public override void _Ready() + { + this.Position = new Vector2(0.0f, GetViewportRect().Size.Y * percentYPositionValue / 100.0f); + } + + public override void _Process(double delta) + { + // update each GameButton so that the animations play out + foreach (GameButton gameButton in gameButtons) + { + gameButton.process(delta); + } + } + + public void nextGame() + { + ++index; + if(index > numberOfGames - 1) + { + index = numberOfGames - 1; + } + setFocusedGame(index); + } + + public void previousGame() + { + index = --index; + if(index < 0) + { + index = 0; + } + setFocusedGame(index); + } + + public void updateGames(List games, Action showDescription) + { + foreach (Node child in this.GetChildren()) + { + this.RemoveChild(child); + } + + this.numberOfGames = games.Count; + + gameButtons = new List(); + + for (int i = 0; i < games.Count; i++) + { + DevcadeGame game = games[i]; + + BaseButton button; + + if (game.banner != null) + { + + TextureButton textureButton = new TextureButton + { + IgnoreTextureSize = true, + StretchMode = TextureButton.StretchModeEnum.KeepAspectCentered, + + Name = game.name, + + TextureDisabled = game.banner, + TextureNormal = game.banner, + TextureHover = game.banner, + TexturePressed = game.banner, + TextureFocused = game.banner, + + // dont use inbuilt navigation + FocusMode = FocusModeEnum.Click, + + CustomMinimumSize = gameButtonsSize * gameButtonsScale, + Scale = new Vector2(gameButtonsScale, gameButtonsScale), + }; + + button = textureButton; + } + else + { + Button textButton = new Button + { + SizeFlagsHorizontal = SizeFlags.ExpandFill, + SizeFlagsVertical = SizeFlags.ExpandFill, + + // textButton.Theme = tagButtonTheme; + + Name = game.name, + Text = game.name, + + // dont use inbuilt navigation + FocusMode = FocusModeEnum.Click, + + // size of game buttons + CustomMinimumSize = gameButtonsSize * gameButtonsScale, + Scale = new Vector2(gameButtonsScale, gameButtonsScale), + }; + + button = textButton; + } + + // pivot, inital rotation, and z-index (what should be draw on top of what) + button.PivotOffset = new Vector2(0, gameButtonsSize.Y / 2 * gameButtonsScale); + button.Rotation = i * cardSpacing; + button.ZIndex = games.Count - i; + + GameButton gameButton = new GameButton(i * cardSpacing, button, i); + // skip error games + if (game.name != "Error") + { + // lambda function is required to "bind" the game parameter + // to the function showDescription called when the button is pressed + button.Pressed += () => + { + lastButtonPressed = gameButton; + button.ReleaseFocus(); + showDescription(game); + }; + } + + // add the new button to the game container and the list of game buttons + this.AddChild(button); + gameButtons.Add(gameButton); + + if (i > 5) + { + button.CallDeferred(CanvasItem.MethodName.Hide); + } + + buttonsGames.Add(button, game); + } + + lastButtonPressed = gameButtons[0]; + this.index = 0; + selectCurrentIndexImmediate(); + } + + /// + /// sets all the buttons' target rotation and z-index + /// + /// the index of the button + private void setFocusedGame(int index) + { + this.index = index; + var currentGames = this.GetChildren(); + + // loops over all the game buttons in the saved list + // skiping the ones that are not part of the current tag (aka not in the tree) + // and setting the z-index and rotation of the buttons based on the integer i + // which represents the "index" of the button in the game container (0 being the first/top one) + int i = 0; + foreach (GameButton gameButton in gameButtons) + { + if (!gameButton.isInsideTree) + { + continue; + } + + int offset = Math.Abs(i - index); + + gameButton.childButton.ZIndex = currentGames.Count - offset; + gameButton.targetRotation = (i - index) * cardSpacing; + + float scaleFactor = -cardScaleAmount * offset / 10.0f; + gameButton.childButton.Scale = new Vector2(gameButtonsScale + scaleFactor, gameButtonsScale + scaleFactor); + + if(i - index == 0) + { + gameButton.childButton.GrabFocus(); + } + + ++i; + } + } + + /// + /// sets all the buttons' rotation and z-index + /// + /// the index of the button + private void setFocusedGameImmediate(int index) + { + this.index = index; + var currentGames = this.GetChildren(); + + // loops over all the game buttons in the saved list + // skiping the ones that are not part of the current tag (aka not in the tree) + // and setting the z-index and rotation of the buttons based on the integer i + // which represents the "index" of the button in the game container (0 being the first/top one) + int i = 0; + foreach (GameButton gameButton in gameButtons) + { + if (!gameButton.isInsideTree) + { + continue; + } + + int offset = Math.Abs(i - index); + + gameButton.childButton.ZIndex = currentGames.Count - offset; + float rotation = (i - index) * cardSpacing; + gameButton.targetRotation = rotation; + gameButton.setRotation(rotation); + + float scaleFactor = -cardScaleAmount * offset / 10.0f; + gameButton.childButton.Scale = new Vector2(gameButtonsScale + scaleFactor, gameButtonsScale + scaleFactor); + + if(i - index == 0) + { + gameButton.childButton.GrabFocus(); + } + + ++i; + } + } + + /// + /// used by OriginalGUI.cs to set the tag + /// + /// the new tag + public void setTag(Tag tag) + { + // remove all the buttons + foreach (Node child in this.GetChildren()) + { + this.RemoveChild(child); + } + + // add back the ones that have the tag + int i = 0; + gameButtons.ForEach(buttonWrapper => + { + DevcadeGame game = buttonsGames[buttonWrapper.childButton]; + + if (game.tags.Contains(tag)) + { + this.AddChild(buttonWrapper.childButton); + buttonWrapper.index = i; + ++i; + } + }); + numberOfGames = i; + setFocusedGameImmediate(0); // skip buttons' animations + } + + /// + /// Sets the focus to the last pressed button + /// + public void grabFocus() + { + setFocusedGame(lastButtonPressed.index); + } + + /// + /// Sets the last pressed button to a button with an arbitrary index in the gameButtons list + /// + /// must be within or equal to the length of gameButtons and 0 + public void setLastPressedButton(int index) + { + lastButtonPressed = gameButtons[index]; + } + + /// + /// Sets the last pressed button to the first button + /// + public void resetLastPressedButton() + { + setLastPressedButton(0); + } + + /// + /// Sets the last pressed button to a button with an arbitrary index in the gameButtons list + /// + /// must be within or equal to the length of gameButtons and 0 + public void selectLastPressedButton() + { + setFocusedGame(lastButtonPressed.index); + } + + /// + /// Sets the last pressed button to a button with an arbitrary index in the gameButtons list immediately, skipping any animations + /// + /// must be within or equal to the length of gameButtons and 0 + public void selectLastPressedButtonImmediate() + { + setFocusedGameImmediate(lastButtonPressed.index); + } + + /// + /// Sets the last pressed button to a button with an arbitrary index in the gameButtons list immediately, skipping any animations + /// + /// must be within or equal to the length of gameButtons and 0 + public void selectCurrentIndex() + { + setFocusedGame(this.index); + } + + /// + /// Sets the last pressed button to a button with an arbitrary index in the gameButtons list immediately, skipping any animations + /// + /// must be within or equal to the length of gameButtons and 0 + public void selectCurrentIndexImmediate() + { + setFocusedGameImmediate(this.index); + } + +} diff --git a/onboard/godot-frontend/GUIs/orignial/gamesList/GamesContainer.cs.uid b/onboard/godot-frontend/GUIs/orignial/gamesList/GamesContainer.cs.uid new file mode 100644 index 0000000..609c08b --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/gamesList/GamesContainer.cs.uid @@ -0,0 +1 @@ +uid://d15s735p2xr6b diff --git a/onboard/godot-frontend/GUIs/orignial/tagList/TagButtonStyleBoxFlat.tres b/onboard/godot-frontend/GUIs/orignial/tagList/TagButtonStyleBoxFlat.tres new file mode 100644 index 0000000..7c754bf --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/tagList/TagButtonStyleBoxFlat.tres @@ -0,0 +1,8 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://cybslsr5ecwor"] + +[resource] +bg_color = Color(0.527496, 0.0985789, 0.132855, 1) +corner_radius_top_left = 40 +corner_radius_top_right = 40 +corner_radius_bottom_right = 40 +corner_radius_bottom_left = 40 diff --git a/onboard/godot-frontend/GUIs/orignial/tagList/TagButtonStyleBoxFlatPressed.tres b/onboard/godot-frontend/GUIs/orignial/tagList/TagButtonStyleBoxFlatPressed.tres new file mode 100644 index 0000000..3e7d5de --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/tagList/TagButtonStyleBoxFlatPressed.tres @@ -0,0 +1,8 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://cvou2scxpk5ho"] + +[resource] +bg_color = Color(0.301105, 1.71466e-07, 2.40654e-08, 1) +corner_radius_top_left = 40 +corner_radius_top_right = 40 +corner_radius_bottom_right = 40 +corner_radius_bottom_left = 40 diff --git a/onboard/godot-frontend/GUIs/orignial/tagList/TagContainer.cs b/onboard/godot-frontend/GUIs/orignial/tagList/TagContainer.cs new file mode 100644 index 0000000..3e2cc0d --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/tagList/TagContainer.cs @@ -0,0 +1,165 @@ +using Godot; +using System; +using System.Collections.Generic; + +namespace onboard.devcade.GUI.originalGUI; + +public partial class TagContainer : GridContainer +{ + /// + /// the custom theme for the tag buttons + /// as well as any game that does not have a banner texture + /// + [Export] + public Theme tagButtonTheme; + + public Tag currentHoveredTag; + + private Button[] tagButtons; + + public int numberOfTags {get; private set;} = 1; + + private int maxX; + private int maxY; + + public int currentX {get; private set;} = 0; + public int currentY {get; private set;} = 0; + + /// + /// Attempt to select the tag above the current one + /// + public void selectUp() + { + select(currentX, currentY - 1); + } + + /// + /// Attempt to select the tag below the current one + /// + public void selectDown() + { + select(currentX, currentY + 1); + } + + /// + /// Attempt to select the tag left of the current one + /// + public void selectLeft() + { + select(currentX - 1, currentY); + } + + /// + /// Attempt to select the tag right of the current one + /// + public void selectRight() + { + select(currentX + 1, currentY); + } + + public void select(int x, int y) + { + if(x < 0) + { + x = 0; + } + if(x > maxX) + { + x = maxX; + } + if(y < 0) + { + y = 0; + } + if(y > maxY) + { + y = maxY; + } + + if((y * Columns + x) > (numberOfTags - 1)) + { + currentX = (numberOfTags - 1) % Columns; + currentY = (numberOfTags - 1) / Columns; + + this.tagButtons[numberOfTags - 1].CallDeferred("grab_focus"); + + return; + } + + currentX = x; + currentY = y; + + this.tagButtons[y * Columns + x].CallDeferred("grab_focus"); + } + + public void updateTags(List tagList, Action on_tag_pressed) + { + if(tagList == null) {return;} + + foreach (Node child in this.GetChildren()) + { + this.RemoveChild(child); + } + + if (tagList.Count <= 0) + { + return; + } + + numberOfTags = tagList.Count; + + tagButtons = new Button[tagList.Count]; + + currentHoveredTag = tagList[0]; + + for (int i = 0; i < tagList.Count; i++) + { + Button button = new Button + { + SizeFlagsHorizontal = SizeFlags.ExpandFill, + SizeFlagsVertical = SizeFlags.ExpandFill, + + Theme = tagButtonTheme, + + Text = tagList[i].name, + // Text = i.ToString(), + }; + + Tag tag = tagList[i]; + button.Pressed += () => on_tag_pressed(tag); + button.FocusEntered += () => currentHoveredTag = tag; + + tagButtons[i] = button; + + MarginContainer marginContainer = new MarginContainer + { + SizeFlagsHorizontal = SizeFlags.ExpandFill, + SizeFlagsVertical = SizeFlags.ExpandFill, + + Theme = tagButtonTheme, + + + }; + + marginContainer.CallDeferred(Node.MethodName.AddChild, button); + + this.AddChild(marginContainer); + + // Ignores basic ui_"whatever" input actions + // while still allowing them to be focused + marginContainer.FocusMode = FocusModeEnum.Click; + button.FocusMode = FocusModeEnum.Click; + } + + maxX = Columns - 1; + maxY = (int) Mathf.Ceil(numberOfTags / (float) Columns) - 1; + + currentX = 0; + currentY = 0; + } + + public void grabFocus() + { + select(0, 0); + } +} diff --git a/onboard/godot-frontend/GUIs/orignial/tagList/TagContainer.cs.uid b/onboard/godot-frontend/GUIs/orignial/tagList/TagContainer.cs.uid new file mode 100644 index 0000000..d4e9067 --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/tagList/TagContainer.cs.uid @@ -0,0 +1 @@ +uid://bjnrrsq5svi0h diff --git a/onboard/godot-frontend/GUIs/orignial/tagList/tagButtonTheme.tres b/onboard/godot-frontend/GUIs/orignial/tagList/tagButtonTheme.tres new file mode 100644 index 0000000..4cedd45 --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/tagList/tagButtonTheme.tres @@ -0,0 +1,35 @@ +[gd_resource type="Theme" load_steps=4 format=3 uid="uid://cfha8h63q5pfc"] + +[ext_resource type="FontFile" uid="uid://cnha1ohwbh3ts" path="res://CSHAssets/VT323-Regular.ttf" id="1_f6y80"] +[ext_resource type="StyleBox" uid="uid://cybslsr5ecwor" path="res://GUIs/orignial/tagList/TagButtonStyleBoxFlat.tres" id="1_gcu4m"] +[ext_resource type="StyleBox" uid="uid://cvou2scxpk5ho" path="res://GUIs/orignial/tagList/TagButtonStyleBoxFlatPressed.tres" id="3_ajf2s"] + +[resource] +Button/colors/font_color = Color(0.525102, 0.525101, 0.525101, 1) +Button/colors/font_disabled_color = Color(0.875, 0.875, 0.875, 0.5) +Button/colors/font_focus_color = Color(0.95, 0.95, 0.95, 1) +Button/colors/font_hover_color = Color(1, 1, 1, 1) +Button/colors/font_hover_pressed_color = Color(0.71213, 0.712129, 0.712129, 1) +Button/colors/font_outline_color = Color(0, 0, 0, 1) +Button/colors/font_pressed_color = Color(0, 0, 0, 1) +Button/colors/icon_disabled_color = Color(1, 1, 1, 0.4) +Button/colors/icon_focus_color = Color(1, 1, 1, 1) +Button/colors/icon_hover_color = Color(1, 1, 1, 1) +Button/colors/icon_hover_pressed_color = Color(1, 1, 1, 1) +Button/colors/icon_normal_color = Color(1, 1, 1, 1) +Button/colors/icon_pressed_color = Color(1, 1, 1, 1) +Button/constants/align_to_largest_stylebox = 0 +Button/constants/h_separation = 4 +Button/constants/icon_max_width = 0 +Button/constants/outline_size = 0 +Button/font_sizes/font_size = 120 +Button/fonts/font = ExtResource("1_f6y80") +Button/styles/disabled = ExtResource("1_gcu4m") +Button/styles/focus = ExtResource("1_gcu4m") +Button/styles/hover = ExtResource("1_gcu4m") +Button/styles/normal = ExtResource("1_gcu4m") +Button/styles/pressed = ExtResource("3_ajf2s") +MarginContainer/constants/margin_bottom = 15 +MarginContainer/constants/margin_left = 10 +MarginContainer/constants/margin_right = 10 +MarginContainer/constants/margin_top = 15 diff --git a/onboard/godot-frontend/GUIs/orignial/tagList/tagList.tscn b/onboard/godot-frontend/GUIs/orignial/tagList/tagList.tscn new file mode 100644 index 0000000..0d2d891 --- /dev/null +++ b/onboard/godot-frontend/GUIs/orignial/tagList/tagList.tscn @@ -0,0 +1,9 @@ +[gd_scene format=3 uid="uid://csdse6vpe4120"] + +[node name="TagList" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 diff --git a/onboard/godot-frontend/GUIs/template/TemplateGui.cs b/onboard/godot-frontend/GUIs/template/TemplateGui.cs new file mode 100644 index 0000000..0d092b0 --- /dev/null +++ b/onboard/godot-frontend/GUIs/template/TemplateGui.cs @@ -0,0 +1,81 @@ +using Godot; + +using System.Collections.Generic; + +namespace onboard.devcade.GUI.template; + +/// +/// this is somewhat simple GUI that implements all the basic functions +/// to show how to create a simple UI script +/// all logic for creating buttons and adding functions to the buttons is done here +/// +public partial class TemplateGui : Node +{ + /// + /// runs once after the node is loaded into the scene tree, + /// used in this case to set the monochrome missing texture, + /// and to get the screen width/height + /// + public override void _Ready() + { + GuiManagerGlobal.instance.gameTitlesUpdated += () => + { + List games = GuiManagerGlobal.gameTitles; + // called on the list of games List being updated + }; + + GuiManagerGlobal.instance.tagListUpdated += () => + { + List tags = GuiManagerGlobal.tagList; + // called on the list of tags List being updated + }; + } + + public override void _Input(InputEvent @event) + { + // add any input that happends once per a given keypress here + + // if both black buttons are pressed + if(@event.IsActionPressed("Player1_Menu") && @event.IsActionPressed("Player2_Menu")) + { + GuiManagerGlobal.instance.reloadGameList(); + } + } + + public override void _Process(double delta) + { + + } + + /// + /// launches the given game + /// calls the launchGame function of the model + /// + /// + private void launchGame(DevcadeGame game) + { + // discard result, in this instance we don't care when the game is closed or the result, + // only that it is killed at some point + _ = GuiManagerGlobal.instance.launchGame(game); + } + + /// + /// sets the current tag variable in the model to the given tag + /// + /// the new tag + private void setCurrentTag(Tag tag) + { + GuiManagerGlobal.instance.setTag(tag); + } + + /// + /// kills the currently running game + /// IMPORTANT: does not wait for the game to be killed for the function to return + /// + private void killCurrentlyRunningGame() + { + // discard the result, + // Because this call is not awaited, execution of the current method continues before the call is completed + _ = GuiManagerGlobal.instance.killGame(); + } +} diff --git a/onboard/godot-frontend/GUIs/template/TemplateGui.cs.uid b/onboard/godot-frontend/GUIs/template/TemplateGui.cs.uid new file mode 100644 index 0000000..b94b828 --- /dev/null +++ b/onboard/godot-frontend/GUIs/template/TemplateGui.cs.uid @@ -0,0 +1 @@ +uid://ghv7wfcn6ii3 diff --git a/onboard/godot-frontend/GUIs/template/template_gui.tscn b/onboard/godot-frontend/GUIs/template/template_gui.tscn new file mode 100644 index 0000000..a179dc8 --- /dev/null +++ b/onboard/godot-frontend/GUIs/template/template_gui.tscn @@ -0,0 +1,129 @@ +[gd_scene load_steps=4 format=3 uid="uid://1pi1bov4mn64"] + +[ext_resource type="Script" uid="uid://dnfisdn7tx8c4" path="res://GUIs/template/TemplateGui.cs" id="1_rsbb1"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_yuvhl"] +bg_color = Color(0.196078, 0.196078, 0.196078, 0.741176) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bimps"] +bg_color = Color(0.162066, 0.162066, 0.162065, 1) +corner_radius_top_left = 35 +corner_radius_top_right = 35 +corner_radius_bottom_right = 35 +corner_radius_bottom_left = 35 + +[node name="test_gui" type="Control" node_paths=PackedStringArray("gameContainer", "tagContainer", "descriptionPanel", "desriptionLabel", "titleLabel", "lauchGameButton")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 2.0 +offset_right = 2.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource("1_rsbb1") +gameContainer = NodePath("TabContainer/Games") +tagContainer = NodePath("TabContainer/Tags") +descriptionPanel = NodePath("Panel") +desriptionLabel = NodePath("Panel/MarginContainer/Panel/MarginContainer/VBoxContainer/MarginContainer/description") +titleLabel = NodePath("Panel/MarginContainer/Panel/MarginContainer/VBoxContainer/title") +lauchGameButton = NodePath("Panel/MarginContainer/Panel/MarginContainer/VBoxContainer/MarginContainer2/launchGame") + +[node name="TabContainer" type="TabContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/side_margin = 15 +theme_override_font_sizes/font_size = 56 +current_tab = 0 + +[node name="Games" type="GridContainer" parent="TabContainer"] +layout_mode = 2 +theme_override_constants/h_separation = 5 +theme_override_constants/v_separation = 5 +columns = 3 +metadata/_tab_index = 0 + +[node name="Tags" type="GridContainer" parent="TabContainer"] +visible = false +layout_mode = 2 +columns = 3 +metadata/_tab_index = 1 + +[node name="Panel" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_yuvhl") + +[node name="MarginContainer" type="MarginContainer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 85 +theme_override_constants/margin_top = 30 +theme_override_constants/margin_right = 85 +theme_override_constants/margin_bottom = 30 + +[node name="Panel" type="Panel" parent="Panel/MarginContainer"] +layout_mode = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_bimps") + +[node name="MarginContainer" type="MarginContainer" parent="Panel/MarginContainer/Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_bottom = 20 + +[node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer/Panel/MarginContainer"] +layout_mode = 2 + +[node name="title" type="Label" parent="Panel/MarginContainer/Panel/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 116 +text = "TITLE" +horizontal_alignment = 1 + +[node name="MarginContainer" type="MarginContainer" parent="Panel/MarginContainer/Panel/MarginContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/margin_left = 40 +theme_override_constants/margin_top = 30 + +[node name="description" type="Label" parent="Panel/MarginContainer/Panel/MarginContainer/VBoxContainer/MarginContainer"] +custom_minimum_size = Vector2(200, 20) +layout_mode = 2 +size_flags_vertical = 1 +theme_override_font_sizes/font_size = 41 +text = "Description" +autowrap_mode = 3 + +[node name="MarginContainer2" type="MarginContainer" parent="Panel/MarginContainer/Panel/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_right = 20 + +[node name="launchGame" type="Button" parent="Panel/MarginContainer/Panel/MarginContainer/VBoxContainer/MarginContainer2"] +layout_mode = 2 +focus_neighbor_left = NodePath(".") +focus_neighbor_top = NodePath(".") +focus_neighbor_right = NodePath(".") +focus_neighbor_bottom = NodePath(".") +focus_next = NodePath(".") +focus_previous = NodePath(".") +theme_override_font_sizes/font_size = 61 +text = "Launch" diff --git a/onboard/godot-frontend/OnboardREADME.md b/onboard/godot-frontend/OnboardREADME.md new file mode 100644 index 0000000..5a0d2ba --- /dev/null +++ b/onboard/godot-frontend/OnboardREADME.md @@ -0,0 +1,62 @@ +# Contributing tips +Make a new branch +If you use Visual Studio or VSCode use spaces instead of tabs. If opening the scripts in the Godot script editor auto formats the files to use tabs, there is an option to change it. + +If you are creating a new GUI refer to the readme at [CreatingAGuiREADME.md](/onboard/godot-frontend/GUIs/CreatingAGuiREADME.md) + +# Docs +There are the main scripts for the onboard system +* Client.cs +* Env.cs +* GuiManager.cs +* GuiManagerGlobal.cs + +## Client.cs +Found in [Client.cs](/onboard/godot-frontend/devcade/Client.cs) + +It is a static class, which is one of the ways to implement a singlton, see: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members + +This script interfaces with the backend through a Unix domain socket, local to the machine the frontend is running on: +```C# +socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP); +``` + +## Env.cs +Found in [Env.cs](/onboard/godot-frontend/util/Env.cs) + +This script handles the enviormental values that are used by various parts of the onboard. See [.env.template](/onboard/.env.template) for the values it contains. It is also a static class. + +It provides: +* Its values in key:value pairs that map strings to strings +* Hardcoded accessors for the current useful keys + + +## GuiManager.cs +Found in [GuiManager.cs](/onboard/godot-frontend/guiManager/GuiManager.cs) + +This script is meant to handle the creation of the seperate GUIs and the states of the loading animation, and screensaver, etc. + +It is a Control node in the scene tree and the root of its scene tree in the current case +the GUIs are added as a child node to the node the script is attached to, +starting with the initial GUI scene as set in the editor for the variable: initialGuiScene + +## GuiManagerGlobal.cs +Found in [GuiManagerGlobal.cs](/onboard/godot-frontend/guiManager/GuiManagerGlobal.cs) + +This script is meant to handle the communication between the Client.cs script and the GUIs. +It handles some of the shared logic such as setting the state of the loading animation. + +Some of the functions that it provides are: +* Kill the current running game +* Set the current tag +* Launching a given game +* Loading/Reloading the game list from the backend +* Interface for showing/hiding the loading animation + +## SupervisorButton.cs +Found in [SupervisorButton.cs](/onboard/godot-frontend/util/SupervisorButton.cs) + +This is a simple class to encapsulate the detecting of the "supervisor button" that will +kill the currently running game if held for some amount of time defined in `GuiManager.cs`. +!IMPORTANT! due to how gamepad and keyboard events are propogated, only gamepad events are able +to be recieved when the onboard is not the focused window \ No newline at end of file diff --git a/onboard/frontend/app.config b/onboard/godot-frontend/app.config similarity index 99% rename from onboard/frontend/app.config rename to onboard/godot-frontend/app.config index 466d5a3..fcd85d8 100644 --- a/onboard/frontend/app.config +++ b/onboard/godot-frontend/app.config @@ -2,7 +2,7 @@
- + diff --git a/onboard/frontend/devcade/Client.cs b/onboard/godot-frontend/devcade/Client.cs similarity index 78% rename from onboard/frontend/devcade/Client.cs rename to onboard/godot-frontend/devcade/Client.cs index fdd563c..ecf037f 100644 --- a/onboard/frontend/devcade/Client.cs +++ b/onboard/godot-frontend/devcade/Client.cs @@ -1,32 +1,35 @@ #nullable enable +using Godot; + using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net.Sockets; -using System.Reflection; using System.Text; using System.Threading; -using log4net; using System.Threading.Tasks; namespace onboard.devcade; + +using System.Runtime.CompilerServices; using util; public static class Client { + private static readonly Logger LOG = Log.get(nameof(Client)); public static event EventHandler onBannerFinished = (_, _) => { - logger.Trace("onBannerFinished Invoked"); + LOG.Info("onBannerFinished Invoked"); }; public static event EventHandler onIconFinished = (_, _) => { - logger.Trace("onIconFinished Invoked"); + LOG.Info("onIconFinished Invoked"); }; public static event EventHandler onGameFinished = (_, _) => { - logger.Trace("onGameFinished Invoked"); + LOG.Info("onGameFinished Invoked"); }; /** - * FS + * File System * tmp/ * |- {game.id}/ * |- banner.png @@ -35,27 +38,43 @@ public static class Client { * |- {game.name} (executable) */ + /// + /// + /// public static bool isProduction { get; private set; } = true; + + /// + /// True if a game is currently launched/running + /// + public static bool gameLauched { get; private set; } = false; private static bool connected; - - private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod()?.DeclaringType?.FullName); - - // Thread handle for the client + + /// + /// Thread handle for the client + /// private static readonly Thread clientThread; - // Path to the working directory + /// + /// Path to the working directory + /// private static readonly string workingDir; - // Unix socket for communication with backend + /// + /// Unix socket for communication with backend + /// private static Socket socket; private static StreamReader reader; private static StreamWriter writer; private static bool brokenPipe; - // Dictionary of started tasks by request id + /// + /// Dictionary of started tasks by request id + /// private static readonly Dictionary> tasks = new(); - // List of requests to be sent to the backend. Only used when the backend is not connected yet. + /// + /// List of requests to be sent to the backend. Only used when the backend is not connected yet. + /// private static readonly List requests = new(); /// @@ -69,14 +88,17 @@ public static void init() { /// Static constructor for the client, initializes the client thread and path variables /// static Client() { - logger.Info("Initializing Devcade Client"); + LOG.Info("Initializing Devcade Client"); + workingDir = Env.get("DEVCADE_PATH").match( v => v, () => { - logger.Warn("DEVCADE_PATH not set, using default"); + LOG.Error("DEVCADE_PATH not set, using default"); + return "/tmp/devcade"; }); - logger.Info("DEVCADE_PATH: " + workingDir); + + LOG.Info("DEVCADE_PATH: " + workingDir); clientThread = new Thread(start) { IsBackground = true @@ -90,7 +112,7 @@ static Client() { /// [DoesNotReturn] private static void start() { - logger.Info("Starting Devcade Client"); + LOG.Info("Starting Devcade Client"); // Open the read/write pipe to the backend @@ -99,18 +121,20 @@ private static void start() { if (!socketResult.is_err()) { break; } - logger.Warn($"Failed to open backend socket, retrying in 500ms: {socketResult.unwrap_err()}"); + LOG.Warn($"Failed to open backend socket, retrying in 500ms: {socketResult.unwrap_err()}"); + Thread.Sleep(500); } - logger.Info($"Opened read pipe: {workingDir}/onboard.sock"); + LOG.Info($"Opened read pipe: {workingDir}/onboard.sock"); repeatPing(5000, 5000); // Start the main loop while (true) { if (brokenPipe) { - logger.Debug("Attempting to fix broken pipe"); + LOG.Info("Attempting to fix broken pipe"); + fixPipes(); Thread.Sleep(500); } @@ -123,13 +147,15 @@ private static void start() { continue; } - logger.Trace("Received message: " + message); + LOG.Verbose("Received message: " + message); + LOG.Debug(tasks.ToString()); // Parse the message Response res = Response.deserialize(message); if (!tasks.ContainsKey(res.request_id)) { - logger.Warn("Received response for unknown request id: " + res.request_id); + LOG.Warn("Received response for unknown request id: " + res.request_id); + continue; } @@ -145,34 +171,33 @@ private static void start() { // Log the response switch (res.type) { case Response.ResponseType.Pong: - logger.Trace($"Received pong response for request {res.request_id}"); + LOG.Info($"Received pong response for request {res.request_id}"); break; case Response.ResponseType.Err: // Result is always an error here, so type parameter doesn't matter - logger.Error($"Received error response for request {res.request_id}: {res.into_result().unwrap_err()}"); + LOG.Error($"Received error response for request {res.request_id}: {res.into_result().unwrap_err()}"); break; case Response.ResponseType.Ok: - logger.Debug($"Received ok response for request {res.request_id}"); + LOG.Info($"Received ok response for request {res.request_id}"); break; case Response.ResponseType.Game: - logger.Debug($"Received game response for request {res.request_id}"); + LOG.Info($"Received game response for request {res.request_id}"); break; case Response.ResponseType.GameList: - logger.Debug($"Received game list response for request {res.request_id} (contained {res.unwrap>().Count} games)"); + LOG.Info($"Received game list response for request {res.request_id} (contained {res.unwrap>().Count} games)"); break; case Response.ResponseType.TagList: - logger.Debug( - $"Received tag list response for request {res.request_id} (contained {res.unwrap>().Count} tags)"); + LOG.Info($"Received tag list response for request {res.request_id} (contained {res.unwrap>().Count} tags)"); break; case Response.ResponseType.Tag: - logger.Debug($"Received tag response for request {res.request_id}"); + LOG.Info($"Received tag response for request {res.request_id}"); break; case Response.ResponseType.User: - logger.Debug($"Received user response for request {res.request_id}"); + LOG.Info($"Received user response for request {res.request_id}"); break; default: - logger.Warn($"Received unknown response type for request {res.request_id}: {res.type}"); - logger.Warn("Did you forget to add a case to the switch statement?"); + LOG.Warn($"Received unknown response type for request {res.request_id}: {res.type}"); + LOG.Warn("Did you forget to add a case to the switch statement?"); break; } } @@ -210,7 +235,7 @@ private static string read() { try { return reader.ReadLine() ?? ""; } catch (Exception e) { - logger.Error("Failed to read from read pipe: " + e); + LOG.Error("Failed to read from read pipe: " + e); brokenPipe = true; return ""; } @@ -235,7 +260,7 @@ private static void write(string message) { /// /// A Task that will be completed when the backend responds, containing a list of games or an error public static Task getGameList() { - logger.Debug("Getting game list"); + LOG.Info("Getting game list"); return sendRequest(Request.GetGameList()); } @@ -245,7 +270,7 @@ public static Task getGameList() { /// The id of the game to fetch /// A Task that will be completed when the backend responds, containing a game or an error public static Task getGame(string id) { - logger.Debug($"Getting game with id {id}"); + LOG.Info($"Getting game with id {id}"); return sendRequest(Request.GetGame(id)); } @@ -256,7 +281,7 @@ public static Task getGame(string id) { /// The id of the game to download the banner for /// A Task that will be completed once the banner has been downloaded public static Task downloadBanner(string id) { - logger.Debug($"Downloading banner for game with id {id}"); + LOG.Info($"Downloading banner for game with id {id}"); return sendRequest(Request.DownloadBanner(id)) .ContinueWith(response => { onBannerFinished.Invoke(null, getGame(id).Result.into_option().unwrap_or(new DevcadeGame())); @@ -270,7 +295,7 @@ public static Task downloadBanner(string id) { /// The id of the game to download the icon for /// A Task that will be completed once the icon has been downloaded public static Task downloadIcon(string id) { - logger.Debug($"Downloading icon for game with id {id}"); + LOG.Info($"Downloading icon for game with id {id}"); return sendRequest(Request.DownloadIcon(id)) .ContinueWith(_ => { onIconFinished.Invoke(null, getGame(id).Result.into_option().unwrap_or(new DevcadeGame())); @@ -284,7 +309,7 @@ public static Task downloadIcon(string id) { /// The id of the game to download /// A Task that will be completed once the game has been downloaded public static Task downloadGame(string id) { - logger.Debug($"Downloading game with id {id}"); + LOG.Info($"Downloading game with id {id}"); return sendRequest(Request.DownloadGame(id)) .ContinueWith(_ => { onGameFinished.Invoke(null, getGame(id).Result.into_option().unwrap_or(new DevcadeGame())); @@ -298,8 +323,9 @@ public static Task downloadGame(string id) { /// The id of the game to launch /// A Task that will be completed once the game has exited public static Task launchGame(string id) { - logger.Debug($"Launching game with id {id}"); - return sendRequest(Request.LaunchGame(id)); + LOG.Info($"Launching game with id {id}"); + gameLauched = true; + return sendRequest(Request.LaunchGame(id)).ContinueWith(task => {gameLauched = false; return task.Result;} ); } /// @@ -307,7 +333,7 @@ public static Task launchGame(string id) { /// /// A Task that will be completed once the game has exited public static Task killGame() { - logger.Debug($"Killing game"); + LOG.Info($"Killing game"); return sendRequest(Request.KillGame()); } @@ -318,7 +344,7 @@ public static Task killGame() { /// A Task that will be completed when the backend has responded public static Task setProduction(bool prod) { string prodStr = prod ? "Production" : "Development"; - logger.Info($"Setting API to {prodStr}"); + LOG.Info($"Setting API to {prodStr}"); return sendRequest(Request.SetProduction(prod)).ContinueWith(res => { if (res.Result.type == Response.ResponseType.Ok) { isProduction = prod; @@ -328,19 +354,19 @@ public static Task setProduction(bool prod) { public static Task getTags() { Request req = Request.GetTagList(); - logger.Debug($"Getting tags list (id {req.request_id})"); + LOG.Info($"Getting tags list (id {req.request_id})"); return sendRequest(req); } public static Task getTag(string name) { Request req = Request.GetTag(name); - logger.Debug($"Getting tag with name '{name}' (id {req.request_id})"); + LOG.Info($"Getting tag with name '{name}' (id {req.request_id})"); return sendRequest(req); } public static Task getGamesWithTag(string name) { Request req = Request.GetGameListFromTag(name); - logger.Debug($"Getting games with tag '{name}' (id {req.request_id})"); + LOG.Info($"Getting games with tag '{name}' (id {req.request_id})"); return sendRequest(req); } @@ -350,7 +376,7 @@ public static Task getGamesWithTag(Tag tag) { public static Task getUser(string username) { Request req = Request.getUser(username); - logger.Debug($"Getting user with username '{username}' (id {req.request_id})"); + LOG.Info($"Getting user with username '{username}' (id {req.request_id})"); return sendRequest(req); } @@ -366,14 +392,14 @@ void ping(int _intervalMillis, int _timeoutMillis) { .ContinueWith(res => { if (res is { IsCompletedSuccessfully: true, Result.type: Response.ResponseType.Pong }) { DateTime end = DateTime.Now; - logger.Trace($"Ping successful ({(int)Math.Round((end - start).TotalMilliseconds)}ms)"); + LOG.Info($"Ping successful ({(int)Math.Round((end - start).TotalMilliseconds)}ms)"); if (!connected) { sendQueuedRequests(); } connected = true; } else { - logger.Error($"Failed to ping backend (no response after {_timeoutMillis}ms)"); + LOG.Error($"Failed to ping backend (no response after {_timeoutMillis}ms)"); connected = false; } }) @@ -405,7 +431,7 @@ private static Task sendRequest(Request req) { try { write(req.serialize()); } catch (Exception e) { - logger.Error($"Failed to send request {req.request_id} to backend: {e.Message}"); + LOG.Error($"Failed to send request {req.request_id} to backend: {e.Message}"); connected = false; brokenPipe = true; return Task.FromResult(Response.fromError(req.request_id, e.Message)); @@ -418,6 +444,7 @@ private static Task sendRequest(Request req) { TaskCompletionSource tcs = new(); + LOG.Debug($"created task with id: {req.request_id}"); tasks.Add(req.request_id, tcs); return tcs.Task; @@ -434,7 +461,7 @@ private static Task forceSendRequest(Request req) { try { write(req.serialize()); } catch (Exception e) { - logger.Error($"Failed to send request {req.request_id} to backend: {e.Message}"); + LOG.Error($"Failed to send request {req.request_id} to backend: {e.Message}"); connected = false; brokenPipe = true; return Task.FromResult(Response.fromError(req.request_id, e.Message)); @@ -456,7 +483,7 @@ private static void killAllTasks() { try { t.TrySetCanceled(); } catch (Exception e) { - logger.Error("Failed to cancel task: " + e); + LOG.Error("Failed to cancel task: " + e); } } } @@ -479,11 +506,11 @@ private static bool fixPipes() { var socketResult = tryOpenSocket($"{workingDir}/onboard.sock"); if (socketResult.is_ok()) { brokenPipe = false; - logger.Info("Reconnected to backend"); + LOG.Info("Reconnected to backend"); return true; } - logger.Warn("Failed to reconnect to backend (is it running?)"); + LOG.Warn("Failed to reconnect to backend (is it running?)"); return false; } } diff --git a/onboard/godot-frontend/devcade/Client.cs.uid b/onboard/godot-frontend/devcade/Client.cs.uid new file mode 100644 index 0000000..5e7687b --- /dev/null +++ b/onboard/godot-frontend/devcade/Client.cs.uid @@ -0,0 +1 @@ +uid://bu16lyi1grdop diff --git a/onboard/frontend/devcade/DevcadeGame.cs b/onboard/godot-frontend/devcade/DevcadeGame.cs similarity index 59% rename from onboard/frontend/devcade/DevcadeGame.cs rename to onboard/godot-frontend/devcade/DevcadeGame.cs index 3a8c010..5020ffb 100644 --- a/onboard/frontend/devcade/DevcadeGame.cs +++ b/onboard/godot-frontend/devcade/DevcadeGame.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Linq; +using Godot; + namespace onboard.devcade; /// @@ -47,8 +49,27 @@ public class DevcadeGame { /// The user that uploaded the game. /// public User user { get; set; } + + /// + /// The banner associated with this game. + /// Will have the value null if the banner cannot be found + /// + public Texture2D banner { get; set; } + - public DevcadeGame(string author, string description, string hash, string id, string name, List tags, string upload_date, User user) { + /// + /// An instance of a game with all parameters filled out + /// + /// The user that uploaded the game. + /// The description of the game, as provided by the author. + /// The hash of the game, used to verify the integrity of the game, and to determine whether the game has been updated. + /// The game's ID, used to identify the game. This will not change even if the game is updated. + /// The name of the game, as provided by the author. + /// The tags associated with the game, used to categorize and filter games. + /// The date the game was uploaded, in the format YYYY-MM-DD. + /// The user that uploaded the game. + /// The banner associated with this game. + public DevcadeGame(string author, string description, string hash, string id, string name, List tags, string upload_date, User user, Texture2D banner) { this.author = author; this.description = description; this.hash = hash; @@ -57,8 +78,27 @@ public DevcadeGame(string author, string description, string hash, string id, st this.tags = tags; this.upload_date = upload_date; this.user = user; + this.banner = banner; } + + /// + /// An instance of a game with all parameters filled out, except for the banner + /// + /// The user that uploaded the game. + /// The description of the game, as provided by the author. + /// The hash of the game, used to verify the integrity of the game, and to determine whether the game has been updated. + /// The game's ID, used to identify the game. This will not change even if the game is updated. + /// The name of the game, as provided by the author. + /// The tags associated with the game, used to categorize and filter games. + /// The date the game was uploaded, in the format YYYY-MM-DD. + /// The user that uploaded the game. + public DevcadeGame(string author, string description, string hash, string id, string name, List tags, string upload_date, User user) + : this(author, description, hash, id, name, tags, upload_date, user, null) { } + + /// + /// The default constructor, fills out all the fields as empty + /// public DevcadeGame() { this.author = ""; this.description = ""; @@ -68,8 +108,14 @@ public DevcadeGame() { this.tags = new List(); this.upload_date = ""; this.user = new User(); + this.banner = null; } + /// + /// Check if this game contains the given tag. + /// + /// The tag name to check. + /// Whether this game has the given tag as one of its tags. public bool containsTag(string tag) { return tags.Any(t => t.name == tag); } @@ -98,6 +144,29 @@ public Tag() { this.name = ""; this.description = ""; } + + /// + /// checks if another object is equal to this object + /// + /// the other object + /// true if the other object is a tag and their names match, otherwise false + public override bool Equals(object obj) + { + Tag otherTag = obj as Tag; + if(otherTag != null) + { + return this.name.Equals(otherTag.name); + } + return false; + } + /// + /// two tags with the same name will have the same hash code + /// + /// the hash code of this tag + public override int GetHashCode() + { + return this.name.GetHashCode(); + } } /// diff --git a/onboard/godot-frontend/devcade/DevcadeGame.cs.uid b/onboard/godot-frontend/devcade/DevcadeGame.cs.uid new file mode 100644 index 0000000..1b2596b --- /dev/null +++ b/onboard/godot-frontend/devcade/DevcadeGame.cs.uid @@ -0,0 +1 @@ +uid://qruqmwcsfrqm diff --git a/onboard/godot-frontend/export_presets.cfg b/onboard/godot-frontend/export_presets.cfg new file mode 100644 index 0000000..7462ab6 --- /dev/null +++ b/onboard/godot-frontend/export_presets.cfg @@ -0,0 +1,44 @@ +[preset.0] + +name="Linux" +platform="Linux" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=false +texture_format/s3tc_bptc=true +texture_format/etc2_astc=false +binary_format/architecture="x86_64" +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="#!/usr/bin/env bash +export DISPLAY=:0 +unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" +\"{temp_dir}/{exe_name}\" {cmd_args}" +ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash +kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") +rm -rf \"{temp_dir}\"" +dotnet/include_scripts_content=false +dotnet/include_debug_symbols=true +dotnet/embed_build_outputs=false diff --git a/onboard/godot-frontend/godot-frontend.csproj b/onboard/godot-frontend/godot-frontend.csproj new file mode 100644 index 0000000..8137e13 --- /dev/null +++ b/onboard/godot-frontend/godot-frontend.csproj @@ -0,0 +1,10 @@ + + + net8.0 + true + godotfrontend + + + + + \ No newline at end of file diff --git a/onboard/godot-frontend/godot-frontend.csproj.old b/onboard/godot-frontend/godot-frontend.csproj.old new file mode 100644 index 0000000..8118c03 --- /dev/null +++ b/onboard/godot-frontend/godot-frontend.csproj.old @@ -0,0 +1,10 @@ + + + net8.0 + true + godotfrontend + + + + + \ No newline at end of file diff --git a/onboard/godot-frontend/godot-frontend.sln b/onboard/godot-frontend/godot-frontend.sln new file mode 100644 index 0000000..5a25370 --- /dev/null +++ b/onboard/godot-frontend/godot-frontend.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "godot-frontend", "godot-frontend.csproj", "{709739FE-739C-4914-8722-33C64CF694FC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + ExportDebug|Any CPU = ExportDebug|Any CPU + ExportRelease|Any CPU = ExportRelease|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {709739FE-739C-4914-8722-33C64CF694FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {709739FE-739C-4914-8722-33C64CF694FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {709739FE-739C-4914-8722-33C64CF694FC}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU + {709739FE-739C-4914-8722-33C64CF694FC}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU + {709739FE-739C-4914-8722-33C64CF694FC}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU + {709739FE-739C-4914-8722-33C64CF694FC}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU + EndGlobalSection +EndGlobal diff --git a/onboard/godot-frontend/guiManager/FlashingRichTextLabel.cs b/onboard/godot-frontend/guiManager/FlashingRichTextLabel.cs new file mode 100644 index 0000000..005ab0f --- /dev/null +++ b/onboard/godot-frontend/guiManager/FlashingRichTextLabel.cs @@ -0,0 +1,45 @@ +using Godot; + +public partial class FlashingRichTextLabel : RichTextLabel +{ + [Export] + Color start_color = Colors.White; + [Export] + Color end_color = Colors.Black; + + [Export] + double animation_speed = 1.0; + + public override void _Ready() + { + this.Set("theme_override_colors/default_color", start_color); + } + + private bool color_dir = true; + private double t = 0.0; + public override void _Process(double delta) + { + t += delta; + + if(t > 1.0) + { + t = 0.0; + color_dir = !color_dir; + } + + if(color_dir) + { + set_font_color(start_color.Lerp(end_color, (float) t)); + } + else + { + set_font_color(end_color.Lerp(start_color, (float) t)); + } + } + + private void set_font_color(Color color) + { + this.Set("theme_override_colors/default_color", color); + // this.AddThemeColorOverride("theme_override_colors/default_color", color); + } +} diff --git a/onboard/godot-frontend/guiManager/FlashingRichTextLabel.cs.uid b/onboard/godot-frontend/guiManager/FlashingRichTextLabel.cs.uid new file mode 100644 index 0000000..af2a2ad --- /dev/null +++ b/onboard/godot-frontend/guiManager/FlashingRichTextLabel.cs.uid @@ -0,0 +1 @@ +uid://ei3nslcsxscs diff --git a/onboard/godot-frontend/guiManager/GuiManager.cs b/onboard/godot-frontend/guiManager/GuiManager.cs new file mode 100644 index 0000000..90ccdbe --- /dev/null +++ b/onboard/godot-frontend/guiManager/GuiManager.cs @@ -0,0 +1,323 @@ +using System; +using System.Collections.Generic; + +using onboard.util; + +using Godot; +using onboard.util.supervisor_button; + +namespace onboard.devcade.GUI; + +public partial class GuiManager : Control +{ + util.Logger LOG = Log.get(nameof(GuiManager)); + + /// + /// the initial GUI scene to show when devcade starts up + /// + [Export] + public PackedScene initialGuiScene; + + /// + /// the root node of the loading screen + /// the control node to hide when not showing the loading animation + /// + [Export] + public Control loadingScreen; + + /// + /// the node to animate when showing the loading animation + /// + [Export] + public AnimatedSprite2D loadingAnimation; + + /// + /// the root node of the screen saver + /// the control node to hide when not showing the screen saver + /// + [Export] + public Screensaver screenSaver; + + /// + /// true if the screensaver animation is being shown + /// + public bool showingScreenSaverAnimation { get; private set; } = false; + + /// + /// A list of all the games + /// + public List gameList; + + /// + /// if the cabneit is in demo mode + /// i.e. show only the curated game list + /// + public bool isDemoMode = false; + + ////////// + // TAGS // + ////////// + + /// + /// a list of all the tags + /// + public List tagList = new List() { allTag }; + + /// + /// A dictionary of tags to lists of games that have that tag + /// + private Dictionary> tagLists = new Dictionary>(); + public readonly static Tag curatedTag = new Tag("Curated", "Curated by the Devcade Team"); + public readonly static Tag allTag = new Tag("All Games", "View all available games"); + public Tag currentTag = allTag; + + /// + /// True if the game list is being reloaded from the backend + /// + public bool reloadingGameList { get; private set; } = false; + + /// + /// The root node of the GUI scene + /// + Node guiSceneRootNode; + + private static readonly DevcadeGame defaultGame = new DevcadeGame { + name = "Error", + description = "There was a problem loading games from the API. Please check the logs for more information.", + id = "error", + author = "None", + }; + + // the game list is set to this if an error conditions is encountered + private static readonly List errorList = new List { defaultGame }; + + /// + /// A godot specific function that is ran once after this node is initialized + /// + public override void _Ready() + { + try + { + GuiManagerGlobal.instance.onGameLaunched += (bool isOpened) => + { + if(isOpened) + { + // pause when a game is launched so the onboard does not receive input when not focused + // ProcessMode = ProcessModeEnum.Disabled; + guiSceneRootNode.ProcessMode = ProcessModeEnum.Disabled; + } + else + { + guiSceneRootNode.SetDeferred("process_mode", (long) ProcessModeEnum.Inherit); + } + }; + + GuiManagerGlobal.instance.setLoadingAnimation += (bool show) => + { + if(show) + { + showLoadingAnimation(); + } + else + { + hideLoadingAnimation(); + } + }; + } catch (Exception e) + { + LOG.Error(e.Message); + throw new ApplicationException("unable to set proccess mode onGameLaunched and show/hide loading animation, yeah this is unrecoverable"); + } + + supervisorButtonTimeoutSeconds = Env.SUPERVISOR_BUTTON_TIMEOUT_SEC(); // default 5 seconds + supervisorButtonTimerSeconds = supervisorButtonTimeoutSeconds; + + screenSaverTimeoutSeconds = Env.SCREENSAVER_TIMEOUT_SEC(); // default 2 minutes + screenSaverTimerSeconds = screenSaverTimeoutSeconds; + + LOG.Info("supervisorButtonTimeoutSeconds: " + supervisorButtonTimeoutSeconds); + LOG.Info("screenSaverTimeoutSeconds" + screenSaverTimeoutSeconds); + + // hide the loading screen by default + hideLoadingAnimation(); + // hide the screen saver by default + hideScreenSaver(); + + // spawn initial gui scene + guiSceneRootNode = initialGuiScene.Instantiate(); + + // add the new scene instance as a child of this node + AddChild(guiSceneRootNode); + + // and reload the game list + GuiManagerGlobal.instance.reloadGameList(); + } + + public override void _Input(InputEvent @event) + { + if(@event.IsEcho()) { GetViewport().SetInputAsHandled();} + + // if(@event is InputEventJoypadButton joy) + // { + // LOG.Verbose($"{joy.Device}, {joy.ButtonIndex}"); + // } + + // if(@event is InputEventJoypadMotion axis) + // { + // LOG.Verbose($"{axis.Device}, {axis.Axis}"); + // } + + if(showingScreenSaverAnimation) + { + GetViewport().SetInputAsHandled(); + } + } + + double supervisorButtonTimeoutSeconds; + double supervisorButtonTimerSeconds; + + static readonly double reloadButtonCooldown = 1.0; + double reloadButtonCooldownTimer = reloadButtonCooldown; + static readonly double switchDevButtonCooldown = 1.0; + double switchDevButtonCooldownTimer = switchDevButtonCooldown; + + double screenSaverTimeoutSeconds; + double screenSaverTimerSeconds; + + [Export] + private double secBeforeKeyRepeat = 0.3; + [Export] + private double secBetweenKeyRepeat = 0.2; + + public override void _Process(double delta) + { + reloadButtonCooldownTimer -= delta; + if(reloadButtonCooldownTimer < 0) + { + reloadButtonCooldownTimer = 0; + } + + // frontend reset button, reloads all the games from the backend + if (Input.IsActionPressed("Player1_Menu") && Input.IsActionPressed("Player2_Menu") && reloadButtonCooldownTimer <= 0) + { + GuiManagerGlobal.instance.reloadGameList(); + reloadButtonCooldownTimer = reloadButtonCooldown; + } + + switchDevButtonCooldownTimer -= delta; + if(switchDevButtonCooldownTimer < 0) + { + switchDevButtonCooldownTimer = 0; + } + + // switch between dev and normal mode + if (Env.DEMO_MODE() == false && Input.IsActionPressed("Player1_B4") && Input.IsActionPressed("Player2_B4") && switchDevButtonCooldownTimer <= 0) + { + Client.setProduction(!Client.isProduction).ContinueWith(_ => { GuiManagerGlobal.instance.setTag(allTag); GuiManagerGlobal.instance.reloadGameList(); }); + switchDevButtonCooldownTimer = switchDevButtonCooldown; + } + + // + // supervisor button (aka force kill) + // + if (SupervisorButton.isSupervisorButtonPressed()) + { + supervisorButtonTimerSeconds -= delta; + + if (supervisorButtonTimerSeconds <= 0.0) + { + // if the timer has timed out + // kill the currently running game + _ = GuiManagerGlobal.instance.killGame(); + LOG.Info("log: Killing current running game"); + + supervisorButtonTimerSeconds = supervisorButtonTimeoutSeconds; + } + } + else + { + supervisorButtonTimerSeconds = supervisorButtonTimeoutSeconds; + } + + // + // screen saver + // + if (!Input.IsAnythingPressed()) + { + screenSaverTimerSeconds -= delta; + if (screenSaverTimerSeconds <= 0.0 && showingScreenSaverAnimation == false) + { + // if the timer has timed out + // kill the currently running game + // and show the screensaver + if(Client.gameLauched) + { + _ = GuiManagerGlobal.instance.killGame(); + } + showingScreenSaverAnimation = true; + showScreenSaver(); + } + } + else + { + if (showingScreenSaverAnimation) + { + showingScreenSaverAnimation = false; + hideScreenSaver(); + } + screenSaverTimerSeconds = screenSaverTimeoutSeconds; + } + } + + /// + /// Shows the loading animation, + /// this hides the GUI, + /// shows the node that is the root of the animation tree, + /// and starts the animation. + /// + public void showLoadingAnimation() + { + LOG.Info("Showing Loading Animation"); + + loadingScreen.CallDeferred("show"); + loadingAnimation.CallDeferred("play", "default", 1.0f, false); + } + + /// + /// Hides the loading animation, + /// this shows the GUI, + /// hides the node that is the root of the animation tree, + /// and stops the animation. + /// + public void hideLoadingAnimation() + { + LOG.Info("Hiding Loading Animation"); + + loadingScreen.CallDeferred("hide"); + loadingAnimation.CallDeferred("stop"); + } + + /// + /// shows the base screen saver + /// thereby hiding the current GUI + /// + public void showScreenSaver() + { + LOG.Info("Showing Screen Saver"); + + screenSaver.CallDeferred("show"); + screenSaver.CallDeferred("play"); + } + + /// + /// hides the base screen saver + /// thereby showing the current GUI + /// + public void hideScreenSaver() + { + LOG.Info("Hiding Screen Saver"); + + screenSaver.CallDeferred("hide"); + screenSaver.CallDeferred("stop"); + } +} diff --git a/onboard/godot-frontend/guiManager/GuiManager.cs.uid b/onboard/godot-frontend/guiManager/GuiManager.cs.uid new file mode 100644 index 0000000..ba316b0 --- /dev/null +++ b/onboard/godot-frontend/guiManager/GuiManager.cs.uid @@ -0,0 +1 @@ +uid://cpnqnrrdr0pbv diff --git a/onboard/godot-frontend/guiManager/GuiManagerGlobal.cs b/onboard/godot-frontend/guiManager/GuiManagerGlobal.cs new file mode 100644 index 0000000..5ed2dd8 --- /dev/null +++ b/onboard/godot-frontend/guiManager/GuiManagerGlobal.cs @@ -0,0 +1,419 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Godot; +using onboard.devcade; +using onboard.util; + +namespace onboard; + +/// +/// A global interface for better interactions with the Client.cs script +/// +/// +/// +/// Notes: +/// the various call_ and state_ variables are used to call their related signals +/// in the proccess loop aka the main thread. +public partial class GuiManagerGlobal : Node +{ + util.Logger LOG = Log.get(nameof(GuiManagerGlobal)); + + /// + /// The one instance of this class + /// + public static GuiManagerGlobal instance; + + /// + /// If the cabneit is in demo mode + /// i.e. show only the curated game list + /// + public static bool isDemoMode { get; private set; } = false; + + /// + /// True if the loading animation is being shown + /// + public static bool showingLoadingAnimation { get; private set; } = false; + /// + /// Emit to set the state of the loading animation + /// + [Signal] public delegate void setLoadingAnimationEventHandler(bool show); + private int call_setLoadingAnimation = 0; + private bool state_setLoadingAnimation = false; + + //////////// +#region Tags /// + //////////// + + /// + /// show only games that are curated (the best of the best) + /// + public readonly static Tag curatedTag = new Tag("Curated", "Curated by the Devcade Team"); + /// + /// Show all games + /// + public readonly static Tag allTag = new Tag("All Games", "View all available games"); + + /// + /// The current tag + /// + public static Tag currentTag; + /// + /// Emitted when the current tag changes value + /// + [Signal] public delegate void currentTagUpdatedEventHandler(); + private int call_currentTagUpdated = 0; + + /// + /// a list of all the tags + /// + public static List tagList = new List() { allTag }; + /// + /// Emitted when the tag list changes + /// + [Signal] public delegate void tagListUpdatedEventHandler(); + private int call_tagListUpdated = 0; + + /// + /// a list of all the tags + /// + public static Dictionary> tagLists = new Dictionary> { { allTag.name, new List() } }; + /// + /// Emitted when the tag list changes + /// + [Signal] public delegate void tagListsUpdatedEventHandler(); + private int call_tagListsUpdated = 0; + +#endregion + ///////////// +#region Games /// + ///////////// + + /// + /// True if the game list is being refreshed from the backend + /// + public static bool reloadingGameList = false; + /// + /// Emitted when the reloading game list value is changed + /// + [Signal] public delegate void reloadingGameListUpdatedEventHandler(bool reloadGameList); + private int call_reloadingGameListUpdated = 0; + + /// + /// The default game to use for errors + /// + private static readonly DevcadeGame defaultGame = new DevcadeGame { + name = "Error", + description = "There was a problem loading games from the API. Please check the logs for more information.", + id = "error", + author = "None", + banner = null, + }; + + /// + /// the game list is set to this if an error conditions is encountered + /// + private static readonly List errorList = new List { defaultGame }; + + /// + /// A list of all the games + /// + public static List gameTitles; + + /// + /// Emitted when the game titles changes + /// + [Signal] public delegate void gameTitlesUpdatedEventHandler(); + private int call_gameTitlesUpdated = 0; + + /// + /// Emitted when a game is launched or closed + /// True when launched, False when closed + /// + [Signal] public delegate void onGameLaunchedEventHandler(bool launched); + private int call_onGameLaunched = 0; + private bool state_onGameLaunched = false; + +#endregion + /////////////// +#region Methods /// + /////////////// + + public GuiManagerGlobal() + { + instance = this; + + isDemoMode = Env.DEMO_MODE(); + } + + public void setTag(Tag newTag) + { + LOG.Info($"setting tag, {newTag.name}"); + currentTag = newTag; + + Interlocked.Increment(ref call_currentTagUpdated); + } + + /// + /// Fetches the game list from the backend, does take time to do so + /// + /// - Shows the loading animation + /// - Calls the gameTitlesUpdated signal + /// + /// + /// + public Task reloadGameList() + { + LOG.Info($"Reloading game list"); + + reloadingGameList = true; + Interlocked.Increment(ref call_reloadingGameListUpdated); + + state_setLoadingAnimation = true; + Interlocked.Increment(ref call_setLoadingAnimation); + + tagLists = new Dictionary> { { allTag.name, new List() } }; + tagList = new List() { allTag }; + + gameTitles = errorList; + + Task gameTask = Client.getGameList() + .ContinueWith(t => + { + if (!t.IsCompletedSuccessfully) { + LOG.Error($"Failed to fetch game list: {t.Exception}"); + gameTitles = errorList; + return; + } + + var res = t.Result.into_result>(); + if (!res.is_ok()) { + LOG.Error($"Failed to fetch game list: {res.err().unwrap()}"); + gameTitles = errorList; + return; + } + + LOG.Info("Got game list, setting titles"); + + gameTitles = res.unwrap(); + + // each game does not have the "all tag" + // adding it removes the requirement for an extra condition in each gui's code + gameTitles.ForEach(game => + { + game.tags.Add(allTag); + }); + + // remove all games that do not have the curated tag if + // demo mode is enabled + if(isDemoMode) + { + for (int i = 0; i < gameTitles.Count; i++) + { + DevcadeGame game = gameTitles[i]; + // if it does not have the curatedTag + if(!game.tags.Contains(curatedTag)) + { + // remove it + gameTitles.Remove(game); + i--; // fix the index, so we don't skip any games + } + } + } + + }) + .ContinueWith(_ => + { + LOG.Info("Setting cards"); + + downloadBanners().ContinueWith(_ => + { + loadBanners(); + + reloadingGameList = false; + + call_reloadingGameListUpdated++; + Interlocked.Increment(ref call_gameTitlesUpdated); + + state_setLoadingAnimation = false; + Interlocked.Increment(ref call_setLoadingAnimation); + }); + }); + return gameTask; + } + + /// + /// Sends requests to the backend to download the banners for each game in gameTitles + /// + /// A task that completes when all banners are downloaded + private Task downloadBanners() + { + List bannerTasks = new(); + foreach(DevcadeGame game in gameTitles) + { + // Start downloading the textures + if (game.id != "error") + { + // don't download the banner for the default game + bannerTasks.Add(Client.downloadBanner(game.id)); + } // check if /tmp/ has the banner + } + + return Task.WhenAll(bannerTasks).WaitAsync(TimeSpan.FromSeconds(10)); + } + + /// + /// loads the banners from the downloaded files from the database + /// and saves the images to the DevcadeGame gamse + /// + public void loadBanners() + { + LOG.Info("loading banners"); + foreach(DevcadeGame game in gameTitles) + { + string bannerPath = $"{Env.DEVCADE_PATH()}/{game.id}/banner.png"; + + if (File.Exists(bannerPath)) + { + try + { + // godot image class + Image image = Image.LoadFromFile(bannerPath); + ImageTexture texture = ImageTexture.CreateFromImage(image); // inherits from godot texture2D class + + game.banner = texture; + } + catch (Exception e) { + LOG.Warn($"Unable to set card: {e.Message}"); + } + } + + // for each tag that this game has, add it to the corresponding list + // this allows for easy filtering by tag + foreach(Tag tag in game.tags) + { + // if the tag does not exist in the dictionary, + // init it as an empty list + if(!tagLists.ContainsKey(tag.name)) + { + tagLists.Add(tag.name, new List()); + } + tagLists[tag.name].Add(game); + + // if the overall tag list does not contain the tag + // add it to the list + if(!tagList.Contains(tag)) + { + tagList.Add(tag); + } + } + + } + + Interlocked.Increment(ref call_tagListsUpdated); + Interlocked.Increment(ref call_tagListUpdated); + } + + + /// + /// launch the given game + /// + /// the game to launch + public async Task launchGame(DevcadeGame game) + { + Interlocked.Increment(ref call_setLoadingAnimation); + state_setLoadingAnimation = true; + + Interlocked.Increment(ref call_onGameLaunched); + state_onGameLaunched = true; + + LOG.Info("launching game: " + game.name); + + await Client.launchGame( + game.id).ContinueWith(res => { + if (res.IsCompletedSuccessfully) { + // runs after the game completes running + Interlocked.Increment(ref call_onGameLaunched); + state_onGameLaunched = false; + + Interlocked.Increment(ref call_setLoadingAnimation); + state_setLoadingAnimation = false; + } + else { + LOG.Error("Failed to launch game: " + res.Exception); + } + // ProcessMode = ProcessModeEnum.Always; + }); + } + + /// + /// kill the currently running game. + /// will run async, but can await the function call + /// + public async Task killGame() + { + await Client.killGame(); + + Interlocked.Increment(ref call_onGameLaunched); + state_onGameLaunched = false; + } + + + public override void _Process(double delta) + { + // call all signals in the main thread + // avoids having to deal with all the call_defered() calls and issues with that + if(call_currentTagUpdated > 0) + { + LOG.Info("currentTagUpdated emitted"); + EmitSignal(SignalName.currentTagUpdated); + Interlocked.Decrement(ref call_currentTagUpdated); + } + + if(call_gameTitlesUpdated > 0) + { + LOG.Info("gameTitlesUpdated emitted"); + EmitSignal(SignalName.gameTitlesUpdated); + Interlocked.Decrement(ref call_gameTitlesUpdated); + } + + if(call_onGameLaunched > 0) + { + LOG.Info("onGameLaunched emitted"); + EmitSignal(SignalName.onGameLaunched, state_onGameLaunched); + Interlocked.Decrement(ref call_onGameLaunched); + } + + if(call_reloadingGameListUpdated > 0) + { + LOG.Info("reloadingGameListUpdated emitted"); + EmitSignal(SignalName.reloadingGameListUpdated, reloadingGameList); + Interlocked.Decrement(ref call_reloadingGameListUpdated); + } + + if(call_setLoadingAnimation > 0) + { + LOG.Info("setLoadingAnimation emitted"); + EmitSignal(SignalName.setLoadingAnimation, state_setLoadingAnimation); + Interlocked.Decrement(ref call_setLoadingAnimation); + } + + if(call_tagListsUpdated > 0) + { + LOG.Info("tagListsUpdated emitted"); + EmitSignal(SignalName.tagListsUpdated); + Interlocked.Decrement(ref call_tagListsUpdated); + } + + if(call_tagListUpdated > 0) + { + LOG.Info("tagListUpdated emitted"); + EmitSignal(SignalName.tagListUpdated); + Interlocked.Decrement(ref call_tagListUpdated); + } + } + + #endregion +} diff --git a/onboard/godot-frontend/guiManager/GuiManagerGlobal.cs.uid b/onboard/godot-frontend/guiManager/GuiManagerGlobal.cs.uid new file mode 100644 index 0000000..d789381 --- /dev/null +++ b/onboard/godot-frontend/guiManager/GuiManagerGlobal.cs.uid @@ -0,0 +1 @@ +uid://mxnxru87vvsc diff --git a/onboard/godot-frontend/guiManager/screensaver/RecordingREADME.md b/onboard/godot-frontend/guiManager/screensaver/RecordingREADME.md new file mode 100644 index 0000000..d4d9ec6 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/RecordingREADME.md @@ -0,0 +1,23 @@ +# Video +### Recording + +The current display's resolution is **2160 by 3840**.
+So make sure that the game is running at the correct resolution or can be cropped, converted, etc. to a better resolution. + +It is recommended to use high-quality or lossless video formats so when converting the video the least quality is dropped. + +### Converting + +This is a command to convert an input file of a given type to the **.ogv** format that Godot supports using **ffmpeg**.
+***-q:v*** changes the quality of the video and ranges from 1-10
+A Lower quality is recomended as high quality video runs poorly, as always test it first.
+***-q:a*** changes the quality of the audio and ranges from 1-10
+A quality of 1 is recomended as no audio is played anyways.
+***-vf*** specifies video filter options: **scale** changes the output resolution, the width:height of the video and **fps** sets the frame rate. +```Bash +ffmpeg -i input_file.type -vf "scale=1080:1920,fps=30" -q:v 4 -q:a 1 output.ogv +``` +The reason for the half scale and lower frame rate is because the .ogv format only supports cpu sided decoding and is extremly laggy on the DCU which has an older i5-8500 cpu. + +## Videos **MUST** be under **100MB** or else GitHub will reject the Commit +And fixing it is annoying. For reference a 1 min, 30 sec video took about 26MB of space \ No newline at end of file diff --git a/onboard/godot-frontend/guiManager/screensaver/ScreenSaverGameAnimation.cs b/onboard/godot-frontend/guiManager/screensaver/ScreenSaverGameAnimation.cs new file mode 100644 index 0000000..55b2a4f --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/ScreenSaverGameAnimation.cs @@ -0,0 +1,65 @@ +using Godot; + +public partial class ScreenSaverGameAnimation : Control +{ + [Export] + public string game_name = "Null"; + + [Export] + public VideoStreamPlayer videoStreamPlayer; + + private float screenWidth = 0; + + public override void _Ready() + { + this.Hide(); + this.screenWidth = GetViewport().GetVisibleRect().Size.X; + GetViewport().SizeChanged += () => { + this.screenWidth = GetViewport().GetVisibleRect().Size.X; + }; + } + + public override void _Notification(int what) + { + if(videoStreamPlayer == null) { return; } + + if(what == NotificationVisibilityChanged && this.videoStreamPlayer.IsInsideTree()) + { + if(this.Visible) + { + this.play(); + } + else + { + this.stop(); + } + } + } + + public override void _Process(double delta) + { + float xPosition = this.GlobalPosition.X; + if(xPosition > -screenWidth && xPosition < screenWidth) + { + this.play(); + } + else + { + this.stop(); + } + } + + public void play() + { + if(videoStreamPlayer == null) { return; } + + videoStreamPlayer.Paused = false; + } + + public void stop() + { + if(videoStreamPlayer == null) { return; } + + videoStreamPlayer.Paused = true; + } +} diff --git a/onboard/godot-frontend/guiManager/screensaver/ScreenSaverGameAnimation.cs.uid b/onboard/godot-frontend/guiManager/screensaver/ScreenSaverGameAnimation.cs.uid new file mode 100644 index 0000000..cb6f3a5 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/ScreenSaverGameAnimation.cs.uid @@ -0,0 +1 @@ +uid://3dod6y75r7mb diff --git a/onboard/godot-frontend/guiManager/screensaver/Screensaver.cs b/onboard/godot-frontend/guiManager/screensaver/Screensaver.cs new file mode 100644 index 0000000..ec239d2 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/Screensaver.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using Godot; +using onboard; +using onboard.devcade; + +public partial class Screensaver : Control +{ + [Export] + public float scrollSpeed = 1.0f; + + [Export] + private Control gamesAnimationsContainer; + + [Export] + private TextureRect backgroundIcons; + + private List shownGameAnimationNodes = new List(); + private List gameAnimationNodes = new List(); + + private bool playing = false; + private int currentGameAnimationIndex = 0; + + private readonly Vector2 startPosition = new Vector2(0, 0); + private Vector2 endPosition; + float screenWidth; + + Vector2 shaderVelInit; + + public void play() + { + playing = true; + currentGameAnimationIndex = 0; + gamesAnimationsContainer.Position = startPosition; + + foreach(var anim in shownGameAnimationNodes) + { + anim.Show(); + } + } + + public void stop() + { + playing = false; + + foreach(var anim in shownGameAnimationNodes) + { + anim.Hide(); + } + } + + public override void _Ready() + { + screenWidth = GetViewportRect().Size.X; + + foreach(Node node in gamesAnimationsContainer.GetChildren()) + { + if(node is ScreenSaverGameAnimation gameAnimation) + { + gameAnimation.ZIndex = 4000; + gameAnimation.YSortEnabled = true; + + gameAnimationNodes.Add(gameAnimation); + gameAnimation.Position = startPosition; + } + else + { + GD.PrintErr($"found child node {node.Name} that is not a ScreenSaverGameAnimation"); + } + } + + shaderVelInit = getShaderVel(); + + setScreenSaversShown(GuiManagerGlobal.gameTitles); + + GuiManagerGlobal.instance.gameTitlesUpdated += () => + { + setScreenSaversShown(GuiManagerGlobal.gameTitles); + }; + } + + public override void _Process(double delta) + { + if(!playing) + { + setShaderVel(shaderVelInit); + return; + } + + setShaderVel(new Vector2(-scrollSpeed / 2.918f, shaderVelInit.Y)); + + gamesAnimationsContainer.Position += new Vector2((float) delta * -scrollSpeed * 100.0f, 0); + + if(gamesAnimationsContainer.Position.X < endPosition.X) + { + gamesAnimationsContainer.Position = startPosition; + } + } + + private void setScreenSaversShown(List games) + { + if(games == null) { return; } + + shownGameAnimationNodes.Clear(); + + foreach(ScreenSaverGameAnimation anim in gameAnimationNodes) + { + if(anim.game_name == "Background" || anim.game_name == "Background2") + { + shownGameAnimationNodes.Add(anim); + continue; + } + + foreach(DevcadeGame game in games) + { + string gameName = game.name; + // GD.Print($"found game: {game.name}"); + + if(anim.game_name == gameName) + { + GD.Print($"found matching anim: {anim.game_name}"); + shownGameAnimationNodes.Add(anim); + } + } + } + + endPosition = new Vector2(-1 * screenWidth * (shownGameAnimationNodes.Count - 1), 0); + + for (int i = 0; i < shownGameAnimationNodes.Count; i++) + { + ScreenSaverGameAnimation anim = shownGameAnimationNodes[i]; + + anim.Position = new Vector2(screenWidth * i, 0); + } + } + + private void setShaderVel(Vector2 v) + { + backgroundIcons.Material.Set("shader_parameter/direction", v); + } + + private Vector2 getShaderVel() + { + return backgroundIcons.Material.Get("shader_parameter/direction").AsVector2(); + } +} diff --git a/onboard/godot-frontend/guiManager/screensaver/Screensaver.cs.uid b/onboard/godot-frontend/guiManager/screensaver/Screensaver.cs.uid new file mode 100644 index 0000000..0b7fcc7 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/Screensaver.cs.uid @@ -0,0 +1 @@ +uid://bd2ibq7qygtdu diff --git a/onboard/godot-frontend/guiManager/screensaver/outline.gdshader b/onboard/godot-frontend/guiManager/screensaver/outline.gdshader new file mode 100644 index 0000000..7374787 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/outline.gdshader @@ -0,0 +1,24 @@ +shader_type canvas_item; + + +uniform int outline_width: hint_range(0, 50, 1) = 10; +uniform vec4 start_outline_color: source_color = vec4(1.0, 0.0, 0.0, 1.0); +uniform vec4 end_outline_color: source_color = vec4 (0.0, 1.0, 0.0, 1.0); + +void fragment() { + float max_alpha = 0.0; + for(int x = -outline_width; x < outline_width; x++) { + for(int y = -outline_width; y < outline_width; y++) { + float texture_x = float(x) * TEXTURE_PIXEL_SIZE.x; + float texture_y = float(y) * TEXTURE_PIXEL_SIZE.y; + vec2 offset = vec2(texture_x, texture_y); + + float alpha = texture(TEXTURE, UV + offset).a; + max_alpha = max(max_alpha, alpha); + } + } + + vec4 outline_color = mix(start_outline_color, end_outline_color, UV.x); + vec4 normal_color = texture(TEXTURE, UV); + COLOR = mix(max_alpha * outline_color, normal_color, normal_color.a); +} \ No newline at end of file diff --git a/onboard/godot-frontend/guiManager/screensaver/outline.gdshader.uid b/onboard/godot-frontend/guiManager/screensaver/outline.gdshader.uid new file mode 100644 index 0000000..b3fcb09 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/outline.gdshader.uid @@ -0,0 +1 @@ +uid://bh58gmppws1x4 diff --git a/onboard/godot-frontend/guiManager/screensaver/outline_shine.gdshader b/onboard/godot-frontend/guiManager/screensaver/outline_shine.gdshader new file mode 100644 index 0000000..3b591b6 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/outline_shine.gdshader @@ -0,0 +1,42 @@ +shader_type canvas_item; + + +uniform int outline_width: hint_range(0, 50, 1) = 10; +uniform vec4 start_outline_color: source_color = vec4(1.0, 0.0, 0.0, 1.0); +uniform vec4 end_outline_color: source_color = vec4 (0.0, 1.0, 0.0, 1.0); + +uniform float shine_speed = -0.2f; +uniform float shine_width = 0.1f; +uniform int num_segments = 10; + +void fragment() { + + float max_alpha = 0.0; + for(int x = -outline_width; x < outline_width; x++) { + for(int y = -outline_width; y < outline_width; y++) { + float texture_x = float(x) * TEXTURE_PIXEL_SIZE.x; + float texture_y = float(y) * TEXTURE_PIXEL_SIZE.y; + vec2 offset = vec2(texture_x, texture_y); + + float alpha = texture(TEXTURE, UV + offset).a; + max_alpha = max(max_alpha, alpha); + } + } + + vec4 outline_color = mix(start_outline_color, end_outline_color, UV.x); + vec4 normal_color = texture(TEXTURE, UV); + COLOR = mix(max_alpha * outline_color, normal_color, normal_color.a); + + float d = TIME * -shine_speed; + + for(int i = 0; i < num_segments; i++) { + if(UV.x > mod(d + (float(i) * shine_width / float(num_segments)), 1.0) && UV.x < mod(d + (float(i + 1) * shine_width / float(num_segments)), 1.0)) { + COLOR = mix(vec4(1.0,1.0,1.0,1.0 * COLOR.a), COLOR, 1.0 * float(num_segments - i) / float(num_segments)); + } + } + + vec4 tex_color = texture(TEXTURE, UV); + if(tex_color.r < 0.001 && tex_color.g < 0.0001 && tex_color.b < 0.0001 && tex_color.a > 0.001) { + COLOR = vec4(0.0,0.0,0.0,1.0); + } +} \ No newline at end of file diff --git a/onboard/godot-frontend/guiManager/screensaver/outline_shine.gdshader.uid b/onboard/godot-frontend/guiManager/screensaver/outline_shine.gdshader.uid new file mode 100644 index 0000000..227f128 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/outline_shine.gdshader.uid @@ -0,0 +1 @@ +uid://b8yw4hl6yvxdq diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver.tscn b/onboard/godot-frontend/guiManager/screensaver/screensaver.tscn new file mode 100644 index 0000000..2235ab0 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver.tscn @@ -0,0 +1,238 @@ +[gd_scene load_steps=19 format=3 uid="uid://dlupqaxwr7sdp"] + +[ext_resource type="Script" uid="uid://bd2ibq7qygtdu" path="res://guiManager/screensaver/Screensaver.cs" id="1_fcd5e"] +[ext_resource type="FontFile" uid="uid://cnha1ohwbh3ts" path="res://CSHAssets/VT323-Regular.ttf" id="2_fcd5e"] +[ext_resource type="Texture2D" uid="uid://gsf5hvgxlwu8" path="res://CSHAssets/OnboardBackgroundGradient.png" id="2_s0yr3"] +[ext_resource type="Script" uid="uid://ei3nslcsxscs" path="res://guiManager/FlashingRichTextLabel.cs" id="3_anfla"] +[ext_resource type="Shader" uid="uid://b1box1f256y3s" path="res://guiManager/screensaver/screensaverCSHIcons.gdshader" id="3_s0yr3"] +[ext_resource type="Script" uid="uid://3dod6y75r7mb" path="res://guiManager/screensaver/ScreenSaverGameAnimation.cs" id="4_q4yft"] +[ext_resource type="Texture2D" uid="uid://d4nlo4q03fi77" path="res://CSHAssets/CSH.png" id="5_d5f7k"] +[ext_resource type="PackedScene" uid="uid://camd641ivrd43" path="res://guiManager/screensaver/screensaver_games_animations/worldOfWallHoppers/WorldOfWallHoppersScreenSaver.tscn" id="7_s0yr3"] +[ext_resource type="PackedScene" uid="uid://rf2ubr76htca" path="res://guiManager/screensaver/screensaver_games_animations/BombOmbSquad/BombOmbSquadScreenSaver.tscn" id="7_usgw7"] +[ext_resource type="PackedScene" uid="uid://bnpwhpnwx1dh2" path="res://guiManager/screensaver/screensaver_games_animations/CubeClimber/cube_climber_game_animation.tscn" id="8_xxjuo"] +[ext_resource type="PackedScene" uid="uid://crjqi1vferh30" path="res://guiManager/screensaver/screensaver_games_animations/DevcadeDebugger/devcade_debugger_game_animation.tscn" id="9_57oxp"] +[ext_resource type="PackedScene" uid="uid://chkoyuus5i6o6" path="res://guiManager/screensaver/screensaver_games_animations/lunarLander/lunar_lander_game_animation.tscn" id="10_57oxp"] +[ext_resource type="Texture2D" uid="uid://lqrfprpd7kh3" path="res://CSHAssets/transparent-logo.png" id="10_n0j55"] +[ext_resource type="Script" uid="uid://62sckdwbwl8r" path="res://GUIs/orignial/DevcadeIcon.cs" id="11_dt653"] +[ext_resource type="PackedScene" uid="uid://dv35g6sqgpna8" path="res://guiManager/screensaver/screensaver_games_animations/ThreeDTetris/three_d_tetris_game_animation.tscn" id="11_h8c1n"] +[ext_resource type="Texture2D" uid="uid://bj333tajmq7mt" path="res://CSHAssets/transparent-dev-logo.png" id="12_spuds"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_oh2v1"] +shader = ExtResource("3_s0yr3") +shader_parameter/scale = 7.27 +shader_parameter/direction = Vector2(-0.4, -0.4) +shader_parameter/x_offset = 1.0 +shader_parameter/alphaScale = 0.72 + +[sub_resource type="Theme" id="Theme_q4yft"] + +[node name="Control" type="Control" node_paths=PackedStringArray("gamesAnimationsContainer", "backgroundIcons")] +z_index = 4096 +y_sort_enabled = true +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -1.0 +offset_right = -1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_fcd5e") +scrollSpeed = 2.0 +gamesAnimationsContainer = NodePath("gameAnimationsContainer") +backgroundIcons = NodePath("gameAnimationsContainer/background/background/backgroundCSHIcons") + +[node name="gameAnimationsContainer" type="Control" parent="."] +z_as_relative = false +y_sort_enabled = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="background" type="Control" parent="gameAnimationsContainer"] +z_as_relative = false +y_sort_enabled = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("4_q4yft") +game_name = "Background" + +[node name="background" type="TextureRect" parent="gameAnimationsContainer/background"] +z_index = 1000 +z_as_relative = false +y_sort_enabled = true +custom_minimum_size = Vector2(900, 0) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +texture = ExtResource("2_s0yr3") +expand_mode = 5 + +[node name="backgroundCSHIcons" type="TextureRect" parent="gameAnimationsContainer/background/background"] +z_index = 1500 +z_as_relative = false +y_sort_enabled = true +texture_repeat = 2 +material = SubResource("ShaderMaterial_oh2v1") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("5_d5f7k") +expand_mode = 1 + +[node name="MarginContainer" type="MarginContainer" parent="gameAnimationsContainer/background"] +layout_mode = 1 +anchors_preset = 10 +anchor_right = 1.0 +offset_bottom = 464.444 +grow_horizontal = 2 +theme_override_constants/margin_left = 300 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 300 +theme_override_constants/margin_bottom = 10 + +[node name="DevcadeIcon" type="TextureRect" parent="gameAnimationsContainer/background/MarginContainer"] +z_index = 4096 +z_as_relative = false +y_sort_enabled = true +layout_mode = 2 +texture = ExtResource("10_n0j55") +expand_mode = 5 +script = ExtResource("11_dt653") +prodTexture = ExtResource("10_n0j55") +devTexture = ExtResource("12_spuds") + +[node name="WorldOfWallHoppersScreenSaver" parent="gameAnimationsContainer" instance=ExtResource("7_s0yr3")] +z_index = 4000 +z_as_relative = false +y_sort_enabled = true +layout_mode = 1 + +[node name="BombOmbSquadScreenSaver" parent="gameAnimationsContainer" instance=ExtResource("7_usgw7")] +z_index = 4000 +z_as_relative = false +y_sort_enabled = true +layout_mode = 1 + +[node name="CubeClimberScreenSaver" parent="gameAnimationsContainer" instance=ExtResource("8_xxjuo")] +layout_mode = 1 + +[node name="LunarLanderScreenSaver" parent="gameAnimationsContainer" instance=ExtResource("10_57oxp")] +layout_mode = 1 + +[node name="DevcadeDebuggerScreenSaver" parent="gameAnimationsContainer" instance=ExtResource("9_57oxp")] +layout_mode = 1 + +[node name="ThreeDTetrisScreenSaver" parent="gameAnimationsContainer" instance=ExtResource("11_h8c1n")] +layout_mode = 1 + +[node name="background2" type="Control" parent="gameAnimationsContainer"] +z_as_relative = false +y_sort_enabled = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("4_q4yft") +game_name = "Background2" + +[node name="background" type="TextureRect" parent="gameAnimationsContainer/background2"] +z_index = 1000 +z_as_relative = false +y_sort_enabled = true +custom_minimum_size = Vector2(900, 0) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +texture = ExtResource("2_s0yr3") +expand_mode = 5 + +[node name="backgroundCSHIcons" type="TextureRect" parent="gameAnimationsContainer/background2/background"] +z_index = 1500 +z_as_relative = false +y_sort_enabled = true +texture_repeat = 2 +material = SubResource("ShaderMaterial_oh2v1") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("5_d5f7k") +expand_mode = 1 + +[node name="MarginContainer" type="MarginContainer" parent="gameAnimationsContainer/background2"] +layout_mode = 1 +anchors_preset = 10 +anchor_right = 1.0 +offset_bottom = 742.222 +grow_horizontal = 2 +theme_override_constants/margin_left = 300 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 300 +theme_override_constants/margin_bottom = 10 + +[node name="DevcadeIcon" type="TextureRect" parent="gameAnimationsContainer/background2/MarginContainer"] +z_index = 4096 +z_as_relative = false +y_sort_enabled = true +layout_mode = 2 +texture = ExtResource("10_n0j55") +expand_mode = 5 +script = ExtResource("11_dt653") +prodTexture = ExtResource("10_n0j55") +devTexture = ExtResource("12_spuds") + +[node name="continueText" type="RichTextLabel" parent="."] +z_index = 4096 +z_as_relative = false +y_sort_enabled = true +layout_mode = 1 +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -711.0 +offset_bottom = 4.0 +grow_horizontal = 2 +grow_vertical = 0 +theme = SubResource("Theme_q4yft") +theme_override_constants/outline_size = 100 +theme_override_fonts/normal_font = ExtResource("2_fcd5e") +theme_override_fonts/mono_font = ExtResource("2_fcd5e") +theme_override_fonts/italics_font = ExtResource("2_fcd5e") +theme_override_fonts/bold_italics_font = ExtResource("2_fcd5e") +theme_override_fonts/bold_font = ExtResource("2_fcd5e") +theme_override_font_sizes/bold_italics_font_size = 325 +theme_override_font_sizes/italics_font_size = 325 +theme_override_font_sizes/mono_font_size = 325 +theme_override_font_sizes/normal_font_size = 325 +theme_override_font_sizes/bold_font_size = 325 +text = "Press any Button to Resume" +horizontal_alignment = 1 +vertical_alignment = 1 +script = ExtResource("3_anfla") +end_color = Color(1, 1, 1, 0) diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaverCSHIcons.gdshader b/onboard/godot-frontend/guiManager/screensaver/screensaverCSHIcons.gdshader new file mode 100644 index 0000000..6437fb5 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaverCSHIcons.gdshader @@ -0,0 +1,22 @@ +shader_type canvas_item; + +uniform float scale = 1.0; +uniform vec2 direction; + +uniform float x_offset = 0.0; + +uniform float alphaScale = 1.0; + +void fragment() { + // Called for every pixel the material is visible on. + float screen_width = 1.0 / SCREEN_PIXEL_SIZE.r; + float screen_height = 1.0 / SCREEN_PIXEL_SIZE.g; + + // delta movement value + float dx = -direction.r * mod(TIME, 1.0 / direction.r); + float dy = direction.g * mod(TIME, 1.0 / direction.g); + + COLOR = texture(TEXTURE, fract(SCREEN_UV * vec2(scale, scale * (screen_height / screen_width))) + vec2(dx + x_offset, dy)); + + COLOR.a *= alphaScale; +} diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaverCSHIcons.gdshader.uid b/onboard/godot-frontend/guiManager/screensaver/screensaverCSHIcons.gdshader.uid new file mode 100644 index 0000000..6b56b6d --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaverCSHIcons.gdshader.uid @@ -0,0 +1 @@ +uid://b1box1f256y3s diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/BombOmbSquad/BombOmbSquad.ogv b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/BombOmbSquad/BombOmbSquad.ogv new file mode 100644 index 0000000..a6435af Binary files /dev/null and b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/BombOmbSquad/BombOmbSquad.ogv differ diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/BombOmbSquad/BombOmbSquad.ogv.uid b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/BombOmbSquad/BombOmbSquad.ogv.uid new file mode 100644 index 0000000..9981cd2 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/BombOmbSquad/BombOmbSquad.ogv.uid @@ -0,0 +1 @@ +uid://cufvdkpq44xob diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/BombOmbSquad/BombOmbSquadScreenSaver.tscn b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/BombOmbSquad/BombOmbSquadScreenSaver.tscn new file mode 100644 index 0000000..6d7f283 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/BombOmbSquad/BombOmbSquadScreenSaver.tscn @@ -0,0 +1,40 @@ +[gd_scene load_steps=4 format=3 uid="uid://rf2ubr76htca"] + +[ext_resource type="Script" uid="uid://3dod6y75r7mb" path="res://guiManager/screensaver/ScreenSaverGameAnimation.cs" id="1_wfpkx"] +[ext_resource type="VideoStream" uid="uid://cufvdkpq44xob" path="res://guiManager/screensaver/screensaver_games_animations/BombOmbSquad/BombOmbSquad.ogv" id="2_2r8ub"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2r8ub"] +bg_color = Color(0, 0, 0, 1) + +[node name="BombOmbSquadScreenSaver" type="Control" node_paths=PackedStringArray("videoStreamPlayer")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_wfpkx") +game_name = "Bob-omb Squad" +videoStreamPlayer = NodePath("Panel/VideoStreamPlayer") + +[node name="Panel" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_2r8ub") + +[node name="VideoStreamPlayer" type="VideoStreamPlayer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +stream = ExtResource("2_2r8ub") +autoplay = true +paused = true +expand = true +loop = true diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/CubeClimber/cube_climber.ogv b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/CubeClimber/cube_climber.ogv new file mode 100644 index 0000000..41b367f Binary files /dev/null and b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/CubeClimber/cube_climber.ogv differ diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/CubeClimber/cube_climber.ogv.uid b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/CubeClimber/cube_climber.ogv.uid new file mode 100644 index 0000000..d90ad35 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/CubeClimber/cube_climber.ogv.uid @@ -0,0 +1 @@ +uid://b0tiady62cu3w diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/CubeClimber/cube_climber_game_animation.tscn b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/CubeClimber/cube_climber_game_animation.tscn new file mode 100644 index 0000000..a0d0583 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/CubeClimber/cube_climber_game_animation.tscn @@ -0,0 +1,40 @@ +[gd_scene load_steps=4 format=3 uid="uid://bnpwhpnwx1dh2"] + +[ext_resource type="Script" uid="uid://3dod6y75r7mb" path="res://guiManager/screensaver/ScreenSaverGameAnimation.cs" id="1_jp3t1"] +[ext_resource type="VideoStream" uid="uid://b0tiady62cu3w" path="res://guiManager/screensaver/screensaver_games_animations/CubeClimber/cube_climber.ogv" id="2_2eafm"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2ko63"] +bg_color = Color(0, 0, 0, 1) + +[node name="CubeClimberScreenSaver" type="Control" node_paths=PackedStringArray("videoStreamPlayer")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_jp3t1") +game_name = "Cube Climber" +videoStreamPlayer = NodePath("Panel/VideoStreamPlayer") + +[node name="Panel" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_2ko63") + +[node name="VideoStreamPlayer" type="VideoStreamPlayer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +stream = ExtResource("2_2eafm") +autoplay = true +paused = true +expand = true +loop = true diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/DevcadeDebugger/devcade_debugger.ogv b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/DevcadeDebugger/devcade_debugger.ogv new file mode 100644 index 0000000..5aa2f76 Binary files /dev/null and b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/DevcadeDebugger/devcade_debugger.ogv differ diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/DevcadeDebugger/devcade_debugger.ogv.uid b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/DevcadeDebugger/devcade_debugger.ogv.uid new file mode 100644 index 0000000..1466462 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/DevcadeDebugger/devcade_debugger.ogv.uid @@ -0,0 +1 @@ +uid://clr5tv712y28l diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/DevcadeDebugger/devcade_debugger_game_animation.tscn b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/DevcadeDebugger/devcade_debugger_game_animation.tscn new file mode 100644 index 0000000..b0e5d5b --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/DevcadeDebugger/devcade_debugger_game_animation.tscn @@ -0,0 +1,40 @@ +[gd_scene load_steps=4 format=3 uid="uid://crjqi1vferh30"] + +[ext_resource type="Script" uid="uid://3dod6y75r7mb" path="res://guiManager/screensaver/ScreenSaverGameAnimation.cs" id="1_nls3r"] +[ext_resource type="VideoStream" uid="uid://clr5tv712y28l" path="res://guiManager/screensaver/screensaver_games_animations/DevcadeDebugger/devcade_debugger.ogv" id="2_j4flq"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2ko63"] +bg_color = Color(0, 0, 0, 1) + +[node name="DevcadeDebuggerScreenSaver" type="Control" node_paths=PackedStringArray("videoStreamPlayer")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_nls3r") +game_name = "Devcade Test" +videoStreamPlayer = NodePath("Panel/VideoStreamPlayer") + +[node name="Panel" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_2ko63") + +[node name="VideoStreamPlayer" type="VideoStreamPlayer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +stream = ExtResource("2_j4flq") +autoplay = true +paused = true +expand = true +loop = true diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/ThreeDTetris/three_d_tetris.ogv b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/ThreeDTetris/three_d_tetris.ogv new file mode 100644 index 0000000..1e65477 Binary files /dev/null and b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/ThreeDTetris/three_d_tetris.ogv differ diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/ThreeDTetris/three_d_tetris.ogv.uid b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/ThreeDTetris/three_d_tetris.ogv.uid new file mode 100644 index 0000000..e7ce573 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/ThreeDTetris/three_d_tetris.ogv.uid @@ -0,0 +1 @@ +uid://cwsdl1jgl7oif diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/ThreeDTetris/three_d_tetris_game_animation.tscn b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/ThreeDTetris/three_d_tetris_game_animation.tscn new file mode 100644 index 0000000..254bb7e --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/ThreeDTetris/three_d_tetris_game_animation.tscn @@ -0,0 +1,40 @@ +[gd_scene load_steps=4 format=3 uid="uid://dv35g6sqgpna8"] + +[ext_resource type="Script" uid="uid://3dod6y75r7mb" path="res://guiManager/screensaver/ScreenSaverGameAnimation.cs" id="1_epw25"] +[ext_resource type="VideoStream" uid="uid://cwsdl1jgl7oif" path="res://guiManager/screensaver/screensaver_games_animations/ThreeDTetris/three_d_tetris.ogv" id="2_dq48y"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2ko63"] +bg_color = Color(0, 0, 0, 1) + +[node name="ThreeDTetrisScreenSaver" type="Control" node_paths=PackedStringArray("videoStreamPlayer")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_epw25") +game_name = "ThreeDTetris" +videoStreamPlayer = NodePath("Panel/VideoStreamPlayer") + +[node name="Panel" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_2ko63") + +[node name="VideoStreamPlayer" type="VideoStreamPlayer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +stream = ExtResource("2_dq48y") +autoplay = true +paused = true +expand = true +loop = true diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/lunarLander/lunar_lander.ogv b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/lunarLander/lunar_lander.ogv new file mode 100644 index 0000000..a795b0e Binary files /dev/null and b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/lunarLander/lunar_lander.ogv differ diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/lunarLander/lunar_lander.ogv.uid b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/lunarLander/lunar_lander.ogv.uid new file mode 100644 index 0000000..879ca22 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/lunarLander/lunar_lander.ogv.uid @@ -0,0 +1 @@ +uid://csfsb2jqoopco diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/lunarLander/lunar_lander_game_animation.tscn b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/lunarLander/lunar_lander_game_animation.tscn new file mode 100644 index 0000000..2bacde0 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/lunarLander/lunar_lander_game_animation.tscn @@ -0,0 +1,40 @@ +[gd_scene load_steps=4 format=3 uid="uid://chkoyuus5i6o6"] + +[ext_resource type="Script" uid="uid://3dod6y75r7mb" path="res://guiManager/screensaver/ScreenSaverGameAnimation.cs" id="1_rfur2"] +[ext_resource type="VideoStream" uid="uid://csfsb2jqoopco" path="res://guiManager/screensaver/screensaver_games_animations/lunarLander/lunar_lander.ogv" id="2_e0ae2"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2ko63"] +bg_color = Color(0, 0, 0, 1) + +[node name="LunarLanderScreenSaver" type="Control" node_paths=PackedStringArray("videoStreamPlayer")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_rfur2") +game_name = "Lunar Lander" +videoStreamPlayer = NodePath("Panel/VideoStreamPlayer") + +[node name="Panel" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_2ko63") + +[node name="VideoStreamPlayer" type="VideoStreamPlayer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +stream = ExtResource("2_e0ae2") +autoplay = true +paused = true +expand = true +loop = true diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/temp_game_animation.tscn b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/temp_game_animation.tscn new file mode 100644 index 0000000..643a7ba --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/temp_game_animation.tscn @@ -0,0 +1,37 @@ +[gd_scene load_steps=3 format=3 uid="uid://dir1di2qk6g4o"] + +[ext_resource type="Script" uid="uid://3dod6y75r7mb" path="res://guiManager/screensaver/ScreenSaverGameAnimation.cs" id="1_2ko63"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2ko63"] +bg_color = Color(0.454314, 0.165774, 0.263227, 1) + +[node name="TempGameAnimation" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_2ko63") + +[node name="Panel" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_2ko63") + +[node name="RichTextLabel" type="RichTextLabel" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/normal_font_size = 161 +text = "??INSERT GAME PLAY HERE??" +horizontal_alignment = 1 +vertical_alignment = 1 +metadata/_edit_use_anchors_ = true diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/worldOfWallHoppers/WorldOfWallHoppersScreenSaver.tscn b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/worldOfWallHoppers/WorldOfWallHoppersScreenSaver.tscn new file mode 100644 index 0000000..b725d57 --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/worldOfWallHoppers/WorldOfWallHoppersScreenSaver.tscn @@ -0,0 +1,40 @@ +[gd_scene load_steps=4 format=3 uid="uid://camd641ivrd43"] + +[ext_resource type="Script" uid="uid://3dod6y75r7mb" path="res://guiManager/screensaver/ScreenSaverGameAnimation.cs" id="1_d5uki"] +[ext_resource type="VideoStream" uid="uid://djrj50vajtc3m" path="res://guiManager/screensaver/screensaver_games_animations/worldOfWallHoppers/world_of_wallhoppers_devcade_screensaver.ogv" id="2_pvo00"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pvo00"] +bg_color = Color(0, 0, 0, 1) + +[node name="WorldOfWallHoppersScreenSaver" type="Control" node_paths=PackedStringArray("videoStreamPlayer")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_d5uki") +game_name = "World of Wallhoppers" +videoStreamPlayer = NodePath("Panel/VideoStreamPlayer") + +[node name="Panel" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_pvo00") + +[node name="VideoStreamPlayer" type="VideoStreamPlayer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +stream = ExtResource("2_pvo00") +autoplay = true +paused = true +expand = true +loop = true diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/worldOfWallHoppers/world_of_wallhoppers_devcade_screensaver.ogv b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/worldOfWallHoppers/world_of_wallhoppers_devcade_screensaver.ogv new file mode 100644 index 0000000..1ed0d83 Binary files /dev/null and b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/worldOfWallHoppers/world_of_wallhoppers_devcade_screensaver.ogv differ diff --git a/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/worldOfWallHoppers/world_of_wallhoppers_devcade_screensaver.ogv.uid b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/worldOfWallHoppers/world_of_wallhoppers_devcade_screensaver.ogv.uid new file mode 100644 index 0000000..6dac18f --- /dev/null +++ b/onboard/godot-frontend/guiManager/screensaver/screensaver_games_animations/worldOfWallHoppers/world_of_wallhoppers_devcade_screensaver.ogv.uid @@ -0,0 +1 @@ +uid://djrj50vajtc3m diff --git a/onboard/godot-frontend/icon.svg b/onboard/godot-frontend/icon.svg new file mode 100644 index 0000000..9d8b7fa --- /dev/null +++ b/onboard/godot-frontend/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/onboard/godot-frontend/icon.svg.import b/onboard/godot-frontend/icon.svg.import new file mode 100644 index 0000000..cb76e7b --- /dev/null +++ b/onboard/godot-frontend/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c73kxs0gtawat" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/onboard/godot-frontend/main.tscn b/onboard/godot-frontend/main.tscn new file mode 100644 index 0000000..c275bed --- /dev/null +++ b/onboard/godot-frontend/main.tscn @@ -0,0 +1,98 @@ +[gd_scene format=3 uid="uid://br86segmlm7yg"] + +[ext_resource type="Script" uid="uid://cpnqnrrdr0pbv" path="res://guiManager/GuiManager.cs" id="1_ig7tw"] +[ext_resource type="PackedScene" uid="uid://bs2l3u3gpwk3a" path="res://GUIs/orignial/Original.tscn" id="2_lquwl"] +[ext_resource type="Texture2D" uid="uid://cm8hlcb1in1rd" path="res://CSHAssets/transparent-logo-white.png" id="3_1bvp3"] +[ext_resource type="SpriteFrames" uid="uid://bfm4pfog3lttd" path="res://CSHAssets/loading animation/loadingAnimation.tres" id="4_272bh"] +[ext_resource type="PackedScene" uid="uid://dlupqaxwr7sdp" path="res://guiManager/screensaver/screensaver.tscn" id="5_lquwl"] +[ext_resource type="FontFile" uid="uid://cnha1ohwbh3ts" path="res://CSHAssets/VT323-Regular.ttf" id="7_5vw27"] +[ext_resource type="Script" uid="uid://bft2wse50dh2e" path="res://FpsLabel.cs" id="7_272bh"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1bvp3"] +bg_color = Color(0.0235294, 0.0235294, 0.0235294, 1) + +[node name="main_node" type="Control" unique_id=965575267 node_paths=PackedStringArray("loadingScreen", "loadingAnimation", "screenSaver")] +process_mode = 1 +y_sort_enabled = true +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_ig7tw") +initialGuiScene = ExtResource("2_lquwl") +loadingScreen = NodePath("loadingScreen") +loadingAnimation = NodePath("loadingScreen/VBoxContainer/CenterContainer/loadingAnimation") +screenSaver = NodePath("screensaver") + +[node name="loadingScreen" type="Panel" parent="." unique_id=1605447585] +visible = false +top_level = true +z_index = 4096 +y_sort_enabled = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_1bvp3") + +[node name="VBoxContainer" type="VBoxContainer" parent="loadingScreen" unique_id=200168907] +y_sort_enabled = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="MarginContainer" type="MarginContainer" parent="loadingScreen/VBoxContainer" unique_id=1422817411] +y_sort_enabled = true +layout_mode = 2 +theme_override_constants/margin_left = 50 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 50 +theme_override_constants/margin_bottom = 10 + +[node name="TextureRect" type="TextureRect" parent="loadingScreen/VBoxContainer/MarginContainer" unique_id=437073334] +y_sort_enabled = true +layout_mode = 2 +texture = ExtResource("3_1bvp3") +expand_mode = 5 + +[node name="CenterContainer" type="CenterContainer" parent="loadingScreen/VBoxContainer" unique_id=729470700] +y_sort_enabled = true +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 6 + +[node name="loadingAnimation" type="AnimatedSprite2D" parent="loadingScreen/VBoxContainer/CenterContainer" unique_id=1598772190] +y_sort_enabled = true +scale = Vector2(2, 2) +sprite_frames = ExtResource("4_272bh") +speed_scale = 12.0 + +[node name="screensaver" parent="." unique_id=1558778186 instance=ExtResource("5_lquwl")] +layout_mode = 1 + +[node name="fpsLabel" type="Label" parent="." unique_id=496146832] +top_level = true +z_index = 4096 +z_as_relative = false +y_sort_enabled = true +custom_minimum_size = Vector2(200, 200) +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -40.0 +offset_bottom = 23.0 +grow_horizontal = 0 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 20 +theme_override_fonts/font = ExtResource("7_5vw27") +theme_override_font_sizes/font_size = 110 +text = "Null" +script = ExtResource("7_272bh") diff --git a/onboard/godot-frontend/notification-system/NotificationWindow.cs b/onboard/godot-frontend/notification-system/NotificationWindow.cs new file mode 100644 index 0000000..2e24c08 --- /dev/null +++ b/onboard/godot-frontend/notification-system/NotificationWindow.cs @@ -0,0 +1,46 @@ +using Godot; + +public partial class NotificationWindow : Window +{ + private Vector2I correct_position; + + public override void _EnterTree() + { + this.CloseRequested += this.QueueFree; + + Vector2I screenSize = DisplayServer.ScreenGetSize(); + correct_position = screenSize - this.Size - new Vector2I((screenSize.X - this.Size.X) / 2, 0); + this.Position = correct_position; + + this.AlwaysOnTop = true; + } + + private int id; + public override void _Ready() + { + this.id = this.GetWindowId(); + this.show(); + } + + public override void _ExitTree() + { + // when killed kill the main onboard window/process too + GetTree().Quit(0); + } + + public void show() + { + if(Visible) { return; } + + // this.Show(); + // DisplayServer.WindowMoveToForeground(id); + // this.GrabFocus(); + } + + public void hide() + { + if(!Visible) { return; } + + this.hide(); + } +} diff --git a/onboard/godot-frontend/notification-system/NotificationWindow.cs.uid b/onboard/godot-frontend/notification-system/NotificationWindow.cs.uid new file mode 100644 index 0000000..ad9843f --- /dev/null +++ b/onboard/godot-frontend/notification-system/NotificationWindow.cs.uid @@ -0,0 +1 @@ +uid://bqco2vfdm8n1c diff --git a/onboard/godot-frontend/notification-system/SupervisorButtonHint.cs b/onboard/godot-frontend/notification-system/SupervisorButtonHint.cs new file mode 100644 index 0000000..ea9fe6c --- /dev/null +++ b/onboard/godot-frontend/notification-system/SupervisorButtonHint.cs @@ -0,0 +1,30 @@ +using Godot; +using onboard.devcade; + +public partial class SupervisorButtonHint : MarginContainer +{ + [Export] + private double inactiveMaxTime = 60.0; // time in seconds + double time = 0.0; + + public override void _Ready() + { + this.Hide(); + } + + public override void _Process(double delta) + { + if(Input.IsAnythingPressed() || !Client.gameLauched) + { + time = 0.0; + this.Hide(); + return; + } + + time+=delta; + if(time >= inactiveMaxTime) + { + this.Show(); + } + } +} diff --git a/onboard/godot-frontend/notification-system/SupervisorButtonHint.cs.uid b/onboard/godot-frontend/notification-system/SupervisorButtonHint.cs.uid new file mode 100644 index 0000000..323449e --- /dev/null +++ b/onboard/godot-frontend/notification-system/SupervisorButtonHint.cs.uid @@ -0,0 +1 @@ +uid://daxmr7hxidxcx diff --git a/onboard/godot-frontend/notification-system/VolumeBar.cs b/onboard/godot-frontend/notification-system/VolumeBar.cs new file mode 100644 index 0000000..6627471 --- /dev/null +++ b/onboard/godot-frontend/notification-system/VolumeBar.cs @@ -0,0 +1,101 @@ +using System.Diagnostics; +using System.Net; +using Godot; +using onboard.util; + +public partial class VolumeBar : ProgressBar +{ + private onboard.util.Logger LOG = Log.get(nameof(VolumeBar)); + + [Export] + NotificationWindow window; + + [Export] + Label percent_text; + + private Process process = new Process { + StartInfo = new ProcessStartInfo + { + FileName = "pamixer", + Arguments = $"--get-volume", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }, + EnableRaisingEvents = true + }; + + private int last_volume = -1; + + [Export] + double secondsBetweenPolls = 0.2; + private double seconds = 0.0; + + [Export] + double lingerTime = 1.0; + private double lingerSec; + public override void _Ready() + { + lingerSec = lingerTime; + + this.Visible = false; + + last_volume = getVolume(); + set_text(); + } + + public override void _Process(double delta) + { + seconds += delta; + lingerSec += delta; + + if(lingerSec >= lingerTime) + { + this.Visible = false; + lingerSec = lingerTime; + } + + if(seconds < secondsBetweenPolls) + { + return; + } + seconds = 0.0; + + int volume = getVolume(); + + if(volume != last_volume) + { + window.show(); + this.Visible = true; + lingerSec = 0.0; + } + + last_volume = volume; + + this.Value = getVolume() / 100.0f; + set_text(); + } + + private int getVolume() + { + bool started = process.Start(); + + if(!started) + { + LOG.Warn("command not run"); + return -1; + } + + process.WaitForExit(); + + string stdout = process.StandardOutput.ReadToEnd(); + + return stdout.ToInt(); + } + + private void set_text() + { + percent_text.Text = ((int)(this.Value * 100)).ToString() + "%"; + } +} diff --git a/onboard/godot-frontend/notification-system/VolumeBar.cs.uid b/onboard/godot-frontend/notification-system/VolumeBar.cs.uid new file mode 100644 index 0000000..4a0fbc5 --- /dev/null +++ b/onboard/godot-frontend/notification-system/VolumeBar.cs.uid @@ -0,0 +1 @@ +uid://br8tslvjy5jci diff --git a/onboard/godot-frontend/notification-system/notificationWindow.tscn b/onboard/godot-frontend/notification-system/notificationWindow.tscn new file mode 100644 index 0000000..7366710 --- /dev/null +++ b/onboard/godot-frontend/notification-system/notificationWindow.tscn @@ -0,0 +1,120 @@ +[gd_scene load_steps=10 format=3 uid="uid://cswy8mipsaud4"] + +[ext_resource type="Script" uid="uid://bqco2vfdm8n1c" path="res://notification-system/NotificationWindow.cs" id="1_65dmo"] +[ext_resource type="Script" uid="uid://br8tslvjy5jci" path="res://notification-system/VolumeBar.cs" id="2_65dmo"] +[ext_resource type="Shader" uid="uid://bkjuh1qsaperx" path="res://notification-system/volume_bar_text.gdshader" id="2_evtjt"] +[ext_resource type="FontFile" uid="uid://cnha1ohwbh3ts" path="res://CSHAssets/VT323-Regular.ttf" id="2_w84xk"] +[ext_resource type="Script" uid="uid://daxmr7hxidxcx" path="res://notification-system/SupervisorButtonHint.cs" id="5_kn466"] +[ext_resource type="PackedScene" uid="uid://bglh47iehhwwm" path="res://CSHAssets/button/devcade_button.tscn" id="5_trcrt"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_evtjt"] +bg_color = Color(0, 0, 0, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_w84xk"] +bg_color = Color(1, 1, 1, 1) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_w84xk"] +shader = ExtResource("2_evtjt") + +[node name="NotificationWindow" type="Window"] +transparent_bg = true +size = Vector2i(1000, 200) +exclusive = true +unresizable = true +borderless = true +always_on_top = true +transparent = true +exclude_from_capture = true +script = ExtResource("1_65dmo") + +[node name="MarginContainer" type="MarginContainer" parent="."] +texture_filter = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="volume" type="MarginContainer" parent="MarginContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 50 +theme_override_constants/margin_top = 70 +theme_override_constants/margin_right = 50 +theme_override_constants/margin_bottom = 75 + +[node name="volumeBar" type="ProgressBar" parent="MarginContainer/volume" node_paths=PackedStringArray("window", "percent_text")] +layout_mode = 2 +size_flags_vertical = 1 +theme_override_styles/background = SubResource("StyleBoxFlat_evtjt") +theme_override_styles/fill = SubResource("StyleBoxFlat_w84xk") +max_value = 1.0 +value = 0.35 +show_percentage = false +script = ExtResource("2_65dmo") +window = NodePath("../../..") +percent_text = NodePath("Label") + +[node name="Label" type="Label" parent="MarginContainer/volume/volumeBar"] +material = SubResource("ShaderMaterial_w84xk") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_fonts/font = ExtResource("2_w84xk") +theme_override_font_sizes/font_size = 60 +text = "100%" +horizontal_alignment = 1 + +[node name="supervisor button hint" type="MarginContainer" parent="MarginContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 10 +theme_override_constants/margin_bottom = 10 +script = ExtResource("5_kn466") +inactiveMaxTime = 180.0 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/supervisor button hint"] +clip_contents = true +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/supervisor button hint/VBoxContainer"] +layout_mode = 2 +theme_override_fonts/font = ExtResource("2_w84xk") +theme_override_font_sizes/font_size = 49 +text = "Press and Hold the Two black buttons to Force Quit" +horizontal_alignment = 1 + +[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/supervisor button hint/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/margin_left = 170 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 170 +theme_override_constants/margin_bottom = 5 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/supervisor button hint/VBoxContainer/MarginContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="CenterContainer" type="CenterContainer" parent="MarginContainer/supervisor button hint/VBoxContainer/MarginContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 4 + +[node name="DevcadeButton" parent="MarginContainer/supervisor button hint/VBoxContainer/MarginContainer/HBoxContainer/CenterContainer" instance=ExtResource("5_trcrt")] +texture_filter = 3 +scale = Vector2(4.37556, 4.37556) + +[node name="CenterContainer2" type="CenterContainer" parent="MarginContainer/supervisor button hint/VBoxContainer/MarginContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 4 + +[node name="DevcadeButton" parent="MarginContainer/supervisor button hint/VBoxContainer/MarginContainer/HBoxContainer/CenterContainer2" instance=ExtResource("5_trcrt")] +texture_filter = 3 +scale = Vector2(4.37556, 4.37556) diff --git a/onboard/godot-frontend/notification-system/volume_bar_text.gdshader b/onboard/godot-frontend/notification-system/volume_bar_text.gdshader new file mode 100644 index 0000000..f0434b3 --- /dev/null +++ b/onboard/godot-frontend/notification-system/volume_bar_text.gdshader @@ -0,0 +1,8 @@ +shader_type canvas_item; +uniform sampler2D existing_screen_texture: hint_screen_texture, filter_linear_mipmap; + +void fragment() { + vec3 new_color = texture(existing_screen_texture, SCREEN_UV, 0.0).rgb; + new_color = vec3(1.0) - new_color; + COLOR.rgb = new_color; +} diff --git a/onboard/godot-frontend/notification-system/volume_bar_text.gdshader.uid b/onboard/godot-frontend/notification-system/volume_bar_text.gdshader.uid new file mode 100644 index 0000000..176ceb0 --- /dev/null +++ b/onboard/godot-frontend/notification-system/volume_bar_text.gdshader.uid @@ -0,0 +1 @@ +uid://bkjuh1qsaperx diff --git a/onboard/godot-frontend/project.godot b/onboard/godot-frontend/project.godot new file mode 100644 index 0000000..6271347 --- /dev/null +++ b/onboard/godot-frontend/project.godot @@ -0,0 +1,270 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[animation] + +compatibility/default_parent_skeleton_in_mesh_instance_3d=true + +[application] + +config/name="godot-frontend" +run/main_scene="uid://br86segmlm7yg" +run/enable_alt_space_menu=true +config/features=PackedStringArray("4.7", "C#", "GL Compatibility") +run/low_processor_mode=true +boot_splash/bg_color=Color(0, 0, 0, 1) +boot_splash/image="uid://cm8hlcb1in1rd" +config/icon="res://icon.svg" + +[autoload] + +AutoLoad="*res://AutoLoad.cs" +GuiManagerGlobal="*res://guiManager/GuiManagerGlobal.cs" + +[debug] + +file_logging/enable_file_logging=true +file_logging/max_log_files=10 + +[display] + +window/size/viewport_width=2160 +window/size/viewport_height=3840 +window/size/borderless=true +window/subwindows/embed_subwindows=false +window/stretch/mode="canvas_items" +window/per_pixel_transparency/allowed=true +window/viewport_width=900 +window/viewport_height=1600 +window/size/width=900 +window/size/height=1600 + +[dotnet] + +project/assembly_name="godot-frontend" + +[input] + +ui_accept={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":2,"pressure":0.0,"pressed":true,"script":null) +] +} +ui_select={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +] +} +ui_cancel={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":3,"pressure":0.0,"pressed":true,"script":null) +] +} +ui_left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +] +} +ui_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} +ui_up={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":11,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":-1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +] +} +ui_down={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +] +} +Player1_A1={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":2,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +Player1_A2={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":3,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +Player1_A3={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":10,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} +Player1_A4={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":9,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"location":0,"echo":false,"script":null) +] +} +Player1_B1={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":90,"key_label":0,"unicode":122,"location":0,"echo":false,"script":null) +] +} +Player1_B2={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":1,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":88,"key_label":0,"unicode":120,"location":0,"echo":false,"script":null) +] +} +Player1_B3={ +"deadzone": 0.2, +"events": [null, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":5,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":67,"key_label":0,"unicode":99,"location":0,"echo":false,"script":null) +] +} +Player1_B4={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":2,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":86,"key_label":0,"unicode":118,"location":0,"echo":false,"script":null) +] +} +Player1_Menu={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":7,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":82,"key_label":0,"unicode":114,"location":0,"echo":false,"script":null) +] +} +Player1_StickDown={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +] +} +Player1_StickUp={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":-1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":50,"key_label":0,"unicode":50,"location":0,"echo":false,"script":null) +] +} +Player1_StickLeft={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null) +] +} +Player1_StickRight={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null) +] +} +Player2_A1={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":2,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":72,"key_label":0,"unicode":104,"location":0,"echo":false,"script":null) +] +} +Player2_A2={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":3,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":74,"key_label":0,"unicode":106,"location":0,"echo":false,"script":null) +] +} +Player2_A3={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":10,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":75,"key_label":0,"unicode":107,"location":0,"echo":false,"script":null) +] +} +Player2_A4={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":9,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":76,"key_label":0,"unicode":108,"location":0,"echo":false,"script":null) +] +} +Player2_B1={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":0,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":78,"key_label":0,"unicode":110,"location":0,"echo":false,"script":null) +] +} +Player2_B2={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":1,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":77,"key_label":0,"unicode":109,"location":0,"echo":false,"script":null) +] +} +Player2_B3={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":1,"axis":5,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":44,"key_label":0,"unicode":44,"location":0,"echo":false,"script":null) +] +} +Player2_B4={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":1,"axis":2,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":46,"key_label":0,"unicode":46,"location":0,"echo":false,"script":null) +] +} +Player2_Menu={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":7,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":79,"key_label":0,"unicode":111,"location":0,"echo":false,"script":null) +] +} +Player2_StickDown={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":1,"axis":1,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":85,"key_label":0,"unicode":117,"location":0,"echo":false,"script":null) +] +} +Player2_StickUp={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":1,"axis":1,"axis_value":-1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":55,"key_label":0,"unicode":55,"location":0,"echo":false,"script":null) +] +} +Player2_StickLeft={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":1,"axis":0,"axis_value":-1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":89,"key_label":0,"unicode":121,"location":0,"echo":false,"script":null) +] +} +Player2_StickRight={ +"deadzone": 0.2, +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":1,"axis":0,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":73,"key_label":0,"unicode":105,"location":0,"echo":false,"script":null) +] +} + +[rendering] + +rendering_device/vsync/frame_queue_size=3 +textures/canvas_textures/default_texture_filter=0 +rendering_device/driver="d3d12" diff --git a/onboard/godot-frontend/util/Env.cs b/onboard/godot-frontend/util/Env.cs new file mode 100644 index 0000000..5fbadbc --- /dev/null +++ b/onboard/godot-frontend/util/Env.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using Godot; + +namespace onboard.util; + +public static class Env { + private static readonly Logger LOG = Log.get(nameof(Env)); + + private static readonly Dictionary env = new(); + + // Logging level for the backend + // Allowed log levels: trace, debug, info, warn, error + public static string RUST_LOG() { return get("RUST_LOG").unwrap_or("error"); } + public static Option DEVCADE_API_DOMAIN() { return get("DEVCADE_API_DOMAIN"); } + public static Option DEVCADE_DEV_API_DOMAIN() { return get("DEVCADE_DEV_API_DOMAIN"); } + + // Frontend + // Logging level for the frontend + // Allowed log levels: trace, verbose, debug, info, warn, error, fatal + public static string FRONTEND_LOG() { return get("FRONTEND_LOG").unwrap_or("error"); } + // Amount of time in seconds until the screen saver is shown + public static double SCREENSAVER_TIMEOUT_SEC() { return get("SCREENSAVER_TIMEOUT_SEC").map_or(5.0, double.Parse); } + // Amount of time in seconds that the supervisor buttons need to be heldW + public static double SUPERVISOR_BUTTON_TIMEOUT_SEC() { return get("SUPERVISOR_BUTTON_TIMEOUT_SEC").map_or(5.0, double.Parse); } + + // Demo mode will not display games with certain tags (e.g. "CSH Only") + // Allowed values: true, false + public static bool DEMO_MODE() { return get("DEMO_MODE").map_or(true, bool.Parse); } + + // Shared + // Games data and shared sockets will be placed here, defaults to ~/devcade + public static string DEVCADE_PATH() { return get("DEVCADE_PATH").unwrap_or(System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile) + "/devcade" ); } + // Where to place the log files + public static string LOG_LOCATION() { return get("LOG_LOCATION").unwrap_or(System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile) + "/devcade/logs" ); } + + public static bool LOW_PERFORMANCE_MODE() { return get("LOW_PERFORMANCE_MODE").map_or(true, bool.Parse); } + + static Env() { + if(File.Exists("../.env")) + { + load("../.env"); + } + + foreach (DictionaryEntry entry in System.Environment.GetEnvironmentVariables()) { + env.Add((string)entry.Key, (string)entry.Value); + } + } + + public static Option get(string key) { + return env.ContainsKey(key) ? Option.Some(env[key]) : Option.None(); + } + + public static void set(string key, string value) { + env[key] = value; + } + + public static void unset(string key) { + env.Remove(key); + } + + public static void clear() { + env.Clear(); + } + + public static void load(string path) { + if (!File.Exists(path)) { + LOG.Error($"File: {path} does not exist"); + return; + } + string[] lines = File.ReadAllLines(path); + for (int i = 0; i < lines.Length; i++) { + string line = lines[i]; + // remove all comments + int index = line.Find("#"); + if(index != -1) + { + line = line.Remove(index); + } + + string[] parts = line.Split('='); + if (parts.Length == 2) { + LOG.Verbose($"found env value: {parts[0]} = {parts[1]}"); + + env[parts[0]] = parts[1]; + } + } + } +} \ No newline at end of file diff --git a/onboard/godot-frontend/util/Env.cs.uid b/onboard/godot-frontend/util/Env.cs.uid new file mode 100644 index 0000000..c53f14a --- /dev/null +++ b/onboard/godot-frontend/util/Env.cs.uid @@ -0,0 +1 @@ +uid://dynoc0r6i5aao diff --git a/onboard/godot-frontend/util/Log.cs b/onboard/godot-frontend/util/Log.cs new file mode 100644 index 0000000..7336a03 --- /dev/null +++ b/onboard/godot-frontend/util/Log.cs @@ -0,0 +1,76 @@ +using System.ComponentModel.DataAnnotations; +using Godot; + +namespace onboard.util; + +/// +/// +/// +public static class Log +{ + private static Level logLevel; + + public enum Level + { + trace, + verbose, + debug, + info, + warn, + error, + fatal, + } + + static Log() + { + string level = Env.FRONTEND_LOG(); + + logLevel = Level.error; + if(level == "trace") { logLevel = Level.trace; } + if(level == "verbose") { logLevel = Level.verbose; } + if(level == "debug") { logLevel = Level.debug; } + if(level == "info") { logLevel = Level.info; } + if(level == "warn") { logLevel = Level.warn; } + if(level == "error") { logLevel = Level.error; } + if(level == "fatal") { logLevel = Level.fatal; } + + string time = Time.GetTimeStringFromSystem(); + logMessage($"[{time} INFO Log] Set current Log level to {logLevel}", Level.info); + } + + public static Level currentLogLevel = Level.debug; + public static Logger get(string className) + { + return new Logger(className); + } + + /// + /// Logs (prints) an arbitrary message at a given log level + /// + /// the message to send + /// The level to log + public static void logMessage(string message, Level logLevel) + { + if(logLevel < Log.logLevel) + { + return; + } + + if( + logLevel == Level.warn || + logLevel == Level.debug + ) + { + GD.PushWarning(message); + } + if( + logLevel == Level.error || + logLevel == Level.fatal + ) + { + GD.PushError(message); + } + + GD.Print(message); + } +} diff --git a/onboard/godot-frontend/util/Log.cs.uid b/onboard/godot-frontend/util/Log.cs.uid new file mode 100644 index 0000000..e6024de --- /dev/null +++ b/onboard/godot-frontend/util/Log.cs.uid @@ -0,0 +1 @@ +uid://dpstw66t1svjy diff --git a/onboard/godot-frontend/util/Logger.cs b/onboard/godot-frontend/util/Logger.cs new file mode 100644 index 0000000..360bd83 --- /dev/null +++ b/onboard/godot-frontend/util/Logger.cs @@ -0,0 +1,64 @@ +using Godot; + +namespace onboard.util; + +public class Logger +{ + public string className {get; private set;}= "null"; + + public Logger(string className) + { + this.className = className; + } + + /// + /// Logs (prints) an arbitrary message at a given log level + /// + /// the message to send + /// The level to log + public void logMessage(string msg, Log.Level logLevel) + { + string time = Time.GetTimeStringFromSystem(); + + string message = $"[{time} {logLevel.ToString().ToUpperInvariant()} {className}] {msg}"; + + Log.logMessage(message, logLevel); + } + + // trace, verbose, debug, info, warn, error, fatal methods + + public void Trace(string msg) + { + logMessage(msg, Log.Level.trace); + } + + public void Verbose(string msg) + { + logMessage(msg, Log.Level.verbose); + } + + public void Debug(string msg) + { + logMessage(msg, Log.Level.debug); + } + + public void Info(string msg) + { + logMessage(msg, Log.Level.info); + } + + public void Warn(string msg) + { + logMessage(msg, Log.Level.warn); + } + + public void Error(string msg) + { + logMessage(msg, Log.Level.error); + } + + public void Fatal(string msg) + { + logMessage(msg, Log.Level.fatal); + } +} \ No newline at end of file diff --git a/onboard/godot-frontend/util/Logger.cs.uid b/onboard/godot-frontend/util/Logger.cs.uid new file mode 100644 index 0000000..d71dbff --- /dev/null +++ b/onboard/godot-frontend/util/Logger.cs.uid @@ -0,0 +1 @@ +uid://ddijsmk2e1kd8 diff --git a/onboard/frontend/util/Option.cs b/onboard/godot-frontend/util/Option.cs similarity index 100% rename from onboard/frontend/util/Option.cs rename to onboard/godot-frontend/util/Option.cs diff --git a/onboard/godot-frontend/util/Option.cs.uid b/onboard/godot-frontend/util/Option.cs.uid new file mode 100644 index 0000000..11f0f38 --- /dev/null +++ b/onboard/godot-frontend/util/Option.cs.uid @@ -0,0 +1 @@ +uid://dg367jllemw1r diff --git a/onboard/frontend/util/Request.cs b/onboard/godot-frontend/util/Request.cs similarity index 100% rename from onboard/frontend/util/Request.cs rename to onboard/godot-frontend/util/Request.cs diff --git a/onboard/godot-frontend/util/Request.cs.uid b/onboard/godot-frontend/util/Request.cs.uid new file mode 100644 index 0000000..217880f --- /dev/null +++ b/onboard/godot-frontend/util/Request.cs.uid @@ -0,0 +1 @@ +uid://cbhgfonbx0678 diff --git a/onboard/frontend/util/Response.cs b/onboard/godot-frontend/util/Response.cs similarity index 88% rename from onboard/frontend/util/Response.cs rename to onboard/godot-frontend/util/Response.cs index a313b7a..4c68d36 100644 --- a/onboard/frontend/util/Response.cs +++ b/onboard/godot-frontend/util/Response.cs @@ -2,14 +2,15 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Reflection; -using log4net; +using Godot; using Newtonsoft.Json; using onboard.devcade; namespace onboard.util; public class Response { + Logger LOG = Log.get(nameof(Response)); + public enum ResponseType { Pong, @@ -25,8 +26,6 @@ public enum ResponseType { Unknown, } - - private ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod()?.DeclaringType?.FullName); public ResponseType type { get; private set; } private object? data { get; set; } @@ -61,12 +60,16 @@ public Result into_result() { if (type == ResponseType.Err) { return Result.Err(data); } - // logger.Trace($"Serialized internal data to {data}"); + if(data.Length < 100) + { + GD.Print($"Serialized internal data to {data}"); + } + T deserializeT; try { deserializeT = JsonConvert.DeserializeObject(data) ?? throw new NullReferenceException(); } catch (Exception e) { - logger.Error($"Failed to deserialize {data} to {typeof(T)}", e); + LOG.Error($"Failed to deserialize {data} to {typeof(T)}: {e}"); return Result.Err("Failed to deserialize response"); } if (deserializeT == null) { @@ -82,7 +85,7 @@ public Result into_result() { _ => throw new ArgumentOutOfRangeException() }; if (typeof(T) != expected) { - logger.Error($"Invalid response type and data combination: {type}\nTypeof(T) was {typeof(T)} but expected {expected}"); + LOG.Error($"Invalid response type and data combination: {type}\nTypeof(T) was {typeof(T)} but expected {expected}"); } return type switch { ResponseType.Ok => Result.Ok(deserializeT), diff --git a/onboard/godot-frontend/util/Response.cs.uid b/onboard/godot-frontend/util/Response.cs.uid new file mode 100644 index 0000000..c26b19c --- /dev/null +++ b/onboard/godot-frontend/util/Response.cs.uid @@ -0,0 +1 @@ +uid://d22v2gnjo1dw5 diff --git a/onboard/frontend/util/Result.cs b/onboard/godot-frontend/util/Result.cs similarity index 100% rename from onboard/frontend/util/Result.cs rename to onboard/godot-frontend/util/Result.cs diff --git a/onboard/godot-frontend/util/Result.cs.uid b/onboard/godot-frontend/util/Result.cs.uid new file mode 100644 index 0000000..5149ca0 --- /dev/null +++ b/onboard/godot-frontend/util/Result.cs.uid @@ -0,0 +1 @@ +uid://ba3nwbg1sojoo diff --git a/onboard/godot-frontend/util/SupervisorButton.cs b/onboard/godot-frontend/util/SupervisorButton.cs new file mode 100644 index 0000000..dff2b26 --- /dev/null +++ b/onboard/godot-frontend/util/SupervisorButton.cs @@ -0,0 +1,21 @@ +using Godot; + +namespace onboard.util.supervisor_button +{ + static class SupervisorButton + { + public static bool anyButtonPressed { get { return Input.IsAnythingPressed(); } private set { } } + + public static bool isSupervisorButtonPressed() + { + bool player1_menu_pressed; + bool player2_menu_pressed; + + // these are hard coded values that should be changed to something else + player1_menu_pressed = Input.IsJoyButtonPressed(0, JoyButton.LeftStick); + player2_menu_pressed = Input.IsJoyButtonPressed(1, JoyButton.LeftStick); + + return player1_menu_pressed && player2_menu_pressed; + } + } +} \ No newline at end of file diff --git a/onboard/godot-frontend/util/SupervisorButton.cs.uid b/onboard/godot-frontend/util/SupervisorButton.cs.uid new file mode 100644 index 0000000..96034f5 --- /dev/null +++ b/onboard/godot-frontend/util/SupervisorButton.cs.uid @@ -0,0 +1 @@ +uid://be73xo05idfbf