Import from private repo
This commit is contained in:
commit
ff7ca6cc0f
9 changed files with 536 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
target/
|
||||
*.import
|
||||
Cargo.lock
|
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "rust"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
godot = { git = "https://github.com/godot-rust/gdext", branch = "master" }
|
||||
rand = "0.8.5"
|
||||
toml = "0.8.19"
|
15
MiniDemoTools.gdextension
Normal file
15
MiniDemoTools.gdextension
Normal file
|
@ -0,0 +1,15 @@
|
|||
[configuration]
|
||||
entry_symbol = "gdext_rust_init"
|
||||
compatibility_minimum = 4.1
|
||||
reloadable = true
|
||||
|
||||
[libraries]
|
||||
linux.debug.x86_64 = "res://addons/MiniDemoTools/librust.so"
|
||||
linux.release.x86_64 = "res://addons/MiniDemoTools/librust.so"
|
||||
##windows.debug.x86_64 = "res://../rust/target/debug/librust.dll"
|
||||
##windows.release.x86_64 = "res://../rust/target/release/librust.dll"
|
||||
|
||||
|
||||
[icons]
|
||||
|
||||
ScramblingText = "res://addons/MiniDemoTools/icons/ScrambledText.svg"
|
81
icons/ScrambledText.svg
Normal file
81
icons/ScrambledText.svg
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg20"
|
||||
sodipodi:docname="Label.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs24">
|
||||
<inkscape:path-effect
|
||||
effect="dashed_stroke"
|
||||
id="path-effect3649"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
numberdashes="2"
|
||||
holefactor="0"
|
||||
splitsegments="true"
|
||||
halfextreme="true"
|
||||
unifysegment="true"
|
||||
message="Add <b>"Fill Between Many LPE"</b> to add fill." />
|
||||
<inkscape:path-effect
|
||||
effect="dashed_stroke"
|
||||
id="path-effect3439"
|
||||
is_visible="false"
|
||||
lpeversion="1"
|
||||
numberdashes="2"
|
||||
holefactor="0.02"
|
||||
splitsegments="true"
|
||||
halfextreme="true"
|
||||
unifysegment="true"
|
||||
message="Add <b>"Fill Between Many LPE"</b> to add fill." />
|
||||
<inkscape:path-effect
|
||||
effect="dashed_stroke"
|
||||
id="path-effect3334"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
numberdashes="2"
|
||||
holefactor="0.02"
|
||||
splitsegments="true"
|
||||
halfextreme="true"
|
||||
unifysegment="true"
|
||||
message="Add <b>"Fill Between Many LPE"</b> to add fill." />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview22"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="57.715148"
|
||||
inkscape:cx="7.4503837"
|
||||
inkscape:cy="7.7016176"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1008"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg20" />
|
||||
<path
|
||||
style="stroke-opacity:1;stroke-dasharray:none;stroke-width:0.6;stroke:#8eef97;fill-rule:nonzero;fill-opacity:1;fill:none;"
|
||||
d="M 5,7 A 1,1 0 0 1 5,9 1,1 0 0 1 5,7 Z"
|
||||
id="path3332" />
|
||||
<path
|
||||
style="stroke-opacity:1;stroke-dashoffset:0;stroke-dasharray:1.8;stroke-width:1;stroke:#8eef97;fill-rule:nonzero;fill-opacity:1;fill:none;"
|
||||
d="M 6,3 A 1,1 0 0 0 5.293,3.293 l -4,4 a 1,1 0 0 0 0,1.414 l 4,4 A 1,1 0 0 0 6,13 h 8 a 1,1 0 0 0 1,-1 V 4 A 1,1 0 0 0 14,3 Z"
|
||||
id="path18" />
|
||||
<path
|
||||
style="stroke-opacity:1;stroke-dasharray:none;stroke-width:1;stroke:#8eef97;fill-rule:nonzero;fill-opacity:1;fill:none;"
|
||||
d="M 8.5606241,3 H 6 C 5.7348052,3.0000566 5.4804927,3.1054506 5.293,3.293 l -4,4 c -0.39038194,0.3904999 -0.39038194,1.0235001 0,1.414 l 4,4 C 5.4804927,12.894549 5.7348052,12.999943 6,13 h 2.5606241"
|
||||
id="path3435"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
17
src/MiniEditorPlugin.rs
Normal file
17
src/MiniEditorPlugin.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
#[derive(GodotClass)]
|
||||
#[class(tool, init, editor_plugin, base=EditorPlugin)]
|
||||
|
||||
struct MiniEditorPlugin {
|
||||
base: Base<EditorPlugin>,
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl IEditorPlugin for MiniEditorPlugin {
|
||||
fn enter_tree(&mut self) {
|
||||
|
||||
}
|
||||
|
||||
fn exit_tree(&mut self) {
|
||||
|
||||
}
|
||||
}
|
139
src/config.rs
Normal file
139
src/config.rs
Normal file
|
@ -0,0 +1,139 @@
|
|||
use godot::prelude::*;
|
||||
use godot::classes::{ConfigFile, Engine, DisplayServer, display_server::{WindowFlags, WindowMode, VSyncMode}, window::WindowInitialPosition};
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=Node)]
|
||||
|
||||
struct Config {
|
||||
// DISCUSSION: Should we expose these in-editor so the default values can be modified?
|
||||
|
||||
#[export(file = "")]
|
||||
config_path: GString,
|
||||
#[var]
|
||||
window_height: i32,
|
||||
#[var]
|
||||
window_width: i32,
|
||||
#[var]
|
||||
is_fullscreen: bool,
|
||||
#[var]
|
||||
is_borderless: bool,
|
||||
#[var]
|
||||
monitor: i32,
|
||||
#[var]
|
||||
window_position_x: i32,
|
||||
#[var]
|
||||
window_position_y: i32,
|
||||
#[var]
|
||||
max_framerate: i32,
|
||||
#[var]
|
||||
vertical_sync: bool,
|
||||
#[var]
|
||||
resolution_scale: f32,
|
||||
|
||||
|
||||
base: Base<Node>,
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl INode for Config {
|
||||
fn init(base: Base<Node>) -> Self {
|
||||
|
||||
Self {
|
||||
config_path: GString::from("res://config.toml"),
|
||||
window_height: 600,
|
||||
window_width: 400,
|
||||
is_fullscreen: false,
|
||||
is_borderless: true,
|
||||
monitor: 0,
|
||||
window_position_x: 0,
|
||||
window_position_y: 0,
|
||||
max_framerate: 60,
|
||||
vertical_sync: true,
|
||||
resolution_scale: 1.0,
|
||||
|
||||
base,
|
||||
|
||||
}
|
||||
}
|
||||
fn enter_tree(&mut self) {
|
||||
let editor_hint = Engine::singleton().is_editor_hint();
|
||||
|
||||
let mut config = ConfigFile::new_gd();
|
||||
|
||||
let res = config.load(GString::from("res://config.toml"));
|
||||
|
||||
if res != godot::global::Error::OK {
|
||||
godot_error!("Failed to load config file");
|
||||
}
|
||||
|
||||
else {
|
||||
//TODO This could be improved
|
||||
//// GDext doesn's support defining defaults at this moment, and there's no way in hell I'm implementing checks for each config key.
|
||||
//// If a config field is missing, it should throw an error, but not halt the whole demo
|
||||
//
|
||||
// We can get the keys and sections with get_sections() and get_section_keys() and then iterate over each one and load them if they match with the Config struct
|
||||
//
|
||||
self.window_width = config.get_value(GString::from("window"), GString::from("width")).to::<i32>();
|
||||
self.window_height = config.get_value(GString::from("window"), GString::from("height")).to::<i32>();
|
||||
self.is_fullscreen = config.get_value(GString::from("window"), GString::from("fullscreen")).to::<bool>();
|
||||
self.is_borderless = config.get_value(GString::from("window"), GString::from("borderless")).to::<bool>();
|
||||
self.window_position_x = config.get_value(GString::from("window"), GString::from("position_x")).to::<i32>();
|
||||
self.window_position_y = config.get_value(GString::from("window"), GString::from("position_y")).to::<i32>();
|
||||
self.monitor = config.get_value(GString::from("window"), GString::from("monitor")).to::<i32>();
|
||||
|
||||
self.max_framerate = config.get_value(GString::from("graphics"), GString::from("max_framerate")).to::<i32>();
|
||||
self.vertical_sync = config.get_value(GString::from("graphics"), GString::from("vertical_sync")).to::<bool>();
|
||||
self.resolution_scale = config.get_value(GString::from("graphics"), GString::from("resolution_scale")).to::<f32>();
|
||||
}
|
||||
if !editor_hint {
|
||||
// THIS CHECK IS VERY IMPORTANT!!! I don't want to fuck my godot up again by resizing the editor and moving it out of bounds..
|
||||
Self::apply_video_settings(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl Config {
|
||||
#[func]
|
||||
fn apply_video_settings(&mut self) {
|
||||
let mut disp_server = DisplayServer::singleton();
|
||||
let mut window = self.base().get_window().unwrap();
|
||||
let mut engine = Engine::singleton();
|
||||
let initial_window_size = disp_server.window_get_size();
|
||||
|
||||
let window_offset_after_resize: Vector2i = Vector2i::from_array([(self.window_width - initial_window_size.x) / 2, (self.window_height - initial_window_size.y) / 2]);
|
||||
if self.is_fullscreen {
|
||||
disp_server.window_set_mode(WindowMode::FULLSCREEN)
|
||||
}
|
||||
else {
|
||||
disp_server.window_set_mode(WindowMode::WINDOWED)
|
||||
}
|
||||
|
||||
if self.vertical_sync {
|
||||
disp_server.window_set_vsync_mode(VSyncMode::ENABLED);
|
||||
}
|
||||
else {
|
||||
disp_server.window_set_vsync_mode(VSyncMode::DISABLED);
|
||||
}
|
||||
|
||||
disp_server.window_set_flag(WindowFlags::BORDERLESS, self.is_borderless);
|
||||
disp_server.window_set_current_screen(self.monitor);
|
||||
disp_server.window_set_size(Vector2i::from_array([self.window_width, self.window_height]));
|
||||
|
||||
//FIXME: Doesn't seem to work on Linux, at least not on KDE+Wayland. Most likely an issue with Godot itself
|
||||
// as trying to set the window position from GDscript doesn't work either...
|
||||
// EDIT: It worked for a moment, then broke again... weird
|
||||
window.set_initial_position(WindowInitialPosition::ABSOLUTE);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
godot_print!("WINDOW SIZE: {}, {}", initial_window_size.x, initial_window_size.y);
|
||||
godot_print!("Initial size: {}, {}", initial_window_size.x, initial_window_size.y);
|
||||
godot_print!("offset: {}, {}", window_offset_after_resize.x, window_offset_after_resize.y);
|
||||
godot_print!("New pos: {}, {}", self.window_position_x - window_offset_after_resize.x, self.window_position_y - window_offset_after_resize.y);
|
||||
}
|
||||
|
||||
window.set_position(Vector2i::from_array([self.window_position_x - window_offset_after_resize.x, initial_window_size.y - window_offset_after_resize.y]));
|
||||
engine.set_max_fps(self.max_framerate);
|
||||
|
||||
|
||||
}
|
||||
}
|
29
src/lib.rs
Normal file
29
src/lib.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use godot::{init::EditorRunBehavior, prelude::*};
|
||||
|
||||
struct MDT;
|
||||
mod scrambling_text;
|
||||
mod config;
|
||||
mod manager;
|
||||
|
||||
#[gdextension]
|
||||
unsafe impl ExtensionLibrary for MDT {
|
||||
|
||||
fn editor_run_behavior() -> EditorRunBehavior {
|
||||
return EditorRunBehavior::AllClasses
|
||||
}
|
||||
|
||||
fn min_level() -> InitLevel {
|
||||
return InitLevel::Scene
|
||||
}
|
||||
|
||||
fn on_level_init(level: InitLevel) {
|
||||
match level {
|
||||
InitLevel::Scene => {
|
||||
if cfg!(debug_assertions) {
|
||||
godot_print_rich!("[b]MiniDemoTools[/b] is in [color=red][b][u]DEBUG[/u][/b][/color] mode!")
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
69
src/manager.rs
Normal file
69
src/manager.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
use godot::prelude::*;
|
||||
use godot::global::Key;
|
||||
use godot::classes::{AudioStreamOggVorbis, Engine, InputEvent, InputEventKey};
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=Node)]
|
||||
struct Manager {
|
||||
#[export(file = "*.ogg")]
|
||||
bgm: GString,
|
||||
#[var]
|
||||
beat_count: i32,
|
||||
#[var]
|
||||
scenes: Array<Gd<PackedScene>>,
|
||||
instanced_scenes: Vec<Gd<Node>>,
|
||||
base: Base<Node>
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl INode for Manager {
|
||||
fn init(base: Base<Node>) -> Self {
|
||||
Self {
|
||||
bgm: GString::from(""),
|
||||
beat_count: 0,
|
||||
scenes: Array::new(),
|
||||
instanced_scenes: Vec::new(),
|
||||
base,
|
||||
}
|
||||
}
|
||||
|
||||
fn unhandled_input(&mut self, event: Gd<InputEvent>) {
|
||||
if event.is_released() {
|
||||
if let Ok(key) = event.clone().try_cast::<InputEventKey>() {
|
||||
if key.get_keycode() == Key::ESCAPE {
|
||||
godot_print!("Goodbye!");
|
||||
self.base_mut().get_tree().unwrap().quit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl Manager {
|
||||
#[func]
|
||||
fn set_scene(&mut self, number: i32) {
|
||||
let scene = self.instanced_scenes[number as usize].clone(); // Memory leak??? Idk
|
||||
self.base_mut().add_child(scene);
|
||||
}
|
||||
|
||||
#[func]
|
||||
fn register_scene(&mut self, scene: Gd<PackedScene>) {
|
||||
self.instanced_scenes.push(scene.instantiate_as())
|
||||
}
|
||||
|
||||
#[func]
|
||||
fn start_audio(&mut self) {
|
||||
let editor_hint = Engine::singleton().is_editor_hint();
|
||||
if !editor_hint {
|
||||
let bgm = self.bgm.clone();
|
||||
godot_print!(" Hello world fj");
|
||||
let mut stream: Gd<AudioStreamOggVorbis> = load(bgm);
|
||||
stream.set_loop(false);
|
||||
let mut player = AudioStreamPlayer::new_alloc();
|
||||
player.set_stream(stream);
|
||||
player.set_autoplay(true);
|
||||
self.base_mut().add_child(player);
|
||||
}
|
||||
}
|
||||
}
|
168
src/scrambling_text.rs
Normal file
168
src/scrambling_text.rs
Normal file
|
@ -0,0 +1,168 @@
|
|||
|
||||
use godot::prelude::*;
|
||||
use godot::classes::{Label, Engine};
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=Label)]
|
||||
struct ScramblingText {
|
||||
#[export(multiline)]
|
||||
chars: GString,
|
||||
#[export]
|
||||
scrambling: bool,
|
||||
#[export]
|
||||
speed: f64,
|
||||
#[export]
|
||||
duration: f64,
|
||||
#[export]
|
||||
infinite: bool,
|
||||
forbidden_chars: Option<Vec<u8>>,
|
||||
original_text: Option<String>,
|
||||
time_since_scramble: f64, //[DOC] This is the variable used for measuring time since the letters have been scrambled
|
||||
time_since_step: f64, //[DOC] This is the variable used for measuring time since the amount of static letters was changed
|
||||
step_iterator: i32,
|
||||
editor_hint: bool,
|
||||
base: Base<Label>
|
||||
}
|
||||
|
||||
|
||||
use godot::classes::ILabel;
|
||||
use rand::Rng;
|
||||
|
||||
#[godot_api]
|
||||
impl ILabel for ScramblingText {
|
||||
fn init(base: Base<Label>) -> Self {
|
||||
|
||||
let editor_hint = Engine::singleton().is_editor_hint();
|
||||
|
||||
Self {
|
||||
chars: GString::from("ABCDEFGHIJKLMNOPQRSTUVWXYZ124567890".to_string()),
|
||||
forbidden_chars: Some(vec![0x20, 0x0A]),
|
||||
scrambling: false,
|
||||
speed: 15.0,
|
||||
duration: 1.0,
|
||||
infinite: editor_hint,
|
||||
original_text: None,
|
||||
step_iterator: 0, //[DOC] This is the iterator used for changing which letters to scramble
|
||||
time_since_scramble: 0.0,
|
||||
time_since_step: 0.0,
|
||||
editor_hint: editor_hint,
|
||||
base
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn set_property(&mut self, property: StringName, value: Variant) -> bool {
|
||||
// godot_print!("{:?}, {:?}", property, value);
|
||||
if property == StringName::from("scrambling") {
|
||||
let boolean_value = value.booleanize();
|
||||
let original_text = self.original_text.clone();
|
||||
if !boolean_value && !self.infinite {
|
||||
Self::set_text(self, GString::from(original_text.unwrap_or("Hello World!".to_string())));
|
||||
self.step_iterator = 0;
|
||||
}
|
||||
self.scrambling = boolean_value;
|
||||
return false
|
||||
}
|
||||
else if property == StringName::from("infinite") {
|
||||
self.step_iterator = 0;
|
||||
return false
|
||||
}
|
||||
else {
|
||||
return false
|
||||
}
|
||||
|
||||
// Self::set_text(self, GString::from(self.original_text.clone().unwrap()))
|
||||
|
||||
}
|
||||
|
||||
fn enter_tree(&mut self) {
|
||||
let text = Self::get_text(self);
|
||||
self.original_text = Some(text.to_string());
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn process(&mut self, delta: f64) {
|
||||
if self.scrambling {
|
||||
let original_text = self.original_text.clone().unwrap();
|
||||
let duration = self.duration;
|
||||
let speed = self.speed;
|
||||
|
||||
// godot_print!("{}", Engine::singleton().is_editor_hint());
|
||||
|
||||
self.time_since_scramble += delta;
|
||||
self.time_since_step += delta;
|
||||
|
||||
if self.time_since_scramble > duration / speed && self.time_since_step < duration {
|
||||
self.time_since_scramble = 0.0;
|
||||
let chars = self.chars.to_string();
|
||||
let forbidden_chars = self.forbidden_chars.clone().unwrap();
|
||||
|
||||
let left = Self::slice_left(original_text.clone(), self.step_iterator);
|
||||
let right = Self::slice_right(original_text.clone(), self.step_iterator);
|
||||
|
||||
// if cfg!(debug_assertions) {
|
||||
// godot_print!("'{}'{}'", left.clone(), right.clone());
|
||||
// }
|
||||
|
||||
self.base_mut().set_text(GString::from(String::from(left + &Self::get_glitch_text(right, chars, forbidden_chars))));
|
||||
}
|
||||
else if self.time_since_step > duration && self.step_iterator < original_text.chars().count() as i32 {
|
||||
if !self.infinite | !self.editor_hint {
|
||||
self.step_iterator += 1;
|
||||
}
|
||||
self.time_since_step = 0.0;
|
||||
}
|
||||
else if !self.infinite && self.step_iterator >= original_text.chars().count() as i32 {
|
||||
self.step_iterator = 0;
|
||||
self.scrambling = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl ScramblingText {
|
||||
|
||||
#[func]
|
||||
fn start(&mut self) {
|
||||
self.scrambling = true;
|
||||
}
|
||||
|
||||
fn get_glitch_text(text: String, chars: String, forbidden_characters: Vec<u8>) -> String {
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let collect: Vec<u8> = text.bytes()
|
||||
.map(|b| if !forbidden_characters.contains(&b) { chars.to_string().as_bytes()[rng.gen_range(0..chars.len())] } else { b })
|
||||
.collect();
|
||||
return String::from_utf8(collect).unwrap()
|
||||
|
||||
}
|
||||
#[func]
|
||||
fn set_text(&mut self, text: GString) {
|
||||
self.base_mut().set_text(text)
|
||||
}
|
||||
|
||||
#[func]
|
||||
fn get_text(&mut self) -> GString {
|
||||
return self.base_mut().get_text()
|
||||
}
|
||||
|
||||
fn slice_left(text: String, amount: i32) -> String {
|
||||
// let len: i32 = text.chars().count() as i32;
|
||||
let mut res: String = String::new();
|
||||
for i in 0..amount {
|
||||
res.push(text.chars().nth(i as usize).unwrap());
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
|
||||
fn slice_right(text: String, amount: i32) -> String {
|
||||
let res = text.clone().split_off(amount as usize);
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue