224 lines
5.7 KiB
Rust
224 lines
5.7 KiB
Rust
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<String>,
|
|
}
|
|
|
|
#[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<String, (Character, Texture2D)> = HashMap::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);
|
|
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<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");
|
|
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::<Vec<_>>()
|
|
.chunks(30)
|
|
.map(|chunk| chunk.iter().collect::<String>())
|
|
.collect::<Vec<_>>()
|
|
.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)
|
|
}
|