Initial Commit

This commit is contained in:
2025-11-30 03:04:45 -06:00
commit b23796fc77
18 changed files with 5349 additions and 0 deletions

14
.cargo/config.toml Normal file
View File

@@ -0,0 +1,14 @@
[target.xtensa-esp32-none-elf]
runner = "espflash flash --monitor --chip esp32"
[env]
[build]
rustflags = [
"-C", "link-arg=-nostartfiles",
]
target = "xtensa-esp32-none-elf"
[unstable]
build-std = ["alloc", "core"]

17
.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

7
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"rust-lang.rust-analyzer",
"tamasfe.even-better-toml",
"fill-labs.dependi"
]
}

13
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"rust-analyzer.cargo.allTargets": false,
"rust-analyzer.cargo.target": "xtensa-esp32-none-elf",
"rust-analyzer.server.extraEnv": {
"RUSTUP_TOOLCHAIN": "stable"
},
"rust-analyzer.check.extraEnv": {
"RUSTUP_TOOLCHAIN": "esp"
},
"rust-analyzer.cargo.extraEnv": {
"RUSTUP_TOOLCHAIN": "esp"
},
}

1231
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

42
Cargo.toml Normal file
View File

@@ -0,0 +1,42 @@
[package]
edition = "2021"
name = "mtgcount-rs"
version = "0.1.0"
[[bin]]
name = "mtgcount-rs"
path = "./src/bin/main.rs"
[dependencies]
critical-section = "1.2.0"
display-interface = "0.5.0"
embedded-graphics = "0.8.1"
embedded-hal = "1.0.0"
embedded-hal-bus = "0.3.0"
esp-alloc = "0.7.0"
esp-backtrace = { version = "0.15.1", features = [
"esp32",
"exception-handler",
"panic-handler",
"println",
] }
esp-hal = { version = "1.0.0-beta.0", features = ["esp32", "unstable"] }
esp-println = { version = "0.13.0", features = ["esp32"] }
esp32 = "0.38.0"
micromath = "2.1.0"
mipidsi = "0.9.0"
u8g2-fonts = "0.7.1"
[profile.dev]
# Rust debug is too slow.
# For debug builds always builds with some optimization
opt-level = "s"
[profile.release]
codegen-units = 1 # LLVM can perform better optimizations using a single thread
debug = 2
debug-assertions = false
incremental = false
lto = 'fat'
opt-level = 's'
overflow-checks = false

40
build.rs Normal file
View File

@@ -0,0 +1,40 @@
fn main() {
linker_be_nice();
// make sure linkall.x is the last linker script (otherwise might cause problems with flip-link)
println!("cargo:rustc-link-arg=-Tlinkall.x");
}
fn linker_be_nice() {
let args: Vec<String> = std::env::args().collect();
if args.len() > 1 {
let kind = &args[1];
let what = &args[2];
match kind.as_str() {
"undefined-symbol" => match what.as_str() {
"_defmt_timestamp" => {
eprintln!();
eprintln!("💡 `defmt` not found - make sure `defmt.x` is added as a linker script and you have included `use defmt_rtt as _;`");
eprintln!();
}
"_stack_start" => {
eprintln!();
eprintln!("💡 Is the linker script `linkall.x` missing?");
eprintln!();
}
_ => (),
},
// we don't have anything helpful for "missing-lib" yet
_ => {
std::process::exit(1);
}
}
std::process::exit(0);
}
println!(
"cargo:rustc-link-arg=-Wl,--error-handling-script={}",
std::env::current_exe().unwrap().display()
);
}

2
rust-toolchain.toml Normal file
View File

@@ -0,0 +1,2 @@
[toolchain]
channel = "esp"

216
src/bin/main.rs Normal file
View File

@@ -0,0 +1,216 @@
#![no_std]
#![no_main]
use embedded_graphics::framebuffer::{buffer_size, Framebuffer};
use embedded_graphics::pixelcolor::raw::LittleEndian;
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::draw_target::DrawTarget;
use embedded_graphics::image::ImageDrawable;
use esp_backtrace as _;
use esp_hal::clock::CpuClock;
use esp_hal::gpio::{self, InputConfig, OutputConfig};
use esp_hal::rtc_cntl::sleep::{GpioWakeupSource, RtcSleepConfig};
use esp_hal::rtc_cntl::Rtc;
use esp_hal::main;
use esp_println::println;
extern crate alloc;
use mtgcount_rs::{self, TP_GESTURE, TP_X, TP_Y};
#[main]
fn main() -> ! {
// generator version: 0.3.1
println!("Startup init");
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config);
let pins = mtgcount_rs::init::UsedPins::init(peripherals);
let mut buffer = [0_u8;256];
//set clocks;
let mut iic = pins.init_iic();
let mut display = pins.init_display(&mut buffer);
let mut framebuf: Framebuffer<Rgb565, _, LittleEndian, 240, 240, {buffer_size::<Rgb565>(240, 240)}> = Framebuffer::new();
//let mut bgbuf: Framebuffer<Rgb565, _, LittleEndian, 240, 240, {buffer_size::<Rgb565>(240, 240)}> = Framebuffer::new();
//start bl
println!("Begin backlight setup");
println!("Start Low Power backlight config");
let low_power = unsafe {esp_hal::peripherals::LPWR::steal()};
low_power.register_block().clk_conf().modify(|_,w| w.ck8m_force_pd().clear_bit());
low_power.register_block().clk_conf().modify(|_,w| w.ck8m_force_pu().set_bit());
low_power.register_block().clk_conf().modify(|_,w| w.enb_ck8m().clear_bit());
low_power.register_block().clk_conf().modify(|_,w| w.dig_clk8m_en().set_bit());
println!("Init Backlight peripherals");
let (led, bl, rtcio) = pins.init_backlight();
let mux = unsafe{esp_hal::peripherals::IO_MUX::steal()};
mux.register_block().gpio32().modify(|_,w| w.mcu_oe().set_bit());
rtcio.register_block().enable().modify(|_,w| unsafe{w.enable().bits(0x80_00_00)});
rtcio.register_block().xtal_32k_pad().modify(|_,w| w.x32n_hold().clear_bit());
rtcio.register_block().xtal_32k_pad().modify(|_,w| unsafe{w.x32n_fun_sel().bits(0)});
rtcio.register_block().xtal_32k_pad().modify(|_,w| w.x32n_slp_oe().set_bit());
let _ledc = esp_hal::ledc::Ledc::new(led);
//ledc.set_global_slow_clock(esp_hal::ledc::LSGlobalClkSource::APBClk);
println!("Set apb_clk_sel");
let test = unsafe {esp_hal::peripherals::LEDC::steal()};
test.register_block().conf().write(|w| w.apb_clk_sel().clear_bit());
test.register_block().lstimer(0).conf().modify(|_,w| w.para_up().set_bit());
//let mut lstimer0 = ledc.timer::<LowSpeed>(esp_hal::ledc::timer::Number::Timer0);
//lstimer0.configure(esp_hal::ledc::timer::config::Config {
// duty: esp_hal::ledc::timer::config::Duty::Duty5Bit,
// clock_source: esp_hal::ledc::timer::LSClockSource::APBClk,
// frequency: Rate::from_khz(2),
//}).unwrap();
let precision = 1 << 8u64;
let divisor = ((8_000_000u64) << 8) / 1_000 / precision;
println!("Set up lstimer0");
println!("Lstimer divisor is:{}", divisor);
test.register_block().lstimer(0).conf().modify(|_,w| unsafe {
w.tick_sel().bit(true);
w.rst().clear_bit();
w.pause().clear_bit();
w.div_num().bits(divisor as u32);
w.duty_res().bits(8)
});
test.register_block().lstimer(0).conf().modify(|_,w| w.para_up().set_bit());
//let mut channel0 = ledc.channel(esp_hal::ledc::channel::Number::Channel0, bl);
//channel0.configure(esp_hal::ledc::channel::config::Config {
// timer: &lstimer0,
// duty_pct: 50,
// pin_config: esp_hal::ledc::channel::config::PinConfig::PushPull,
//}).unwrap();
let mut duty_percent = 100;
let duty_range = 2u32.pow(8);
let mut duty_value = (duty_range * duty_percent) / 100;
println!("Setup channel0");
test.register_block().lsch(0).duty().write(|w| unsafe {
w.duty().bits(duty_value<<4)
});
test.register_block().lsch(0).conf1().write(|w| unsafe {
w.duty_start().set_bit();
w.duty_inc().set_bit();
w.duty_num().bits(0x1);
w.duty_cycle().bits(0x1);
w.duty_scale().bits(0x0)
});
test.register_block().lsch(0).conf0().modify(|_,w| w.para_up().set_bit());
println!("Configure output signal");
let newbl: esp_hal::gpio::interconnect::OutputSignal = bl.into();
println!("line1");
newbl.apply_output_config(&OutputConfig::default().with_drive_mode(gpio::DriveMode::PushPull));
println!("line2");
newbl.set_output_enable(true);
println!("Completed output signal config");
test.register_block().lsch(0).hpoint().write(|w| unsafe {
w.hpoint().bits(0x0)
});
println!("Completed hpoint set");
test.register_block().lsch(0).conf0().modify(|_,w| unsafe {
w.sig_out_en().set_bit().timer_sel().bits(0)
});
println!("Completed signal output enable");
test.register_block().lsch(0).conf1().write(|w| unsafe {
w.duty_start().set_bit();
w.duty_inc().set_bit();
w.duty_num().bits(0x1);
w.duty_cycle().bits(0x1);
w.duty_scale().bits(0x0)
});
test.register_block().lsch(0).conf0().modify(|_,w| w.para_up().set_bit());
println!("Completed channel0 reupdate");
let signal = esp_hal::gpio::OutputSignal::LEDC_LS_SIG0;
signal.connect_to(&newbl);
println!("End Backlight setup");
//end bl
let mut read_buffer = [0_u8;1];
esp_alloc::heap_allocator!(size: 16 * 1024);
let pinconf = InputConfig::default();
let irq = pins.irq();
let _wake_pin = esp_hal::gpio::Input::new(irq, pinconf).wakeup_enable(true,esp_hal::gpio::WakeEvent::LowLevel).unwrap();
let waker = GpioWakeupSource::new();
println!("Start Sleep Config setup");
let mut rtc_control = Rtc::new(low_power);
println!("line1");
let mut rtc_config: RtcSleepConfig = RtcSleepConfig::default();
println!("line2");
rtc_config.set_int_8m_pd_en(false);
println!("Finish Sleep Config setup");
//let loop_delay = esp_hal::delay::Delay::new();
let mut gesture;
let mut x;
let mut y;
println!("Systemstate init");
let mut state = mtgcount_rs::state::SystemState::new();
println!("screen: {:?}", state.screen);
framebuf.clear(state.settings.color_scheme.get_color(&mtgcount_rs::display::ColorType::Background)).unwrap();
display.clear(state.settings.color_scheme.get_color(&mtgcount_rs::display::ColorType::Background)).unwrap();
mtgcount_rs::display::draw_screen(&state, &mut framebuf);
framebuf.as_image().draw(&mut display).unwrap();
loop {
//println!("Enterd Loop");
iic.write_read(0x15, &[TP_GESTURE], &mut read_buffer).expect("Failed to do iic");
gesture = read_buffer[0];
iic.write_read(0x15, &[TP_X], &mut read_buffer).expect("Failed to do iic");
x = read_buffer[0];
iic.write_read(0x15, &[TP_Y], &mut read_buffer).expect("Failed to do iic");
y = read_buffer[0];
mtgcount_rs::touch::get_touch_action(x as f32, y as f32, gesture, &mut state);
duty_percent = state.settings.brightness as u32;
duty_value = (duty_range * duty_percent) / 100;
//println!("Duty Percent: {}%", duty_percent);
test.register_block().lsch(0).duty().write(|w| unsafe {w.duty().bits(duty_value<<4)});
test.register_block().lsch(0).conf1().write(|w| unsafe {
w.duty_start().set_bit();
w.duty_inc().set_bit();
w.duty_num().bits(0x1);
w.duty_cycle().bits(0x1);
w.duty_scale().bits(0x0)
});
test.register_block().lsch(0).conf0().modify(|_,w| w.para_up().set_bit());
mtgcount_rs::display::draw_screen(&state, &mut framebuf);
framebuf.as_image().draw(&mut display).unwrap();
framebuf.clear(state.settings.color_scheme.get_color(&mtgcount_rs::display::ColorType::Background)).unwrap();
rtc_control.sleep(&rtc_config,&[&waker]);
}
// for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.0.0-beta.0/examples/src/bin
}

