~jacqueline/pokemon

ee3373aad8434e6d93bbbdf0bdb84a75a1b2de0d — jacqueline leykam 1 year, 2 months ago c8a57eb
WIP on encoding
4 files changed, 325 insertions(+), 2 deletions(-)

M Cargo.toml
M src/bin/pack-resources.rs
A src/lib.rs
M src/main.rs
M Cargo.toml => Cargo.toml +4 -0
@@ 9,9 9,13 @@ embedded-picofont = "0.2.1"
embedded-text = "0.4.1"
embedded-layout = "0.1.0"
tinybmp = {version = "0.2.3", features = ["graphics"]}
minicbor = {version = "0.12.0", features = ["partial-derive-support"]}

[target.'cfg(unix)'.dependencies]
embedded-graphics-simulator = "0.2.1"
serde_json = "1.0.74"
minicbor-derive = "0.8.0"
minicbor-io = "0.7.0"

[target.'cfg(all(target_arch = "xtensa", target_os = "none"))'.dependencies]
esp8266-hal = "0.5.0"

M src/bin/pack-resources.rs => src/bin/pack-resources.rs +155 -1
@@ 1,4 1,158 @@
use std::{env, fs::File};
use std::io::{prelude::*, BufReader, BufWriter};
use minicbor::bytes::ByteArray;
use minicbor_io::Writer;
use pokemon::pokedex::{self, Pokemon, Type, Stats};
use serde_json::Value;

fn main() {
    println!("Hello, World!");
    let args: Vec<String> = env::args().collect();

    if args.len() != 3 {
        println!("usage: {} <path-to-pokemon> <output>", args[0]);
        return;
    }

    println!("you provided {}", args[1]);

    let mut bmp_file = File::open(format!("{}.bmp", args[1])).unwrap();
    let mut buffer = [0u8; 578];
    bmp_file.read_exact(&mut buffer).unwrap();

    let json_file = File::open(format!("{}.json", args[1])).unwrap();
    let json: Value = serde_json::from_reader(BufReader::new(json_file)).unwrap();

    let pokemon = parse_pokemon(json, &buffer).unwrap();

    println!("created pokemon {:?}", pokemon);

    let output_file = BufWriter::new(File::create(&args[2]).unwrap());
    let mut cbor_writer = Writer::new(output_file);
    let outsize = cbor_writer.write(pokemon).unwrap();

    println!("wrote to file. final size was {} bytes", outsize);
}

fn parse_pokemon(json: Value, bitmap: &[u8; 578]) -> Result<Pokemon, &'static str> {
    let name = match json["name"].as_str() {
        Some(n) => {
            let mut i = 0;
            let mut name = ['\0'; 12];
            for c in n.chars() {
                name[i] = c;
                i += 1;
            }
            name
        },
        None => return Err("name was missing")
    };

    let mut types = [Option::None, Option::None];
    match json["types"].as_array() {
        Some(raw_types) => {
            for t in raw_types {
                let slot = match t["slot"].as_i64() {
                    Some(1) => 0,
                    Some(2) => 1,
                    None => return Err("missing type slot"),
                    _ => return Err("invalid type slot")
                };
                types[slot] = match t["type"].as_str() {
                    Some(val) => {
                        match type_from_string(val) {
                            Ok(t) => Some(t),
                            Err(_) => return Err("failed to parse type")
                        }
                    }
                    None => None
                }
            }
        }
        None => return Err("no types found")
    }

    let stats = match json["stats"].as_array() {
        Some(all_stats) => all_stats.iter().flat_map(|j| parse_as_stats(j)).collect(),
        None => return Err("missing stats")
    };

     Ok(pokedex::Pokemon {
        id: match parse_as_u8(&json["id"]) {
            Some(i) => i,
            None => return Err("missing id")
        },
        name,
        type_primary: match types[0] {
            Some(t) => t,
            None => return Err("no primary type given")
        },
        type_secondary: types[1],
        capture_rate: match parse_as_u8(&json["species"]["capture_rate"]) {
            Some(i) => i,
            None => return Err("missing capture rate")
        },
        base_experience: match parse_as_u8(&json["id"]) {
            Some(i) => i,
            None => return Err("missing base experience")
        },
        stats,
        sprite: ByteArray::from(*bitmap),
    })
}

