Compare commits

...

2 Commits

Author SHA1 Message Date
deadvey 20369ef838 added basic variable functionality, doesn't really do anything at the
moment but you can assign and change variables.
2026-05-25 02:19:30 +01:00
deadvey 21bf659718 Began adding support for variables 2026-05-24 15:09:59 +01:00
9 changed files with 367 additions and 31 deletions
+92
View File
@@ -206,6 +206,16 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.4.1" version = "2.4.1"
@@ -379,11 +389,13 @@ dependencies = [
name = "happening-client" name = "happening-client"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"home",
"macroquad", "macroquad",
"phf", "phf",
"reqwest", "reqwest",
"serde", "serde",
"serde_json", "serde_json",
"tokio",
] ]
[[package]] [[package]]
@@ -403,6 +415,15 @@ version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "home"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
dependencies = [
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "http" name = "http"
version = "1.4.0" version = "1.4.0"
@@ -724,6 +745,15 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.29" version = "0.4.29"
@@ -846,6 +876,29 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-link",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.2" version = "2.3.2"
@@ -1047,6 +1100,15 @@ dependencies = [
"getrandom 0.3.4", "getrandom 0.3.4",
] ]
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags 2.11.1",
]
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.13.3" version = "0.13.3"
@@ -1217,6 +1279,12 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "security-framework" name = "security-framework"
version = "3.7.0" version = "3.7.0"
@@ -1295,6 +1363,16 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
[[package]] [[package]]
name = "simd-adler32" name = "simd-adler32"
version = "0.3.9" version = "0.3.9"
@@ -1463,11 +1541,25 @@ dependencies = [
"bytes", "bytes",
"libc", "libc",
"mio", "mio",
"parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry",
"socket2", "socket2",
"tokio-macros",
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "tokio-macros"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.26.4" version = "0.26.4"
+2
View File
@@ -4,8 +4,10 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
home = "0.5.12"
macroquad = "0.4.15" macroquad = "0.4.15"
phf = {version="0.13.1",features=["macros"]} phf = {version="0.13.1",features=["macros"]}
reqwest = {version="0.13.3",features=["blocking","json"]} reqwest = {version="0.13.3",features=["blocking","json"]}
serde = {version="1.0.228",features=["derive"]} serde = {version="1.0.228",features=["derive"]}
serde_json = "1.0.149" serde_json = "1.0.149"
tokio = {version="1.52.3",features=["full"]}
+89 -21
View File
@@ -1,14 +1,24 @@
use macroquad::prelude::*; use macroquad::prelude::*;
use reqwest::*; use reqwest::*;
use reqwest::blocking; use reqwest::blocking;
use std::collections::HashMap;
use phf::phf_map; use phf::phf_map;
use std::{thread, time::Duration}; use std::
{
thread,
time::Duration,
collections::
{
HashMap,
HashSet,
},
process::exit,
};
use serde:: use serde::
{ {
Serialize, Serialize,
Deserialize, Deserialize,
}; };
use home::home_dir;
#[derive(Debug, Deserialize, Serialize, Clone, Default)] #[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct Data { pub struct Data {
@@ -69,45 +79,92 @@ static POSITIONS: phf::Map<&'static str, [f32;2]> = phf_map! [
async fn main() async fn main()
{ {
let mut characters: HashMap<String, (Character, Texture2D)> = HashMap::new(); let mut characters: HashMap<String, (Character, Texture2D)> = HashMap::new();
let mut textures: Vec<(Texture2D, f32, f32, Color)> = Vec::new(); characters.insert("narrator".to_string(), (Character { name: "Narrator".to_string(), ..Default::default() }, Texture2D::empty()));
let mut textures: HashMap<String,(Texture2D, f32, f32, Color)> = HashMap::new();
let mut text: String = String::new();
let mut checking_for_choice: bool = false;
let mut data: Data = next_happening(); // First one should be begin
loop loop
{ {
clear_background(RED); clear_background(RED);
// Get the next character for (name, (texture, x, y, colour)) in &textures
let data = next_happening();
let character_name: &str = data.character.as_str();
// Add the character to the HashMap if it's not already
if character_name != "" && !characters.contains_key(character_name)
{ {
let new_character = get_character(character_name).await; draw_texture(&texture, *x, *y, *colour);
characters.insert(character_name.to_string(), new_character); }
draw_multiline_text(&text, 50.0,30.0,40.0,None,WHITE);
if !is_any_key_down()
{
next_frame().await;
continue
}
let keys: HashSet<KeyCode> = get_keys_pressed();
if checking_for_choice
{
for key in &keys
{
let keycode = *key as u16;
let length: u16 = data.choices.len() as u16;
println!("key: {key:?} {keycode}");
if keycode > 48 && keycode <= length+48
{
checking_for_choice = false;
println!("Sending POST: {}",keycode-49);
let value = keycode - 49;
send_choice(value);
}
else { continue }
}
}
// Get the next character
data = next_happening();
let character_name: String = data.character.to_lowercase();
// Add the character to the HashMap if it's not already
if character_name != "" && !characters.contains_key(&character_name)
{
println!("Fetching {character_name}");
let new_character = get_character(&character_name).await;
characters.insert(character_name.clone(), new_character);
} }
// Matchbox for all the commands // Matchbox for all the commands
match data.action_type.to_lowercase().as_str() match data.action_type.to_lowercase().as_str()
{ {
"choice" =>
{
for (index, choice) in data.choices.iter().enumerate()
{
text += format!("\n{}. {}",index+1, choice).as_str();
}
checking_for_choice = true;
},
"output" => "output" =>
{ {
println!("SAYING"); println!("SAYING");
draw_text(characters[character_name].0.name.clone(), 50.0,20.0,40.0,WHITE); text = format!("{}: {}", characters[&character_name].0.name.clone(),wrap_text(&data.content).as_str());
draw_text(data.content.as_str(), 50.0,40.0,30.0,WHITE);
}, },
"to" => "to" =>
{ {
let position = POSITIONS.get(&data.content).cloned().unwrap(); let position = POSITIONS.get(&data.content).cloned().unwrap();
let texture = &characters[character_name].1; let texture = &characters[&character_name].1;
textures.push((texture.clone(), position[0], position[1], WHITE)); // Heavy textures.insert(character_name.clone(),(texture.clone(), position[0], position[1], WHITE)); // Heavy
} }
_ => println!("Unknown action"), "begin" => (),
"end" => exit(0),
_ => println!("Unknown action, {}", data.action_type),
} }
for (texture, x, y, colour) in &textures
{
draw_texture(&texture, *x, *y, *colour);
}
thread::sleep(Duration::from_millis(1000));
next_frame().await; next_frame().await;
} }
} }
fn wrap_text(text: &str) -> String
{
text.chars()
.collect::<Vec<_>>()
.chunks(30)
.map(|chunk| chunk.iter().collect::<String>())
.collect::<Vec<_>>()
.join("\n")
}
fn next_happening() fn next_happening()
-> Data -> Data
{ {
@@ -116,6 +173,17 @@ fn next_happening()
data data
} }
#[tokio::main]
async fn send_choice(index: u16)
{
let client = reqwest::Client::new();
let res = client.post("http://localhost:20264/choice")
.json(&index)
.send()
.await;
}
async fn get_character(name: &str) async fn get_character(name: &str)
-> (Character, Texture2D) -> (Character, Texture2D)
{ {
@@ -125,7 +193,7 @@ async fn get_character(name: &str)
let skin: Color = Color::from_rgba(skin_colour.red,skin_colour.green,skin_colour.blue,255); let skin: Color = Color::from_rgba(skin_colour.red,skin_colour.green,skin_colour.blue,255);
let hair_colour = character.hair_color.clone(); let hair_colour = character.hair_color.clone();
let hair: Color = Color::from_rgba(hair_colour.red,hair_colour.green,hair_colour.blue,255); let hair: Color = Color::from_rgba(hair_colour.red,hair_colour.green,hair_colour.blue,255);
let head_path: String = format!("../images/head/{}.png",character.head_shape); let head_path: String = format!("/home/deadvey/.local/share/happening/images/head/{}.png",character.head_shape);
let head: Texture2D = change_colour(&mut load_image(head_path.as_str()).await.unwrap(), &skin, &hair); let head: Texture2D = change_colour(&mut load_image(head_path.as_str()).await.unwrap(), &skin, &hair);
(character, head) (character, head)
} }
+16
View File
@@ -18,6 +18,8 @@ use crate::
mod character_parse; mod character_parse;
mod keyword_parse; mod keyword_parse;
mod identifier_parse;
// Parse the tokens in a file // Parse the tokens in a file
// Returns success or an error string // Returns success or an error string
@@ -30,6 +32,7 @@ pub fn token_parse(
) -> Result<(),String> ) -> Result<(),String>
{ {
let mut index: usize = 0; let mut index: usize = 0;
let mut variables: HashMap<String, tokenise::Value> = HashMap::new();
info!("Client has connected"); info!("Client has connected");
// Run an infinite loop // Run an infinite loop
loop loop
@@ -66,6 +69,19 @@ pub fn token_parse(
}, },
}; };
} }
// Identifier
Some(tokenise::Token::Identifier(name)) =>
{
index = match identifier_parse::identifier_parse(index+1,name,tokens,&mut variables)
{
Ok(increment) => increment,
Err((err,increment)) =>
{
warn!("{err}");
increment
},
};
}
Some(_) => Some(_) =>
{ {
warn!("Unexpected token"); warn!("Unexpected token");
+4 -4
View File
@@ -46,7 +46,7 @@ pub fn character_parse
info!("SAYS command with character {character_name}"); info!("SAYS command with character {character_name}");
sum_index += 1; sum_index += 1;
let output = tokenise::get_string_token(tokens, sum_index) let output = tokenise::get_string_token(tokens, sum_index)
.map_err(|err| (err, index))?; .map_err(|err| (err, sum_index))?;
debug!("Saying {output}"); debug!("Saying {output}");
api::modify_data(happening_queue, "output".to_string(), output, character_name, vec![]); api::modify_data(happening_queue, "output".to_string(), output, character_name, vec![]);
}, },
@@ -56,10 +56,10 @@ pub fn character_parse
{ {
sum_index += 1; sum_index += 1;
let feature = tokenise::get_keyword_token(tokens, sum_index) let feature = tokenise::get_keyword_token(tokens, sum_index)
.map_err(|err| (err, index))?; .map_err(|err| (err, sum_index))?;
sum_index += 1; sum_index += 1;
let string = tokenise::get_string_token(tokens, sum_index) let string = tokenise::get_string_token(tokens, sum_index)
.map_err(|err| (err, index))?; .map_err(|err| (err, sum_index))?;
info!("CHANGE command with character {character_name} feature {feature}"); info!("CHANGE command with character {character_name} feature {feature}");
let mut characters = characters.lock().unwrap_or_exit("Character Mutex was poisoned",3); let mut characters = characters.lock().unwrap_or_exit("Character Mutex was poisoned",3);
if let Some(character) = characters.get_mut(&character_name) if let Some(character) = characters.get_mut(&character_name)
@@ -74,7 +74,7 @@ pub fn character_parse
{ {
sum_index += 1; sum_index += 1;
let content = tokenise::get_keyword_token(tokens, sum_index) let content = tokenise::get_keyword_token(tokens, sum_index)
.map_err(|err| (err, index))?; .map_err(|err| (err, sum_index))?;
api::modify_data(happening_queue, keyword.to_lowercase(), content, character_name, vec![]); api::modify_data(happening_queue, keyword.to_lowercase(), content, character_name, vec![]);
}, },
// Catch all condition, if the instruction is unrecognised as a // Catch all condition, if the instruction is unrecognised as a
+72
View File
@@ -0,0 +1,72 @@
use crate::
{
// Internal code
character,
tokenise,
UnwrapOrExit,
parsing,
//Libs
HashMap,
VecDeque,
info,
warn,
debug,
};
#[allow(unused_variables)]
pub fn identifier_parse
(
index: usize,
identifier: &String,
tokens: &[tokenise::Token],
variables: &mut HashMap<String, tokenise::Value>,
) -> Result<usize,(String,usize)>
{
let mut sum_index: usize = index;
if ! variables.contains_key(identifier)
{
variables.insert(identifier.to_string(), tokenise::Value::Null);
}
let operator = tokenise::get_operator_token(tokens, sum_index)
.map_err(|err| (err, sum_index))?;
sum_index += 1;
let value = tokenise::get_value_token(tokens, sum_index)
.map_err(|err| (err, sum_index))?;
match operator
{
// Changing a value
tokenise::Operator::Assignment =>
{
variables.insert(identifier.to_string(), value);
} ,
tokenise::Operator::Add =>
{
let current = variables.get(identifier).unwrap();
let result: tokenise::Value = match (value.clone(), current)
{
(tokenise::Value::Integer(int1),tokenise::Value::Integer(int2)) => tokenise::Value::Integer(int1 + int2),
(tokenise::Value::String(str1),tokenise::Value::String(str2)) => tokenise::Value::String(format!("{str1}{str2}")),
_ => value.clone(), // otherwise invalid
};
variables.insert(identifier.to_string(), result);
},
tokenise::Operator::Sub =>
{
let current = variables.get(identifier).unwrap();
let result: tokenise::Value = match (value.clone(), current)
{
(tokenise::Value::Integer(int1),tokenise::Value::Integer(int2)) => tokenise::Value::Integer(int2 - int1),
_ => value.clone(), // otherwise invalid
};
// TODO comparisons :DDD
variables.insert(identifier.to_string(), result);
},
_ => (),
}
debug!("{variables:?}");
sum_index += 1;
Ok(sum_index)
}
+87 -4
View File
@@ -6,13 +6,21 @@ use crate::
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Token pub enum Token
{ {
String(String), Value(Value),
Operator(Operator),
Keyword(String), // Keywords aren't checked for validity in this stage Keyword(String), // Keywords aren't checked for validity in this stage
#[allow(dead_code)] // This is unused rn, but am going to add it later
Identifier(String), Identifier(String),
Bracket((Bracket,usize)), // Stores the index of the matching deliminator Bracket((Bracket,usize)), // Stores the index of the matching deliminator
Character(String), Character(String),
} }
#[derive(Debug,Clone)]
pub enum Value
{
String(String),
Integer(i64),
Bool(bool),
Null,
}
#[derive(Debug,Clone,PartialEq,Eq)] #[derive(Debug,Clone,PartialEq,Eq)]
pub enum Bracket pub enum Bracket
{ {
@@ -20,11 +28,42 @@ pub enum Bracket
Closing, Closing,
} }
#[derive(Debug, Clone)]
pub enum Operator
{
// Changing a value
Assignment,
Add,
Sub,
// Comparing a value
Comparison,
Greater,
Less,
GreaterOrEqual,
LessOrEqual,
}
pub fn get_operator_token(tokens: &[Token], index: usize)
-> Result<Operator, String>
{
match tokens.get(index) {
Some(Token::Operator(op)) => Ok(op.clone()),
Some(_) => Err("Unexpected token".to_string()),
None => Err("File unexpectedly reached termination point".to_string()),
}
}
pub fn get_string_token(tokens: &[Token], index: usize) pub fn get_string_token(tokens: &[Token], index: usize)
-> Result<String, String> -> Result<String, String>
{ {
match tokens.get(index) { match tokens.get(index) {
Some(Token::String(s)) => Ok(s.clone()), Some(Token::Value(val)) =>
{
match val
{
Value::String(s) => Ok(s.clone()),
_ => Err("Unexpected value type".to_string()),
}
},
Some(_) => Err("Unexpected token".to_string()), Some(_) => Err("Unexpected token".to_string()),
None => Err("File unexpectedly reached termination point".to_string()), None => Err("File unexpectedly reached termination point".to_string()),
} }
@@ -47,6 +86,15 @@ pub fn get_closing_index(tokens: &[Token], index: usize)
None => Err("File unexpectedly reached termination point".to_string()), None => Err("File unexpectedly reached termination point".to_string()),
} }
} }
pub fn get_value_token(tokens: &[Token], index: usize)
-> Result<Value, String>
{
match tokens.get(index) {
Some(Token::Value(val)) => Ok(val.clone()),
Some(_) => Err("Unexpected token".to_string()),
None => Err("File unexpectedly reached termination point".to_string()),
}
}
// Tokenise the story to Vec<Token> // Tokenise the story to Vec<Token>
// It can contain sub-objects (using recursive calls) // It can contain sub-objects (using recursive calls)
@@ -64,6 +112,29 @@ pub fn tokenise(file_contents: &str)
while index < space_seperated.len() while index < space_seperated.len()
{ {
let mut item = space_seperated[index].to_string(); let mut item = space_seperated[index].to_string();
// Operator
let op: Option<Operator> = match item.as_str()
{
"=" => Some(Operator::Assignment),
"+" => Some(Operator::Add),
"-" => Some(Operator::Sub),
"==" => Some(Operator::Comparison),
">" => Some(Operator::Greater),
"<" => Some(Operator::Less),
">=" => Some(Operator::GreaterOrEqual),
"<=" => Some(Operator::LessOrEqual),
_ => None,
};
match op
{
Some(op) =>
{
tokenised_data.push(Token::Operator(op));
index += 1;
continue // skip the rest of this loop
},
None => (),
}
// Characters // Characters
if item.starts_with('@') if item.starts_with('@')
{ {
@@ -78,7 +149,19 @@ pub fn tokenise(file_contents: &str)
else { return Err("File unexpectedly ended: No closing quote".to_string()) }; else { return Err("File unexpectedly ended: No closing quote".to_string()) };
index = new_index; index = new_index;
item = new_item; item = new_item;
tokenised_data.push(Token::String(item)); tokenised_data.push(Token::Value(Value::String(item)));
}
// variable/identifier
else if item.starts_with("$")
{
let mut chars = item.chars();
chars.next();
tokenised_data.push(Token::Identifier(chars.as_str().to_string()));
}
// Integer
else if item.parse::<i64>().is_ok()
{
tokenised_data.push(Token::Value(Value::Integer(item.parse::<i64>().unwrap()))); // unwrap is fine here because I checked
} }
// Labels // Labels
else if item.ends_with(':') else if item.ends_with(':')
+3
View File
@@ -1,5 +1,8 @@
@tim says "hello world, it's a good day" @tim says "hello world, it's a good day"
@tim change name "Timothy Fineshooter" @tim change name "Timothy Fineshooter"
$x = 3
$x + 1
$x - 2
label: label:
choice "choice numero uno" { choice "choice numero uno" {
@tim says "super sad" @tim says "super sad"
BIN
View File
Binary file not shown.