205
src/bin/mini_ulp.s Normal file
View File

@@ -0,0 +1,205 @@
REG_WR 0x103, 0x17, 0x17, 1
REG_WR 0x123, 0x18, 0x18, 0
REG_WR 0x123, 0x11, 0x11, 1
REG_WR 0x123, 0x06, 0x06, 1
entry:
MOVE R0, dutycycle //6
LD R0, R0, 0 //8
LSH R1, R0, 4 //6
ADD R0, R0, R1 //6
ADD R0, R0, 10 //6
JUMP R0 //4
//36
0x0a:
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 12 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 717 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 12 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 717 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 12 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 717 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 12 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 677 //6
JUMP entry //4
0x22:
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 84 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 645 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 84 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 645 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 84 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 645 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 84 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 605 //6
JUMP entry //4
0x3a:
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 156 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 573 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 156 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 573 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 156 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 573 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 156 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 533 //6
JUMP entry //4
0x52:
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 228 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 501 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 228 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 501 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 228 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 501 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 228 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 461 //6
JUMP entry //4
0x6a:
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 300 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 429 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 300 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 429 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 300 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 429 //6
REG_WR 0x101, 0x17, 0x17, 1 //12
WAIT 300 //6
REG_WR 0x102, 0x17, 0x17, 1 //12
WAIT 389 //6
JUMP entry //4
0x82:
REG_WR 0x102, 0x17, 0x17, 1
WAIT 357
REG_WR 0x101, 0x17, 0x17, 1
WAIT 372
REG_WR 0x102, 0x17, 0x17, 1
WAIT 357
REG_WR 0x101, 0x17, 0x17, 1
WAIT 372
REG_WR 0x102, 0x17, 0x17, 1
WAIT 357
REG_WR 0x101, 0x17, 0x17, 1
WAIT 372
REG_WR 0x102, 0x17, 0x17, 1
WAIT 357
REG_WR 0x101, 0x17, 0x17, 1
WAIT 332
JUMP entry
0x9a:
REG_WR 0x102, 0x17, 0x17, 1
WAIT 285
REG_WR 0x101, 0x17, 0x17, 1
WAIT 444
REG_WR 0x102, 0x17, 0x17, 1
WAIT 285
REG_WR 0x101, 0x17, 0x17, 1
WAIT 444
REG_WR 0x102, 0x17, 0x17, 1
WAIT 285
REG_WR 0x101, 0x17, 0x17, 1
WAIT 444
REG_WR 0x102, 0x17, 0x17, 1
WAIT 285
REG_WR 0x101, 0x17, 0x17, 1
WAIT 404
JUMP entry
0xb2:
REG_WR 0x102, 0x17, 0x17, 1
WAIT 213
REG_WR 0x101, 0x17, 0x17, 1
WAIT 516
REG_WR 0x102, 0x17, 0x17, 1
WAIT 213
REG_WR 0x101, 0x17, 0x17, 1
WAIT 516
REG_WR 0x102, 0x17, 0x17, 1
WAIT 213
REG_WR 0x101, 0x17, 0x17, 1
WAIT 516
REG_WR 0x102, 0x17, 0x17, 1
WAIT 213
REG_WR 0x101, 0x17, 0x17, 1
WAIT 476
JUMP entry
0xca:
REG_WR 0x102, 0x17, 0x17, 1
WAIT 141
REG_WR 0x101, 0x17, 0x17, 1
WAIT 606
REG_WR 0x102, 0x17, 0x17, 1
WAIT 141
REG_WR 0x101, 0x17, 0x17, 1
WAIT 606
REG_WR 0x102, 0x17, 0x17, 1
WAIT 141
REG_WR 0x101, 0x17, 0x17, 1
WAIT 606
REG_WR 0x102, 0x17, 0x17, 1
WAIT 141
REG_WR 0x101, 0x17, 0x17, 1
WAIT 566
JUMP entry
0xe2:
REG_WR 0x102, 0x17, 0x17, 1
WAIT 69
REG_WR 0x101, 0x17, 0x17, 1
WAIT 660
REG_WR 0x102, 0x17, 0x17, 1
WAIT 69
REG_WR 0x101, 0x17, 0x17, 1
WAIT 660
REG_WR 0x102, 0x17, 0x17, 1
WAIT 69
REG_WR 0x101, 0x17, 0x17, 1
WAIT 660
REG_WR 0x102, 0x17, 0x17, 1
WAIT 69
REG_WR 0x101, 0x17, 0x17, 1
WAIT 620
JUMP entry
0xfa:
REG_WR 0x102, 0x17, 0x17, 1
WAIT 12
REG_WR 0x101, 0x17, 0x17, 1
WAIT 1482
REG_WR 0x102, 0x17, 0x17, 1
WAIT 12
REG_WR 0x101, 0x17, 0x17, 1
WAIT 1442
JUMP entry
dutycycle:
.long 0

1551
src/bin/ulp_program.s Normal file

File diff suppressed because it is too large Load Diff

447
src/display.rs Normal file
View File

