This commit is contained in:
Leon Liu 2024-12-24 08:27:15 +09:00
commit 4d092d56a5
11 changed files with 4637 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
/.debug
/.vscode

4338
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

12
Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "poe2-loot-filter-iced"
version = "0.1.0"
edition = "2021"
[dependencies]
config = "0.15.4"
iced = "0.13.1"
itertools = "0.13.0"
serde = { version = "1.0.216", features = ["derive"] }
serde_json = "1.0.134"
strum_macros = "0.26.4"

8
config/settings.json Normal file
View File

@ -0,0 +1,8 @@
{
"base_types": [],
"classes": ["Bows"],
"rarities": ["Normal", "Magic", "Rare", "Unique"],
"minimap_icon_shapes": ["Red", "Green", "Blue", "Brown", "White", "Yellow", "Cyan", "Grey", "Orange", "Pink", "Purple"],
"game_colors": ["Circle", "Diamond", "Hexagon", "Square", "Star", "Triangle", "Cross", "Moon", "Raindrop", "Kite", "Pentagon", "UpsideDownHouse"],
"ops": ["=", "!=", "<", "<=", ">", ">="]
}

23
src/components/app.rs Normal file
View File

@ -0,0 +1,23 @@
use iced::widget::{container, text, Container, column};
use crate::models::{FilterConfig, FilterText, Settings};
pub struct App {
pub settings: Settings,
pub filter_config: FilterConfig
}
#[derive(Debug, Clone, Copy)]
pub enum Message {
}
impl App {
pub fn view(&self) -> Container<Message> {
container(column![
text(format!("{:?}", &self.settings)),
text(format!("{:?}", &self.filter_config.to_filter_text()))
]).padding(8)
}
pub fn update(&mut self, message: Message) {
}
}

3
src/components/mod.rs Normal file
View File

@ -0,0 +1,3 @@
mod app;
pub use app::*;

21
src/main.rs Normal file
View File

@ -0,0 +1,21 @@
mod models;
mod components;
use components::App;
use iced::{Size, Task, Theme};
use models::Settings;
use config::Config;
pub fn main() -> iced::Result {
let settings = Config::builder()
.add_source(config::File::with_name("config/settings.json"))
.build().unwrap().try_deserialize::<Settings>().unwrap();
iced::application("POE2 Loot Filter Config Tools", App::update, App::view)
.theme(|_| Theme::GruvboxDark)
.window_size(Size::new(1920.0 / 2., 1080.0 / 2.))
.centered()
.run_with(|| (App {
settings: settings,
filter_config: vec![]
}, Task::none()))
}

19
src/models/base.rs Normal file
View File

@ -0,0 +1,19 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct RangedNumber<const MIN: u32, const MAX: u32>(u32);
impl<const MIN: u32, const MAX: u32> RangedNumber<MIN, MAX> {
pub fn new(value: u32) -> Result<Self, String> {
if value < MIN || value > MAX {
Err(format!("Value {} is out of range ({} - {})", value, MIN, MAX))
} else {
Ok(RangedNumber(value))
}
}
pub fn min() -> u32 {MIN}
pub fn max() -> u32 {MAX}
pub fn value(&self) -> u32 {self.0}
}

51
src/models/filter_text.rs Normal file
View File

