use macroquad::prelude::*; use reqwest::*; use reqwest::blocking; use phf::phf_map; 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 { pub action_type: String, pub content: String, pub character: String, pub choices: Vec, } #[derive(Debug, Deserialize, Serialize, Clone, Default)] #[serde(default)] pub struct Character { name: String, gender: String, eye_color: Colour, hair_color: Colour, skin_color: Colour, pronoun_subject: String, pronoun_object: String, pronoun_deppos: String, pronoun_indpos: String, pronoun_reflex: String, head_shape: String, hair_style: String, torso_shape: String, arm_shape: String, leg_shape: String, clothing: Clothing, } #[derive(Debug,Deserialize,Serialize,Clone,Default)] #[serde(default)] pub struct Clothing { top: String, bottom: String, shoes: String, } #[derive(Debug,Deserialize,Serialize,Clone,Default)] #[serde(default)] pub struct Colour { red: u8, green: u8, blue: u8, } static POSITIONS: phf::Map<&'static str, [f32;2]> = phf_map! [ "fl" => [0.0,500.0], "bl" => [0.0,0.0], "fr" => [500.0,500.0], "br" => [500.0,0.0], ]; #[macroquad::main("happening")] async fn main() { let mut characters: HashMap = HashMap::new(); characters.insert("narrator".to_string(), (Character { name: "Narrator".to_string(), ..Default::default() }, Texture2D::empty())); let mut textures: HashMap = 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); for (name, (texture, x, y, colour)) in &textures { 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 = 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"); 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.insert(character_name.clone(),(texture.clone(), position[0], position[1], WHITE)); // Heavy } "begin" => (), "end" => exit(0), _ => println!("Unknown action, {}", data.action_type), } next_frame().await; } } fn wrap_text(text: &str) -> String { text.chars() .collect::>() .chunks(30) .map(|chunk| chunk.iter().collect::()) .collect::>() .join("\n") } fn next_happening() -> Data { let data: Data = reqwest::blocking::get(format!("http://127.0.0.1:20264/happening")).unwrap().json().unwrap(); println!("{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) -> (Character, Texture2D) { let character: Character = reqwest::blocking::get(format!("http://127.0.0.1:20264/character/{name}")).unwrap().json().unwrap(); println!("{character:?}"); let skin_colour = character.skin_color.clone(); 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!("/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) } fn change_colour(image: &mut Image, skin: &Color, hair: &Color) -> Texture2D { let target = Color::from_rgba(255,0,255,255); for y in 0..image.height() { for x in 0..image.width() { let pixel = image.get_pixel(x as u32, y as u32); if pixel == target { image.set_pixel(x as u32, y as u32, *skin); } } } let target = Color::from_rgba(0,255,255,255); for y in 0..image.height() { for x in 0..image.width() { let pixel = image.get_pixel(x as u32, y as u32); if pixel == target { image.set_pixel(x as u32, y as u32, *hair); } } } Texture2D::from_image(&image) }