fn parse_as_u8(json: &Value) -> Option<u8> {
        return match json.as_u64().map(|i| u8::try_from(i)) {
            Some(i) => {
                match i {
                    Ok(i) => Some(i),
                    Err(_) => None
                }
            },
            None => None
        }
}

fn parse_as_stats(json: &Value) -> Option<Stats> {
    Some(Stats {
        stat: match json["stat"].as_str() {
            Some(s) => match s {
                "hp" => Stat::Hp,
                "attack" => Stat::Attack,
                "defense" => Stat::Defense,
                "special-attack" => Stat::SpecialAttack,
                "special-defense" => Stat::SpecialDefense,
                "speed" => Stat::Speed,
                _ => return None
            }
            None => return None
        },
        base_value: match json["base_state"] {
            Some(i) => i,
            None => return None
        }
    })
}

fn type_from_string(s: &str) -> Result<Type, &str> {
    match s {
        "normal" => Ok(Type::Normal),
        "fighting" => Ok(Type::Fighting),
        "flying" => Ok(Type::Flying),
        "poison" => Ok(Type::Poison),
        "ground" => Ok(Type::Ground),
        "rock" => Ok(Type::Rock),
        "bug" => Ok(Type::Bug),
        "ghost" => Ok(Type::Ghost),
        "steel" => Ok(Type::Steel),
        "fire" => Ok(Type::Fire),
        "water" => Ok(Type::Water),
        "grass" => Ok(Type::Grass),
        "electric" => Ok(Type::Electric),
        "psychic" => Ok(Type::Psychic),
        "ice" => Ok(Type::Ice),
        "dragon" => Ok(Type::Dragon),
        "dark" => Ok(Type::Dark),
        "fairy" => Ok(Type::Fairy),
        _ => Err("unknown type")
    }
}
\ No newline at end of file

A src/lib.rs => src/lib.rs +165 -0
@@ 0,0 1,165 @@
#![no_std]

pub mod pokedex {
    use minicbor::{Encode, Decode, bytes::ByteArray};

    #[derive(Encode, Decode, Debug)]
    pub struct Pokemon {
        #[n(0)] pub id: u8,
        #[n(1)] pub name: [char; 12],
        #[n(2)] pub type_primary: Type,
        #[n(3)] pub type_secondary: Option<Type>,

        #[n(4)] pub capture_rate: u8,
        #[n(5)] pub base_experience: u8,
        #[n(6)] pub stats: [Stats; 6],

        #[b(7)] pub sprite: ByteArray<578>,
    }

    #[derive(Encode, Decode, Debug)]
    #[cbor(index_only)]
    pub enum Stat {
        #[n(0)] Hp,
        #[n(1)] Attack,
        #[n(2)] Defense,
        #[n(3)] SpecialAttack,
        #[n(4)] SpecialDefense,
        #[n(5)] Speed,
    }

    #[derive(Encode, Decode, Debug)]
    pub struct Stats {
        #[n(0)] stat: Stat,
        #[n(1)] base_value: u16,
    }

    // These could be created dynamically from the JSON data. This isn't so bad
    // though so /shruggie.
    #[derive(Encode, Decode, Debug, Clone, Copy)]
    #[cbor(index_only)]
    pub enum Type {
        #[n(0)] Normal,
        #[n(1)] Fighting,
        #[n(2)] Flying,
        #[n(3)] Poison,
        #[n(4)] Ground,
        #[n(5)] Rock,
        #[n(6)] Bug,
        #[n(7)] Ghost,
        #[n(8)] Steel,
        #[n(9)] Fire,
        #[n(10)] Water,
        #[n(11)] Grass,
        #[n(12)] Electric,
        #[n(13)] Psychic,
        #[n(14)] Ice,
        #[n(15)] Dragon,
        #[n(16)] Dark,
        #[n(17)] Fairy,
    }