@ -0,0 +1,51 @@
use super::{Color, Filter, FilterConfig, Line};
pub trait FilterText {
fn to_filter_text(&self) -> String;
}
impl FilterText for FilterConfig {
fn to_filter_text(&self) -> String {
self.filters.iter().rev()
.map(|filter| {filter.to_filter_text()})
.collect::<Vec<_>>().join("\n\n")
}
}
impl FilterText for Filter {
fn to_filter_text(&self) -> String {
let head = if self.show {"Show"} else {"Hide"};
head.to_owned() + "\n" + &self.lines.iter().map(|line| {line.to_filter_text()})
.collect::<Vec<_>>().join("\n")
}
}
impl FilterText for Line {
fn to_filter_text(&self) -> String {
match self {
Line::Class(item_class) => format!("Class {}", item_class.iter().map(|item| format!(r#""{}""#, item.value())).collect::<Vec<_>>().join(" ")),
Line::BaseType(item_base_type) => format!("BaseType {}", item_base_type.iter().map(|item| format!(r#""{}""#, item.value())).collect::<Vec<_>>().join(" ")),
Line::AreaLevel(op, level) => format!("AreaLevel {} {}", op.value(), level.value()),
Line::DropLevel(op, level) => format!("DropLevel {} {}", op.value(), level.value()),
Line::ItemLevel(op, level) => format!("ItemLevel {} {}", op.value(), level.value()),
Line::Rarity(op, item_rarity) => format!("Rarity {} {}", op.value(), item_rarity.value()),
Line::Sockets(op, i) => format!("Sockets {} {}", op.value(), i),
Line::Quality(op, i) => format!("Quality {} {}", op.value(), i),
Line::StackSize(op, i) => format!("StackSize {} {}", op.value(), i),
Line::WaystoneTier(op, waystone_tier) => format!("WaystoneTier {} {}", op.value(), waystone_tier.value()),
Line::SetFontSize(font_size) => format!("SetFontSize {}", font_size.value()),
Line::SetTextColor(color) => format!("SetTextColor {}", color.to_filter_text()),
Line::SetBorderColor(color) => format!("SetBorderColor {}", color.to_filter_text()),
Line::SetBackgroundColor(color) => format!("SetBackgroundColor {}", color.to_filter_text()),
Line::PlayAlertSound(alert_sound_id, alert_sound_volume) => format!("PlayAlertSound {} {}", alert_sound_id.value(), alert_sound_volume.value()),
Line::PlayEffect(game_color) => format!("PlayEffect {}", game_color.value()),
Line::MinimapIcon(minimap_icon_size, game_color, minimap_icon_shape) => format!("MinimapIcon {} {} {}", minimap_icon_size.value(), game_color.value(), minimap_icon_shape.value()),
}
}
}
impl FilterText for Color {
fn to_filter_text(&self) -> String {
format!("{} {} {} {}", self.r, self.g, self.b, self.a)
}
}

152
src/models/loot_filter.rs Normal file
View File

@ -0,0 +1,152 @@
use serde::{Deserialize, Serialize};
use super::base::RangedNumber;
use itertools::Itertools;
#[derive(Debug)]
pub struct FilterConfig {
pub filters: Vec<Filter>,
test: dyn Test
}
trait Test: std::fmt::Debug {}
#[derive(Serialize, Deserialize, Debug)]
pub struct Filter {
pub show: bool,
pub lines: Vec<Line>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
pub type Level = RangedNumber<1, 100>;
#[derive(Serialize, Deserialize, Debug)]
pub enum Line {
Class(Vec<ItemClass>),
BaseType(Vec<ItemBaseType>),
AreaLevel(Op, Level),
DropLevel(Op, Level),
ItemLevel(Op, Level),
Rarity(Op, ItemRarity),
Sockets(Op, u32),
Quality(Op, u32),
StackSize(Op, u32),
// waystones
WaystoneTier(Op, RangedNumber<1, 16>),
// effects
SetFontSize(RangedNumber<1, 45>),
SetTextColor(Color),
SetBorderColor(Color),
SetBackgroundColor(Color),
PlayAlertSound(RangedNumber<1, 16>, RangedNumber<0, 300>),
PlayEffect(GameColor),
MinimapIcon(RangedNumber<0, 2>, GameColor, MinimapIconShape),
}
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct Settings {
base_types: Vec<String>,
classes: Vec<String>,
rarities: Vec<String>,
minimap_icon_shapes: Vec<String>,
game_colors: Vec<String>,
ops: Vec<String>,
}
impl Settings {
fn format_err(value: &str, category: &str, options: &Vec<String>) -> String {
format!("{} is not a valid {}, options are: {}", value, category, options.iter().join(" "))
}
pub fn base_type(&self, s: &String) -> Result<ItemBaseType, String> {
if self.base_types.contains(s) {
Err(Self::format_err(s, "BaseType", &self.base_types))
} else {
Ok(ItemBaseType(s.to_string()))
}
}
pub fn class(&self, s: &String) -> Result<ItemClass, String> {
if self.classes.contains(s) {
Err(Self::format_err(s, "Class", &self.classes))
} else {
Ok(ItemClass(s.to_string()))
}
}
pub fn rarity(&self, s: &String) -> Result<ItemRarity, String> {
if self.rarities.contains(s) {
Err(Self::format_err(s, "Rarity", &self.rarities))
} else {
Ok(ItemRarity(s.to_string()))
}
}
pub fn op(&self, s: &String) -> Result<Op, String> {
if self.ops.contains(s) {
Err(Self::format_err(s, "Operator", &self.ops))
} else {
Ok(Op(s.to_string()))
}
}
pub fn minimap_icon_shape(&self, s: &String) -> Result<MinimapIconShape, String> {
if self.minimap_icon_shapes.contains(s) {
Err(Self::format_err(s, "MinimapIconShape", &self.minimap_icon_shapes))
} else {
Ok(MinimapIconShape(s.to_string()))
}
}
pub fn game_color(&self, s: &String) -> Result<GameColor, String> {
if self.game_colors.contains(s) {
Err(Self::format_err(s, "GameColor", &self.game_colors))
} else {
Ok(GameColor(s.to_string()))
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ItemBaseType(String);
impl ItemBaseType {
pub fn value(&self) -> &str {&self.0}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ItemClass(String);
impl ItemClass {
pub fn value(&self) -> &str {&self.0}
}
#[derive(Deserialize, Serialize, Debug)]
pub struct ItemRarity(String);
impl ItemRarity {
pub fn value(&self) -> &str {&self.0}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MinimapIconShape(String);
impl MinimapIconShape {
pub fn value(&self) -> &str {&self.0}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct GameColor(String);
impl GameColor {
pub fn value(&self) -> &str {&self.0}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Op(String);
impl Op {
pub fn value(&self) -> &str {&self.0}
}

7
src/models/mod.rs Normal file
View File

@ -0,0 +1,7 @@
mod loot_filter;
mod filter_text;
mod base;
pub use loot_filter::*;
pub use filter_text::*;