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"
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]]
name = "fastrand"
version = "2.4.1"
@@ -379,11 +389,13 @@ dependencies = [
name = "happening-client"
version = "0.1.0"
dependencies = [
"home",
"macroquad",
"phf",
"reqwest",
"serde",
"serde_json",
"tokio",
]
[[package]]
@@ -403,6 +415,15 @@ version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "http"
version = "1.4.0"
@@ -724,6 +745,15 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "log"
version = "0.4.29"
@@ -846,6 +876,29 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "percent-encoding"
version = "2.3.2"
@@ -1047,6 +1100,15 @@ dependencies = [
"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]]
name = "reqwest"
version = "0.13.3"
@@ -1217,6 +1279,12 @@ dependencies = [
"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]]
name = "security-framework"
version = "3.7.0"
@@ -1295,6 +1363,16 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "simd-adler32"
version = "0.3.9"
@@ -1463,11 +1541,25 @@ dependencies = [
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"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]]
name = "tokio-rustls"
version = "0.26.4"
+2
View File
@@ -4,8 +4,10 @@ version = "0.1.0"
edition = "2024"
[dependencies]
home = "0.5.12"
macroquad = "0.4.15"
phf = {version="0.13.1",features=["macros"]}
reqwest = {version="0.13.3",features=["blocking","json"]}
serde = {version="1.0.228",features=["derive"]}
serde_json = "1.0.149"
tokio = {version="1.52.3",features=["full"]}
+89 -21
View File
@@ -1,14 +1,24 @@
use macroquad::prelude::*;
use reqwest::*;
use reqwest::blocking;
use std::collections::HashMap;
use phf::phf_map;
use std::{thread, time::Duration};
use std::
{
thread,
time::Duration,
collections::
{
HashMap,
HashSet,
},
process::exit,
};
use serde::
{
Serialize,
Deserialize,
};
use home::home_dir;
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct Data {
@@ -69,45 +79,92 @@ static POSITIONS: phf::Map<&'static str, [f32;2]> = phf_map! [
async fn main()
{
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
{
clear_background(RED);
// Get the next character
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)
for (name, (texture, x, y, colour)) in &textures
{
let new_character = get_character(character_name).await;
characters.insert(character_name.to_string(), new_character);
draw_texture(&texture, *x, *y, *colour);
}
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
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" =>
{
println!("SAYING");
draw_text(characters[character_name].0.name.clone(), 50.0,20.0,40.0,WHITE);
draw_text(data.content.as_str(), 50.0,40.0,30.0,WHITE);
text = format!("{}: {}", characters[&character_name].0.name.clone(),wrap_text(&data.content).as_str());
},
"to" =>
{
let position = POSITIONS.get(&data.content).cloned().unwrap();
let texture = &characters[character_name].1;
textures.push((texture.clone(), position[0], position[1], WHITE)); // Heavy
let texture = &characters[&character_name].1;
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;
}
}
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()
-> Data
{
@@ -116,6 +173,17 @@ fn next_happening()
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)
-> (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 hair_colour = character.hair_color.clone();
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);
(character, head)
}
+16
View File
@@ -18,6 +18,8 @@ use crate::
mod character_parse;
mod keyword_parse;
mod identifier_parse;
// Parse the tokens in a file
// Returns success or an error string
@@ -30,6 +32,7 @@ pub fn token_parse(
) -> Result<(),String>
{
let mut index: usize = 0;
let mut variables: HashMap<String, tokenise::Value> = HashMap::new();
info!("Client has connected");
// Run an infinite 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(_) =>
{
warn!("Unexpected token");
+4 -4
View File
@@ -46,7 +46,7 @@ pub fn character_parse
info!("SAYS command with character {character_name}");
sum_index += 1;
let output = tokenise::get_string_token(tokens, sum_index)
.map_err(|err| (err, index))?;
.map_err(|err| (err, sum_index))?;
debug!("Saying {output}");
api::modify_data(happening_queue, "output".to_string(), output, character_name, vec![]);
},
@@ -56,10 +56,10 @@ pub fn character_parse
{
sum_index += 1;
let feature = tokenise::get_keyword_token(tokens, sum_index)
.map_err(|err| (err, index))?;
.map_err(|err| (err, sum_index))?;
sum_index += 1;
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}");
let mut characters = characters.lock().unwrap_or_exit("Character Mutex was poisoned",3);
if let Some(character) = characters.get_mut(&character_name)
@@ -74,7 +74,7 @@ pub fn character_parse
{
sum_index += 1;
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![]);
},
// 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)]
pub enum Token
{
String(String),
Value(Value),
Operator(Operator),
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),
Bracket((Bracket,usize)), // Stores the index of the matching deliminator
Character(String),
}
#[derive(Debug,Clone)]
pub enum Value
{
String(String),
Integer(i64),
Bool(bool),
Null,
}
#[derive(Debug,Clone,PartialEq,Eq)]
pub enum Bracket
{
@@ -20,11 +28,42 @@ pub enum Bracket
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)
-> Result<String, String>
{
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()),
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()),
}
}
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>
// It can contain sub-objects (using recursive calls)
@@ -64,6 +112,29 @@ pub fn tokenise(file_contents: &str)
while index < space_seperated.len()
{
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
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()) };
index = new_index;
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
else if item.ends_with(':')
+3
View File
@@ -1,5 +1,8 @@
@tim says "hello world, it's a good day"
@tim change name "Timothy Fineshooter"
$x = 3
$x + 1
$x - 2
label:
choice "choice numero uno" {
@tim says "super sad"
BIN
View File
Binary file not shown.