@@ -0,0 +1,447 @@
use embedded_graphics::prelude::{Angle, DrawTarget, Point, Primitive, RgbColor};
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::Drawable;
use u8g2_fonts::{fonts::{u8g2_font_7x13_tr, u8g2_font_9x15B_tr, u8g2_font_logisoso24_tr, u8g2_font_logisoso42_tr, u8g2_font_open_iconic_all_2x_t, u8g2_font_open_iconic_check_4x_t}, FontRenderer};
use micromath::F32Ext;
extern crate alloc;
use alloc::{vec::Vec, string::ToString};
use embedded_graphics::primitives::PrimitiveStyle;
use crate::state::{MainScreenItem, Screen, SystemState};
use crate::setting_screen::{setting_screen, setting_scroll};
pub const LARGE_FONT:FontRenderer = FontRenderer::new::<u8g2_font_logisoso42_tr>();
pub const MED_PLUS_FONT:FontRenderer = FontRenderer::new::<u8g2_font_logisoso24_tr>();
pub const MED_FONT:FontRenderer = FontRenderer::new::<u8g2_font_9x15B_tr>();
pub const SMALL_FONT:FontRenderer = FontRenderer::new::<u8g2_font_7x13_tr>();
pub const ICONS:FontRenderer = FontRenderer::new::<u8g2_font_open_iconic_all_2x_t>();
pub const CHECK:FontRenderer = FontRenderer::new::<u8g2_font_open_iconic_check_4x_t>();
const LIGHTCREAM: Rgb565 = Rgb565::new(31, 62, 26);
const LIGHTBLUE: Rgb565 = Rgb565::new(21, 55, 30);
const DARKBLUE: Rgb565 = Rgb565::new(1, 27, 22);
const LIGHTGREY: Rgb565 = Rgb565::new(25, 48, 23);
const DARKGREY: Rgb565 = Rgb565::new(8, 16, 8);
const LIGHTRED: Rgb565 = Rgb565::new(30, 42, 17);
const LIGHTGREEN: Rgb565 = Rgb565::new(19,52,21);
const ALMOSTBLACK: Rgb565 = Rgb565::new(3, 6, 3);
const CENTER: Point = Point::new(120, 120);
#[derive(Debug,Clone, Copy)]
pub enum ColorType {
Primary,
Secondary,
Tertiary,
Background
}
#[derive(Debug,Clone, Copy)]
pub enum ColorScheme {
Light,
Dark,
White,
Blue,
Black,
Red,
Green,
Azorius,
Dimir,
}
impl ColorScheme {
pub fn get_color(&self, color_type: &ColorType) -> Rgb565 {
match self {
Self::Light => {
match color_type {
ColorType::Primary => Rgb565::BLACK,
ColorType::Secondary => Rgb565::BLACK,
ColorType::Tertiary => Rgb565::BLACK,
ColorType::Background => Rgb565::WHITE,
}
},
Self::Dark => {
match color_type {
ColorType::Primary => Rgb565::WHITE,
ColorType::Secondary => ALMOSTBLACK,
ColorType::Tertiary => Rgb565::WHITE,
ColorType::Background => Rgb565::BLACK,
}
},
Self::White => {
match color_type {
ColorType::Primary => Rgb565::BLACK,
ColorType::Secondary => Rgb565::BLACK,
ColorType::Tertiary => Rgb565::BLACK,
ColorType::Background => LIGHTCREAM,
}
},
Self::Blue => {
match color_type {
ColorType::Primary => Rgb565::BLACK,
ColorType::Secondary => DARKBLUE,
ColorType::Tertiary => Rgb565::BLACK,
ColorType::Background => LIGHTBLUE
}
},
Self::Black => {
match color_type {
ColorType::Primary => Rgb565::BLACK,
ColorType::Secondary => Rgb565::BLACK,
ColorType::Tertiary => Rgb565::BLACK,
ColorType::Background => LIGHTGREY,
}
},
Self::Red => {
match color_type {
ColorType::Primary => Rgb565::BLACK,
ColorType::Secondary => Rgb565::BLACK,
ColorType::Tertiary => Rgb565::BLACK,
ColorType::Background => LIGHTRED,
}
},
Self::Green => {
match color_type {
ColorType::Primary => Rgb565::BLACK,
ColorType::Secondary => Rgb565::BLACK,
ColorType::Tertiary => Rgb565::BLACK,
ColorType::Background => LIGHTGREEN,
}
},
Self::Azorius => {
match color_type {
ColorType::Primary => DARKBLUE,
ColorType::Secondary => DARKBLUE,
ColorType::Tertiary => DARKBLUE,
ColorType::Background => LIGHTCREAM,
}
},
Self::Dimir => {
match color_type {
ColorType::Primary => DARKGREY,
ColorType::Secondary => DARKGREY,
ColorType::Tertiary => DARKGREY,
ColorType::Background => LIGHTBLUE,
}
},
}
}
pub(crate) fn as_str(&self) -> &'static str {
match self {
ColorScheme::Light => "Light",
ColorScheme::Dark => "Dark",
ColorScheme::White => "White",
ColorScheme::Blue => "Blue",
ColorScheme::Black => "Black",
ColorScheme::Red => "Red",
ColorScheme::Green => "Green",
ColorScheme::Azorius => "Azorius",
ColorScheme::Dimir => "Dmir",
}
}
pub fn inc(self) -> Self {
let mut tmp:u8 = self.into();
tmp += 1;
if tmp > 8 {
tmp = 0
}
Self::from(tmp)
}
pub fn dec(self) -> Self {
let tmp:u8 = self.into();
let tmp = tmp.checked_sub(1);
let tmp = match tmp {
Some(u) => u,
None => 8,
};
Self::from(tmp)
}
}
pub const COLORSCHEMES: usize = 9;
impl From<u8> for ColorScheme {
fn from(value: u8) -> Self {
match value {
1 => Self::Dark,
2 => Self::White,
3 => Self::Blue,
4 => Self::Black,
5 => Self::Red,
6 => Self::Green,
7 => Self::Azorius,
8 => Self::Dimir,
_ => Self::Light,
}
}
}
impl Into<u8> for ColorScheme {
fn into(self) -> u8 {
match self {
ColorScheme::Light => 0,
ColorScheme::Dark => 1,
ColorScheme::White => 2,
ColorScheme::Blue => 3,
ColorScheme::Black => 4,
ColorScheme::Red => 5,
ColorScheme::Green => 6,
ColorScheme::Azorius => 7,
ColorScheme::Dimir => 8,
}
}
}
pub fn draw_screen<A: DrawTarget<Color = Rgb565>>(ctx: &SystemState, target: &mut A) -> () {
let primary = ctx.settings.color_scheme.get_color(&ColorType::Primary);
let background = ctx.settings.color_scheme.get_color(&ColorType::Background);
_ = target.clear(background);
match &ctx.screen {
Screen::Main(selection) => {
let style = PrimitiveStyle::with_stroke(ctx.settings.color_scheme.get_color(&ColorType::Secondary), 3);
let bottom_count =
ctx.settings.show_cmd_tax as u8 +
ctx.settings.show_energy as u8 +
ctx.settings.show_experience as u8 +
ctx.settings.show_infect as u8 +
ctx.settings.show_tickets as u8;
if (ctx.settings.opponenets > 0) & (ctx.settings.opponenets < 7) {
let top_positions = ctx.positions.get_top_list(ctx.settings.opponenets);
for (pos, dmg) in top_positions.iter().zip(ctx.game.cmd_dmg) {
_ = SMALL_FONT.render_aligned(
dmg.as_string_horizontal().as_str(),
pos.position,
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
}
let start = (ctx.settings.opponenets as f32 * 15.)-90.;
let sweep = ctx.settings.opponenets as f32 * -30.;
let arc = embedded_graphics::primitives::Arc::with_center(CENTER, 160, Angle::from_degrees(start), Angle::from_degrees(sweep));
//println!("start: {}", start);
let _ = arc.into_styled(style).draw(target);
for i in 0..= ctx.settings.opponenets {
let angle = (start + 90. - (30.*(i as f32))) * crate::DEG_TO_RAD;
let (sin_t, cos_t) = angle.sin_cos();
//println!("angle: {}, sin: {}, cos: {}", angle, sin_t, cos_t);
let line1 = Point::new(
((79. * sin_t)+120.) as i32,
((-79. * cos_t)+120.) as i32
);
let line2 = Point::new(
((125. * sin_t)+120.) as i32,
((-125. * cos_t)+120.) as i32
);
let drawline = embedded_graphics::primitives::Line::new(line1, line2);
//println!("{:?}", drawline);
let _ = drawline.into_styled(style).draw(target);
}
}
if bottom_count != 0 {
let bottom_positions = ctx.positions.get_bottom_list(bottom_count);
let mut bottom_vec = Vec::with_capacity(bottom_count as usize);
if ctx.settings.show_cmd_tax {
let tmp = ctx.game.cmd_tax.as_string_horizontal();
bottom_vec.push((tmp, '\u{0092}'));
}
if ctx.settings.show_energy {
let tmp = ctx.game.energy.to_string();
bottom_vec.push((tmp,'\u{0060}'));
}
if ctx.settings.show_experience {
let tmp = ctx.game.experience.to_string();
bottom_vec.push((tmp,'\u{0103}'));
}
if ctx.settings.show_tickets {
let tmp = ctx.game.tickets.to_string();
bottom_vec.push((tmp, '\u{0106}'));
}
if ctx.settings.show_infect {
let tmp = ctx.game.infect.to_string();
bottom_vec.push((tmp,'\u{005c}'));
}
for (pos, content) in bottom_positions.iter().zip(bottom_vec) {
_ = SMALL_FONT.render_aligned(
content.0.as_str(),
pos.position,
u8g2_fonts::types::VerticalPosition::Bottom,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target);
_ = ICONS.render_aligned(
content.1,
pos.position,
u8g2_fonts::types::VerticalPosition::Top,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target);
}
let start = (bottom_count as f32 * 15.)+90.;
let sweep = bottom_count as f32 * -30.;
let arc = embedded_graphics::primitives::Arc::with_center(CENTER, 160, Angle::from_degrees(start), Angle::from_degrees(sweep));
let _ = arc.into_styled(style).draw(target);
for i in 0..=bottom_count {
let angle = (start + 90. - (30.*(i as f32))) * crate::DEG_TO_RAD;
let (sin_t, cos_t) = angle.sin_cos();
//println!("angle: {}, sin: {}, cos: {}", angle, sin_t, cos_t);
let line1 = Point::new(
((79. * sin_t)+120.) as i32,
((-79. * cos_t)+120.) as i32
);
let line2 = Point::new(
((125. * sin_t)+120.) as i32,
((-125. * cos_t)+120.) as i32
);
let drawline = embedded_graphics::primitives::Line::new(line1, line2);
//println!("{:?}", drawline);
let _ = drawline.into_styled(style).draw(target);
}
}
let center_item = match selection {
MainScreenItem::Dmg1 => (ctx.game.cmd_dmg[0].as_string_horizontal(),"Opponent 1"),
MainScreenItem::Dmg2 => (ctx.game.cmd_dmg[1].as_string_horizontal(),"Opponent 2"),
MainScreenItem::Dmg3 => (ctx.game.cmd_dmg[2].as_string_horizontal(),"Opponent 3"),
MainScreenItem::Dmg4 => (ctx.game.cmd_dmg[3].as_string_horizontal(),"Opponent 4"),
MainScreenItem::Dmg5 => (ctx.game.cmd_dmg[4].as_string_horizontal(),"Opponent 5"),
MainScreenItem::Dmg6 => (ctx.game.cmd_dmg[5].as_string_horizontal(),"Opponent 6"),
MainScreenItem::Tax => (ctx.game.cmd_tax.as_string_horizontal(),"Commander Tax"),
MainScreenItem::Energy => (ctx.game.energy.to_string(),"Energy"),
MainScreenItem::Experience => (ctx.game.experience.to_string(),"Experience"),
MainScreenItem::Tickets => (ctx.game.tickets.to_string(),"Tickets"),
MainScreenItem::Poison => (ctx.game.infect.to_string(),"Poison"),
_ => (ctx.game.life.to_string(),"Life"),
};
let _ = LARGE_FONT.render_aligned(
center_item.0.as_str(),
ctx.positions.center,
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
let _ = SMALL_FONT.render_aligned(
center_item.1,
Point::new(120, 150),
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
let content = match selection {
MainScreenItem::Dmg1 => Some(0),
MainScreenItem::Dmg2 => Some(1),
MainScreenItem::Dmg3 => Some(2),
MainScreenItem::Dmg4 => Some(3),
MainScreenItem::Dmg5 => Some(4),
MainScreenItem::Dmg6 => Some(5),
_ => None,
};
if let Some(pos) = content {
let content = if ctx.game.cmd_dmg[pos].is_partner {
"0"
} else {
"0|0"
};
let _ = SMALL_FONT.render_aligned(
content,
Point { x: 120, y: 170 },
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
} else if *selection == MainScreenItem::Tax {
let content = if ctx.game.cmd_tax.is_partner {
"0"
} else {
"0|0"
};
let _ = SMALL_FONT.render_aligned(
content,
Point { x: 120, y: 170 },
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
}
}
Screen::Settings(selection) => {
setting_scroll(ctx, selection, target);
}
}
}
/*
opponents 1-6
life total
tax
energy
experience
tix
poison
opponents
starting life
new game
colors
brightness
show tax
show energy
show experience
show tickets
show poison
*/

115
src/init.rs Normal file
View File

@@ -0,0 +1,115 @@
use core::cell::Cell;
use embedded_hal_bus::spi::ExclusiveDevice;
use esp_hal::{delay::Delay, gpio::Output, i2c::master::I2c, peripherals::*, spi::master::Spi, time::Rate, Blocking};
use mipidsi::{interface::SpiInterface, models::GC9A01};
pub struct UsedPins<'a> {
sda: Cell<Option<GPIO21<'a>>>,
scl: Cell<Option<GPIO22<'a>>>,
irq: Cell<Option<GPIO19<'a>>>,
tp_rst: Cell<Option<GPIO4<'a>>>,
sclk: Cell<Option<GPIO14<'a>>>,
mosi: Cell<Option<GPIO15<'a>>>,
cs: Cell<Option<GPIO5<'a>>>,
dc: Cell<Option<GPIO27<'a>>>,
reset: Cell<Option<GPIO33<'a>>>,
bl: Cell<Option<GPIO32<'a>>>,
iic: Cell<Option<I2C0<'a>>>,
spi: Cell<Option<SPI2<'a>>>,
lpwr: Cell<Option<LPWR<'a>>>,
ledc: Cell<Option<LEDC<'a>>>,
//dport: Cell<Option<DPORT<'a>>>,
io_mux: Cell<Option<RTC_IO<'a>>>,
//rtc_io: Cell<Option<LPWR<'a>>>,
}
impl<'a> UsedPins<'a> {
pub fn init(peripherals: esp_hal::peripherals::Peripherals) -> Self {
UsedPins {
sda: Cell::new(Some(peripherals.GPIO21)),
scl: Cell::new(Some(peripherals.GPIO22)),
irq: Cell::new(Some(peripherals.GPIO19)),
tp_rst: Cell::new(Some(peripherals.GPIO4)),
sclk: Cell::new(Some(peripherals.GPIO14)),
mosi: Cell::new(Some(peripherals.GPIO15)),
cs: Cell::new(Some(peripherals.GPIO5)),
dc: Cell::new(Some(peripherals.GPIO27)),
reset: Cell::new(Some(peripherals.GPIO33)),
bl: Cell::new(Some(peripherals.GPIO32)),
iic: Cell::new(Some(peripherals.I2C0)),
spi: Cell::new(Some(peripherals.SPI2)),
lpwr: Cell::new(Some(peripherals.LPWR)),
ledc: Cell::new(Some(peripherals.LEDC)),
//dport: Cell::new(Some(peripherals.DPORT)),
io_mux: Cell::new(Some(peripherals.RTC_IO)),
//rtc_io: Cell::new(Some(peripherals.LPWR)),
}
}
pub fn irq(&self) -> GPIO19 {
let irq = self.irq.replace(None).unwrap();
irq
}
pub fn tp_rst(&self) -> GPIO4 {
let tp_rst = self.tp_rst.replace(None).unwrap();
tp_rst
}
pub fn get_lpwr(&self) -> LPWR<'a> {
let pin = self.lpwr.replace(None).unwrap();
pin
}
pub fn init_backlight(&self) -> (LEDC<'a>, GPIO32<'a>, RTC_IO<'a>) {
let bl = self.bl.replace(None).unwrap();
let ledc = self.ledc.replace(None).unwrap();
//let rtc_io = self.rtc_io.replace(None).unwrap();
let io_mux = self.io_mux.replace(None).unwrap();
(ledc, bl, io_mux)
}
pub fn init_iic(&self) -> I2c<'_, Blocking> {
let mut iic = I2c::new(self.iic.replace(None).unwrap(), esp_hal::i2c::master::Config::default()).unwrap()
.with_sda(self.sda.replace(None).unwrap())
.with_scl(self.scl.replace(None).unwrap());
iic.write(0x15, &[0xFA_u8, 0b00010001]).expect("Failed to set irqctl");
iic
}
pub fn init_display(&'a self, buffer: &'a mut [u8]) -> mipidsi::Display<SpiInterface<'a, ExclusiveDevice<esp_hal::spi::master::Spi<'a, Blocking>, Output<'a>, Delay>, Output<'a>>, GC9A01, Output<'a>> {
let spi = Spi::new(
self.spi.replace(None).unwrap(),
esp_hal::spi::master::Config::default()
.with_frequency(Rate::from_mhz(40))
).unwrap()
.with_sck(self.sclk.replace(None).unwrap())
.with_mosi(self.mosi.replace(None).unwrap());
let cs_pin = esp_hal::gpio::Output::new(self.cs.replace(None).unwrap(), esp_hal::gpio::Level::Low, esp_hal::gpio::OutputConfig::default());
let dc_pin = esp_hal::gpio::Output::new(self.dc.replace(None).unwrap(), esp_hal::gpio::Level::High, esp_hal::gpio::OutputConfig::default());
let rs_pin = esp_hal::gpio::Output::new(self.reset.replace(None).unwrap(), esp_hal::gpio::Level::High, esp_hal::gpio::OutputConfig::default().with_pull(esp_hal::gpio::Pull::Up));
let mut delay = esp_hal::delay::Delay::new();
let spi_dev = ExclusiveDevice::new(spi, cs_pin, delay).unwrap();
let mipi_interface = mipidsi::interface::SpiInterface::new(spi_dev, dc_pin, buffer);
let display = mipidsi::Builder::new(GC9A01, mipi_interface)
.reset_pin(rs_pin)
.invert_colors(mipidsi::options::ColorInversion::Inverted)
.color_order(mipidsi::options::ColorOrder::Bgr)
.init(&mut delay).unwrap();
display
}
}

15
src/lib.rs Normal file
View File

@@ -0,0 +1,15 @@
#![no_std]
pub mod init;
pub mod state;
pub mod display;
pub mod touch;
pub mod setting_screen;
pub const IIC_ADDRESS: u8 = 0x15_u8;
pub const TP_GESTURE: u8 = 0x01_u8;
pub const TP_X: u8 = 0x04_u8;
pub const TP_Y: u8 = 0x06_u8;
pub const RAD_TO_DEG: f32 = 57.2957795;
pub const DEG_TO_RAD: f32 = 0.017453293;

492
src/setting_screen.rs Normal file
View File

@@ -0,0 +1,492 @@
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::prelude::{DrawTarget, Point, Primitive, RgbColor, Size};
use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
use embedded_graphics::Drawable;
extern crate alloc;
use alloc::{vec::Vec, string::ToString};
use u8g2_fonts::types::{FontColor, HorizontalAlignment, VerticalPosition};
use crate::state::{SettingScreenEntry, SettingScreenItem, SystemState};
use crate::display::{CHECK, SMALL_FONT, MED_FONT, MED_PLUS_FONT};
use crate::display::{ColorScheme, COLORSCHEMES};
pub(crate) fn setting_screen<A>(ctx: &SystemState, selection: &SettingScreenItem, target: &mut A) -> ()
where A: DrawTarget<Color = Rgb565>
{
let primary = ctx.settings.color_scheme.get_color(&crate::display::ColorType::Primary);
let background = ctx.settings.color_scheme.get_color(&crate::display::ColorType::Background);
let top_count = 5;
let bottom_count = 5;
let top_positions = &ctx.positions.top_setting_locations;
let bottom_positions = &ctx.positions.bottom_setting_locations;
let mut top_vec = Vec::with_capacity(top_count as usize);
top_vec.push("Opponents");
top_vec.push("Starting\nLife");
top_vec.push("New Game");
top_vec.push("Color\nScheme");
top_vec.push("Brightness");
let mut bottom_vec = Vec::with_capacity(bottom_count as usize);
bottom_vec.push("Commander\nTax");
bottom_vec.push("Energy");
bottom_vec.push("Exp");
bottom_vec.push("Tix");
bottom_vec.push("Poison");
for (pos, content) in top_positions.iter().chain(bottom_positions.iter()).zip(top_vec.into_iter().chain(bottom_vec.into_iter())) {
_ = SMALL_FONT.render_aligned(
content,
pos.position,
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target);
}
match selection.entry {
SettingScreenEntry::NewGame => {
let _ = MED_FONT.render_aligned(
"Start\nNew Game.",
ctx.positions.center,
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
},
SettingScreenEntry::Opponents => {
let _ = MED_PLUS_FONT.render_aligned(
ctx.settings.opponenets.to_string().as_str(),
ctx.positions.center,
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
},
SettingScreenEntry::StartingLife => {
let _ = MED_PLUS_FONT.render_aligned(
ctx.settings.starting_life.to_string().as_str(),
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
},
SettingScreenEntry::Colors => {
let current_scheme_number: u8 = ctx.settings.color_scheme.into();
let prev_scheme_number;
let check_sub = current_scheme_number.checked_sub(1);
if let Some(p) = check_sub {
prev_scheme_number = p;
} else {
prev_scheme_number = COLORSCHEMES as u8 - 1;
}
let prev_scheme: ColorScheme = prev_scheme_number.into();
let next_scheme_number;
let check_add = current_scheme_number + 1;
if check_add >= COLORSCHEMES as u8 {
next_scheme_number = 0;
} else {
next_scheme_number = check_add;
}
let next_scheme: ColorScheme = next_scheme_number.into();
let _ = MED_FONT.render_aligned(
prev_scheme.as_str(),
Point::new(120, 80),
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
let _ = MED_PLUS_FONT.render_aligned(
ctx.settings.color_scheme.as_str(),
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
let _ = MED_FONT.render_aligned(
next_scheme.as_str(),
Point::new(120, 160),
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
},
SettingScreenEntry::Brightness => {
let _ = MED_PLUS_FONT.render_aligned(
ctx.settings.brightness.as_str(),
ctx.positions.center,
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
},
SettingScreenEntry::ShowTax => {
//Yes/No rendering
let _ = MED_FONT.render_aligned(
"Show\nCommand Tax?",
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Bottom,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
draw_yes_no(primary, background, target, Some(ctx.settings.show_cmd_tax));
},
SettingScreenEntry::ShowEnergy => {
//Yes/No rendering
let _ = MED_FONT.render_aligned(
"Show\nEnergy?",
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Bottom,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
draw_yes_no(primary, background, target, Some(ctx.settings.show_energy));
},
SettingScreenEntry::ShowExperience => {
//Yes/No rendering
let _ = MED_FONT.render_aligned(
"Show\nExperience?",
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Bottom,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
draw_yes_no(primary, background, target, Some(ctx.settings.show_experience));
},
SettingScreenEntry::ShowTickets => {
//Yes/No rendering
let _ = MED_FONT.render_aligned(
"Show\nTickets?",
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Bottom,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
draw_yes_no(primary, background, target, Some(ctx.settings.show_tickets));
},
SettingScreenEntry::ShowPoison => {
//Yes/No rendering
let _ = MED_FONT.render_aligned(
"Show\nPoison?",
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Bottom,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
draw_yes_no(primary, background, target, Some(ctx.settings.show_infect));
},
};
}
fn draw_yes_no<A: DrawTarget<Color = Rgb565>>(primary: Rgb565, background: Rgb565, display: &mut A, selected: Option<bool>) -> () {
let yes_color;
let no_color;
let rect_point;
if let Some(sel) = selected {
if sel {
yes_color = background;
no_color = primary;
rect_point = Point::new(75, 125);
} else {
yes_color = primary;
no_color = background;
rect_point = Point::new(120,125);
}
let style = PrimitiveStyleBuilder::new()
.fill_color(primary)
.build();
let _ = Rectangle::new(rect_point, Size::new(45, 25)).into_styled(style).draw(display);
} else {
yes_color = primary;
no_color = primary;
}
_ = MED_FONT.render_aligned(
"Yes",
Point{x: 110, y: 130},
u8g2_fonts::types::VerticalPosition::Top,
u8g2_fonts::types::HorizontalAlignment::Right,
u8g2_fonts::types::FontColor::Transparent(yes_color),
display,
);
_ = MED_FONT.render_aligned(
"No",
Point{x: 135, y: 130},
u8g2_fonts::types::VerticalPosition::Top,
u8g2_fonts::types::HorizontalAlignment::Left,
u8g2_fonts::types::FontColor::Transparent(no_color),
display,
);
}
pub(crate) fn setting_scroll<A>(ctx: &SystemState, selection: &SettingScreenItem, target: &mut A) -> ()
where A: DrawTarget<Color = Rgb565>
{
let primary = ctx.settings.color_scheme.get_color(&crate::display::ColorType::Primary);
let background = ctx.settings.color_scheme.get_color(&crate::display::ColorType::Background);
if !selection.inner {
let above = selection.entry.prev().as_str();
let selected = selection.entry.as_str();
let below = selection.entry.next().as_str();
let _above = MED_FONT.render_aligned(
above,
Point::new(120, 50),
VerticalPosition::Center,
HorizontalAlignment::Center,
FontColor::Transparent(primary),
target
);
let _selected = MED_PLUS_FONT.render_aligned(
selected,
Point::new(120, 120),
VerticalPosition::Center,
HorizontalAlignment::Center,
FontColor::Transparent(primary),
target
);
let _below = MED_FONT.render_aligned(
below,
Point::new(120, 190),
VerticalPosition::Center,
HorizontalAlignment::Center,
FontColor::Transparent(primary),
target
);
} else {
setting_item(target);
match selection.entry {
SettingScreenEntry::NewGame => {
let _ = MED_PLUS_FONT.render_aligned(
selection.entry.as_str(),
Point::new(120, 120),
VerticalPosition::Center,
HorizontalAlignment::Center,
FontColor::Transparent(primary),
target,
);
},
SettingScreenEntry::Opponents => {
let _ = MED_PLUS_FONT.render_aligned(
ctx.settings.opponenets.to_string().as_str(),
ctx.positions.center,
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
},
SettingScreenEntry::StartingLife => {
let _ = MED_PLUS_FONT.render_aligned(
ctx.settings.starting_life.to_string().as_str(),
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
},
SettingScreenEntry::Colors => {
let current_scheme_number: u8 = ctx.settings.color_scheme.into();
let prev_scheme_number;
let check_sub = current_scheme_number.checked_sub(1);
if let Some(p) = check_sub {
prev_scheme_number = p;
} else {
prev_scheme_number = COLORSCHEMES as u8 - 1;
}
let prev_scheme: ColorScheme = prev_scheme_number.into();
let next_scheme_number;
let check_add = current_scheme_number + 1;
if check_add >= COLORSCHEMES as u8 {
next_scheme_number = 0;
} else {
next_scheme_number = check_add;
}
let next_scheme: ColorScheme = next_scheme_number.into();
let _ = MED_FONT.render_aligned(
prev_scheme.as_str(),
Point::new(120, 70),
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
let _ = MED_PLUS_FONT.render_aligned(
ctx.settings.color_scheme.as_str(),
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
let _ = MED_FONT.render_aligned(
next_scheme.as_str(),
Point::new(120, 170),
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
},
SettingScreenEntry::Brightness => {
let _ = MED_PLUS_FONT.render_aligned(
ctx.settings.brightness.as_str(),
ctx.positions.center,
u8g2_fonts::types::VerticalPosition::Center,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
}
SettingScreenEntry::ShowTax => {
let _ = MED_FONT.render_aligned(
"Show\nCommand Tax?",
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Bottom,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
draw_yes_no(primary, background, target, Some(ctx.settings.show_cmd_tax));
},
SettingScreenEntry::ShowEnergy => {
let _ = MED_FONT.render_aligned(
"Show\nEnergy?",
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Bottom,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
draw_yes_no(primary, background, target, Some(ctx.settings.show_energy));
},
SettingScreenEntry::ShowExperience => {
let _ = MED_FONT.render_aligned(
"Show\nExperience?",
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Bottom,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
draw_yes_no(primary, background, target, Some(ctx.settings.show_experience));
},
SettingScreenEntry::ShowTickets => {
let _ = MED_FONT.render_aligned(
"Show\nTickets?",
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Bottom,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
draw_yes_no(primary, background, target, Some(ctx.settings.show_tickets));
},
SettingScreenEntry::ShowPoison => {
let _ = MED_FONT.render_aligned(
"Show\nPoison?",
Point::new(120, 120),
u8g2_fonts::types::VerticalPosition::Bottom,
u8g2_fonts::types::HorizontalAlignment::Center,
u8g2_fonts::types::FontColor::Transparent(primary),
target
);
draw_yes_no(primary, background, target, Some(ctx.settings.show_infect));
},
}
}
}
pub fn setting_item<A>(target: &mut A) -> ()
where A: DrawTarget<Color = Rgb565>
{
let _okay_outline_1 = CHECK.render_aligned(
'\u{0040}',
Point::new(26, 121),
VerticalPosition::Center,
HorizontalAlignment::Center,
FontColor::Transparent(Rgb565::BLACK),
target
);
let _okay_outline_2 = CHECK.render_aligned(
'\u{0040}',
Point::new(26, 119),
VerticalPosition::Center,
HorizontalAlignment::Center,
FontColor::Transparent(Rgb565::BLACK),
target
);
let _okay_outline_3 = CHECK.render_aligned(
'\u{0040}',
Point::new(24, 121),
VerticalPosition::Center,
HorizontalAlignment::Center,
FontColor::Transparent(Rgb565::BLACK),
target
);
let _okay_outline_4 = CHECK.render_aligned(
'\u{0040}',
Point::new(24, 119),
VerticalPosition::Center,
HorizontalAlignment::Center,
FontColor::Transparent(Rgb565::BLACK),
target
);
let _okay = CHECK.render_aligned(
'\u{0040}',
Point::new(25, 120),
VerticalPosition::Center,
HorizontalAlignment::Center,
FontColor::Transparent(Rgb565::new(1, 54, 10)),
target
);
let _cancel = CHECK.render_aligned(
'\u{0044}',
Point::new(215, 120),
VerticalPosition::Center,
HorizontalAlignment::Center,
FontColor::Transparent(Rgb565::new(25, 8, 5)),
target
);
}
/*
setting scroller -> internal screen check ok/ x cancel
*/

387
src/state.rs Normal file
View File

@@ -0,0 +1,387 @@
use core::panic;
use crate::display::ColorScheme;
use embedded_graphics::prelude::{Angle, Point};
extern crate alloc;
use alloc::{string::String, string::ToString};
pub struct SystemState {
pub settings: Settings,
pub game: Game,
pub positions: Positions,
pub screen: Screen,
}
impl SystemState {
pub fn new() -> Self {
Self {
settings: Settings {
opponenets: 1,
starting_life: 40,
brightness: Brightness::Three,
color_scheme: ColorScheme::Dark,
show_cmd_tax: true,
show_infect: true,
show_energy: true,
show_experience: true,
show_tickets: true,
},
game: Game { life: 40, cmd_dmg: [CommanderDamage{main: 0, partner: 0, is_partner: true};6], infect: 0, cmd_tax: CommanderDamage { main: 0, partner: 0, is_partner: false }, energy: 0, experience: 0, tickets: 0, monarch: false},
positions: Positions::init(),
screen: Screen::Main(MainScreenItem::Life),
}
}
}
pub struct Settings {
pub opponenets: u8,
pub starting_life: i32,
pub color_scheme: ColorScheme,
pub brightness: Brightness,
pub show_cmd_tax: bool,
pub show_infect: bool,
pub show_experience: bool,
pub show_energy: bool,
pub show_tickets: bool,
}
pub struct Game {
pub life: i32,
pub cmd_dmg: [CommanderDamage;6],
pub cmd_tax: CommanderDamage,
pub infect: u8,
pub energy: u16,
pub experience: u16,
pub tickets: u16,
pub monarch: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct CommanderDamage {
pub main: u8,
pub partner: u8,
pub is_partner: bool,
}
impl CommanderDamage {
pub fn as_string_horizontal(&self) -> String {
match self.is_partner {
true => {
let mut tmpa = self.main.to_string();
let tmpb = self.partner.to_string();
tmpa.push('|');
tmpa.push_str(&tmpb);
tmpa
},
false => {
self.main.to_string()
}
}
}
pub fn as_string_vetical(&self) -> String {
match self.is_partner {
true => {
let mut tmpa = self.main.to_string();
let tmpb = self.partner.to_string();
tmpa.push('\n');
tmpa.push_str(&tmpb);
tmpa
},
false => {
self.main.to_string()
}
}
}
}
pub struct Positions {
top_even: [PeripheralLocation;6],
top_odd: [PeripheralLocation;5],
bottom_even: [PeripheralLocation;6],
bottom_odd: [PeripheralLocation;5],
pub top_setting_locations: [PeripheralLocation;5],
pub bottom_setting_locations: [PeripheralLocation;5],
pub center: Point,
}
impl Positions {
pub fn init() -> Self {
let top_odd = [
PeripheralLocation{ position: Point::new(38, 71), angle: Angle::from_degrees(150.)},
PeripheralLocation{ position: Point::new(72, 37), angle: Angle::from_degrees(120.)},
PeripheralLocation{ position: Point::new(120, 20), angle: Angle::from_degrees(90.)},
PeripheralLocation{ position: Point::new(168, 37), angle: Angle::from_degrees(60.)},
PeripheralLocation{ position: Point::new(202, 71), angle: Angle::from_degrees(30.)},
];
let top_even = [
PeripheralLocation{ position: Point::new(23, 96), angle: Angle::from_degrees(165.)},
PeripheralLocation{ position: Point::new(53, 52), angle: Angle::from_degrees(135.)},
PeripheralLocation{ position: Point::new(95, 27), angle: Angle::from_degrees(105.)},
PeripheralLocation{ position: Point::new(145, 27), angle: Angle::from_degrees(75.)},
PeripheralLocation{ position: Point::new(187, 52), angle: Angle::from_degrees(45.)},
PeripheralLocation{ position: Point::new(212, 96), angle: Angle::from_degrees(15.)},
];
let bottom_odd = [
PeripheralLocation{ position: Point::new(32, 170), angle: Angle::from_degrees(210.)},
PeripheralLocation{ position: Point::new(70, 208), angle: Angle::from_degrees(240.)},
PeripheralLocation{ position: Point::new(120, 215), angle: Angle::from_degrees(270.)},
PeripheralLocation{ position: Point::new(168, 203), angle: Angle::from_degrees(300.)},
PeripheralLocation{ position: Point::new(202, 169), angle: Angle::from_degrees(330.)},
];
let bottom_even = [
PeripheralLocation{ position: Point::new(28, 146), angle: Angle::from_degrees(195.)},
PeripheralLocation{ position: Point::new(48, 190), angle: Angle::from_degrees(225.)},
PeripheralLocation{ position: Point::new(95, 213), angle: Angle::from_degrees(255.)},
PeripheralLocation{ position: Point::new(145, 213), angle: Angle::from_degrees(285.)},
PeripheralLocation{ position: Point::new(187, 188), angle: Angle::from_degrees(315.)},
PeripheralLocation{ position: Point::new(212, 146), angle: Angle::from_degrees(345.)},
];
let top_setting_locations = [
PeripheralLocation{ position: Point::new(38, 91), angle: Angle::from_degrees(162.)},
PeripheralLocation{ position: Point::new(64, 53), angle: Angle::from_degrees(126.)},
PeripheralLocation{ position: Point::new(120, 25), angle: Angle::from_degrees(90.)},
PeripheralLocation{ position: Point::new(176, 53), angle: Angle::from_degrees(54.)},
PeripheralLocation{ position: Point::new(210, 91), angle: Angle::from_degrees(18.)},
];
let bottom_setting_locations = [
PeripheralLocation{ position: Point::new(33, 170), angle: Angle::from_degrees(210.)},//(43,169)
PeripheralLocation{ position: Point::new(72, 203), angle: Angle::from_degrees(240.)},
PeripheralLocation{ position: Point::new(120, 215), angle: Angle::from_degrees(270.)},
PeripheralLocation{ position: Point::new(168, 203), angle: Angle::from_degrees(300.)},
PeripheralLocation{ position: Point::new(202, 169), angle: Angle::from_degrees(330.)},
];
Self {
top_even,
top_odd,
bottom_even,
bottom_odd,
top_setting_locations,
bottom_setting_locations,
center: Point { x: 120, y: 120 }
}
}
pub fn get_top_list(&self, count: u8) -> &[PeripheralLocation] {
match count {
1 => &self.top_odd[2..3],
2 => &self.top_even[2..4],
3 => &self.top_odd[1..4],
4 => &self.top_even[1..5],
5 => &self.top_odd,
6 => &self.top_even,
_ => panic!("Invalid count")
}
}
pub fn get_bottom_list(&self, count: u8) -> &[PeripheralLocation] {
match count {
1 => &self.bottom_odd[2..3],
2 => &self.bottom_even[2..4],
3 => &self.bottom_odd[1..4],
4 => &self.bottom_even[1..5],
5 => &self.bottom_odd,
6 => &self.bottom_even,
_ => panic!("Invalid count")
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Screen{
Main(MainScreenItem),
Settings(SettingScreenItem),
}
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub enum MainScreenItem {
Life,
Dmg1,
Dmg2,
Dmg3,
Dmg4,
Dmg5,
Dmg6,
Tax,
Energy,
Experience,
Tickets,
Poison,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct SettingScreenItem {
pub entry: SettingScreenEntry,
pub inner: bool,
pub prev: Option<u32>,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum SettingScreenEntry {
NewGame,
Opponents,
StartingLife,
Colors,
Brightness,
ShowTax,
ShowEnergy,
ShowExperience,
ShowTickets,
ShowPoison,
}
impl SettingScreenEntry {
pub fn prev(&self) -> Self {
match self {
SettingScreenEntry::NewGame => Self::ShowPoison,
SettingScreenEntry::Opponents => Self::NewGame,
SettingScreenEntry::StartingLife => Self::Opponents,
SettingScreenEntry::Colors => Self::StartingLife,
SettingScreenEntry::Brightness => Self::Colors,
SettingScreenEntry::ShowTax => Self::Brightness,
SettingScreenEntry::ShowEnergy => Self::ShowTax,
SettingScreenEntry::ShowExperience => Self::ShowEnergy,
SettingScreenEntry::ShowTickets => Self::ShowExperience,
SettingScreenEntry::ShowPoison => Self::ShowTickets,
}
}
pub fn next(&self) -> Self {
match self {
SettingScreenEntry::NewGame => Self::Opponents,
SettingScreenEntry::Opponents => Self::StartingLife,
SettingScreenEntry::StartingLife => Self::Colors,
SettingScreenEntry::Colors => Self::Brightness,
SettingScreenEntry::Brightness => Self::ShowTax,
SettingScreenEntry::ShowTax => Self::ShowEnergy,
SettingScreenEntry::ShowEnergy => Self::ShowExperience,
SettingScreenEntry::ShowExperience => Self::ShowTickets,
SettingScreenEntry::ShowTickets => Self::ShowPoison,
SettingScreenEntry::ShowPoison => Self::NewGame,
}
}
pub fn as_str(&self) -> &'static str {
match self {
SettingScreenEntry::NewGame => "New Game",
SettingScreenEntry::Opponents => "Opponents",
SettingScreenEntry::StartingLife => "Starting\nLife",
SettingScreenEntry::Colors => "Color\nScheme",
SettingScreenEntry::Brightness => "Brightness",
SettingScreenEntry::ShowTax => "Show\nCommander\nTax",
SettingScreenEntry::ShowEnergy => "Show\nEnergy",
SettingScreenEntry::ShowExperience => "Show\nExperience",
SettingScreenEntry::ShowTickets => "Show\nTickets",
SettingScreenEntry::ShowPoison => "Show\nPoison",
}
}
}
#[derive(Clone, Copy)]
pub enum Brightness {
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
}
impl Brightness {
pub fn inc(self) -> Self {
match self {
Brightness::One => Self::Two,
Brightness::Two => Self::Three,
Brightness::Three => Self::Four,
Brightness::Four => Self::Five,
Brightness::Five => Self::Six,
Brightness::Six => Self::Seven,
Brightness::Seven => Self::Eight,
Brightness::Eight => Self::Nine,
Brightness::Nine => Self::Ten,
Brightness::Ten => Self::Ten,
}
}
pub fn dec(self) -> Self {
match self {
Brightness::One => Self::One,
Brightness::Two => Self::One,
Brightness::Three => Self::Two,
Brightness::Four => Self::Three,
Brightness::Five => Self::Four,
Brightness::Six => Self::Five,
Brightness::Seven => Self::Six,
Brightness::Eight => Self::Seven,
Brightness::Nine => Self::Eight,
Brightness::Ten => Self::Nine,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Brightness::One => "Min",
Brightness::Two => "2",
Brightness::Three => "3",
Brightness::Four => "4",
Brightness::Five => "5",
Brightness::Six => "6",
Brightness::Seven => "7",
Brightness::Eight => "8",
Brightness::Nine => "9",
Brightness::Ten => "Max",
}
}
}
impl Into<u32> for Brightness {
fn into(self) -> u32 {
match self {
Brightness::One => 15,
Brightness::Two => 20,
Brightness::Three => 30,
Brightness::Four => 40,
Brightness::Five => 50,
Brightness::Six => 60,
Brightness::Seven => 70,
Brightness::Eight => 80,
Brightness::Nine => 90,
Brightness::Ten => 100,
}
}
}
impl From<u32> for Brightness {
fn from(value: u32) -> Self {
if value <= 15 {
Brightness::One
} else if value <= 20 {
Brightness::Two
} else if value <= 30 {
Brightness::Three
} else if value <= 40 {
Brightness::Four
} else if value <= 50 {
Brightness::Five
} else if value <= 60 {
Brightness::Six
} else if value <= 70 {
Brightness::Seven
} else if value <= 80 {
Brightness::Eight
} else if value <= 90 {
Brightness::Nine
} else {
Brightness::Ten
}
}
}
pub struct PeripheralLocation {
pub position: Point,
pub angle: Angle,
}

406
src/touch.rs Normal file
View File

@@ -0,0 +1,406 @@
use crate::{display::ColorScheme, state::{CommanderDamage, Game, MainScreenItem, Screen, SettingScreenEntry, SettingScreenItem, SystemState}};
const CMDDMGLIST: [MainScreenItem;6] = [
MainScreenItem::Dmg1,
MainScreenItem::Dmg2,
MainScreenItem::Dmg3,
MainScreenItem::Dmg4,
MainScreenItem::Dmg5,
MainScreenItem::Dmg6,
];
pub fn get_touch_action(x: f32, y: f32, gesture: u8, ctx: &mut SystemState) -> () {
let x = x - 120.;
let y = -(y - 120.);
let touch_rad = (x*x)+(y*y);
let angle;
let next_state;
if (gesture == 0x3) | (gesture == 0x4) {
ctx.screen = match &ctx.screen {
Screen::Main(_) => Screen::Settings(SettingScreenItem { entry: SettingScreenEntry::NewGame, inner: false , prev: None}),
Screen::Settings(_) => Screen::Main(MainScreenItem::Life),
};
return;
}
match &ctx.screen {
Screen::Main(selected) => {
let mut bottom_selectable_list: [Option<MainScreenItem>;5] = [const {None};5];
let mut pos = 0;
if ctx.settings.show_cmd_tax {
bottom_selectable_list[pos] = Some(crate::state::MainScreenItem::Tax);
pos += 1;
}
if ctx.settings.show_energy {
bottom_selectable_list[pos] = Some(crate::state::MainScreenItem::Energy);
pos+=1;
};
if ctx.settings.show_experience {
bottom_selectable_list[pos] = Some(crate::state::MainScreenItem::Experience);
pos+=1;
};
if ctx.settings.show_tickets {
bottom_selectable_list[pos] = Some(crate::state::MainScreenItem::Tickets);
pos+=1;
};
if ctx.settings.show_infect {
bottom_selectable_list[pos] = Some(crate::state::MainScreenItem::Poison);
};
let bottom_count = bottom_selectable_list.iter().fold(0, |acc, x| acc+x.is_some() as u8);
if touch_rad > (70.*70.) {
angle = atan2_norm(y, x)*90.;
//ctx.game.life = angle as i32; //debug
let top;
let check_angles = if y >= 0. {
if ctx.settings.opponenets == 0 {
return;
}
top = true;
ctx.positions.get_top_list(ctx.settings.opponenets)
} else {
if bottom_count == 0 {
return;
}
top = false;
ctx.positions.get_bottom_list(bottom_count)
};
let touch_item = match check_angles.iter().position(|x| (x.angle.to_degrees()-angle).abs()<15.) {
Some(loc) => loc,
None => return,
};
let sel_match = if top {
Some(CMDDMGLIST.into_iter().nth(touch_item).unwrap_or(MainScreenItem::Dmg1))
} else {
bottom_selectable_list[touch_item]
};
if let Some(selected_item) = sel_match {
if selected_item == *selected {
next_state = Screen::Main(MainScreenItem::Life);
} else {
next_state = Screen::Main(selected_item);
}
} else {
next_state = Screen::Main(MainScreenItem::Life);
}
} else {
match selected {
crate::state::MainScreenItem::Life => {
let d = touch_inc_dec(y, gesture);
ctx.game.life = ctx.game.life.saturating_add(d as i32);
},
crate::state::MainScreenItem::Dmg1 => {
cmd_dmg_w_swap(ctx, x, y, gesture, 0);
},
crate::state::MainScreenItem::Dmg2 => {
cmd_dmg_w_swap(ctx, x, y, gesture, 1);
},
crate::state::MainScreenItem::Dmg3 => {
cmd_dmg_w_swap(ctx, x, y, gesture, 2);
},
crate::state::MainScreenItem::Dmg4 => {
cmd_dmg_w_swap(ctx, x, y, gesture, 3);
},
crate::state::MainScreenItem::Dmg5 => {
cmd_dmg_w_swap(ctx, x, y, gesture, 4);
},
crate::state::MainScreenItem::Dmg6 => {
cmd_dmg_w_swap(ctx, x, y, gesture, 5);
},
crate::state::MainScreenItem::Tax => {
cmd_tax_w_swap(ctx, x, y, gesture);
},
crate::state::MainScreenItem::Energy => {
let d = touch_inc_dec(y, gesture);
ctx.game.energy = ctx.game.energy.saturating_add_signed(d.into());
},
crate::state::MainScreenItem::Experience => {
let d = touch_inc_dec(y, gesture);
ctx.game.experience = ctx.game.experience.saturating_add_signed(d.into());
},
crate::state::MainScreenItem::Tickets => {
let d = touch_inc_dec(y, gesture);
ctx.game.tickets = ctx.game.tickets.saturating_add_signed(d.into());
},
crate::state::MainScreenItem::Poison => {
let d = touch_inc_dec(y, gesture);
ctx.game.infect = ctx.game.infect.saturating_add_signed(d.into());
},
};
next_state = ctx.screen;
};
},
Screen::Settings(selected) => {
if !selected.inner {
if gesture == 0x01_u8 {
next_state = Screen::Settings(SettingScreenItem { entry: selected.entry.prev(), inner: false, prev: selected.prev });
} else if gesture == 0x02_u8 {
next_state = Screen::Settings(SettingScreenItem { entry: selected.entry.next(), inner: false, prev: selected.prev });
} else {
if y > 55. {
next_state = Screen::Settings(SettingScreenItem { entry: selected.entry.prev(), inner: false, prev: selected.prev});
} else if y < -55. {
next_state = Screen::Settings(SettingScreenItem { entry: selected.entry.next(), inner: false, prev: selected.prev });
} else {
let prev = match selected.entry {
SettingScreenEntry::NewGame => None,
SettingScreenEntry::Opponents => Some(ctx.settings.opponenets as u32),
SettingScreenEntry::StartingLife => Some(ctx.settings.starting_life as u32),
SettingScreenEntry::Colors => Some(Into::<u8>::into(ctx.settings.color_scheme) as u32),
SettingScreenEntry::Brightness => Some(ctx.settings.brightness.into()),
SettingScreenEntry::ShowTax => Some(ctx.settings.show_cmd_tax as u32),
SettingScreenEntry::ShowEnergy => Some(ctx.settings.show_energy as u32),
SettingScreenEntry::ShowExperience => Some(ctx.settings.show_experience as u32),
SettingScreenEntry::ShowTickets => Some(ctx.settings.show_tickets as u32),
SettingScreenEntry::ShowPoison => Some(ctx.settings.show_infect as u32),
};
next_state = Screen::Settings(SettingScreenItem { entry: selected.entry, inner: true, prev: prev});
}
}
} else {
if x < -75. {
match selected.entry {
SettingScreenEntry::NewGame => {
ctx.game = Game {
life: ctx.settings.starting_life,
cmd_dmg: [CommanderDamage{main: 0, partner: 0, is_partner: false};6],
cmd_tax: CommanderDamage { main: 0, partner: 0, is_partner: false },
infect: 0,
energy: 0,
experience: 0,
tickets: 0,
monarch: false,
};
next_state = Screen::Main(MainScreenItem::Life);
},
_ => {
next_state = Screen::Settings(SettingScreenItem { entry: selected.entry, inner: false, prev: None })
}
}
} else if x > 75. {
match selected.entry {
SettingScreenEntry::NewGame => {},
SettingScreenEntry::Opponents => {
ctx.settings.opponenets = selected.prev.unwrap_or(0) as u8;
},
SettingScreenEntry::StartingLife => {
ctx.settings.starting_life = selected.prev.unwrap_or(0) as i32;
},
SettingScreenEntry::Colors => {
ctx.settings.color_scheme =Into::<ColorScheme>::into(selected.prev.unwrap_or(0) as u8);
},
SettingScreenEntry::Brightness => {
ctx.settings.brightness = selected.prev.unwrap_or(0).into();
},
SettingScreenEntry::ShowTax => {
ctx.settings.show_cmd_tax = selected.prev.unwrap_or_default() != 0;
},
SettingScreenEntry::ShowEnergy => {
ctx.settings.show_energy = selected.prev.unwrap_or_default() != 0;
},
SettingScreenEntry::ShowExperience => {
ctx.settings.show_experience = selected.prev.unwrap_or_default() != 0;
},
SettingScreenEntry::ShowTickets => {
ctx.settings.show_tickets = selected.prev.unwrap_or_default() != 0;
},
SettingScreenEntry::ShowPoison => {
ctx.settings.show_infect = selected.prev.unwrap_or_default() != 0;
},
}
next_state = Screen::Settings(SettingScreenItem { entry: selected.entry, inner: false, prev: None })
} else {
match selected.entry {
SettingScreenEntry::NewGame => {
},
SettingScreenEntry::Opponents => {
ctx.settings.opponenets = ctx.settings.opponenets.saturating_add_signed(touch_inc_dec(y, gesture));
},
SettingScreenEntry::StartingLife => {
ctx.settings.starting_life = ctx.settings.starting_life.saturating_add(touch_inc_dec(y, gesture) as i32);
},
SettingScreenEntry::Colors => {
if y > 40. {
ctx.settings.color_scheme = ctx.settings.color_scheme.dec();
} else if y < -40. {
ctx.settings.color_scheme = ctx.settings.color_scheme.inc();
}
},
SettingScreenEntry::Brightness => {
let tmp = touch_inc_dec(y, gesture);
if tmp > 0 {
ctx.settings.brightness = ctx.settings.brightness.inc();
} else {
ctx.settings.brightness = ctx.settings.brightness.dec();
}
},
SettingScreenEntry::ShowTax => {
let det = select_button(x, y, gesture);
if let Some(i) = det {
ctx.settings.show_cmd_tax = i;
}
},
SettingScreenEntry::ShowEnergy => {
let det = select_button(x, y, gesture);
if let Some(i) = det {
ctx.settings.show_energy = i;
}
},
SettingScreenEntry::ShowExperience => {
let det = select_button(x, y, gesture);
if let Some(i) = det {
ctx.settings.show_experience = i;
}
},
SettingScreenEntry::ShowTickets => {
let det = select_button(x, y, gesture);
if let Some(i) = det {
ctx.settings.show_tickets = i;
}
},
SettingScreenEntry::ShowPoison => {
let det = select_button(x, y, gesture);
if let Some(i) = det {
ctx.settings.show_infect = i;
}
},
}
next_state = Screen::Settings(SettingScreenItem { entry: selected.entry, inner: selected.inner, prev: selected.prev })
}
}
}
}
ctx.screen = next_state;
}
/// Approximates `atan2(y,x)` normalized to the `[0, 4)` range with a maximum
/// error of `0.1620` degrees.
fn atan2_norm(lhs: f32, rhs: f32) -> f32 {
const SIGN_MASK: u32 = 0x8000_0000;
const B: f32 = 0.596_227;
let y = lhs;
let x = rhs;
// Extract sign bits from floating point values
let ux_s = SIGN_MASK & x.to_bits();
let uy_s = SIGN_MASK & y.to_bits();
// Determine quadrant offset
let q = ((!ux_s & uy_s) >> 29 | ux_s >> 30) as f32;
// Calculate arctangent in the first quadrant
let bxy_a = (B * x * y).abs();
let n = bxy_a + y * y;
let atan_1q = n / (x * x + bxy_a + n);
// Translate it to the proper quadrant
let uatan_2q = (ux_s ^ uy_s) | atan_1q.to_bits();
q + f32::from_bits(uatan_2q)
}
fn touch_inc_dec(y: f32, gesture: u8) -> i8 {
if gesture == 0x05 {
if y >= 0. {
1
} else {
-1
}
} else if gesture == 0x02 {
5
} else if gesture == 0x01 {
-5
} else {
0
}
}
fn touch_left_right(x: f32, y: f32, gesture: u8) -> (i8, bool) {
if x <= 0. {
(touch_inc_dec(y, gesture), true)
} else {
(touch_inc_dec(y, gesture), false)
}
}
fn cmd_dmg_calc(ctx: &mut SystemState, x: f32, y: f32, gesture: u8, pos: usize) {
if ctx.game.cmd_dmg[pos].is_partner {
let (d, left) = touch_left_right(x, y, gesture);
if left {
ctx.game.cmd_dmg[pos].main = ctx.game.cmd_dmg[pos].main.saturating_add_signed(d);
} else {
ctx.game.cmd_dmg[pos].partner = ctx.game.cmd_dmg[pos].partner.saturating_add_signed(d);
}
} else {
let d = touch_inc_dec(y, gesture);
ctx.game.cmd_dmg[pos].main = ctx.game.cmd_dmg[pos].main.saturating_add_signed(d);
}
}
fn cmd_tax_calc(ctx: &mut SystemState, x: f32, y: f32, gesture: u8) {
if ctx.game.cmd_tax.is_partner {
let (mut d, left) = touch_left_right(x, y, gesture);
if d >= 0 {
d = 2;
} else {
d =-2;
}
if left {
ctx.game.cmd_tax.main = ctx.game.cmd_tax.main.saturating_add_signed(d);
} else {
ctx.game.cmd_tax.partner = ctx.game.cmd_tax.partner.saturating_add_signed(d);
}
} else {
let mut d = touch_inc_dec(y, gesture);
if d >= 0 {
d = 2;
} else {
d =-2;
}
ctx.game.cmd_tax.main = ctx.game.cmd_tax.main.saturating_add_signed(d);
}
}
/// true is left selection false is right selection
fn select_button(x: f32, y: f32, _gesture: u8) -> Option<bool> {
if (y<60.) & (y>-60.) {
if x<=0. {
Some(true)
} else {
Some(false)
}
} else {
None
}
}
fn cmd_dmg_w_swap(ctx: &mut SystemState, x: f32, y: f32, gesture: u8, pos: usize) {
if (x>-20.) & (x<20.) & (y< -30.)&(y>-50.) {
ctx.game.cmd_dmg[pos].is_partner = !ctx.game.cmd_dmg[pos].is_partner;
} else {
cmd_dmg_calc(ctx, x, y, gesture, pos);
}
}
fn cmd_tax_w_swap(ctx: &mut SystemState, x: f32, y: f32, gesture: u8) {
if (x>-20.) & (x<20.) & (y< -30.)&(y>-50.) {
ctx.game.cmd_tax.is_partner = !ctx.game.cmd_tax.is_partner;
} else {
cmd_tax_calc(ctx, x, y, gesture);
}
}

149
template.yaml Normal file
View File

@@ -0,0 +1,149 @@
options:
- !Option
name: unstable-hal
display_name: Enable unstable HAL features.
help: "This configuration enables unstable esp-hal features.
These come with no stability guarantees, and could be changed or removed at any time."
- !Option
name: alloc
display_name: Enable allocations via the esp-alloc crate.
help: esp-alloc comes with no stability guarantees at this time.
- !Option
name: wifi
display_name: Enable Wi-Fi via the esp-wifi crate.
help: esp-wifi comes with no stability guarantees at this time.
requires:
- alloc
- unstable-hal
chips:
- esp32
- esp32c2
- esp32c3
- esp32c6
- esp32s2
- esp32s3
- !Option
name: ble
display_name: Enable BLE via the esp-wifi crate.
help: esp-wifi comes with no stability guarantees at this time.
requires:
- alloc
- unstable-hal
chips:
- esp32
- esp32c2
- esp32c3
- esp32c6
- esp32h2
- esp32s3
- !Option
name: embassy
display_name: Add embassy framework support.
help: esp-hal-embassy comes with no stability guarantees at this time.
requires:
- unstable-hal
- !Option
name: probe-rs
display_name: Use probe-rs to flash and monitor instead of espflash.
help: probe-rs is a debugger that connects to the chips over JTAG. It can be used to flash and
monitor, and it can also be used to interactively debug an application, or run tests on the
hardware. Semihosting or RTT-based technologies like defmt-rtt require probe-rs.
chips:
- esp32c6
- esp32h2
- esp32s3
- !Option
name: probe-rs
display_name: Use probe-rs to flash and monitor instead of espflash.
help: probe-rs is a debugger that connects to the chips over JTAG. It can be used to flash and
monitor, and it can also be used to interactively debug an application, or run tests on the
hardware. Semihosting or RTT-based technologies like defmt-rtt require probe-rs.
probe-rs requires a debug probe like esp-prog, and will not work with USB-UART adapters that
often come on development boards.
chips:
- esp32
- esp32s2
- esp32c2
- esp32c3
- !Category
name: flashing-probe-rs
display_name: Flashing, logging and debugging (probe-rs)
requires:
- probe-rs
options:
- !Option
name: defmt
display_name: Use defmt to print messages.
selection_group: log-frontend
- !Option
name: panic-rtt-target
display_name: Use panic-rtt-target as the panic handler.
selection_group: panic-handler
requires:
- probe-rs
- !Category
name: flashing-espflash
display_name: Flashing, logging and debugging (espflash)
requires:
- "!probe-rs"
options:
- !Option
name: log
display_name: Use the log crate to print messages.
selection_group: log-frontend
requires:
- "!probe-rs"
- !Option
name: defmt
display_name: Use defmt to print messages.
selection_group: log-frontend
- !Option
name: esp-backtrace
display_name: Use esp-backtrace as the panic handler.
selection_group: panic-handler
requires:
- "!probe-rs"
- !Category
name: optional
display_name: Options
options:
- !Option
name: wokwi
display_name: Add support for Wokwi simulation using VS Code Wokwi extension.
chips:
- esp32
- esp32c3
- esp32c6
- esp32h2
- esp32s2
- esp32s3
- !Option
name: dev-container
display_name: Add support for VS Code Dev Containers and GitHub Codespaces.
- !Option
name: ci
display_name: Add GitHub Actions support with some basic checks.
- !Category
name: editor
display_name: Optional editor config files for rust-analyzer
options:
- !Option
name: helix
display_name: Add rust-analyzer settings for Helix Editor
- !Option
name: vscode
display_name: Add rust-analyzer settings for Visual Studio Code