    #[derive(Encode, Decode, Debug)]
    pub struct MoveListChunk {
        #[n(0)] is_final_chunk: bool,
        #[n(1)] moves: [Option<LearnableMove>; 16],
    }

    #[derive(Encode, Decode, Debug)]
    pub struct LearnableMove {
        #[n(0)] id: u16,
        #[n(1)] condition: LearnCondition,
    }

    #[derive(Encode, Decode, Debug)]
    pub enum LearnCondition {
        #[n(0)] LevelUp {
            #[n(0)] level: u8
        },
        #[n(1)] Machine,
    }

    #[derive(Encode, Decode, Debug)]
    pub struct Move {
        #[n(0)] id: u16,
        #[n(1)] name: [char; 12],

        #[n(2)] type_: Type,
        #[n(3)] damage_class: DamageClass,
        #[n(4)] target: Target,

        #[n(5)] accuracy: u8,
        #[n(6)] power: u8,
        #[n(7)] pp: u8,
        #[n(8)] priority: u8,

        #[n(9)] parameters: Parameters,
        #[n(10)] stat_changes: [Option<StatChange>; 2]
    }

    #[derive(Encode, Decode, Debug)]
    #[cbor(map)]
    pub struct Parameters {
        #[n(0)] ailment: Option<AilmentParameter>,
        #[n(1)] crit_rate: u8,
        #[n(2)] drain: u8,
        #[n(3)] flinch_chance: u8,
        #[n(4)] healing: u8,
        #[n(5)] stat_chance: u8,
        #[n(6)] turn_range: Option<ParameterRange>,
        #[n(7)] hit_range: Option<ParameterRange>,
    }

    #[derive(Encode, Decode, Debug)]
    pub struct AilmentParameter {
        #[n(0)] ailment: AilmentType,
        #[n(1)] chance: u8,
    }

    #[derive(Encode, Decode, Debug)]
    #[cbor(index_only)]
    pub enum AilmentType {
        #[n(0)] Burn,
        #[n(1)] Confusion,
        #[n(2)] Disable,
        #[n(3)] Freeze,
        #[n(4)] LeechSeed,
        #[n(5)] Paralysis,
        #[n(6)] Poison,
        #[n(7)] Sleep,
        #[n(8)] Trap,
    }

    #[derive(Encode, Decode, Debug)]
    pub struct ParameterRange {
        #[n(0)] min: u8,
        #[n(1)] max: u8,
    }

    #[derive(Encode, Decode, Debug)]
    #[cbor(index_only)]
    pub enum DamageClass {
        #[n(0)] Physical,
        #[n(1)] Special,
        #[n(2)] Status,
    }

    #[derive(Encode, Decode, Debug)]
    #[cbor(index_only)]
    pub enum Target {
        #[n(0)] AllOpponents,
        #[n(1)] AllOtherPokemon,
        #[n(2)] EntireField,
        #[n(3)] RandomOpponent,
        #[n(4)] SelectedPokemon,
        #[n(5)] SpecificMove,
        #[n(6)] User,
        #[n(7)] UserField,
    }

    #[derive(Encode, Decode, Debug)]
    pub struct StatChange {
        #[n(0)] amount: u8,
        #[n(1)] stat: Stat,
    }
}

M src/main.rs => src/main.rs +1 -1
@@ 107,4 107,4 @@ fn show_component(timer: &mut Timer1, pin: &mut Gpio15<Output<PushPull>>, compon
        }
        mask >>= 1;
    }
}
\ No newline at end of file
}