added the readme
This commit is contained in:
@@ -1 +0,0 @@
|
|||||||
/target
|
|
||||||
Generated
+23
-24
@@ -31,9 +31,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.12.1"
|
version = "2.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a"
|
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitvec"
|
name = "bitvec"
|
||||||
@@ -206,7 +206,7 @@ version = "3.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8283e7331b8c93b9756e0cfdbcfb90312852f953c6faf9bf741e684cc3b6ad69"
|
checksum = "8283e7331b8c93b9756e0cfdbcfb90312852f953c6faf9bf741e684cc3b6ad69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.12.1",
|
"bitflags 2.13.0",
|
||||||
"crc",
|
"crc",
|
||||||
"log",
|
"log",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -259,13 +259,12 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.99"
|
version = "0.3.102"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
|
checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"once_cell",
|
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -322,9 +321,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.8.1"
|
version = "2.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
|
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
@@ -384,7 +383,7 @@ version = "1.1.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.12.1",
|
"bitflags 2.13.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
@@ -525,9 +524,9 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.23.2"
|
version = "1.23.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7"
|
checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@@ -542,9 +541,9 @@ checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasip2"
|
name = "wasip2"
|
||||||
version = "1.0.3+wasi-0.2.9"
|
version = "1.0.4+wasi-0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
|
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wit-bindgen 0.57.1",
|
"wit-bindgen 0.57.1",
|
||||||
]
|
]
|
||||||
@@ -560,9 +559,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.122"
|
version = "0.2.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
|
checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -573,9 +572,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.122"
|
version = "0.2.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
|
checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@@ -583,9 +582,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.122"
|
version = "0.2.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
|
checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -596,9 +595,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.122"
|
version = "0.2.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
|
checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -631,7 +630,7 @@ version = "0.244.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.12.1",
|
"bitflags 2.13.0",
|
||||||
"hashbrown 0.15.5",
|
"hashbrown 0.15.5",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"semver",
|
"semver",
|
||||||
@@ -716,7 +715,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags 2.12.1",
|
"bitflags 2.13.0",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -762,7 +761,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c101112411baafbb4bf8d33e4c4a80ab5b02d74d2612331c61e8192fc9710491"
|
checksum = "c101112411baafbb4bf8d33e4c4a80ab5b02d74d2612331c61e8192fc9710491"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"bitflags 2.12.1",
|
"bitflags 2.13.0",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"volatile",
|
"volatile",
|
||||||
]
|
]
|
||||||
|
|||||||
+2
-2
@@ -35,7 +35,7 @@ fn main() {
|
|||||||
println!("Creating UEFI boot image from {}", kernel_path.display());
|
println!("Creating UEFI boot image from {}", kernel_path.display());
|
||||||
let mut uefi_img = bootloader::UefiBoot::new(&kernel_path);
|
let mut uefi_img = bootloader::UefiBoot::new(&kernel_path);
|
||||||
uefi_img.set_boot_config(&boot_config);
|
uefi_img.set_boot_config(&boot_config);
|
||||||
let uefi_path = Path::new(&out_dir).join("wordleos-uefi.img");
|
let uefi_path = Path::new(&out_dir).join("engfinal-uefi.img");
|
||||||
uefi_img.create_disk_image(&uefi_path)
|
uefi_img.create_disk_image(&uefi_path)
|
||||||
.expect("failed to write UEFI image");
|
.expect("failed to write UEFI image");
|
||||||
println!("UEFI image written to: {}", uefi_path.display());
|
println!("UEFI image written to: {}", uefi_path.display());
|
||||||
@@ -44,7 +44,7 @@ fn main() {
|
|||||||
println!("Creating BIOS boot image from {}", kernel_path.display());
|
println!("Creating BIOS boot image from {}", kernel_path.display());
|
||||||
let mut bios_img = bootloader::BiosBoot::new(&kernel_path);
|
let mut bios_img = bootloader::BiosBoot::new(&kernel_path);
|
||||||
bios_img.set_boot_config(&boot_config);
|
bios_img.set_boot_config(&boot_config);
|
||||||
let bios_path = Path::new(&out_dir).join("wordleos-bios.img");
|
let bios_path = Path::new(&out_dir).join("engfinal-bios.img");
|
||||||
bios_img.create_disk_image(&bios_path)
|
bios_img.create_disk_image(&bios_path)
|
||||||
.expect("failed to write BIOS image");
|
.expect("failed to write BIOS image");
|
||||||
println!("BIOS image written to: {}", bios_path.display());
|
println!("BIOS image written to: {}", bios_path.display());
|
||||||
|
|||||||
@@ -0,0 +1,818 @@
|
|||||||
|
use crate::keyboard::{poll_key, KeyAction};
|
||||||
|
use crate::rng::Lcg;
|
||||||
|
use crate::vga_buffer::{self, Color, ColorCode};
|
||||||
|
|
||||||
|
const SUITS: [u8; 4] = [b'H', b'D', b'C', b'S'];
|
||||||
|
const RANK_NAMES: [&str; 15] = ["", "", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];
|
||||||
|
const DECK_CAP: usize = 64;
|
||||||
|
const MAX_EXTRA: usize = 12;
|
||||||
|
const SHOP_ITEMS: usize = 6;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
enum PokerHand {
|
||||||
|
HighCard, Pair, TwoPair, ThreeOfAKind,
|
||||||
|
Straight, Flush, FullHouse, FourOfAKind,
|
||||||
|
StraightFlush, RoyalFlush,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hand_name(h: PokerHand) -> &'static str {
|
||||||
|
match h {
|
||||||
|
PokerHand::HighCard => "HIGH CARD",
|
||||||
|
PokerHand::Pair => "PAIR",
|
||||||
|
PokerHand::TwoPair => "TWO PAIR",
|
||||||
|
PokerHand::ThreeOfAKind => "THREE OF A KIND",
|
||||||
|
PokerHand::Straight => "STRAIGHT",
|
||||||
|
PokerHand::Flush => "FLUSH",
|
||||||
|
PokerHand::FullHouse => "FULL HOUSE",
|
||||||
|
PokerHand::FourOfAKind => "FOUR OF A KIND",
|
||||||
|
PokerHand::StraightFlush => "STRAIGHT FLUSH",
|
||||||
|
PokerHand::RoyalFlush => "ROYAL FLUSH",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hand_stats(h: PokerHand) -> (u32, u32) {
|
||||||
|
match h {
|
||||||
|
PokerHand::HighCard => (5, 1),
|
||||||
|
PokerHand::Pair => (10, 2),
|
||||||
|
PokerHand::TwoPair => (20, 2),
|
||||||
|
PokerHand::ThreeOfAKind => (30, 3),
|
||||||
|
PokerHand::Straight => (30, 4),
|
||||||
|
PokerHand::Flush => (35, 4),
|
||||||
|
PokerHand::FullHouse => (40, 4),
|
||||||
|
PokerHand::FourOfAKind => (60, 7),
|
||||||
|
PokerHand::StraightFlush => (100, 8),
|
||||||
|
PokerHand::RoyalFlush => (100, 8),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn card_chip_value(rank: u8) -> u32 {
|
||||||
|
if rank <= 10 { rank as u32 }
|
||||||
|
else if rank <= 13 { 10 }
|
||||||
|
else { 11 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn card_rank_name(rank: u8) -> &'static str {
|
||||||
|
RANK_NAMES[rank as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Card {
|
||||||
|
rank: u8,
|
||||||
|
suit: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_suit(rng: &mut Lcg) -> u8 {
|
||||||
|
SUITS[rng.next_range(4)]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_rank(rng: &mut Lcg) -> u8 {
|
||||||
|
2 + rng.next_range(13) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_face_rank(rng: &mut Lcg) -> u8 {
|
||||||
|
11 + rng.next_range(3) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_number_rank(rng: &mut Lcg) -> u8 {
|
||||||
|
2 + rng.next_range(9) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_card(rng: &mut Lcg) -> Card {
|
||||||
|
Card { rank: random_rank(rng), suit: random_suit(rng) }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Game {
|
||||||
|
deck: [Card; DECK_CAP],
|
||||||
|
deck_size: usize,
|
||||||
|
draw: usize,
|
||||||
|
hand: [Card; 8],
|
||||||
|
hand_len: usize,
|
||||||
|
sel: [bool; 8],
|
||||||
|
score: u32,
|
||||||
|
target: u32,
|
||||||
|
hands: u32,
|
||||||
|
discards: u32,
|
||||||
|
ante: u32,
|
||||||
|
blind: u32,
|
||||||
|
money: u32,
|
||||||
|
extra_cards: [Card; MAX_EXTRA],
|
||||||
|
extra_count: usize,
|
||||||
|
extra_hands: u32,
|
||||||
|
extra_discards: u32,
|
||||||
|
hand_bonus: [u32; 10],
|
||||||
|
rng: Lcg,
|
||||||
|
phase: u8,
|
||||||
|
shop_sel: usize,
|
||||||
|
last_hand: PokerHand,
|
||||||
|
last_chips: u32,
|
||||||
|
last_mult: u32,
|
||||||
|
last_total: u32,
|
||||||
|
sel_count: usize,
|
||||||
|
shop_msg: [u8; 60],
|
||||||
|
shop_msg_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Game {
|
||||||
|
fn new(rng: Lcg) -> Self {
|
||||||
|
let mut g = Game {
|
||||||
|
deck: [Card { rank: 0, suit: 0 }; DECK_CAP],
|
||||||
|
deck_size: 0,
|
||||||
|
draw: 0,
|
||||||
|
hand: [Card { rank: 0, suit: 0 }; 8],
|
||||||
|
hand_len: 0,
|
||||||
|
sel: [false; 8],
|
||||||
|
score: 0,
|
||||||
|
target: 300,
|
||||||
|
hands: 4,
|
||||||
|
discards: 3,
|
||||||
|
ante: 1,
|
||||||
|
blind: 0,
|
||||||
|
money: 4,
|
||||||
|
extra_cards: [Card { rank: 0, suit: 0 }; MAX_EXTRA],
|
||||||
|
extra_count: 0,
|
||||||
|
extra_hands: 0,
|
||||||
|
extra_discards: 0,
|
||||||
|
hand_bonus: [0; 10],
|
||||||
|
rng,
|
||||||
|
phase: 0,
|
||||||
|
shop_sel: 0,
|
||||||
|
last_hand: PokerHand::HighCard,
|
||||||
|
last_chips: 0,
|
||||||
|
last_mult: 0,
|
||||||
|
last_total: 0,
|
||||||
|
sel_count: 0,
|
||||||
|
shop_msg: [0; 60],
|
||||||
|
shop_msg_len: 0,
|
||||||
|
};
|
||||||
|
g.build_deck();
|
||||||
|
g.shuffle();
|
||||||
|
g.deal();
|
||||||
|
g
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_deck(&mut self) {
|
||||||
|
let mut i = 0;
|
||||||
|
for suit in 0..4 {
|
||||||
|
for rank in 2..=14 {
|
||||||
|
self.deck[i] = Card { rank, suit: SUITS[suit] };
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for j in 0..self.extra_count {
|
||||||
|
self.deck[i] = self.extra_cards[j];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
self.deck_size = 52 + self.extra_count;
|
||||||
|
self.draw = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shuffle(&mut self) {
|
||||||
|
for i in (1..self.deck_size).rev() {
|
||||||
|
let j = self.rng.next_range(i + 1);
|
||||||
|
let tmp = self.deck[i];
|
||||||
|
self.deck[i] = self.deck[j];
|
||||||
|
self.deck[j] = tmp;
|
||||||
|
}
|
||||||
|
self.draw = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deal(&mut self) {
|
||||||
|
self.hand_len = 0;
|
||||||
|
for i in 0..8 {
|
||||||
|
if self.draw < self.deck_size {
|
||||||
|
self.hand[i] = self.deck[self.draw];
|
||||||
|
self.draw += 1;
|
||||||
|
self.hand_len += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.sel = [false; 8];
|
||||||
|
self.sel_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_played(&mut self, _count: usize) {
|
||||||
|
for i in 0..self.hand_len {
|
||||||
|
if self.sel[i] {
|
||||||
|
if self.draw < self.deck_size {
|
||||||
|
self.hand[i] = self.deck[self.draw];
|
||||||
|
self.draw += 1;
|
||||||
|
}
|
||||||
|
self.sel[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.sel_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate(&self, indices: &[usize]) -> (PokerHand, u32, u32, u32) {
|
||||||
|
let n = indices.len();
|
||||||
|
if n == 0 { return (PokerHand::HighCard, 0, 0, 0); }
|
||||||
|
|
||||||
|
let mut ranks = [0u8; 15];
|
||||||
|
let suit = self.hand[indices[0]].suit;
|
||||||
|
let mut all_same = true;
|
||||||
|
let mut total_chips = 0u32;
|
||||||
|
|
||||||
|
for &idx in indices {
|
||||||
|
let c = self.hand[idx];
|
||||||
|
ranks[c.rank as usize] += 1;
|
||||||
|
total_chips += card_chip_value(c.rank);
|
||||||
|
if c.suit != suit { all_same = false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pairs = 0u32;
|
||||||
|
let mut threes = 0u32;
|
||||||
|
let mut fours = 0u32;
|
||||||
|
for r in 2..=14 {
|
||||||
|
match ranks[r] {
|
||||||
|
2 => pairs += 1,
|
||||||
|
3 => threes += 1,
|
||||||
|
4 => fours += 1,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let straight = if n >= 5 {
|
||||||
|
let mut found = false;
|
||||||
|
for start in (2..=10).rev() {
|
||||||
|
if ranks[start] > 0 && ranks[start+1] > 0 && ranks[start+2] > 0
|
||||||
|
&& ranks[start+3] > 0 && ranks[start+4] > 0
|
||||||
|
{ found = true; break; }
|
||||||
|
}
|
||||||
|
if !found && ranks[14] > 0 && ranks[2] > 0 && ranks[3] > 0
|
||||||
|
&& ranks[4] > 0 && ranks[5] > 0
|
||||||
|
{ found = true; }
|
||||||
|
found
|
||||||
|
} else { false };
|
||||||
|
|
||||||
|
let flush = all_same && n >= 5;
|
||||||
|
|
||||||
|
let hand = if flush && straight {
|
||||||
|
if ranks[10] > 0 && ranks[11] > 0 && ranks[12] > 0
|
||||||
|
&& ranks[13] > 0 && ranks[14] > 0
|
||||||
|
{ PokerHand::RoyalFlush } else { PokerHand::StraightFlush }
|
||||||
|
} else if fours > 0 { PokerHand::FourOfAKind }
|
||||||
|
else if threes > 0 && pairs > 0 { PokerHand::FullHouse }
|
||||||
|
else if flush { PokerHand::Flush }
|
||||||
|
else if straight { PokerHand::Straight }
|
||||||
|
else if threes > 0 { PokerHand::ThreeOfAKind }
|
||||||
|
else if pairs >= 2 { PokerHand::TwoPair }
|
||||||
|
else if pairs == 1 { PokerHand::Pair }
|
||||||
|
else { PokerHand::HighCard };
|
||||||
|
|
||||||
|
let (base_chips, base_mult) = hand_stats(hand);
|
||||||
|
let bonus = self.hand_bonus[hand as usize];
|
||||||
|
(hand, base_chips + bonus, base_mult, total_chips)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selected_indices(&self) -> ([usize; 5], usize) {
|
||||||
|
let mut idx = [0usize; 5];
|
||||||
|
let mut n = 0;
|
||||||
|
for i in 0..self.hand_len {
|
||||||
|
if self.sel[i] && n < 5 {
|
||||||
|
idx[n] = i;
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(idx, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn play_hand(&mut self) {
|
||||||
|
if self.sel_count == 0 { return; }
|
||||||
|
if self.hands == 0 { return; }
|
||||||
|
|
||||||
|
let (indices, ic) = self.selected_indices();
|
||||||
|
if ic == 0 { return; }
|
||||||
|
|
||||||
|
let (hand, base_chips, base_mult, card_chips) = self.evaluate(&indices[..ic]);
|
||||||
|
let hand_score = (base_chips + card_chips) * base_mult;
|
||||||
|
|
||||||
|
self.score += hand_score;
|
||||||
|
self.hands -= 1;
|
||||||
|
self.money += 1 + hand_score / 10;
|
||||||
|
self.last_hand = hand;
|
||||||
|
self.last_chips = base_chips + card_chips;
|
||||||
|
self.last_mult = base_mult;
|
||||||
|
self.last_total = hand_score;
|
||||||
|
|
||||||
|
self.replace_played(indices.len());
|
||||||
|
|
||||||
|
if self.score >= self.target {
|
||||||
|
self.phase = 2;
|
||||||
|
} else if self.hands == 0 {
|
||||||
|
self.phase = 3;
|
||||||
|
} else {
|
||||||
|
self.phase = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discard_selected(&mut self) {
|
||||||
|
if self.sel_count == 0 { return; }
|
||||||
|
if self.discards == 0 { return; }
|
||||||
|
|
||||||
|
for i in 0..self.hand_len {
|
||||||
|
if self.sel[i] {
|
||||||
|
if self.draw < self.deck_size {
|
||||||
|
self.hand[i] = self.deck[self.draw];
|
||||||
|
self.draw += 1;
|
||||||
|
}
|
||||||
|
self.sel[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.sel_count = 0;
|
||||||
|
self.discards -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn advance_blind(&mut self) {
|
||||||
|
self.blind += 1;
|
||||||
|
if self.blind >= 3 {
|
||||||
|
self.blind = 0;
|
||||||
|
self.ante += 1;
|
||||||
|
}
|
||||||
|
self.score = 0;
|
||||||
|
self.hands = 4 + self.extra_hands;
|
||||||
|
self.discards = 3 + self.extra_discards;
|
||||||
|
self.target = self.blind_target();
|
||||||
|
self.build_deck();
|
||||||
|
self.shuffle();
|
||||||
|
self.deal();
|
||||||
|
self.phase = 0;
|
||||||
|
self.last_total = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blind_target(&self) -> u32 {
|
||||||
|
let base = match self.blind {
|
||||||
|
0 => 300,
|
||||||
|
1 => 450,
|
||||||
|
_ => 600,
|
||||||
|
};
|
||||||
|
base * self.ante
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blind_name(&self) -> &'static str {
|
||||||
|
match self.blind {
|
||||||
|
0 => "SMALL BLIND",
|
||||||
|
1 => "BIG BLIND",
|
||||||
|
_ => "BOSS BLIND",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_extra_card(&mut self, card: Card) {
|
||||||
|
if self.extra_count < MAX_EXTRA {
|
||||||
|
self.extra_cards[self.extra_count] = card;
|
||||||
|
self.extra_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Shop ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
fn shop_item_name(i: usize) -> &'static str {
|
||||||
|
match i {
|
||||||
|
0 => "FACE CARD",
|
||||||
|
1 => "NUMBER CARD",
|
||||||
|
2 => "BOOSTER PACK",
|
||||||
|
3 => "EXTRA HAND",
|
||||||
|
4 => "EXTRA DISCARD",
|
||||||
|
_ => "DONE",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shop_item_price(i: usize) -> u32 {
|
||||||
|
match i {
|
||||||
|
0 => 4,
|
||||||
|
1 => 3,
|
||||||
|
2 => 5,
|
||||||
|
3 => 6,
|
||||||
|
4 => 4,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shop_item_desc(i: usize) -> &'static str {
|
||||||
|
match i {
|
||||||
|
0 => "ADD A FACE CARD (J/Q/K) TO YOUR DECK",
|
||||||
|
1 => "ADD A NUMBER CARD (2-10) TO YOUR DECK",
|
||||||
|
2 => "ADD 2 RANDOM CARDS TO YOUR DECK",
|
||||||
|
3 => "PERMANENTLY +1 HAND PER ROUND",
|
||||||
|
4 => "PERMANENTLY +1 DISCARD PER ROUND",
|
||||||
|
_ => "LEAVE THE SHOP",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buy_shop_item(&mut self, i: usize) {
|
||||||
|
self.shop_msg_len = 0;
|
||||||
|
if i == SHOP_ITEMS - 1 {
|
||||||
|
// DONE — advance to next blind
|
||||||
|
self.advance_blind();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let price = Self::shop_item_price(i);
|
||||||
|
if self.money < price {
|
||||||
|
let msg = b"NOT ENOUGH MONEY!";
|
||||||
|
self.shop_msg[..msg.len()].copy_from_slice(msg);
|
||||||
|
self.shop_msg_len = msg.len();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.money -= price;
|
||||||
|
match i {
|
||||||
|
0 => {
|
||||||
|
let card = Card { rank: random_face_rank(&mut self.rng), suit: random_suit(&mut self.rng) };
|
||||||
|
self.add_extra_card(card);
|
||||||
|
let mut buf = [0u8; 40];
|
||||||
|
let name = card_rank_name(card.rank);
|
||||||
|
let name_b = name.as_bytes();
|
||||||
|
buf[..5].copy_from_slice(b"GOT: ");
|
||||||
|
buf[5..5+name_b.len()].copy_from_slice(name_b);
|
||||||
|
buf[5+name_b.len()] = card.suit;
|
||||||
|
self.shop_msg[..5+name_b.len()+1].copy_from_slice(&buf[..5+name_b.len()+1]);
|
||||||
|
self.shop_msg_len = 5 + name_b.len() + 1;
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
let card = Card { rank: random_number_rank(&mut self.rng), suit: random_suit(&mut self.rng) };
|
||||||
|
self.add_extra_card(card);
|
||||||
|
let mut buf = [0u8; 40];
|
||||||
|
let name = card_rank_name(card.rank);
|
||||||
|
let name_b = name.as_bytes();
|
||||||
|
buf[..5].copy_from_slice(b"GOT: ");
|
||||||
|
buf[5..5+name_b.len()].copy_from_slice(name_b);
|
||||||
|
buf[5+name_b.len()] = card.suit;
|
||||||
|
self.shop_msg[..5+name_b.len()+1].copy_from_slice(&buf[..5+name_b.len()+1]);
|
||||||
|
self.shop_msg_len = 5 + name_b.len() + 1;
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
let c1 = random_card(&mut self.rng);
|
||||||
|
let c2 = random_card(&mut self.rng);
|
||||||
|
self.add_extra_card(c1);
|
||||||
|
self.add_extra_card(c2);
|
||||||
|
let msg = b"GOT 2 CARDS!";
|
||||||
|
self.shop_msg[..msg.len()].copy_from_slice(msg);
|
||||||
|
self.shop_msg_len = msg.len();
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
self.extra_hands += 1;
|
||||||
|
let msg = b"+1 HAND PER ROUND!";
|
||||||
|
self.shop_msg[..msg.len()].copy_from_slice(msg);
|
||||||
|
self.shop_msg_len = msg.len();
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
self.extra_discards += 1;
|
||||||
|
let msg = b"+1 DISCARD PER ROUND!";
|
||||||
|
self.shop_msg[..msg.len()].copy_from_slice(msg);
|
||||||
|
self.shop_msg_len = msg.len();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_num(n: u32, buf: &mut [u8; 12]) -> usize {
|
||||||
|
if n == 0 {
|
||||||
|
buf[0] = b'0';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
let mut i = 0;
|
||||||
|
let mut n = n;
|
||||||
|
let mut digits = [0u8; 12];
|
||||||
|
while n > 0 {
|
||||||
|
digits[i] = b'0' + (n % 10) as u8;
|
||||||
|
i += 1;
|
||||||
|
n /= 10;
|
||||||
|
}
|
||||||
|
for j in 0..i {
|
||||||
|
buf[j] = digits[i - 1 - j];
|
||||||
|
}
|
||||||
|
i
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_balatro() {
|
||||||
|
let rng = Lcg::from_tsc();
|
||||||
|
let mut game = Game::new(rng);
|
||||||
|
render_all(&game);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Some(action) = poll_key() {
|
||||||
|
match action {
|
||||||
|
KeyAction::Digit(n) => {
|
||||||
|
if game.phase == 0 {
|
||||||
|
let i = (n - 1) as usize;
|
||||||
|
if i < game.hand_len {
|
||||||
|
game.sel[i] = !game.sel[i];
|
||||||
|
if game.sel[i] {
|
||||||
|
game.sel_count += 1;
|
||||||
|
if game.sel_count > 5 { game.sel[i] = false; game.sel_count -= 1; }
|
||||||
|
} else {
|
||||||
|
game.sel_count -= 1;
|
||||||
|
}
|
||||||
|
render_all(&game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyAction::Letter(b'D') | KeyAction::Letter(b'd') => {
|
||||||
|
if game.phase == 0 { game.discard_selected(); render_all(&game); }
|
||||||
|
}
|
||||||
|
KeyAction::Up => {
|
||||||
|
if game.phase == 4 {
|
||||||
|
if game.shop_sel > 0 { game.shop_sel -= 1; render_all(&game); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyAction::Down => {
|
||||||
|
if game.phase == 4 {
|
||||||
|
if game.shop_sel + 1 < SHOP_ITEMS { game.shop_sel += 1; render_all(&game); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyAction::Enter => {
|
||||||
|
match game.phase {
|
||||||
|
0 => { game.play_hand(); render_all(&game); }
|
||||||
|
1 => { game.phase = 0; render_all(&game); }
|
||||||
|
2 => { game.phase = 4; game.shop_sel = 0; game.shop_msg_len = 0; render_all(&game); }
|
||||||
|
3 => { return; }
|
||||||
|
4 => { game.buy_shop_item(game.shop_sel); render_all(&game); }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyAction::Tilde => { return; }
|
||||||
|
KeyAction::Backspace => {
|
||||||
|
match game.phase {
|
||||||
|
4 => { game.phase = 2; render_all(&game); }
|
||||||
|
p if p > 0 => { game.phase = 0; render_all(&game); }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Rendering ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
fn render_all(game: &Game) {
|
||||||
|
if game.phase == 4 {
|
||||||
|
render_shop(game);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vga_buffer::clear_screen(Color::Black);
|
||||||
|
let white = ColorCode::new(Color::White, Color::Black);
|
||||||
|
let yellow = ColorCode::new(Color::Yellow, Color::Black);
|
||||||
|
let gray = ColorCode::new(Color::DarkGray, Color::Black);
|
||||||
|
let green = ColorCode::new(Color::Green, Color::Black);
|
||||||
|
let red = ColorCode::new(Color::Red, Color::Black);
|
||||||
|
|
||||||
|
// Row 0: title, money, ante
|
||||||
|
let title = b"BALATRO";
|
||||||
|
let start = (vga_buffer::WIDTH - title.len()) / 2;
|
||||||
|
vga_buffer::write_str(0, start, "BALATRO", yellow);
|
||||||
|
|
||||||
|
let mut buf = [0u8; 12];
|
||||||
|
let mlen = fmt_num(game.money, &mut buf);
|
||||||
|
let mut col = vga_buffer::WIDTH - mlen - 8;
|
||||||
|
vga_buffer::write_str(0, col, "MONEY: ", yellow);
|
||||||
|
for j in 0..mlen {
|
||||||
|
vga_buffer::write_char(0, col + 7 + j, buf[j], yellow);
|
||||||
|
}
|
||||||
|
|
||||||
|
let alen = fmt_num(game.ante, &mut buf);
|
||||||
|
col = 2;
|
||||||
|
vga_buffer::write_str(0, col, "ANTE ", gray);
|
||||||
|
for j in 0..alen {
|
||||||
|
vga_buffer::write_char(0, col + 5 + j, buf[j], yellow);
|
||||||
|
}
|
||||||
|
vga_buffer::write_str(0, col + 5 + alen, "/8", gray);
|
||||||
|
|
||||||
|
// Row 1: score, hands, discards
|
||||||
|
let mut score_buf = [0u8; 12];
|
||||||
|
let slen = fmt_num(game.score, &mut score_buf);
|
||||||
|
let mut target_buf = [0u8; 12];
|
||||||
|
let tlen = fmt_num(game.target, &mut target_buf);
|
||||||
|
|
||||||
|
let mut line = [b' '; 80];
|
||||||
|
let mut pos = 2;
|
||||||
|
line[pos..pos+6].copy_from_slice(b"SCORE:");
|
||||||
|
pos += 6;
|
||||||
|
line[pos] = b' '; pos += 1;
|
||||||
|
line[pos..pos+slen].copy_from_slice(&score_buf[..slen]);
|
||||||
|
pos += slen;
|
||||||
|
line[pos] = b'/'; pos += 1;
|
||||||
|
line[pos..pos+tlen].copy_from_slice(&target_buf[..tlen]);
|
||||||
|
pos += tlen;
|
||||||
|
while pos < 30 { line[pos] = b' '; pos += 1; }
|
||||||
|
|
||||||
|
line[pos..pos+6].copy_from_slice(&b" HANDS"[..]);
|
||||||
|
pos += 6;
|
||||||
|
line[pos] = b':'; pos += 1;
|
||||||
|
line[pos] = b' '; pos += 1;
|
||||||
|
let hlen = fmt_num(game.hands, &mut buf);
|
||||||
|
line[pos..pos+hlen].copy_from_slice(&buf[..hlen]);
|
||||||
|
pos += hlen;
|
||||||
|
while pos < 55 { line[pos] = b' '; pos += 1; }
|
||||||
|
line[pos] = b'D'; pos += 1;
|
||||||
|
line[pos] = b':'; pos += 1;
|
||||||
|
line[pos] = b' '; pos += 1;
|
||||||
|
let dlen = fmt_num(game.discards, &mut buf);
|
||||||
|
line[pos..pos+dlen].copy_from_slice(&buf[..dlen]);
|
||||||
|
|
||||||
|
let score_color = if game.score >= game.target { green } else { white };
|
||||||
|
for (i, &ch) in line.iter().enumerate() {
|
||||||
|
if i < 6 { vga_buffer::write_char(1, i, ch, white); }
|
||||||
|
else if i < 6 + 1 + slen + 1 + tlen { vga_buffer::write_char(1, i, ch, score_color); }
|
||||||
|
else { vga_buffer::write_char(1, i, ch, white); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row 2: blind name
|
||||||
|
vga_buffer::write_str(2, 2, game.blind_name(), yellow);
|
||||||
|
|
||||||
|
// Row 4: YOUR HAND:
|
||||||
|
vga_buffer::write_str(4, 2, "YOUR HAND:", white);
|
||||||
|
|
||||||
|
// Rows 5-6: cards
|
||||||
|
for i in 0..game.hand_len {
|
||||||
|
let row = 5 + i / 4;
|
||||||
|
let col = 3 + (i % 4) * 18;
|
||||||
|
let inverted = game.sel[i];
|
||||||
|
let fg = if inverted { Color::Black } else { Color::White };
|
||||||
|
let bg = if inverted { Color::White } else { Color::Black };
|
||||||
|
let color = ColorCode::new(fg, bg);
|
||||||
|
|
||||||
|
vga_buffer::write_char(row, col, b'[', color);
|
||||||
|
vga_buffer::write_char(row, col + 1, b'1' + i as u8, color);
|
||||||
|
vga_buffer::write_char(row, col + 2, b']', color);
|
||||||
|
vga_buffer::write_char(row, col + 3, b' ', color);
|
||||||
|
|
||||||
|
let name = card_rank_name(game.hand[i].rank);
|
||||||
|
let name_b = name.as_bytes();
|
||||||
|
for j in 0..name_b.len() {
|
||||||
|
vga_buffer::write_char(row, col + 4 + j, name_b[j], color);
|
||||||
|
}
|
||||||
|
let suit_pos = col + 4 + name_b.len();
|
||||||
|
vga_buffer::write_char(row, suit_pos, game.hand[i].suit, color);
|
||||||
|
for j in (suit_pos + 1)..(col + 17) {
|
||||||
|
vga_buffer::write_char(row, j, b' ', color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row 8: playing / selection hint
|
||||||
|
let mut sel_cards = [b' '; 80];
|
||||||
|
let mut spos = 2;
|
||||||
|
sel_cards[spos..spos+8].copy_from_slice(b"PLAYING:");
|
||||||
|
spos += 9;
|
||||||
|
for i in 0..game.hand_len {
|
||||||
|
if game.sel[i] {
|
||||||
|
let name = card_rank_name(game.hand[i].rank);
|
||||||
|
let name_b = name.as_bytes();
|
||||||
|
for j in 0..name_b.len() { sel_cards[spos] = name_b[j]; spos += 1; }
|
||||||
|
sel_cards[spos] = game.hand[i].suit; spos += 1;
|
||||||
|
sel_cards[spos] = b' '; spos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if game.sel_count == 0 {
|
||||||
|
let hint = b"SELECT CARDS WITH [1-8]";
|
||||||
|
sel_cards[spos..spos+hint.len()].copy_from_slice(hint);
|
||||||
|
}
|
||||||
|
for (j, &ch) in sel_cards.iter().enumerate() {
|
||||||
|
vga_buffer::write_char(8, j, ch, white);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row 9: hand evaluation
|
||||||
|
if game.sel_count > 0 && game.phase == 0 {
|
||||||
|
let (indices, ic) = game.selected_indices();
|
||||||
|
if ic > 0 {
|
||||||
|
let (hand, base_chips, base_mult, card_chips) = game.evaluate(&indices[..ic]);
|
||||||
|
vga_buffer::write_str(9, 2, hand_name(hand), yellow);
|
||||||
|
|
||||||
|
let mut chips_buf = [0u8; 12];
|
||||||
|
let clen = fmt_num(base_chips + card_chips, &mut chips_buf);
|
||||||
|
let cp = 40;
|
||||||
|
vga_buffer::write_str(9, cp, "CHIPS: ", white);
|
||||||
|
for j in 0..clen { vga_buffer::write_char(9, cp + 7 + j, chips_buf[j], white); }
|
||||||
|
|
||||||
|
let mlen = fmt_num(base_mult, &mut chips_buf);
|
||||||
|
let mp = 56;
|
||||||
|
vga_buffer::write_str(9, mp, "MULT: X", white);
|
||||||
|
for j in 0..mlen { vga_buffer::write_char(9, mp + 7 + j, chips_buf[j], white); }
|
||||||
|
}
|
||||||
|
} else if game.phase > 0 && game.last_total > 0 {
|
||||||
|
vga_buffer::write_str(9, 2, hand_name(game.last_hand), yellow);
|
||||||
|
let mut val_buf = [0u8; 12];
|
||||||
|
let vlen = fmt_num(game.last_chips, &mut val_buf);
|
||||||
|
vga_buffer::write_str(9, 40, "CHIPS: ", white);
|
||||||
|
for j in 0..vlen { vga_buffer::write_char(9, 47 + j, val_buf[j], white); }
|
||||||
|
let vlen2 = fmt_num(game.last_mult, &mut val_buf);
|
||||||
|
vga_buffer::write_str(9, 56, "MULT: X", white);
|
||||||
|
for j in 0..vlen2 { vga_buffer::write_char(9, 63 + j, val_buf[j], white); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row 10: last hand score
|
||||||
|
if game.last_total > 0 {
|
||||||
|
let mut val_buf = [0u8; 12];
|
||||||
|
let vlen = fmt_num(game.last_total, &mut val_buf);
|
||||||
|
vga_buffer::write_str(10, 2, "HAND: +", white);
|
||||||
|
for j in 0..vlen { vga_buffer::write_char(10, 9 + j, val_buf[j], white); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row 12: phase messages
|
||||||
|
match game.phase {
|
||||||
|
1 => { vga_buffer::write_str(12, 2, "HAND SCORED! PRESS ENTER TO CONTINUE", gray); }
|
||||||
|
2 => { vga_buffer::write_str(12, 2, "BLIND DEFEATED! PRESS ENTER FOR SHOP", green); }
|
||||||
|
3 => { vga_buffer::write_str(12, 2, "GAME OVER! PRESS ENTER TO RETURN", red); }
|
||||||
|
_ => { vga_buffer::write_str(12, 2, "[1-8] SELECT [ENTER] PLAY [D] DISCARD", gray); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row 24: score bar
|
||||||
|
let bar_w = vga_buffer::WIDTH - 4;
|
||||||
|
let filled = if game.target > 0 {
|
||||||
|
((game.score as u64 * bar_w as u64) / game.target as u64) as usize
|
||||||
|
} else { 0 };
|
||||||
|
let filled = filled.min(bar_w);
|
||||||
|
vga_buffer::write_char(24, 1, b'[', gray);
|
||||||
|
let bar_color = if game.score >= game.target { ColorCode::new(Color::Black, Color::Green) }
|
||||||
|
else { ColorCode::new(Color::Black, Color::DarkGray) };
|
||||||
|
for i in 0..bar_w {
|
||||||
|
let ch = if i < filled { b'#' } else { b'.' };
|
||||||
|
let c = if i < filled { bar_color } else { ColorCode::new(Color::DarkGray, Color::Black) };
|
||||||
|
vga_buffer::write_char(24, 2 + i, ch, c);
|
||||||
|
}
|
||||||
|
vga_buffer::write_char(24, 2 + bar_w, b']', gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_shop(game: &Game) {
|
||||||
|
vga_buffer::clear_screen(Color::Black);
|
||||||
|
let white = ColorCode::new(Color::White, Color::Black);
|
||||||
|
let yellow = ColorCode::new(Color::Yellow, Color::Black);
|
||||||
|
let gray = ColorCode::new(Color::DarkGray, Color::Black);
|
||||||
|
let green = ColorCode::new(Color::Green, Color::Black);
|
||||||
|
let cyan = ColorCode::new(Color::LightCyan, Color::Black);
|
||||||
|
|
||||||
|
// Row 0: title and money
|
||||||
|
let title = b"BALATRO";
|
||||||
|
let start = (vga_buffer::WIDTH - title.len()) / 2;
|
||||||
|
vga_buffer::write_str(0, start, "BALATRO", yellow);
|
||||||
|
|
||||||
|
let mut buf = [0u8; 12];
|
||||||
|
let mlen = fmt_num(game.money, &mut buf);
|
||||||
|
let col = vga_buffer::WIDTH - mlen - 8;
|
||||||
|
vga_buffer::write_str(0, col, "MONEY: ", yellow);
|
||||||
|
for j in 0..mlen { vga_buffer::write_char(0, col + 7 + j, buf[j], yellow); }
|
||||||
|
|
||||||
|
// Row 2: SHOP
|
||||||
|
vga_buffer::write_str(2, 2, "SHOP", cyan);
|
||||||
|
|
||||||
|
// Row 4+: items
|
||||||
|
for i in 0..SHOP_ITEMS {
|
||||||
|
let row = 4 + i * 2;
|
||||||
|
let selected = i == game.shop_sel;
|
||||||
|
let fg = if selected { Color::Black } else { Color::White };
|
||||||
|
let bg = if selected { Color::White } else { Color::Black };
|
||||||
|
let color = ColorCode::new(fg, bg);
|
||||||
|
|
||||||
|
let name = Game::shop_item_name(i);
|
||||||
|
let name_b = name.as_bytes();
|
||||||
|
let price = Game::shop_item_price(i);
|
||||||
|
|
||||||
|
if selected {
|
||||||
|
vga_buffer::write_char(row, 2, b'>', color);
|
||||||
|
} else {
|
||||||
|
vga_buffer::write_char(row, 2, b' ', color);
|
||||||
|
}
|
||||||
|
vga_buffer::write_char(row, 3, b' ', color);
|
||||||
|
for j in 0..name_b.len() {
|
||||||
|
vga_buffer::write_char(row, 4 + j, name_b[j], color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Price
|
||||||
|
let mut pbuf = [0u8; 12];
|
||||||
|
let plen = fmt_num(price, &mut pbuf);
|
||||||
|
let pc = 30;
|
||||||
|
vga_buffer::write_char(row, pc, b'$', color);
|
||||||
|
for j in 0..plen {
|
||||||
|
vga_buffer::write_char(row, pc + 1 + j, pbuf[j], color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Affordability
|
||||||
|
let can_afford = price <= game.money || price == 0;
|
||||||
|
if selected && !can_afford {
|
||||||
|
vga_buffer::write_str(row, pc + 2 + plen, " (CAN'T AFFORD)", gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description row
|
||||||
|
let desc_row = 4 + game.shop_sel * 2 + 1;
|
||||||
|
let desc = Game::shop_item_desc(game.shop_sel);
|
||||||
|
vga_buffer::write_str(desc_row, 2, desc, gray);
|
||||||
|
|
||||||
|
// Message
|
||||||
|
if game.shop_msg_len > 0 {
|
||||||
|
vga_buffer::write_str(18, 2, core::str::from_utf8(&game.shop_msg[..game.shop_msg_len]).unwrap_or(""), green);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controls
|
||||||
|
vga_buffer::write_str(20, 2, "[UP/DOWN] NAVIGATE [ENTER] BUY [BACKSPACE] BACK", gray);
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
vga_buffer::write_str(22, 2, "DECK SIZE:", white);
|
||||||
|
let mut sbuf = [0u8; 12];
|
||||||
|
let slen = fmt_num(52u32 + game.extra_count as u32, &mut sbuf);
|
||||||
|
vga_buffer::write_str(22, 12, " ", white);
|
||||||
|
for j in 0..slen { vga_buffer::write_char(22, 13 + j, sbuf[j], white); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,459 @@
|
|||||||
|
use crate::keyboard;
|
||||||
|
use crate::keyboard::KeyAction;
|
||||||
|
use crate::sound;
|
||||||
|
use crate::vga_buffer::{self, Color, ColorCode};
|
||||||
|
|
||||||
|
struct Choice {
|
||||||
|
text: &'static str,
|
||||||
|
score_modifier: usize,
|
||||||
|
next_node: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NodeDef {
|
||||||
|
id: &'static str,
|
||||||
|
scene_title: Option<&'static str>,
|
||||||
|
text: &'static str,
|
||||||
|
choices: &'static [Choice],
|
||||||
|
is_terminal: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_node(id: &str) -> &'static NodeDef {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < ALL_NODES.len() {
|
||||||
|
if ALL_NODES[i].id == id {
|
||||||
|
return &ALL_NODES[i];
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
&ALL_NODES[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
const ALL_NODES: &[NodeDef] = &[
|
||||||
|
NodeDef {
|
||||||
|
id: "START",
|
||||||
|
scene_title: Some("Scene 1: The Old Woman's Attic"),
|
||||||
|
text: "You stand in a dimly lit attic filled with contraband books. The old woman refuses to leave her collection. Captain Beatty holds the igniter and looks at you: 'Do your duty, Montag.'",
|
||||||
|
choices: &[
|
||||||
|
Choice { text: "Burn the books and drag her out. Maintain order.", score_modifier: 10, next_node: "SCENE_2A_PARLOR" },
|
||||||
|
Choice { text: "Hesitate and secretly slip a book into your coat sleeve.", score_modifier: 25, next_node: "SCENE_2B_SUBWAY" },
|
||||||
|
Choice { text: "Drop your igniter, openly defend her, and refuse to burn history.", score_modifier: 15, next_node: "SCENE_2C_IMMEDIATE_REBELLION" },
|
||||||
|
],
|
||||||
|
is_terminal: false,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "SCENE_2A_PARLOR",
|
||||||
|
scene_title: Some("Scene 2A: The Parlor Walls"),
|
||||||
|
text: "You are at home. Mildred and her friends are completely brainwashed, watching the 'White Clowns' scream on the 3-wall parlor TVs.",
|
||||||
|
choices: &[
|
||||||
|
Choice { text: "Sit down and zone out with them. Forget the attic entirely.", score_modifier: 10, next_node: "SCENE_3A_PROMOTION" },
|
||||||
|
Choice { text: "Pull out a hidden book and force them to listen to poetry.", score_modifier: 30, next_node: "SCENE_3B_TRAP" },
|
||||||
|
Choice { text: "Aggressively smash the parlor wall television screens to wake them up.", score_modifier: 15, next_node: "SCENE_3D_PARLOR_DENUNCIATION" },
|
||||||
|
],
|
||||||
|
is_terminal: false,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "SCENE_2B_SUBWAY",
|
||||||
|
scene_title: Some("Scene 2B: The Sieve and the Sand"),
|
||||||
|
text: "You ride the subway with a stolen Bible. A massive audio advertisement for 'Denham's Dentifrice' blasts over the speakers, trying to numb your brain.",
|
||||||
|
choices: &[
|
||||||
|
Choice { text: "Give in to the noise. Close the book. Conformity is easier.", score_modifier: 10, next_node: "SCENE_3A_PROMOTION" },
|
||||||
|
Choice { text: "Fight the noise. Keep reading intently to memorize the text.", score_modifier: 30, next_node: "SCENE_3C_FABER_HIDE" },
|
||||||
|
Choice { text: "Stand up and scream back at the speakers, shouting scripture at the passengers.", score_modifier: 15, next_node: "SCENE_3E_SUBWAY_ARREST" },
|
||||||
|
],
|
||||||
|
is_terminal: false,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "SCENE_2C_IMMEDIATE_REBELLION",
|
||||||
|
scene_title: Some("Scene 2C: The Attic Standoff"),
|
||||||
|
text: "Beatty looks at you with dangerous contempt. The old woman strikes a kitchen match herself. The attic begins to catch fire with you still inside.",
|
||||||
|
choices: &[
|
||||||
|
Choice { text: "Panic, betray your ideals, and sprint out the door with Beatty.", score_modifier: 5, next_node: "SCENE_2A_PARLOR" },
|
||||||
|
Choice { text: "Try to wrestle the match from her to save her life manually.", score_modifier: 20, next_node: "ENDING_DIE_IN_FLAMES" },
|
||||||
|
Choice { text: "Stand still in solidarity, choosing to burn alongside her and her books.", score_modifier: 25, next_node: "ENDING_MARTYR_FIRE" },
|
||||||
|
],
|
||||||
|
is_terminal: false,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "SCENE_3A_PROMOTION",
|
||||||
|
scene_title: Some("Route Alpha: The Tyrant's Path"),
|
||||||
|
text: "Beatty praises your loyalty at the station and offers you a permanent promotion to Fire Captain. You have successfully killed your own curiosity.",
|
||||||
|
choices: &[
|
||||||
|
Choice { text: "Accept the promotion. Put on the Captain's helmet.", score_modifier: 10, next_node: "ENDING_TOTALITARIAN" },
|
||||||
|
Choice { text: "Vomit from guilt, reject the promotion, and run out of the station.", score_modifier: 20, next_node: "SCENE_3B_TRAP" },
|
||||||
|
Choice { text: "Accept the badge, but secretly vow to use your new power to sabotage firehouses from within.", score_modifier: 25, next_node: "ENDING_INSIDER_SABOTAGE" },
|
||||||
|
],
|
||||||
|
is_terminal: false,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "SCENE_3B_TRAP",
|
||||||
|
scene_title: Some("Route Beta: The Martyr's Fire"),
|
||||||
|
text: "Mildred reports your treason. Beatty drives the Salamander to your own house and forces you to burn it. In a flash of panic, you turn the flamethrower on Beatty.",
|
||||||
|
choices: &[
|
||||||
|
Choice { text: "Flee blindly into the dark alleyways as the Mechanical Hound is deployed.", score_modifier: 20, next_node: "SCENE_4A_THE_HUNT" },
|
||||||
|
Choice { text: "Surrender to the arriving police firemen, dropping your weapon.", score_modifier: 5, next_node: "ENDING_ELIMINATED" },
|
||||||
|
Choice { text: "Stand your ground and attempt to fight off the approaching Mechanical Hound with your bare hands.", score_modifier: 15, next_node: "ENDING_HOUNDED_DEATH" },
|
||||||
|
],
|
||||||
|
is_terminal: false,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "SCENE_3C_FABER_HIDE",
|
||||||
|
scene_title: Some("Route Gamma: The Intellectual Underground"),
|
||||||
|
text: "You safely reach Professor Faber's house. He hands you a small two-way communication radio 'green bullet' and tells you to run toward the river to find the Book People.",
|
||||||
|
choices: &[
|
||||||
|
Choice { text: "Dive directly into the river currents and float away from the mechanical city.", score_modifier: 30, next_node: "SCENE_4B_THE_RIVER" },
|
||||||
|
Choice { text: "Insist on staying to defend Faber from the tracker units coming for him.", score_modifier: 15, next_node: "ENDING_DEFENDING_FABER" },
|
||||||
|
Choice { text: "Lose your nerve, hide in Faber's closet, and pray they pass by.", score_modifier: 5, next_node: "ENDING_HOUNDED_DEATH" },
|
||||||
|
],
|
||||||
|
is_terminal: false,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "SCENE_3D_PARLOR_DENUNCIATION",
|
||||||
|
scene_title: Some("Scene 3D: The Broken Screen"),
|
||||||
|
text: "Mildred's friends scream in absolute terror at your violent outbursts. The neighborhood alarms start sounding. You hear sirens approaching fast.",
|
||||||
|
choices: &[
|
||||||
|
Choice { text: "Sprint out the back window before the authorities encircle the block.", score_modifier: 15, next_node: "SCENE_3B_TRAP" },
|
||||||
|
Choice { text: "Sit down on the floor amid the broken glass and passively await your fate.", score_modifier: 5, next_node: "ENDING_ELIMINATED" },
|
||||||
|
Choice { text: "Force Mildred at gunpoint to flee into the night with you.", score_modifier: 10, next_node: "ENDING_TRAITOR_EXECUTION" },
|
||||||
|
],
|
||||||
|
is_terminal: false,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "SCENE_3E_SUBWAY_ARREST",
|
||||||
|
scene_title: Some("Scene 3E: The Platform Corner"),
|
||||||
|
text: "Underground transit guards intercept you at the next terminal stop. You are pinned aggressively against the cold concrete tile wall. The commuters ignore you.",
|
||||||
|
choices: &[
|
||||||
|
Choice { text: "Quietly swallow an experimental chemical capsule hidden in your lapel to protect Faber's identity.", score_modifier: 25, next_node: "ENDING_SILENT_SUICIDE" },
|
||||||
|
Choice { text: "Beg for mercy and trade the location of Faber's residence for an official pardon.", score_modifier: 0, next_node: "ENDING_BETRAYER" },
|
||||||
|
Choice { text: "Break away violently and jump directly onto the electrified transit rails below.", score_modifier: 10, next_node: "ENDING_RAIL_DEATH" },
|
||||||
|
],
|
||||||
|
is_terminal: false,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "SCENE_4A_THE_HUNT",
|
||||||
|
scene_title: Some("Scene 4A: The Live-Streamed Pursuit"),
|
||||||
|
text: "Helicopters illuminate the alleyways above. The entire city watches your chase live on television. You reach the edge of the dark riverbanks, your feet splashing in mud.",
|
||||||
|
choices: &[
|
||||||
|
Choice { text: "Plunge directly into the cold river to mask your scent trail from the Hound.", score_modifier: 30, next_node: "SCENE_4B_THE_RIVER" },
|
||||||
|
Choice { text: "Turn around to face the camera drones, delivering an impassioned final speech about freedom.", score_modifier: 25, next_node: "ENDING_TELEVISED_EXECUTION" },
|
||||||
|
Choice { text: "Try to cross the open highway on foot to reach the distant countryside factories.", score_modifier: 10, next_node: "ENDING_HOUNDED_DEATH" },
|
||||||
|
],
|
||||||
|
is_terminal: false,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "SCENE_4B_THE_RIVER",
|
||||||
|
scene_title: Some("Scene 4B: The Gathering at the Tracks"),
|
||||||
|
text: "You emerge from the river waters far outside city limits. You discover Granger and a group of traveling, displaced academics cooking over an open fire. They invite you to pick a text to memorize.",
|
||||||
|
choices: &[
|
||||||
|
Choice { text: "Choose to preserve the Book of Ecclesiastes to give future society wisdom.", score_modifier: 35, next_node: "ENDING_RENAISSANCE" },
|
||||||
|
Choice { text: "Choose to preserve the Book of Revelation to remind them of the destructive past.", score_modifier: 35, next_node: "ENDING_RENAISSANCE" },
|
||||||
|
Choice { text: "Refuse to memorize anything, claiming humanity is an unfixable error.", score_modifier: 10, next_node: "ENDING_CYNIC_DESERTION" },
|
||||||
|
],
|
||||||
|
is_terminal: false,
|
||||||
|
},
|
||||||
|
// ── Endings ──────────────────────────────────────────────────────────
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_TOTALITARIAN",
|
||||||
|
scene_title: None,
|
||||||
|
text: "You allowed the Dystopian Setting to conquer your individuality. Montag is dead inside, a permanent agent of the status quo.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_RENAISSANCE",
|
||||||
|
scene_title: None,
|
||||||
|
text: "Jet bombers flatten the city. You survive downriver with the scholars as a living library, marching back to rebuild a literate civilization.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_MARTYR_FIRE",
|
||||||
|
scene_title: None,
|
||||||
|
text: "You burn alive in the attic with the old woman. Your death becomes an unerasable legend whispered among hidden rebels.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_DIE_IN_FLAMES",
|
||||||
|
scene_title: None,
|
||||||
|
text: "You fail to save the woman and the smoke asphyxiates you. You die in the flames, achieving nothing but an accidental casualty report.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_HOUNDED_DEATH",
|
||||||
|
scene_title: None,
|
||||||
|
text: "The Mechanical Hound corners you. A cold procaine needle injects your leg. Your heart stops as the screen fades to cold static.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_ELIMINATED",
|
||||||
|
scene_title: None,
|
||||||
|
text: "You are quietly escorted away to an anonymous state re-education asylum. Your records are completely scrubbed from history.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_TELEVISED_EXECUTION",
|
||||||
|
scene_title: None,
|
||||||
|
text: "The Hound strikes you down on live television. Millions watch your final defiant monologue. You are dead, but you planted the seeds of doubt.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_DEFENDING_FABER",
|
||||||
|
scene_title: None,
|
||||||
|
text: "You hold the door so Faber can flee out the back. You take a fatal blast from state enforcement squads, dying to preserve the old generation.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_TRAITOR_EXECUTION",
|
||||||
|
scene_title: None,
|
||||||
|
text: "Mildred alerts an active patrol hovercraft overhead. They eliminate you both on the front lawn as public enemies.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_SILENT_SUICIDE",
|
||||||
|
scene_title: None,
|
||||||
|
text: "You die silently in the transit terminal. The police never find Faber, and your secrets die with you.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_BETRAYER",
|
||||||
|
scene_title: None,
|
||||||
|
text: "You save your skin but betray Faber. You are forced to watch his library burn before they execute you anyway for your crimes.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_RAIL_DEATH",
|
||||||
|
scene_title: None,
|
||||||
|
text: "You die instantly on the high-voltage tracks. The evening news reports you as an unnamed, unhinged vagrant to avoid reporting the book.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_INSIDER_SABOTAGE",
|
||||||
|
scene_title: None,
|
||||||
|
text: "You spend years quietly misrouting alarms and corrupting databases from within the department before being discovered and quietly eliminated.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
NodeDef {
|
||||||
|
id: "ENDING_CYNIC_DESERTION",
|
||||||
|
scene_title: None,
|
||||||
|
text: "You wander alone away from both the rebels and the city. When the bombs fall, you survive in the radioactive wilderness, entirely alone.",
|
||||||
|
choices: &[],
|
||||||
|
is_terminal: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
struct FahrenheitGame {
|
||||||
|
current_node: &'static str,
|
||||||
|
score: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn route_label(node_id: &str) -> &'static str {
|
||||||
|
if node_id.starts_with("SCENE_3A") || node_id == "SCENE_4A_THE_HUNT" {
|
||||||
|
"Route Alpha: The Tyrant's Path"
|
||||||
|
} else if node_id == "SCENE_3B_TRAP" || node_id.starts_with("SCENE_4A") {
|
||||||
|
"Route Beta: The Martyr's Fire"
|
||||||
|
} else if node_id.starts_with("SCENE_3C") || node_id == "SCENE_4B_THE_RIVER" {
|
||||||
|
"Route Gamma: The Intellectual Underground"
|
||||||
|
} else if node_id == "SCENE_3D_PARLOR_DENUNCIATION" {
|
||||||
|
"Route Delta: The Broken Screen"
|
||||||
|
} else if node_id == "SCENE_3E_SUBWAY_ARREST" {
|
||||||
|
"Route Epsilon: The Platform Corner"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_wrapped(start_row: usize, text: &str, color: ColorCode, max_width: usize) -> usize {
|
||||||
|
let bytes = text.as_bytes();
|
||||||
|
let len = bytes.len();
|
||||||
|
let mut row = start_row;
|
||||||
|
let mut pos = 0;
|
||||||
|
|
||||||
|
while pos < len && row < vga_buffer::HEIGHT {
|
||||||
|
let remaining = len - pos;
|
||||||
|
let line_len = if remaining <= max_width {
|
||||||
|
remaining
|
||||||
|
} else {
|
||||||
|
let mut break_pos = pos + max_width;
|
||||||
|
while break_pos > pos && bytes[break_pos] != b' ' {
|
||||||
|
break_pos -= 1;
|
||||||
|
}
|
||||||
|
if break_pos == pos {
|
||||||
|
max_width
|
||||||
|
} else {
|
||||||
|
break_pos - pos
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let line = &text[pos..pos + line_len];
|
||||||
|
let line = line.trim_end();
|
||||||
|
if !line.is_empty() {
|
||||||
|
let col = (vga_buffer::WIDTH - line.len()) / 2;
|
||||||
|
vga_buffer::write_str(row, col, line, color);
|
||||||
|
}
|
||||||
|
row += 1;
|
||||||
|
|
||||||
|
pos += line_len;
|
||||||
|
if pos < len && bytes[pos] == b' ' {
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
row
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_number(row: usize, col: usize, num: usize, color: ColorCode) -> usize {
|
||||||
|
let mut n = num;
|
||||||
|
let mut c = col;
|
||||||
|
if n == 0 {
|
||||||
|
vga_buffer::write_char(row, c, b'0', color);
|
||||||
|
return c + 1;
|
||||||
|
}
|
||||||
|
let mut digits = [b'0'; 10];
|
||||||
|
let mut len = 0;
|
||||||
|
while n > 0 {
|
||||||
|
digits[len] = b'0' + (n % 10) as u8;
|
||||||
|
n /= 10;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
for i in (0..len).rev() {
|
||||||
|
vga_buffer::write_char(row, c, digits[i], color);
|
||||||
|
c += 1;
|
||||||
|
}
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_game(game: &FahrenheitGame) {
|
||||||
|
vga_buffer::clear_screen(Color::Black);
|
||||||
|
|
||||||
|
let node = get_node(game.current_node);
|
||||||
|
let white = ColorCode::new(Color::White, Color::Black);
|
||||||
|
let yellow = ColorCode::new(Color::Yellow, Color::Black);
|
||||||
|
let cyan = ColorCode::new(Color::Cyan, Color::Black);
|
||||||
|
let dgray = ColorCode::new(Color::DarkGray, Color::Black);
|
||||||
|
|
||||||
|
if node.is_terminal {
|
||||||
|
let title = "GAME OVER";
|
||||||
|
let col = (vga_buffer::WIDTH - title.len()) / 2;
|
||||||
|
vga_buffer::write_str(2, col, title, yellow);
|
||||||
|
|
||||||
|
let end_row = render_wrapped(5, node.text, white, 74);
|
||||||
|
|
||||||
|
let mut c = (vga_buffer::WIDTH - 24) / 2;
|
||||||
|
let score_label = "LITERARY SCORE: ";
|
||||||
|
vga_buffer::write_str(end_row + 1, c, score_label, cyan);
|
||||||
|
c += score_label.len();
|
||||||
|
c = write_number(end_row + 1, c, game.score, cyan);
|
||||||
|
vga_buffer::write_str(end_row + 1, c, "/100", cyan);
|
||||||
|
|
||||||
|
let msg = "PRESS ANY KEY TO RETURN";
|
||||||
|
let col = (vga_buffer::WIDTH - msg.len()) / 2;
|
||||||
|
vga_buffer::write_str(end_row + 3, col, msg, dgray);
|
||||||
|
} else {
|
||||||
|
if let Some(title) = node.scene_title {
|
||||||
|
let col = (vga_buffer::WIDTH - title.len()) / 2;
|
||||||
|
vga_buffer::write_str(0, col, title, yellow);
|
||||||
|
}
|
||||||
|
|
||||||
|
let text_end = render_wrapped(2, node.text, white, 74);
|
||||||
|
|
||||||
|
let mut choice_row = text_end + 1;
|
||||||
|
for i in 0..node.choices.len() {
|
||||||
|
let choice = &node.choices[i];
|
||||||
|
let prefix = match i {
|
||||||
|
0 => "1. ",
|
||||||
|
1 => "2. ",
|
||||||
|
_ => "3. ",
|
||||||
|
};
|
||||||
|
let mut buf = [0u8; 80];
|
||||||
|
let mut len = 0;
|
||||||
|
for &b in prefix.as_bytes() {
|
||||||
|
buf[len] = b;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
for &b in choice.text.as_bytes() {
|
||||||
|
if len < 80 {
|
||||||
|
buf[len] = b;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let line = core::str::from_utf8(&buf[..len]).unwrap();
|
||||||
|
let col = (vga_buffer::WIDTH - line.len()) / 2;
|
||||||
|
vga_buffer::write_str(choice_row, col, line, white);
|
||||||
|
choice_row += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let route = route_label(game.current_node);
|
||||||
|
if !route.is_empty() {
|
||||||
|
let sep = "----------------------------------------";
|
||||||
|
let col = (vga_buffer::WIDTH - sep.len()) / 2;
|
||||||
|
vga_buffer::write_str(choice_row + 1, col, sep, dgray);
|
||||||
|
let col = (vga_buffer::WIDTH - route.len()) / 2;
|
||||||
|
vga_buffer::write_str(choice_row + 2, col, route, cyan);
|
||||||
|
}
|
||||||
|
|
||||||
|
let score_text = "SCORE: ";
|
||||||
|
let mut c = (vga_buffer::WIDTH - 20) / 2;
|
||||||
|
vga_buffer::write_str(choice_row + 4, c, score_text, cyan);
|
||||||
|
c += score_text.len();
|
||||||
|
write_number(choice_row + 4, c, game.score, cyan);
|
||||||
|
|
||||||
|
let msg = "PRESS 1-3 TO CHOOSE ~ TO EXIT";
|
||||||
|
let col = (vga_buffer::WIDTH - msg.len()) / 2;
|
||||||
|
vga_buffer::write_str(choice_row + 6, col, msg, dgray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_fahrenheit() {
|
||||||
|
let mut game = FahrenheitGame {
|
||||||
|
current_node: "START",
|
||||||
|
score: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
render_game(&game);
|
||||||
|
let node = get_node(game.current_node);
|
||||||
|
|
||||||
|
if node.is_terminal {
|
||||||
|
sound::game_over_beep();
|
||||||
|
loop {
|
||||||
|
if keyboard::poll_key().is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Some(action) = keyboard::poll_key() {
|
||||||
|
match action {
|
||||||
|
KeyAction::Tilde => return,
|
||||||
|
KeyAction::Digit(d) if d >= 1 && d <= 3 => {
|
||||||
|
let idx = (d - 1) as usize;
|
||||||
|
if idx < node.choices.len() {
|
||||||
|
let choice = &node.choices[idx];
|
||||||
|
game.score += choice.score_modifier;
|
||||||
|
if game.score > 100 {
|
||||||
|
game.score = 100;
|
||||||
|
}
|
||||||
|
sound::select_beep();
|
||||||
|
game.current_node = choice.next_node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,510 @@
|
|||||||
|
use crate::keyboard;
|
||||||
|
use crate::keyboard::KeyAction;
|
||||||
|
use crate::rng::Lcg;
|
||||||
|
use crate::sound;
|
||||||
|
use crate::vga_buffer::{self, Color, ColorCode};
|
||||||
|
|
||||||
|
const QUESTIONS_PER_GAME: usize = 10;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum FigurativeType {
|
||||||
|
Simile,
|
||||||
|
Metaphor,
|
||||||
|
Personification,
|
||||||
|
Hyperbole,
|
||||||
|
Pun,
|
||||||
|
Allusion,
|
||||||
|
Symbol,
|
||||||
|
Oxymoron,
|
||||||
|
Repetition,
|
||||||
|
Imagery,
|
||||||
|
RhetoricalQuestion,
|
||||||
|
Alliteration,
|
||||||
|
Onomatopoeia,
|
||||||
|
}
|
||||||
|
|
||||||
|
const TYPE_COUNT: usize = 13;
|
||||||
|
|
||||||
|
const ALL_TYPES: [FigurativeType; TYPE_COUNT] = [
|
||||||
|
FigurativeType::Simile,
|
||||||
|
FigurativeType::Metaphor,
|
||||||
|
FigurativeType::Personification,
|
||||||
|
FigurativeType::Hyperbole,
|
||||||
|
FigurativeType::Pun,
|
||||||
|
FigurativeType::Allusion,
|
||||||
|
FigurativeType::Symbol,
|
||||||
|
FigurativeType::Oxymoron,
|
||||||
|
FigurativeType::Repetition,
|
||||||
|
FigurativeType::Imagery,
|
||||||
|
FigurativeType::RhetoricalQuestion,
|
||||||
|
FigurativeType::Alliteration,
|
||||||
|
FigurativeType::Onomatopoeia,
|
||||||
|
];
|
||||||
|
|
||||||
|
fn type_name(t: FigurativeType) -> &'static str {
|
||||||
|
match t {
|
||||||
|
FigurativeType::Simile => "Simile",
|
||||||
|
FigurativeType::Metaphor => "Metaphor",
|
||||||
|
FigurativeType::Personification => "Personification",
|
||||||
|
FigurativeType::Hyperbole => "Hyperbole",
|
||||||
|
FigurativeType::Pun => "Pun",
|
||||||
|
FigurativeType::Allusion => "Allusion",
|
||||||
|
FigurativeType::Symbol => "Symbol",
|
||||||
|
FigurativeType::Oxymoron => "Oxymoron",
|
||||||
|
FigurativeType::Repetition => "Repetition",
|
||||||
|
FigurativeType::Imagery => "Imagery",
|
||||||
|
FigurativeType::RhetoricalQuestion => "Rhetorical Question",
|
||||||
|
FigurativeType::Alliteration => "Alliteration",
|
||||||
|
FigurativeType::Onomatopoeia => "Onomatopoeia",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Sentence {
|
||||||
|
text: &'static str,
|
||||||
|
type_: FigurativeType,
|
||||||
|
}
|
||||||
|
|
||||||
|
const SENTENCES: &[Sentence] = &[
|
||||||
|
// Simile (6)
|
||||||
|
Sentence { text: "Her smile is as bright as the sun.", type_: FigurativeType::Simile },
|
||||||
|
Sentence { text: "He runs like the wind.", type_: FigurativeType::Simile },
|
||||||
|
Sentence { text: "She is as brave as a lion.", type_: FigurativeType::Simile },
|
||||||
|
Sentence { text: "He fought like a lion in the battle.", type_: FigurativeType::Simile },
|
||||||
|
Sentence { text: "The water sparkled like diamonds in the light.", type_: FigurativeType::Simile },
|
||||||
|
Sentence { text: "She sings like an angel.", type_: FigurativeType::Simile },
|
||||||
|
// Metaphor (6)
|
||||||
|
Sentence { text: "Time is a thief.", type_: FigurativeType::Metaphor },
|
||||||
|
Sentence { text: "The world is a stage.", type_: FigurativeType::Metaphor },
|
||||||
|
Sentence { text: "His heart is a cold iron.", type_: FigurativeType::Metaphor },
|
||||||
|
Sentence { text: "The classroom was a zoo.", type_: FigurativeType::Metaphor },
|
||||||
|
Sentence { text: "Life is a rollercoaster.", type_: FigurativeType::Metaphor },
|
||||||
|
Sentence { text: "Her voice is music to my ears.", type_: FigurativeType::Metaphor },
|
||||||
|
// Personification (6)
|
||||||
|
Sentence { text: "The wind whispered through the trees.", type_: FigurativeType::Personification },
|
||||||
|
Sentence { text: "The sun smiled down on us.", type_: FigurativeType::Personification },
|
||||||
|
Sentence { text: "The leaves danced in the breeze.", type_: FigurativeType::Personification },
|
||||||
|
Sentence { text: "The thunder roared across the sky.", type_: FigurativeType::Personification },
|
||||||
|
Sentence { text: "The flowers begged for water.", type_: FigurativeType::Personification },
|
||||||
|
Sentence { text: "Time crawled as we waited.", type_: FigurativeType::Personification },
|
||||||
|
// Hyperbole (6)
|
||||||
|
Sentence { text: "I told you a million times.", type_: FigurativeType::Hyperbole },
|
||||||
|
Sentence { text: "I am so hungry I could eat a horse.", type_: FigurativeType::Hyperbole },
|
||||||
|
Sentence { text: "This bag weighs a ton.", type_: FigurativeType::Hyperbole },
|
||||||
|
Sentence { text: "I have a mountain of homework to do.", type_: FigurativeType::Hyperbole },
|
||||||
|
Sentence { text: "She cried a river of tears.", type_: FigurativeType::Hyperbole },
|
||||||
|
Sentence { text: "He is older than the hills.", type_: FigurativeType::Hyperbole },
|
||||||
|
// Pun (6)
|
||||||
|
Sentence { text: "I was a baker but could not make enough dough.", type_: FigurativeType::Pun },
|
||||||
|
Sentence { text: "Time flies like an arrow and fruit flies like a banana.", type_: FigurativeType::Pun },
|
||||||
|
Sentence { text: "I read an anti-gravity book and cannot put it down.", type_: FigurativeType::Pun },
|
||||||
|
Sentence { text: "A bicycle cannot stand because it is two tired.", type_: FigurativeType::Pun },
|
||||||
|
Sentence { text: "I used to be a carpenter but I got boarded.", type_: FigurativeType::Pun },
|
||||||
|
Sentence { text: "Writing with a broken pencil is pointless.", type_: FigurativeType::Pun },
|
||||||
|
// Allusion (6)
|
||||||
|
Sentence { text: "He was a real Romeo with the ladies.", type_: FigurativeType::Allusion },
|
||||||
|
Sentence { text: "She has the patience of Job.", type_: FigurativeType::Allusion },
|
||||||
|
Sentence { text: "This place is a Garden of Eden.", type_: FigurativeType::Allusion },
|
||||||
|
Sentence { text: "She is a real Einstein when it comes to math.", type_: FigurativeType::Allusion },
|
||||||
|
Sentence { text: "He met his Waterloo in the final exam.", type_: FigurativeType::Allusion },
|
||||||
|
Sentence { text: "This is my Achilles heel.", type_: FigurativeType::Allusion },
|
||||||
|
// Symbol (6)
|
||||||
|
Sentence { text: "The white dove represents peace.", type_: FigurativeType::Symbol },
|
||||||
|
Sentence { text: "The cross stands for faith.", type_: FigurativeType::Symbol },
|
||||||
|
Sentence { text: "The red rose symbolizes love.", type_: FigurativeType::Symbol },
|
||||||
|
Sentence { text: "The skull and crossbones means danger.", type_: FigurativeType::Symbol },
|
||||||
|
Sentence { text: "A red light means stop.", type_: FigurativeType::Symbol },
|
||||||
|
Sentence { text: "The four leaf clover stands for luck.", type_: FigurativeType::Symbol },
|
||||||
|
// Oxymoron (6)
|
||||||
|
Sentence { text: "Jumbo shrimp is a funny contradiction.", type_: FigurativeType::Oxymoron },
|
||||||
|
Sentence { text: "The silence was deafening in the room.", type_: FigurativeType::Oxymoron },
|
||||||
|
Sentence { text: "The breakup was bittersweet.", type_: FigurativeType::Oxymoron },
|
||||||
|
Sentence { text: "Living dead is a strange concept.", type_: FigurativeType::Oxymoron },
|
||||||
|
Sentence { text: "The open secret was known to everyone.", type_: FigurativeType::Oxymoron },
|
||||||
|
Sentence { text: "Controlled chaos is how they work.", type_: FigurativeType::Oxymoron },
|
||||||
|
// Repetition (6)
|
||||||
|
Sentence { text: "I have a dream that one day I have a dream.", type_: FigurativeType::Repetition },
|
||||||
|
Sentence { text: "Never never never give up.", type_: FigurativeType::Repetition },
|
||||||
|
Sentence { text: "We shall fight on the beaches we shall fight.", type_: FigurativeType::Repetition },
|
||||||
|
Sentence { text: "Rain rain go away come again another day.", type_: FigurativeType::Repetition },
|
||||||
|
Sentence { text: "Let it snow let it snow let it snow.", type_: FigurativeType::Repetition },
|
||||||
|
Sentence { text: "We will we will rock you.", type_: FigurativeType::Repetition },
|
||||||
|
// Imagery (6)
|
||||||
|
Sentence { text: "The golden sunset painted streaks of orange and purple.", type_: FigurativeType::Imagery },
|
||||||
|
Sentence { text: "Rotten eggs filled the damp dark cellar with a stench.", type_: FigurativeType::Imagery },
|
||||||
|
Sentence { text: "Crisp autumn leaves crunched beneath her heavy boots.", type_: FigurativeType::Imagery },
|
||||||
|
Sentence { text: "The icy wind cut through his thin coat like a knife.", type_: FigurativeType::Imagery },
|
||||||
|
Sentence { text: "Bright red poppies swayed gently in the golden field.", type_: FigurativeType::Imagery },
|
||||||
|
Sentence { text: "The sizzling bacon smelled smoky and sweet.", type_: FigurativeType::Imagery },
|
||||||
|
// Rhetorical Question (6)
|
||||||
|
Sentence { text: "Isn't it obvious.", type_: FigurativeType::RhetoricalQuestion },
|
||||||
|
Sentence { text: "Who would not love a good story.", type_: FigurativeType::RhetoricalQuestion },
|
||||||
|
Sentence { text: "Can anyone really know the answer.", type_: FigurativeType::RhetoricalQuestion },
|
||||||
|
Sentence { text: "What is the meaning of it all.", type_: FigurativeType::RhetoricalQuestion },
|
||||||
|
Sentence { text: "Is there anyone who does not love a good meal.", type_: FigurativeType::RhetoricalQuestion },
|
||||||
|
Sentence { text: "Why must we always wait.", type_: FigurativeType::RhetoricalQuestion },
|
||||||
|
// Alliteration (6)
|
||||||
|
Sentence { text: "Peter Piper picked a peck of pickled peppers.", type_: FigurativeType::Alliteration },
|
||||||
|
Sentence { text: "She sells seashells by the seashore.", type_: FigurativeType::Alliteration },
|
||||||
|
Sentence { text: "Wild and whirling words are a wonder.", type_: FigurativeType::Alliteration },
|
||||||
|
Sentence { text: "Busy bees buzzed by the bright blossoms.", type_: FigurativeType::Alliteration },
|
||||||
|
Sentence { text: "Danny the dog dug a deep ditch.", type_: FigurativeType::Alliteration },
|
||||||
|
Sentence { text: "Gary the goat got a green grass.", type_: FigurativeType::Alliteration },
|
||||||
|
// Onomatopoeia (6)
|
||||||
|
Sentence { text: "The bees buzzed loudly in the garden.", type_: FigurativeType::Onomatopoeia },
|
||||||
|
Sentence { text: "The clock went tick tock all night long.", type_: FigurativeType::Onomatopoeia },
|
||||||
|
Sentence { text: "Bang the door slammed shut with a crash.", type_: FigurativeType::Onomatopoeia },
|
||||||
|
Sentence { text: "The cat went meow at the door.", type_: FigurativeType::Onomatopoeia },
|
||||||
|
Sentence { text: "Pop goes the weasel.", type_: FigurativeType::Onomatopoeia },
|
||||||
|
Sentence { text: "The fire crackled in the hearth.", type_: FigurativeType::Onomatopoeia },
|
||||||
|
];
|
||||||
|
|
||||||
|
const SENTENCE_COUNT: usize = SENTENCES.len();
|
||||||
|
|
||||||
|
struct FigurativeGame {
|
||||||
|
question_order: [usize; QUESTIONS_PER_GAME],
|
||||||
|
current: usize,
|
||||||
|
score: usize,
|
||||||
|
options: [FigurativeType; 4],
|
||||||
|
correct_idx: usize,
|
||||||
|
selected: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FigurativeGame {
|
||||||
|
fn new(rng: &mut Lcg) -> Self {
|
||||||
|
let mut all = [0usize; SENTENCE_COUNT];
|
||||||
|
let mut i = 0;
|
||||||
|
while i < SENTENCE_COUNT {
|
||||||
|
all[i] = i;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
let mut j = SENTENCE_COUNT;
|
||||||
|
while j > 1 {
|
||||||
|
j -= 1;
|
||||||
|
let k = rng.next_range(j + 1);
|
||||||
|
let tmp = all[j];
|
||||||
|
all[j] = all[k];
|
||||||
|
all[k] = tmp;
|
||||||
|
}
|
||||||
|
let mut order = [0usize; QUESTIONS_PER_GAME];
|
||||||
|
let mut i = 0;
|
||||||
|
while i < QUESTIONS_PER_GAME {
|
||||||
|
order[i] = all[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
let mut game = FigurativeGame {
|
||||||
|
question_order: order,
|
||||||
|
current: 0,
|
||||||
|
score: 0,
|
||||||
|
options: [FigurativeType::Simile; 4],
|
||||||
|
correct_idx: 0,
|
||||||
|
selected: None,
|
||||||
|
};
|
||||||
|
game.setup_question(rng);
|
||||||
|
game
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_question(&mut self, rng: &mut Lcg) {
|
||||||
|
let correct_type = SENTENCES[self.question_order[self.current]].type_;
|
||||||
|
|
||||||
|
let mut wrong = [FigurativeType::Simile; 3];
|
||||||
|
let mut count = 0;
|
||||||
|
while count < 3 {
|
||||||
|
let t = ALL_TYPES[rng.next_range(TYPE_COUNT)];
|
||||||
|
let mut dup = t == correct_type;
|
||||||
|
for i in 0..count {
|
||||||
|
if wrong[i] == t {
|
||||||
|
dup = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !dup {
|
||||||
|
wrong[count] = t;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.options = [correct_type, wrong[0], wrong[1], wrong[2]];
|
||||||
|
|
||||||
|
for i in (1..4).rev() {
|
||||||
|
let j = rng.next_range(i + 1);
|
||||||
|
self.options.swap(i, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.correct_idx = 0;
|
||||||
|
for i in 0..4 {
|
||||||
|
if self.options[i] == correct_type {
|
||||||
|
self.correct_idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.selected = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_all(&self) {
|
||||||
|
vga_buffer::clear_screen(Color::Black);
|
||||||
|
|
||||||
|
let white = ColorCode::new(Color::White, Color::Black);
|
||||||
|
let yellow = ColorCode::new(Color::Yellow, Color::Black);
|
||||||
|
let green = ColorCode::new(Color::Green, Color::Black);
|
||||||
|
let red = ColorCode::new(Color::Red, Color::Black);
|
||||||
|
let cyan = ColorCode::new(Color::Cyan, Color::Black);
|
||||||
|
let dgray = ColorCode::new(Color::DarkGray, Color::Black);
|
||||||
|
|
||||||
|
let title = "FIGURATIVE LANGUAGE IDENTIFIER";
|
||||||
|
let col = (vga_buffer::WIDTH - title.len()) / 2;
|
||||||
|
vga_buffer::write_str(0, col, title, yellow);
|
||||||
|
|
||||||
|
let mut c = 2;
|
||||||
|
let p1 = "QUESTION ";
|
||||||
|
vga_buffer::write_str(2, c, p1, cyan);
|
||||||
|
c += p1.len();
|
||||||
|
c = write_number(2, c, self.current + 1, cyan);
|
||||||
|
let p2 = " OF ";
|
||||||
|
vga_buffer::write_str(2, c, p2, cyan);
|
||||||
|
c += p2.len();
|
||||||
|
c = write_number(2, c, QUESTIONS_PER_GAME, cyan);
|
||||||
|
let p3 = " SCORE: ";
|
||||||
|
vga_buffer::write_str(2, c, p3, cyan);
|
||||||
|
c += p3.len();
|
||||||
|
write_number(2, c, self.score, cyan);
|
||||||
|
|
||||||
|
let sentence = SENTENCES[self.question_order[self.current]].text;
|
||||||
|
let col = (vga_buffer::WIDTH - sentence.len()) / 2;
|
||||||
|
vga_buffer::write_str(4, col, sentence, white);
|
||||||
|
|
||||||
|
let labels = ["1. ", "2. ", "3. ", "4. "];
|
||||||
|
for i in 0..4 {
|
||||||
|
let row = 7 + i;
|
||||||
|
let name = type_name(self.options[i]);
|
||||||
|
let mut buf = [0u8; 32];
|
||||||
|
let mut len = 0;
|
||||||
|
for &b in labels[i].as_bytes() {
|
||||||
|
buf[len] = b;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
for &b in name.as_bytes() {
|
||||||
|
buf[len] = b;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
let opt_str = core::str::from_utf8(&buf[..len]).unwrap();
|
||||||
|
let col = (vga_buffer::WIDTH - len) / 2;
|
||||||
|
|
||||||
|
let color = if let Some(sel) = self.selected {
|
||||||
|
if i == self.correct_idx {
|
||||||
|
ColorCode::new(Color::Black, Color::Green)
|
||||||
|
} else if i == sel {
|
||||||
|
ColorCode::new(Color::Black, Color::Red)
|
||||||
|
} else {
|
||||||
|
dgray
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
white
|
||||||
|
};
|
||||||
|
|
||||||
|
vga_buffer::write_str(row, col, opt_str, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(sel) = self.selected {
|
||||||
|
let correct_name = type_name(self.options[self.correct_idx]);
|
||||||
|
if sel == self.correct_idx {
|
||||||
|
let msg = "CORRECT!";
|
||||||
|
let col = (vga_buffer::WIDTH - msg.len()) / 2;
|
||||||
|
vga_buffer::write_str(12, col, msg, green);
|
||||||
|
} else {
|
||||||
|
let msg = "INCORRECT!";
|
||||||
|
let col = (vga_buffer::WIDTH - msg.len()) / 2;
|
||||||
|
vga_buffer::write_str(12, col, msg, red);
|
||||||
|
let mut buf = [0u8; 64];
|
||||||
|
let prefix = b"THE ANSWER WAS: ";
|
||||||
|
let mut len = prefix.len();
|
||||||
|
let mut i = 0;
|
||||||
|
while i < prefix.len() {
|
||||||
|
buf[i] = prefix[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
for &b in correct_name.as_bytes() {
|
||||||
|
buf[len] = b;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
let ans_str = core::str::from_utf8(&buf[..len]).unwrap();
|
||||||
|
let col = (vga_buffer::WIDTH - len) / 2;
|
||||||
|
vga_buffer::write_str(13, col, ans_str, yellow);
|
||||||
|
}
|
||||||
|
let msg = "PRESS ANY KEY TO CONTINUE";
|
||||||
|
let col = (vga_buffer::WIDTH - msg.len()) / 2;
|
||||||
|
vga_buffer::write_str(15, col, msg, dgray);
|
||||||
|
} else {
|
||||||
|
let msg = "PRESS 1-4 TO SELECT ~ TO EXIT";
|
||||||
|
let col = (vga_buffer::WIDTH - msg.len()) / 2;
|
||||||
|
vga_buffer::write_str(15, col, msg, dgray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_number(row: usize, col: usize, num: usize, color: ColorCode) -> usize {
|
||||||
|
let mut n = num;
|
||||||
|
let mut c = col;
|
||||||
|
if n == 0 {
|
||||||
|
vga_buffer::write_char(row, c, b'0', color);
|
||||||
|
return c + 1;
|
||||||
|
}
|
||||||
|
let mut digits = [b'0'; 10];
|
||||||
|
let mut len = 0;
|
||||||
|
while n > 0 {
|
||||||
|
digits[len] = b'0' + (n % 10) as u8;
|
||||||
|
n /= 10;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
for i in (0..len).rev() {
|
||||||
|
vga_buffer::write_char(row, c, digits[i], color);
|
||||||
|
c += 1;
|
||||||
|
}
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_results(score: usize) {
|
||||||
|
vga_buffer::clear_screen(Color::Black);
|
||||||
|
|
||||||
|
let white = ColorCode::new(Color::White, Color::Black);
|
||||||
|
let yellow = ColorCode::new(Color::Yellow, Color::Black);
|
||||||
|
let dgray = ColorCode::new(Color::DarkGray, Color::Black);
|
||||||
|
|
||||||
|
let title = "GAME OVER!";
|
||||||
|
let col = (vga_buffer::WIDTH - title.len()) / 2;
|
||||||
|
vga_buffer::write_str(7, col, title, yellow);
|
||||||
|
|
||||||
|
let mut buf = [0u8; 64];
|
||||||
|
let mut len = 0;
|
||||||
|
for &b in b"FINAL SCORE: " {
|
||||||
|
buf[len] = b;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
let mut n = score;
|
||||||
|
let mut digs = [b'0'; 10];
|
||||||
|
let mut dl = 0;
|
||||||
|
if n == 0 {
|
||||||
|
digs[0] = b'0';
|
||||||
|
dl = 1;
|
||||||
|
} else {
|
||||||
|
while n > 0 {
|
||||||
|
digs[dl] = b'0' + (n % 10) as u8;
|
||||||
|
n /= 10;
|
||||||
|
dl += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in (0..dl).rev() {
|
||||||
|
buf[len] = digs[i];
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
buf[len] = b'/';
|
||||||
|
len += 1;
|
||||||
|
let mut m = QUESTIONS_PER_GAME;
|
||||||
|
let mut digs2 = [b'0'; 10];
|
||||||
|
let mut dl2 = 0;
|
||||||
|
while m > 0 {
|
||||||
|
digs2[dl2] = b'0' + (m % 10) as u8;
|
||||||
|
m /= 10;
|
||||||
|
dl2 += 1;
|
||||||
|
}
|
||||||
|
for i in (0..dl2).rev() {
|
||||||
|
buf[len] = digs2[i];
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
let score_str = core::str::from_utf8(&buf[..len]).unwrap();
|
||||||
|
let col = (vga_buffer::WIDTH - len) / 2;
|
||||||
|
vga_buffer::write_str(9, col, score_str, white);
|
||||||
|
|
||||||
|
let pct = score * 100 / QUESTIONS_PER_GAME;
|
||||||
|
let mut buf2 = [0u8; 64];
|
||||||
|
let mut len2 = 0;
|
||||||
|
for &b in b"PERCENTAGE: " {
|
||||||
|
buf2[len2] = b;
|
||||||
|
len2 += 1;
|
||||||
|
}
|
||||||
|
let mut n2 = pct;
|
||||||
|
let mut digs3 = [b'0'; 10];
|
||||||
|
let mut dl3 = 0;
|
||||||
|
if n2 == 0 {
|
||||||
|
digs3[0] = b'0';
|
||||||
|
dl3 = 1;
|
||||||
|
} else {
|
||||||
|
while n2 > 0 {
|
||||||
|
digs3[dl3] = b'0' + (n2 % 10) as u8;
|
||||||
|
n2 /= 10;
|
||||||
|
dl3 += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in (0..dl3).rev() {
|
||||||
|
buf2[len2] = digs3[i];
|
||||||
|
len2 += 1;
|
||||||
|
}
|
||||||
|
buf2[len2] = b'%';
|
||||||
|
len2 += 1;
|
||||||
|
let pct_str = core::str::from_utf8(&buf2[..len2]).unwrap();
|
||||||
|
let col = (vga_buffer::WIDTH - len2) / 2;
|
||||||
|
vga_buffer::write_str(10, col, pct_str, white);
|
||||||
|
|
||||||
|
let rating = if pct >= 90 {
|
||||||
|
"EXCELLENT!"
|
||||||
|
} else if pct >= 70 {
|
||||||
|
"GOOD JOB!"
|
||||||
|
} else if pct >= 50 {
|
||||||
|
"NOT BAD!"
|
||||||
|
} else {
|
||||||
|
"KEEP PRACTICING!"
|
||||||
|
};
|
||||||
|
let col = (vga_buffer::WIDTH - rating.len()) / 2;
|
||||||
|
vga_buffer::write_str(12, col, rating, yellow);
|
||||||
|
|
||||||
|
let msg = "PRESS ANY KEY TO RETURN";
|
||||||
|
let col = (vga_buffer::WIDTH - msg.len()) / 2;
|
||||||
|
vga_buffer::write_str(14, col, msg, dgray);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if keyboard::poll_key().is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_figurative() {
|
||||||
|
let mut rng = Lcg::from_tsc();
|
||||||
|
let mut game = FigurativeGame::new(&mut rng);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
game.render_all();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Some(action) = keyboard::poll_key() {
|
||||||
|
match action {
|
||||||
|
KeyAction::Tilde => return,
|
||||||
|
KeyAction::Digit(d) if d >= 1 && d <= 4 && game.selected.is_none() => {
|
||||||
|
let idx = (d - 1) as usize;
|
||||||
|
game.selected = Some(idx);
|
||||||
|
if idx == game.correct_idx {
|
||||||
|
game.score += 1;
|
||||||
|
sound::correct_beep();
|
||||||
|
} else {
|
||||||
|
sound::wrong_beep();
|
||||||
|
}
|
||||||
|
game.render_all();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if game.selected.is_some() {
|
||||||
|
game.current += 1;
|
||||||
|
if game.current >= QUESTIONS_PER_GAME {
|
||||||
|
sound::fanfare_beep();
|
||||||
|
show_results(game.score);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
game.setup_question(&mut rng);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
-2
@@ -30,9 +30,55 @@ pub static FONT5X7: [[u8; 7]; 26] = [
|
|||||||
[0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b11111], // Z
|
[0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b11111], // Z
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub static FONT5X7_DIGITS: [[u8; 7]; 10] = [
|
||||||
|
[0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110], // 0
|
||||||
|
[0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110], // 1
|
||||||
|
[0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111], // 2
|
||||||
|
[0b01110, 0b10001, 0b00001, 0b00110, 0b00001, 0b10001, 0b01110], // 3
|
||||||
|
[0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010], // 4
|
||||||
|
[0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110], // 5
|
||||||
|
[0b01110, 0b10001, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110], // 6
|
||||||
|
[0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000], // 7
|
||||||
|
[0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110], // 8
|
||||||
|
[0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b10001, 0b01110], // 9
|
||||||
|
];
|
||||||
|
|
||||||
|
pub static FONT5X7_LOWER: [[u8; 7]; 26] = [
|
||||||
|
[0b00000, 0b00000, 0b01110, 0b00001, 0b01111, 0b10001, 0b01111], // a
|
||||||
|
[0b10000, 0b10000, 0b10110, 0b11001, 0b10001, 0b10001, 0b11110], // b
|
||||||
|
[0b00000, 0b00000, 0b01110, 0b10000, 0b10000, 0b10001, 0b01110], // c
|
||||||
|
[0b00001, 0b00001, 0b01101, 0b10011, 0b10001, 0b10001, 0b01111], // d
|
||||||
|
[0b00000, 0b00000, 0b01110, 0b10001, 0b11111, 0b10000, 0b01110], // e
|
||||||
|
[0b00110, 0b01001, 0b01000, 0b11100, 0b01000, 0b01000, 0b01000], // f
|
||||||
|
[0b00000, 0b00000, 0b01110, 0b10001, 0b10001, 0b01111, 0b00001], // g
|
||||||
|
[0b10000, 0b10000, 0b10110, 0b11001, 0b10001, 0b10001, 0b10001], // h
|
||||||
|
[0b00100, 0b00000, 0b01100, 0b00100, 0b00100, 0b00100, 0b01110], // i
|
||||||
|
[0b00010, 0b00000, 0b00110, 0b00010, 0b00010, 0b10010, 0b01100], // j
|
||||||
|
[0b10000, 0b10000, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010], // k
|
||||||
|
[0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110], // l
|
||||||
|
[0b00000, 0b00000, 0b11010, 0b10101, 0b10101, 0b10001, 0b10001], // m
|
||||||
|
[0b00000, 0b00000, 0b10110, 0b11001, 0b10001, 0b10001, 0b10001], // n
|
||||||
|
[0b00000, 0b00000, 0b01110, 0b10001, 0b10001, 0b10001, 0b01110], // o
|
||||||
|
[0b00000, 0b00000, 0b11110, 0b10001, 0b10001, 0b11110, 0b10000], // p
|
||||||
|
[0b00000, 0b00000, 0b01111, 0b10001, 0b10001, 0b01111, 0b00001], // q
|
||||||
|
[0b00000, 0b00000, 0b01011, 0b01101, 0b01000, 0b01000, 0b01000], // r
|
||||||
|
[0b00000, 0b00000, 0b01110, 0b10000, 0b01110, 0b00001, 0b11110], // s
|
||||||
|
[0b01000, 0b01000, 0b11100, 0b01000, 0b01000, 0b01001, 0b00110], // t
|
||||||
|
[0b00000, 0b00000, 0b10001, 0b10001, 0b10001, 0b10011, 0b01101], // u
|
||||||
|
[0b00000, 0b00000, 0b10001, 0b10001, 0b10001, 0b01010, 0b00100], // v
|
||||||
|
[0b00000, 0b00000, 0b10001, 0b10001, 0b10101, 0b10101, 0b01010], // w
|
||||||
|
[0b00000, 0b00000, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001], // x
|
||||||
|
[0b00000, 0b00000, 0b10001, 0b10001, 0b10001, 0b01111, 0b00001], // y
|
||||||
|
[0b00000, 0b00000, 0b11111, 0b00010, 0b00100, 0b01000, 0b11111], // z
|
||||||
|
];
|
||||||
|
|
||||||
pub fn get_5x7(ch: u8) -> Option<&'static [u8; 7]> {
|
pub fn get_5x7(ch: u8) -> Option<&'static [u8; 7]> {
|
||||||
if ch.is_ascii_uppercase() {
|
if ch.is_ascii_lowercase() {
|
||||||
|
Some(&FONT5X7_LOWER[(ch - b'a') as usize])
|
||||||
|
} else if ch.is_ascii_uppercase() {
|
||||||
Some(&FONT5X7[(ch - b'A') as usize])
|
Some(&FONT5X7[(ch - b'A') as usize])
|
||||||
|
} else if ch.is_ascii_digit() {
|
||||||
|
Some(&FONT5X7_DIGITS[(ch - b'0') as usize])
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -44,7 +90,7 @@ pub static PUNCT5X7: &[(u8, [u8; 7])] = &[
|
|||||||
(b',', [0b00000, 0b00000, 0b00000, 0b00000, 0b00100, 0b00100, 0b01000]),
|
(b',', [0b00000, 0b00000, 0b00000, 0b00000, 0b00100, 0b00100, 0b01000]),
|
||||||
(b'-', [0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000]),
|
(b'-', [0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000]),
|
||||||
(b'.', [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00100, 0b00100]),
|
(b'.', [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00100, 0b00100]),
|
||||||
(b':', [0b00000, 0b00100, 0b00100, 0b00000, 0b00100, 0b00100, 0b00000]),
|
(b':', [0b00000, 0b00100, 0b00000, 0b00000, 0b00000, 0b00100, 0b00000]),
|
||||||
(b'_', [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111]),
|
(b'_', [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111]),
|
||||||
(b' ', [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000]),
|
(b' ', [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000]),
|
||||||
];
|
];
|
||||||
|
|||||||
+78
-6
@@ -234,17 +234,17 @@ impl WordleGame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_message(&self) {
|
pub fn render_message(&self) {
|
||||||
match self.state {
|
match self.state {
|
||||||
GameState::Won => {
|
GameState::Won => {
|
||||||
let msg = "Congratulations! Press any key to restart";
|
let msg = "CONGRATULATIONS! PRESS ANY KEY TO RESTART";
|
||||||
let start_col = (vga_buffer::WIDTH - msg.len()) / 2;
|
let start_col = (vga_buffer::WIDTH - msg.len()) / 2;
|
||||||
vga_buffer::write_str(MESSAGE_ROW, start_col, msg,
|
vga_buffer::write_str(MESSAGE_ROW, start_col, msg,
|
||||||
ColorCode::new(Color::Green, Color::Black));
|
ColorCode::new(Color::Green, Color::Black));
|
||||||
}
|
}
|
||||||
GameState::Lost => {
|
GameState::Lost => {
|
||||||
// Show "Game Over! The word was: XXXXX"
|
// Show "Game Over! The word was: XXXXX"
|
||||||
let prefix = "Game Over! The word was: ";
|
let prefix = "GAME OVER! THE WORD WAS: ";
|
||||||
let total_len = prefix.len() + 5 + 4; // 5 letters + 4 spaces
|
let total_len = prefix.len() + 5 + 4; // 5 letters + 4 spaces
|
||||||
let start_col = (vga_buffer::WIDTH - total_len) / 2;
|
let start_col = (vga_buffer::WIDTH - total_len) / 2;
|
||||||
let mut col = start_col;
|
let mut col = start_col;
|
||||||
@@ -255,11 +255,12 @@ impl WordleGame {
|
|||||||
}
|
}
|
||||||
for i in 0..5 {
|
for i in 0..5 {
|
||||||
col += 1; // space between letters
|
col += 1; // space between letters
|
||||||
let color = status_color(self.evaluations[5][i]);
|
let letter = self.secret[i].to_ascii_uppercase();
|
||||||
vga_buffer::write_char(MESSAGE_ROW, col, self.secret[i], color);
|
vga_buffer::write_char(MESSAGE_ROW, col, letter,
|
||||||
|
ColorCode::new(Color::White, Color::Black));
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg2 = "Press any key to restart";
|
let msg2 = "PRESS ANY KEY TO RESTART";
|
||||||
let start2 = (vga_buffer::WIDTH - msg2.len()) / 2;
|
let start2 = (vga_buffer::WIDTH - msg2.len()) / 2;
|
||||||
vga_buffer::write_str(MESSAGE_ROW + 1, start2, msg2,
|
vga_buffer::write_str(MESSAGE_ROW + 1, start2, msg2,
|
||||||
ColorCode::new(Color::White, Color::Black));
|
ColorCode::new(Color::White, Color::Black));
|
||||||
@@ -269,6 +270,77 @@ impl WordleGame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Incremental rendering ──────────────────────────────────────────
|
||||||
|
|
||||||
|
fn render_guess_cell(&self, row: usize, col: usize) {
|
||||||
|
let screen_row = GUESS_START_ROW + row;
|
||||||
|
let screen_col = GUESS_START_COL + col * 2;
|
||||||
|
|
||||||
|
if row < self.guess_count {
|
||||||
|
let color = status_color(self.evaluations[row][col]);
|
||||||
|
vga_buffer::write_char(screen_row, screen_col, self.guesses[row][col], color);
|
||||||
|
} else if row == self.guess_count && self.state == GameState::Playing {
|
||||||
|
if col < self.current_letter {
|
||||||
|
vga_buffer::write_char(screen_row, screen_col, self.guesses[row][col],
|
||||||
|
ColorCode::new(Color::White, Color::Black));
|
||||||
|
} else if col == self.current_letter {
|
||||||
|
vga_buffer::write_char(screen_row, screen_col, b'_',
|
||||||
|
ColorCode::new(Color::White, Color::Black));
|
||||||
|
} else {
|
||||||
|
vga_buffer::write_char(screen_row, screen_col, b' ',
|
||||||
|
ColorCode::new(Color::White, Color::Black));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vga_buffer::write_char(screen_row, screen_col, b' ',
|
||||||
|
ColorCode::new(Color::White, Color::Black));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_keyboard_key(&self, ch: u8) {
|
||||||
|
for &(line, row) in KB_LINES {
|
||||||
|
for (i, &kc) in line.iter().enumerate() {
|
||||||
|
if kc == ch {
|
||||||
|
let start_col = (vga_buffer::WIDTH - line.len()) / 2;
|
||||||
|
let idx = letter_index(ch);
|
||||||
|
let color = status_color(self.key_status[idx]);
|
||||||
|
vga_buffer::write_char(row, start_col + i, ch, color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_add_letter(&self) {
|
||||||
|
let row = self.guess_count;
|
||||||
|
let prev = self.current_letter - 1;
|
||||||
|
self.render_guess_cell(row, prev);
|
||||||
|
if self.current_letter < 5 {
|
||||||
|
self.render_guess_cell(row, self.current_letter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_delete_letter(&self) {
|
||||||
|
let row = self.guess_count;
|
||||||
|
self.render_guess_cell(row, self.current_letter);
|
||||||
|
if self.current_letter + 1 < 5 {
|
||||||
|
self.render_guess_cell(row, self.current_letter + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_submit_guess(&self) {
|
||||||
|
let submitted_row = self.guess_count - 1;
|
||||||
|
for j in 0..5 {
|
||||||
|
self.render_guess_cell(submitted_row, j);
|
||||||
|
}
|
||||||
|
if self.state == GameState::Playing && self.guess_count < 6 {
|
||||||
|
self.render_guess_cell(self.guess_count, 0);
|
||||||
|
}
|
||||||
|
let guess = self.guesses[submitted_row];
|
||||||
|
for j in 0..5 {
|
||||||
|
self.render_keyboard_key(guess[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Feedback engine ─────────────────────────────────────────────────────
|
// ─── Feedback engine ─────────────────────────────────────────────────────
|
||||||
|
|||||||
+53
-11
@@ -1,10 +1,14 @@
|
|||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
use x86_64::instructions::port::Port;
|
use x86_64::instructions::port::Port;
|
||||||
|
|
||||||
const TIMEOUT: u32 = 1_000_000;
|
const TIMEOUT: u32 = 10_000;
|
||||||
|
|
||||||
static KEYBOARD_READY: AtomicBool = AtomicBool::new(false);
|
static KEYBOARD_READY: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
/// Tracks which scancodes are currently held down.
|
||||||
|
/// Regular scancodes use index 0x00–0x7F; extended (E0-prefixed) use 0x80–0xFF.
|
||||||
|
static mut KEY_HELD: [bool; 256] = [false; 256];
|
||||||
|
|
||||||
/// Initialises the PS/2 controller and keyboard.
|
/// Initialises the PS/2 controller and keyboard.
|
||||||
///
|
///
|
||||||
/// UEFI firmware often leaves the PS/2 controller uninitialised, so we must
|
/// UEFI firmware often leaves the PS/2 controller uninitialised, so we must
|
||||||
@@ -60,6 +64,7 @@ fn poll_write_ready() -> bool {
|
|||||||
if read_status() & 2 == 0 {
|
if read_status() & 2 == 0 {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
core::hint::spin_loop();
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -69,18 +74,23 @@ fn try_read_data() -> Option<u8> {
|
|||||||
if read_status() & 1 != 0 {
|
if read_status() & 1 != 0 {
|
||||||
return Some(unsafe { Port::new(0x60).read() });
|
return Some(unsafe { Port::new(0x60).read() });
|
||||||
}
|
}
|
||||||
|
core::hint::spin_loop();
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Actions produced by the PS/2 keyboard decoder.
|
/// Actions produced by the PS/2 keyboard decoder.
|
||||||
///
|
///
|
||||||
/// Only A–Z letters, Enter, and Backspace are relevant for the Wordle game.
|
/// Only A–Z letters, Enter, Backspace, Up, and Down are relevant.
|
||||||
/// All other keys (modifiers, function keys, arrows) are silently ignored.
|
/// All other keys are silently ignored.
|
||||||
pub enum KeyAction {
|
pub enum KeyAction {
|
||||||
Letter(u8),
|
Letter(u8),
|
||||||
|
Digit(u8),
|
||||||
Enter,
|
Enter,
|
||||||
Backspace,
|
Backspace,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Tilde,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the PS/2 controller status register (port 0x64).
|
/// Reads the PS/2 controller status register (port 0x64).
|
||||||
@@ -131,8 +141,8 @@ fn data_available() -> bool {
|
|||||||
/// keyboard stays in its default Set 2, and the controller handles the
|
/// keyboard stays in its default Set 2, and the controller handles the
|
||||||
/// conversion to Set 1 transparently.
|
/// conversion to Set 1 transparently.
|
||||||
///
|
///
|
||||||
/// We ignore break codes and extended codes since we only care about when
|
/// Break codes update the held-key table but are not returned as actions.
|
||||||
/// a letter key is pressed.
|
/// Extended codes (0xE0 prefix) are decoded for Up/Down arrows.
|
||||||
pub fn poll_key() -> Option<KeyAction> {
|
pub fn poll_key() -> Option<KeyAction> {
|
||||||
if !KEYBOARD_READY.load(Ordering::SeqCst) {
|
if !KEYBOARD_READY.load(Ordering::SeqCst) {
|
||||||
return None;
|
return None;
|
||||||
@@ -143,24 +153,47 @@ pub fn poll_key() -> Option<KeyAction> {
|
|||||||
|
|
||||||
let scancode = read_data();
|
let scancode = read_data();
|
||||||
|
|
||||||
// Ignore the extended-key prefix (0xE0). The next byte is the actual
|
// Extended-key prefix (0xE0) precedes arrow keys, etc.
|
||||||
// scancode for the extended key (e.g., arrow, home, etc.), which we
|
|
||||||
// don't need — discard it too.
|
|
||||||
if scancode == 0xE0 {
|
if scancode == 0xE0 {
|
||||||
while !data_available() {}
|
while !data_available() {}
|
||||||
read_data();
|
let ext = read_data();
|
||||||
|
// Track held state for extended keys (store at offset 0x80)
|
||||||
|
if ext >= 0x80 {
|
||||||
|
unsafe { KEY_HELD[0x80 | (ext & 0x7F) as usize] = false; }
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
unsafe { KEY_HELD[0x80 | ext as usize] = true; }
|
||||||
|
return match ext {
|
||||||
|
0x48 => Some(KeyAction::Up),
|
||||||
|
0x50 => Some(KeyAction::Down),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Break codes (bit 7 set) — key released, not pressed. Ignore them
|
// Break codes — key released, update held table.
|
||||||
// so that holding a key doesn't repeatedly register the same letter.
|
|
||||||
if scancode >= 0x80 {
|
if scancode >= 0x80 {
|
||||||
|
unsafe { KEY_HELD[(scancode & 0x7F) as usize] = false; }
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make code — key pressed
|
||||||
|
unsafe { KEY_HELD[scancode as usize] = true; }
|
||||||
translate_scancode(scancode)
|
translate_scancode(scancode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the given key is currently held down.
|
||||||
|
/// Only supported for Letter(A) and Letter(D).
|
||||||
|
pub fn is_held(action: &KeyAction) -> bool {
|
||||||
|
let sc = match action {
|
||||||
|
KeyAction::Letter(b'A') => 0x1E,
|
||||||
|
KeyAction::Letter(b'D') => 0x20,
|
||||||
|
KeyAction::Letter(b'W') => 0x11,
|
||||||
|
KeyAction::Letter(b'S') => 0x1F,
|
||||||
|
_ => return false,
|
||||||
|
};
|
||||||
|
unsafe { KEY_HELD[sc as usize] }
|
||||||
|
}
|
||||||
|
|
||||||
/// Maps a PS/2 Set 1 make code to a `KeyAction`.
|
/// Maps a PS/2 Set 1 make code to a `KeyAction`.
|
||||||
///
|
///
|
||||||
/// Scan code set 1 layout for the main alphanumeric block:
|
/// Scan code set 1 layout for the main alphanumeric block:
|
||||||
@@ -179,6 +212,14 @@ pub fn poll_key() -> Option<KeyAction> {
|
|||||||
/// Enter: 0x1C Backspace: 0x0E
|
/// Enter: 0x1C Backspace: 0x0E
|
||||||
fn translate_scancode(scancode: u8) -> Option<KeyAction> {
|
fn translate_scancode(scancode: u8) -> Option<KeyAction> {
|
||||||
match scancode {
|
match scancode {
|
||||||
|
0x02 => Some(KeyAction::Digit(1)),
|
||||||
|
0x03 => Some(KeyAction::Digit(2)),
|
||||||
|
0x04 => Some(KeyAction::Digit(3)),
|
||||||
|
0x05 => Some(KeyAction::Digit(4)),
|
||||||
|
0x06 => Some(KeyAction::Digit(5)),
|
||||||
|
0x07 => Some(KeyAction::Digit(6)),
|
||||||
|
0x08 => Some(KeyAction::Digit(7)),
|
||||||
|
0x09 => Some(KeyAction::Digit(8)),
|
||||||
0x0E => Some(KeyAction::Backspace),
|
0x0E => Some(KeyAction::Backspace),
|
||||||
0x1C => Some(KeyAction::Enter),
|
0x1C => Some(KeyAction::Enter),
|
||||||
0x10 => Some(KeyAction::Letter(b'Q')),
|
0x10 => Some(KeyAction::Letter(b'Q')),
|
||||||
@@ -207,6 +248,7 @@ fn translate_scancode(scancode: u8) -> Option<KeyAction> {
|
|||||||
0x30 => Some(KeyAction::Letter(b'B')),
|
0x30 => Some(KeyAction::Letter(b'B')),
|
||||||
0x31 => Some(KeyAction::Letter(b'N')),
|
0x31 => Some(KeyAction::Letter(b'N')),
|
||||||
0x32 => Some(KeyAction::Letter(b'M')),
|
0x32 => Some(KeyAction::Letter(b'M')),
|
||||||
|
0x29 => Some(KeyAction::Tilde),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+111
-17
@@ -6,25 +6,107 @@ mod vga_buffer;
|
|||||||
mod keyboard;
|
mod keyboard;
|
||||||
mod rng;
|
mod rng;
|
||||||
mod game;
|
mod game;
|
||||||
|
mod balatro;
|
||||||
|
mod mario;
|
||||||
|
mod figurative_language;
|
||||||
|
mod fahrenheit;
|
||||||
|
mod story_part;
|
||||||
|
mod sound;
|
||||||
|
|
||||||
use game::{GameState, WordleGame, WORDS};
|
use game::{GameState, WordleGame, WORDS};
|
||||||
use keyboard::KeyAction;
|
use keyboard::KeyAction;
|
||||||
use rng::Lcg;
|
use rng::Lcg;
|
||||||
use vga_buffer::{Color, ColorCode};
|
use vga_buffer::{Color, ColorCode};
|
||||||
|
use balatro::run_balatro;
|
||||||
|
use mario::run_mario;
|
||||||
|
use figurative_language::run_figurative;
|
||||||
|
use fahrenheit::run_fahrenheit;
|
||||||
|
use story_part::run_story_part;
|
||||||
|
|
||||||
use bootloader_api::{entry_point, BootInfo};
|
use bootloader_api::{entry_point, BootInfo};
|
||||||
|
|
||||||
entry_point!(kernel_main);
|
entry_point!(kernel_main);
|
||||||
|
|
||||||
|
const GAMES: &[(&str, &str, fn())] = &[
|
||||||
|
("WORDLE", "it's wordle", run_wordle),
|
||||||
|
("BALATRO", "it's balatro", run_balatro),
|
||||||
|
("MARIO", "little too laggy dont touch it", run_mario),
|
||||||
|
("FIGURATIVE", "identify figuartive language type", run_figurative),
|
||||||
|
("FAHRENHEIT GAME", "literary fahrenheit 451 story game", run_fahrenheit),
|
||||||
|
("STORY PART", "identify narrative arc elements", run_story_part),
|
||||||
|
];
|
||||||
|
|
||||||
fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
|
fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
|
||||||
let fb = boot_info.framebuffer.take()
|
let fb = boot_info.framebuffer.take()
|
||||||
.expect("no framebuffer provided by bootloader");
|
.expect("no framebuffer provided by bootloader");
|
||||||
vga_buffer::init(fb);
|
vga_buffer::init(fb);
|
||||||
|
|
||||||
vga_buffer::clear_screen(Color::Black);
|
|
||||||
keyboard::init();
|
keyboard::init();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
run_launcher();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_launcher() {
|
||||||
|
let mut selected = 0;
|
||||||
|
|
||||||
|
draw_launcher(selected);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Some(action) = keyboard::poll_key() {
|
||||||
|
match action {
|
||||||
|
KeyAction::Up => {
|
||||||
|
if selected > 0 {
|
||||||
|
selected -= 1;
|
||||||
|
draw_launcher(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyAction::Down => {
|
||||||
|
if selected + 1 < GAMES.len() {
|
||||||
|
selected += 1;
|
||||||
|
draw_launcher(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyAction::Enter => {
|
||||||
|
sound::select_beep();
|
||||||
|
GAMES[selected].2();
|
||||||
|
draw_launcher(selected);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_launcher(selected: usize) {
|
||||||
|
vga_buffer::clear_screen(Color::Black);
|
||||||
|
|
||||||
|
let start_col = (vga_buffer::WIDTH - 7) / 2;
|
||||||
|
vga_buffer::write_str(0, start_col, "ENGFINAL",
|
||||||
|
ColorCode::new(Color::Yellow, Color::Black));
|
||||||
|
|
||||||
|
for (i, &(name, desc, _)) in GAMES.iter().enumerate() {
|
||||||
|
let row = 3 + i * 2;
|
||||||
|
let fg = if i == selected { Color::Black } else { Color::White };
|
||||||
|
let bg = if i == selected { Color::White } else { Color::Black };
|
||||||
|
vga_buffer::write_str(row, 2, "> ",
|
||||||
|
ColorCode::new(fg, bg));
|
||||||
|
vga_buffer::write_str(row, 4, name,
|
||||||
|
ColorCode::new(fg, bg));
|
||||||
|
vga_buffer::write_str(row, 24, desc,
|
||||||
|
ColorCode::new(Color::Cyan, Color::Black));
|
||||||
|
}
|
||||||
|
|
||||||
|
vga_buffer::write_str(20, 2, "UP/DOWN: NAVIGATE",
|
||||||
|
ColorCode::new(Color::DarkGray, Color::Black));
|
||||||
|
vga_buffer::write_str(21, 2, "ENTER: SELECT",
|
||||||
|
ColorCode::new(Color::DarkGray, Color::Black));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_wordle() {
|
||||||
let mut rng = Lcg::from_tsc();
|
let mut rng = Lcg::from_tsc();
|
||||||
let secret = WORDS[rng.next_range(WORDS.len())];
|
let secret = WORDS[rng.next_range(WORDS.len())];
|
||||||
|
|
||||||
@@ -34,22 +116,40 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
|
|||||||
loop {
|
loop {
|
||||||
if let Some(action) = keyboard::poll_key() {
|
if let Some(action) = keyboard::poll_key() {
|
||||||
match action {
|
match action {
|
||||||
KeyAction::Letter(ch) => game.add_letter(ch),
|
KeyAction::Letter(ch) => {
|
||||||
KeyAction::Backspace => game.delete_letter(),
|
game.add_letter(ch);
|
||||||
|
game.render_add_letter();
|
||||||
|
sound::select_beep();
|
||||||
|
}
|
||||||
|
KeyAction::Backspace => {
|
||||||
|
game.delete_letter();
|
||||||
|
game.render_delete_letter();
|
||||||
|
}
|
||||||
KeyAction::Enter => {
|
KeyAction::Enter => {
|
||||||
if game.submit_guess().is_none() {}
|
if game.submit_guess().is_some() {
|
||||||
|
game.render_submit_guess();
|
||||||
|
sound::select_beep();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
game.render_all();
|
KeyAction::Tilde => { return; }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
if game.state == GameState::Won || game.state == GameState::Lost {
|
if game.state == GameState::Won {
|
||||||
|
game.render_message();
|
||||||
|
sound::correct_beep();
|
||||||
wait_for_keypress();
|
wait_for_keypress();
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
|
if game.state == GameState::Lost {
|
||||||
|
game.render_message();
|
||||||
|
sound::wrong_beep();
|
||||||
|
wait_for_keypress();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_delay();
|
core::hint::spin_loop();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,12 +158,6 @@ fn wait_for_keypress() {
|
|||||||
if keyboard::poll_key().is_some() {
|
if keyboard::poll_key().is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
spin_delay();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spin_delay() {
|
|
||||||
for _ in 0..10000 {
|
|
||||||
core::hint::spin_loop();
|
core::hint::spin_loop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,8 +169,8 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
|
|||||||
let lines = [
|
let lines = [
|
||||||
" KERNEL PANIC ",
|
" KERNEL PANIC ",
|
||||||
"",
|
"",
|
||||||
"The system has encountered",
|
"THE SYSTEM HAS ENCOUNTERED",
|
||||||
"a fatal error and must halt.",
|
"A FATAL ERROR AND MUST HALT.",
|
||||||
];
|
];
|
||||||
for (i, &line) in lines.iter().enumerate() {
|
for (i, &line) in lines.iter().enumerate() {
|
||||||
let col = (vga_buffer::WIDTH - line.len()) / 2;
|
let col = (vga_buffer::WIDTH - line.len()) / 2;
|
||||||
|
|||||||
@@ -0,0 +1,377 @@
|
|||||||
|
use crate::keyboard::{self, KeyAction};
|
||||||
|
use crate::vga_buffer::{self, Color, ColorCode};
|
||||||
|
|
||||||
|
const LEVEL_W: usize = 180;
|
||||||
|
const LEVEL_H: usize = 25;
|
||||||
|
const SUB: i32 = 8;
|
||||||
|
const GRAV: i32 = 2;
|
||||||
|
const JUMP: i32 = 10;
|
||||||
|
const SPD: i32 = 6;
|
||||||
|
const MAX_FALL: i32 = 12 * SUB;
|
||||||
|
const MAX_GOOMBAS: usize = 12;
|
||||||
|
|
||||||
|
const AIR: u8 = 0;
|
||||||
|
const GROUND: u8 = 1;
|
||||||
|
const BRICK: u8 = 2;
|
||||||
|
const COIN: u8 = 3;
|
||||||
|
const PIPE_TL: u8 = 5;
|
||||||
|
const PIPE_TR: u8 = 6;
|
||||||
|
const PIPE_SL: u8 = 7;
|
||||||
|
const PIPE_SR: u8 = 8;
|
||||||
|
const FLAG: u8 = 9;
|
||||||
|
const QUESTION: u8 = 10;
|
||||||
|
const USED: u8 = 11;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Goomba {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
dir: i32,
|
||||||
|
alive: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_mario() {
|
||||||
|
let mut rng = crate::rng::Lcg::from_tsc();
|
||||||
|
let mut level = [[AIR; LEVEL_W]; LEVEL_H];
|
||||||
|
let mut goombas = [Goomba { x: 0, y: 0, dir: 1, alive: false }; MAX_GOOMBAS];
|
||||||
|
build_level(&mut level, &mut goombas, &mut rng);
|
||||||
|
|
||||||
|
let mut mx = 3i32 * SUB;
|
||||||
|
let mut my = 20i32 * SUB;
|
||||||
|
let mut vy = 0i32;
|
||||||
|
let mut on_ground = false;
|
||||||
|
let mut cam_x = 0i32;
|
||||||
|
let mut score = 0u32;
|
||||||
|
let mut coin_c = 0u32;
|
||||||
|
let mut dead = false;
|
||||||
|
let mut won = false;
|
||||||
|
let mut dead_timer = 0u32;
|
||||||
|
let mut jump_pending = false;
|
||||||
|
let mut goom_tick = 0u32;
|
||||||
|
|
||||||
|
let lw = LEVEL_W as i32;
|
||||||
|
let lh = LEVEL_H as i32;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Some(action) = keyboard::poll_key() {
|
||||||
|
match action {
|
||||||
|
KeyAction::Tilde => return,
|
||||||
|
KeyAction::Up => { if !dead && !won { jump_pending = true; } }
|
||||||
|
KeyAction::Enter => { if dead || won { return; } }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dead || won {
|
||||||
|
if dead_timer > 0 { dead_timer -= 1; }
|
||||||
|
render_all(&level, &goombas, mx, my, cam_x, score, coin_c, dead, won);
|
||||||
|
let start = rdtsc();
|
||||||
|
loop { if rdtsc().wrapping_sub(start) >= 16_000_000 { break; } core::hint::spin_loop(); }
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let left = keyboard::is_held(&KeyAction::Letter(b'A'));
|
||||||
|
let right = keyboard::is_held(&KeyAction::Letter(b'D'));
|
||||||
|
|
||||||
|
vy += GRAV;
|
||||||
|
if vy > MAX_FALL { vy = MAX_FALL; }
|
||||||
|
|
||||||
|
if jump_pending && on_ground {
|
||||||
|
vy = -JUMP;
|
||||||
|
on_ground = false;
|
||||||
|
}
|
||||||
|
jump_pending = false;
|
||||||
|
|
||||||
|
// horizontal
|
||||||
|
let mut dx = 0i32;
|
||||||
|
if left { dx -= SPD; }
|
||||||
|
if right { dx += SPD; }
|
||||||
|
|
||||||
|
let ntx = (mx + dx) / SUB;
|
||||||
|
let ty = my / SUB;
|
||||||
|
if !tile_solid(&level, ntx as usize, ty as usize) {
|
||||||
|
mx += dx;
|
||||||
|
} else if dx > 0 {
|
||||||
|
let tx = (mx / SUB) as usize;
|
||||||
|
if (ty as usize) < LEVEL_H && tx < LEVEL_W && level[ty as usize][tx] == QUESTION {
|
||||||
|
level[ty as usize][tx] = USED;
|
||||||
|
score += 100;
|
||||||
|
coin_c += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// vertical
|
||||||
|
let tx = (mx / SUB) as usize;
|
||||||
|
let falling = vy > 0;
|
||||||
|
let was_in_air = !on_ground;
|
||||||
|
|
||||||
|
if falling {
|
||||||
|
let cur_feet = (my + SUB - 1) / SUB;
|
||||||
|
let new_feet = (my + vy + SUB - 1) / SUB;
|
||||||
|
if new_feet >= lh {
|
||||||
|
dead = true;
|
||||||
|
dead_timer = 60;
|
||||||
|
render_all(&level, &goombas, mx, my, cam_x, score, coin_c, dead, won);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut hit = false;
|
||||||
|
for r in (cur_feet + 1)..=new_feet {
|
||||||
|
if tile_solid(&level, tx, r as usize) {
|
||||||
|
my = (r - 1) * SUB;
|
||||||
|
vy = 0;
|
||||||
|
on_ground = true;
|
||||||
|
hit = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hit {
|
||||||
|
my += vy;
|
||||||
|
on_ground = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let nty = (my + vy) / SUB;
|
||||||
|
if nty < 0 {
|
||||||
|
my = 0; vy = 0;
|
||||||
|
} else if nty < ty {
|
||||||
|
// going up — check tiles between new and current (exclusive of current)
|
||||||
|
let mut hit = false;
|
||||||
|
for r in (nty as usize)..(ty as usize) {
|
||||||
|
if tile_solid(&level, tx, r) {
|
||||||
|
if level[r][tx] == QUESTION {
|
||||||
|
level[r][tx] = USED;
|
||||||
|
score += 100;
|
||||||
|
coin_c += 1;
|
||||||
|
}
|
||||||
|
my = (r as i32 + 1) * SUB;
|
||||||
|
vy = 0;
|
||||||
|
hit = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hit {
|
||||||
|
my += vy;
|
||||||
|
on_ground = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
my += vy;
|
||||||
|
on_ground = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mx < 0 { mx = 0; }
|
||||||
|
if mx >= lw * SUB { mx = lw * SUB - SUB; }
|
||||||
|
if my >= lh * SUB {
|
||||||
|
my = lh * SUB - SUB;
|
||||||
|
dead = true;
|
||||||
|
dead_timer = 60;
|
||||||
|
render_all(&level, &goombas, mx, my, cam_x, score, coin_c, dead, won);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// interactions
|
||||||
|
let tx = (mx / SUB) as usize;
|
||||||
|
let ty = (my / SUB) as usize;
|
||||||
|
if tx < LEVEL_W && ty < LEVEL_H {
|
||||||
|
match level[ty][tx] {
|
||||||
|
COIN => { level[ty][tx] = AIR; score += 50; coin_c += 1; }
|
||||||
|
FLAG => { won = true; dead_timer = 60; }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// goombas
|
||||||
|
for g in goombas.iter_mut() {
|
||||||
|
if !g.alive { continue; }
|
||||||
|
if g.x == tx as i32 && g.y == ty as i32 {
|
||||||
|
if falling && was_in_air {
|
||||||
|
g.alive = false;
|
||||||
|
vy = -8;
|
||||||
|
score += 200;
|
||||||
|
} else if !dead {
|
||||||
|
dead = true;
|
||||||
|
dead_timer = 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if goom_tick % 4 == 0 {
|
||||||
|
for g in goombas.iter_mut() {
|
||||||
|
if !g.alive { continue; }
|
||||||
|
let gnx = g.x + g.dir;
|
||||||
|
if gnx < 0 || gnx >= lw { g.dir = -g.dir; }
|
||||||
|
else if tile_solid(&level, gnx as usize, g.y as usize)
|
||||||
|
|| !tile_solid(&level, gnx as usize, (g.y + 1) as usize) {
|
||||||
|
g.dir = -g.dir;
|
||||||
|
} else {
|
||||||
|
g.x = gnx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goom_tick += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// camera
|
||||||
|
let target = (mx / SUB) - 35;
|
||||||
|
if target > cam_x { cam_x = target; }
|
||||||
|
if cam_x < 0 { cam_x = 0; }
|
||||||
|
let mc = lw - vga_buffer::WIDTH as i32;
|
||||||
|
if cam_x > mc { cam_x = mc; }
|
||||||
|
if cam_x < 0 { cam_x = 0; }
|
||||||
|
|
||||||
|
render_all(&level, &goombas, mx, my, cam_x, score, coin_c, dead, won);
|
||||||
|
|
||||||
|
let start = rdtsc();
|
||||||
|
loop { if rdtsc().wrapping_sub(start) >= 16_000_000 { break; } core::hint::spin_loop(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rdtsc() -> u64 {
|
||||||
|
unsafe { core::arch::x86_64::_rdtsc() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tile_solid(level: &[[u8; LEVEL_W]; LEVEL_H], x: usize, y: usize) -> bool {
|
||||||
|
if x >= LEVEL_W || y >= LEVEL_H { return y >= LEVEL_H; }
|
||||||
|
match level[y][x] {
|
||||||
|
AIR | COIN | FLAG => false,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_all(
|
||||||
|
level: &[[u8; LEVEL_W]; LEVEL_H],
|
||||||
|
goombas: &[Goomba; MAX_GOOMBAS],
|
||||||
|
mx: i32, my: i32,
|
||||||
|
cam_x: i32,
|
||||||
|
score: u32, coins: u32,
|
||||||
|
dead: bool, won: bool,
|
||||||
|
) {
|
||||||
|
vga_buffer::clear_screen(Color::Cyan);
|
||||||
|
|
||||||
|
let mario_tx = mx / SUB;
|
||||||
|
let mario_ty = my / SUB;
|
||||||
|
|
||||||
|
for row in 0..LEVEL_H {
|
||||||
|
for col in 0..vga_buffer::WIDTH {
|
||||||
|
let wx = col as i32 + cam_x;
|
||||||
|
if wx < 0 || wx >= LEVEL_W as i32 { continue; }
|
||||||
|
let tile = level[row][wx as usize];
|
||||||
|
let (mut ch, mut fg, mut bg) = tile_render(tile);
|
||||||
|
|
||||||
|
for g in goombas {
|
||||||
|
if g.alive && g.x == wx && g.y == row as i32 {
|
||||||
|
ch = b'G';
|
||||||
|
fg = Color::White;
|
||||||
|
bg = Color::Red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if row as i32 == mario_ty && wx == mario_tx {
|
||||||
|
if dead {
|
||||||
|
ch = b'X';
|
||||||
|
fg = Color::Black;
|
||||||
|
bg = Color::Red;
|
||||||
|
} else {
|
||||||
|
ch = b'M';
|
||||||
|
fg = Color::White;
|
||||||
|
bg = Color::Red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vga_buffer::write_char(row, col, ch, ColorCode::new(fg, bg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for col in 0..vga_buffer::WIDTH {
|
||||||
|
vga_buffer::write_char(0, col, b' ', ColorCode::new(Color::White, Color::Black));
|
||||||
|
}
|
||||||
|
draw_uint(0, 7, score, ColorCode::new(Color::Yellow, Color::Black));
|
||||||
|
vga_buffer::write_str(0, 1, "SCORE:", ColorCode::new(Color::White, Color::Black));
|
||||||
|
draw_uint(0, 22, coins, ColorCode::new(Color::Yellow, Color::Black));
|
||||||
|
vga_buffer::write_str(0, 16, "COINS:", ColorCode::new(Color::White, Color::Black));
|
||||||
|
|
||||||
|
if dead {
|
||||||
|
vga_buffer::write_str(12, 30, "GAME OVER", ColorCode::new(Color::Red, Color::Black));
|
||||||
|
vga_buffer::write_str(13, 25, "PRESS ENTER TO EXIT", ColorCode::new(Color::White, Color::Black));
|
||||||
|
}
|
||||||
|
if won {
|
||||||
|
vga_buffer::write_str(12, 30, "YOU WIN!", ColorCode::new(Color::Green, Color::Black));
|
||||||
|
vga_buffer::write_str(13, 25, "PRESS ENTER TO EXIT", ColorCode::new(Color::White, Color::Black));
|
||||||
|
}
|
||||||
|
|
||||||
|
vga_buffer::write_str(24, 55, "A/D MOVE UP=JUMP", ColorCode::new(Color::DarkGray, Color::Black));
|
||||||
|
vga_buffer::write_str(24, 1, "~ EXIT", ColorCode::new(Color::DarkGray, Color::Black));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_uint(row: usize, col: usize, n: u32, cc: ColorCode) {
|
||||||
|
if n == 0 { vga_buffer::write_char(row, col, b'0', cc); return; }
|
||||||
|
let mut buf = [0u8; 12];
|
||||||
|
let mut i = 0;
|
||||||
|
let mut v = n;
|
||||||
|
while v > 0 { buf[i] = b'0' + (v % 10) as u8; i += 1; v /= 10; }
|
||||||
|
for j in 0..i { vga_buffer::write_char(row, col + j, buf[i - 1 - j], cc); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tile_render(tile: u8) -> (u8, Color, Color) {
|
||||||
|
match tile {
|
||||||
|
GROUND => (b'#', Color::Black, Color::Green),
|
||||||
|
BRICK => (b'#', Color::Brown, Color::Brown),
|
||||||
|
COIN => (b'$', Color::Yellow, Color::Cyan),
|
||||||
|
PIPE_TL => (b'[', Color::Black, Color::Green),
|
||||||
|
PIPE_TR => (b']', Color::Black, Color::Green),
|
||||||
|
PIPE_SL => (b'|', Color::Green, Color::Green),
|
||||||
|
PIPE_SR => (b'|', Color::Green, Color::Green),
|
||||||
|
FLAG => (b'F', Color::Black, Color::White),
|
||||||
|
QUESTION => (b'?', Color::White, Color::Yellow),
|
||||||
|
USED => (b'.', Color::DarkGray, Color::Brown),
|
||||||
|
_ => (b' ', Color::White, Color::Cyan),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_level(
|
||||||
|
level: &mut [[u8; LEVEL_W]; LEVEL_H],
|
||||||
|
goombas: &mut [Goomba; MAX_GOOMBAS],
|
||||||
|
_rng: &mut crate::rng::Lcg,
|
||||||
|
) {
|
||||||
|
for col in 0..LEVEL_W {
|
||||||
|
for row in 21..LEVEL_H { level[row][col] = GROUND; }
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut gc = 0usize;
|
||||||
|
|
||||||
|
for col in (0..LEVEL_W).step_by(8) {
|
||||||
|
for i in 0..2 { if col + i < LEVEL_W { level[20][col + i] = BRICK; } }
|
||||||
|
}
|
||||||
|
for col in (0..LEVEL_W).step_by(16) {
|
||||||
|
if col + 2 < LEVEL_W {
|
||||||
|
level[17][col] = QUESTION;
|
||||||
|
level[17][col + 1] = BRICK;
|
||||||
|
level[17][col + 2] = QUESTION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for col in (3..LEVEL_W).step_by(16) {
|
||||||
|
for i in 0..3 { if col + i < LEVEL_W { level[15][col + i] = COIN; } }
|
||||||
|
}
|
||||||
|
|
||||||
|
let pipe_spots = [16usize, 32, 55, 72, 95, 115, 140];
|
||||||
|
for &pc in &pipe_spots {
|
||||||
|
if pc + 1 >= LEVEL_W { break; }
|
||||||
|
let ph = 2 + (pc % 3);
|
||||||
|
let tr = 20i32 - ph as i32;
|
||||||
|
if tr < 1 { continue; }
|
||||||
|
for r in tr as usize..20 {
|
||||||
|
level[r][pc] = PIPE_SL;
|
||||||
|
if pc + 1 < LEVEL_W { level[r][pc + 1] = PIPE_SR; }
|
||||||
|
}
|
||||||
|
level[tr as usize][pc] = PIPE_TL;
|
||||||
|
if pc + 1 < LEVEL_W { level[tr as usize][pc + 1] = PIPE_TR; }
|
||||||
|
}
|
||||||
|
|
||||||
|
let goom_spots = [25usize, 48, 65, 88, 110, 130, 155];
|
||||||
|
for &gx in &goom_spots {
|
||||||
|
if gc >= MAX_GOOMBAS { break; }
|
||||||
|
if gx < LEVEL_W && !tile_solid(level, gx, 20) {
|
||||||
|
goombas[gc] = Goomba { x: gx as i32, y: 20, dir: -1, alive: true };
|
||||||
|
gc += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if LEVEL_W > 2 { level[19][LEVEL_W - 3] = FLAG; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
use core::arch::x86_64::_rdtsc;
|
||||||
|
use x86_64::instructions::port::Port;
|
||||||
|
|
||||||
|
fn rdtsc() -> u64 {
|
||||||
|
unsafe { _rdtsc() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delay_us(us: u64) {
|
||||||
|
let start = rdtsc();
|
||||||
|
let target = start + us * 1000;
|
||||||
|
while rdtsc() < target {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delay_ms(ms: u64) {
|
||||||
|
delay_us(ms * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PIT_BASE: u32 = 1193182;
|
||||||
|
|
||||||
|
fn pit_set_freq(freq: u32) {
|
||||||
|
if freq == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let divisor = PIT_BASE / freq;
|
||||||
|
unsafe {
|
||||||
|
let mut cmd: Port<u8> = Port::new(0x43);
|
||||||
|
cmd.write(0xB6u8);
|
||||||
|
let mut data: Port<u8> = Port::new(0x42);
|
||||||
|
data.write((divisor & 0xFF) as u8);
|
||||||
|
data.write(((divisor >> 8) & 0xFF) as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn speaker_on() {
|
||||||
|
unsafe {
|
||||||
|
let mut port: Port<u8> = Port::new(0x61);
|
||||||
|
let val = port.read();
|
||||||
|
port.write(val | 0x03);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn speaker_off() {
|
||||||
|
unsafe {
|
||||||
|
let mut port: Port<u8> = Port::new(0x61);
|
||||||
|
let val = port.read();
|
||||||
|
port.write(val & !0x03);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tone(freq: u32, dur_ms: u64) {
|
||||||
|
pit_set_freq(freq);
|
||||||
|
speaker_on();
|
||||||
|
delay_ms(dur_ms);
|
||||||
|
speaker_off();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_beep() {
|
||||||
|
tone(880, 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn correct_beep() {
|
||||||
|
tone(523, 100);
|
||||||
|
delay_ms(60);
|
||||||
|
tone(659, 100);
|
||||||
|
delay_ms(60);
|
||||||
|
tone(784, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrong_beep() {
|
||||||
|
tone(300, 150);
|
||||||
|
delay_ms(40);
|
||||||
|
tone(190, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn game_over_beep() {
|
||||||
|
tone(440, 200);
|
||||||
|
delay_ms(80);
|
||||||
|
tone(349, 200);
|
||||||
|
delay_ms(80);
|
||||||
|
tone(262, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fanfare_beep() {
|
||||||
|
tone(523, 100);
|
||||||
|
delay_ms(60);
|
||||||
|
tone(659, 100);
|
||||||
|
delay_ms(60);
|
||||||
|
tone(784, 100);
|
||||||
|
delay_ms(60);
|
||||||
|
tone(1047, 300);
|
||||||
|
}
|
||||||
@@ -0,0 +1,523 @@
|
|||||||
|
use crate::keyboard;
|
||||||
|
use crate::keyboard::KeyAction;
|
||||||
|
use crate::rng::Lcg;
|
||||||
|
use crate::sound;
|
||||||
|
use crate::vga_buffer::{self, Color, ColorCode};
|
||||||
|
|
||||||
|
const TOTAL_QUESTIONS: usize = 25;
|
||||||
|
|
||||||
|
const CHOICE_LABELS: &[&str] = &[
|
||||||
|
"Exposition",
|
||||||
|
"Rising Action",
|
||||||
|
"Climax",
|
||||||
|
"Falling Action",
|
||||||
|
"Resolution",
|
||||||
|
];
|
||||||
|
|
||||||
|
struct StoryNode {
|
||||||
|
text: &'static str,
|
||||||
|
correct_input: usize,
|
||||||
|
success_message: &'static str,
|
||||||
|
failure_message: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
const NODES: &[StoryNode] = &[
|
||||||
|
StoryNode {
|
||||||
|
text: "Deep within the quiet kingdom of Eldoria, a humble blacksmith named Arthur spent his days forging basic tools, entirely unaware of the ancient magical legacy hidden within his family bloodline.",
|
||||||
|
correct_input: 1,
|
||||||
|
success_message: "CORRECT: This is the Exposition. It introduces the main character, establishes the initial setting, and outlines the normal status quo before any major disruption occurs.",
|
||||||
|
failure_message: "INCORRECT: This is the Exposition. This section sets up the baseline world, backstory, and characters before the primary plot begins to unfold.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "After discovering his grandfather's glowing sword hidden beneath the floorboards, Arthur faced a series of dangerous trials, fought off roving shadow creatures, and climbed the treacherous peaks of the Whispering Mountain to reach the sorcerer's tower.",
|
||||||
|
correct_input: 2,
|
||||||
|
success_message: "CORRECT: This is the Rising Action. It contains the inciting incident and the series of escalating obstacles that build narrative tension and develop the central conflict.",
|
||||||
|
failure_message: "INCORRECT: This is the Rising Action. These events represent the steady buildup of complications, stakes, and challenges that the protagonist must confront.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "At the pinnacle of the crumbling tower, Arthur stood face-to-face with the Shadow King. With a final, desperate swing of his sword, he shattered the dark monarch's magical amulet, sending a blinding shockwave of light tearing through the fortress.",
|
||||||
|
correct_input: 3,
|
||||||
|
success_message: "CORRECT: This is the Climax. It is the absolute peak of emotional and physical tension in the story, marking the definitive turning point where the main conflict is decided.",
|
||||||
|
failure_message: "INCORRECT: This is the Climax. This pivotal confrontation is the highest point of action and the moment of truth for the protagonist.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "With the dark magic dissipated, the sky cleared and the remaining shadow soldiers scattered into the woods. Arthur slowly made his way back down the mountain tracks, receiving medical aid from the villagers who gathered to welcome him back.",
|
||||||
|
correct_input: 4,
|
||||||
|
success_message: "CORRECT: This is the Falling Action. The main crisis has passed, the immediate consequences of the climax are unfolding, and the narrative tension is steadily winding down.",
|
||||||
|
failure_message: "INCORRECT: This is the Falling Action. This phase deals with the immediate aftermath of the climax as the story begins to de-escalate.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "The kingdom restored its ancient alliances, and peace returned to the valleys. Arthur returned to his forge, no longer a simple blacksmith, but a respected guardian who kept his legendary blade resting safely above the hearth.",
|
||||||
|
correct_input: 5,
|
||||||
|
success_message: "CORRECT: This is the Resolution. It ties up the remaining loose narrative threads, establishes a brand-new normalcy, and brings a sense of finality to the character's journey.",
|
||||||
|
failure_message: "INCORRECT: This is the Resolution. This final block establishes the stable new reality for the characters after all conflicts have been solved.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "In the year 2145, New Horizon was a gleaming underwater metropolis. Dr. Elena Vance worked silently in her bio-dome, monitoring deep-sea kelp crops that provided oxygen for the city's three million inhabitants.",
|
||||||
|
correct_input: 1,
|
||||||
|
success_message: "CORRECT: This is the Exposition. It establishes the futuristic sci-fi setting, introduces the protagonist, and sets up the baseline operational status of the environment.",
|
||||||
|
failure_message: "INCORRECT: This is the Exposition. It outlines the initial world details and character roles before any narrative disruption takes place.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "A massive oceanic tremor cracked the outer structural hull. Elena worked frantically against a ticking clock, sealing sector doors one by one, rerouting emergency power matrices, and hot-wiring old atmospheric scrubbers as water slowly flooded her laboratory.",
|
||||||
|
correct_input: 2,
|
||||||
|
success_message: "CORRECT: This is the Rising Action. The structural breach acts as an inciting incident, sparking a series of escalating complications and life-threatening hurdles.",
|
||||||
|
failure_message: "INCORRECT: This is the Rising Action. This section details the building crisis and consecutive struggles faced by the character to prevent disaster.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "Submerged in freezing black water up to her chest, Elena slammed the manual override lever down with her remaining strength, locking the primary failure valve into place just milliseconds before the entire city's oxygen grid collapsed.",
|
||||||
|
correct_input: 3,
|
||||||
|
success_message: "CORRECT: This is the Climax. This is the moment of greatest consequence and tension, where the survival of the entire city rests on a single final action.",
|
||||||
|
failure_message: "INCORRECT: This is the Climax. It represents the ultimate turning point and highest peak of structural tension in the narrative thread.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "The emergency automated bilge pumps roared to life, clearing the flooded corridors. Fleet submersibles arrived to patch the external fissures, and medical teams wrapped Elena in thermal blankets while hauling her out of the deep-sea sector.",
|
||||||
|
correct_input: 4,
|
||||||
|
success_message: "CORRECT: This is the Falling Action. The immediate deadly threat has been solved, and the story focuses on the cleanup operations and the safety of the main character.",
|
||||||
|
failure_message: "INCORRECT: This is the Falling Action. This sequence shows the narrative tension unwinding as emergency responses bring the crisis under control.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "New Horizon's structural reinforced plates were upgraded to withstand double the pressure. Elena was appointed Chief of Oceanic Security, looking out through the reinforced glass at a safe, glowing, and stable underwater home.",
|
||||||
|
correct_input: 5,
|
||||||
|
success_message: "CORRECT: This is the Resolution. The narrative conflict is entirely closed out, and a stable, modified status quo is established for the city and protagonist.",
|
||||||
|
failure_message: "INCORRECT: This is the Resolution. It seals the plot arc by showing how the world has permanently changed for the better following the conflict.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "Detective Miller sat in his dim, smoke-filled office, listening to the steady rain beat against the windowpane. The city of Oakhaven had been peaceful for months, leaving his desk bare of any major investigation folders.",
|
||||||
|
correct_input: 1,
|
||||||
|
success_message: "CORRECT: This is the Exposition. It establishes a classic noir tone, introduces the static state of the protagonist, and describes a peaceful environment before a crime occurs.",
|
||||||
|
failure_message: "INCORRECT: This is the Exposition. It provides the mood, atmosphere, and baseline context of the detective's idle life before a case arrives.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "An anonymous envelope containing a cryptic map was slipped under Miller's door. Over the next three days, he tracked footprints through abandoned shipyards, decoded encrypted files on a hidden server, and interrogated tight-lipped dockworkers.",
|
||||||
|
correct_input: 2,
|
||||||
|
success_message: "CORRECT: This is the Rising Action. The mysterious map sets the detective on a trail, generating a sequence of procedural investigations that escalate the mystery.",
|
||||||
|
failure_message: "INCORRECT: This is the Rising Action. It showcases the gathering of clues, building tension, and the active pursuit of an elusive antagonist.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "Cornered on the windy roof of the old clock tower, the culprit pulled a revolver. Miller lunged forward through the shadows, wrestling the weapon away just as a shot fired into the night sky, pinning the thief to the roof tiles.",
|
||||||
|
correct_input: 3,
|
||||||
|
success_message: "CORRECT: This is the Climax. It is the direct physical confrontation where the mystery is solved, the antagonist is caught, and the threat is neutralized.",
|
||||||
|
failure_message: "INCORRECT: This is the Climax. This high-stakes struggle serves as the definitive peak of action for the detective's case.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "Backup patrol sirens wailed down on the streets below. Miller handcuffed the suspect to the iron railings of the clock tower and stood back, lighting a cigarette as police officers flooded up the spiral staircase.",
|
||||||
|
correct_input: 4,
|
||||||
|
success_message: "CORRECT: This is the Falling Action. The threat has been completely handled, and the story transitions into processing the captured antagonist.",
|
||||||
|
failure_message: "INCORRECT: This is the Falling Action. The tension subsides as the authorities arrive to manage the aftermath of the rooftop fight.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "The stolen museum artifacts were returned to their glass displays by morning. Miller walked into a warm local diner, ordered a coffee, and smiled as he looked at an empty city newspaper celebrating a safe community.",
|
||||||
|
correct_input: 5,
|
||||||
|
success_message: "CORRECT: This is the Resolution. The narrative loop is finished, the justice system has prevailed, and Miller returns to a quiet baseline routine.",
|
||||||
|
failure_message: "INCORRECT: This is the Resolution. It represents the final balancing of the scales and the arrival of a new, peaceful day.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "The small colony of Crestview sat nestled in a fertile valley. For three generations, the villagers lived isolated lives, harvesting moon-roots and strictly following the elders' laws never to venture past the northern forest border.",
|
||||||
|
correct_input: 1,
|
||||||
|
success_message: "CORRECT: This is the Exposition. It outlines the cultural rules, the geographical boundaries, and the long-standing peaceful traditions of the community.",
|
||||||
|
failure_message: "INCORRECT: This is the Exposition. It provides the background details regarding the isolation and rules of the community before a boundary is crossed.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "When a strange blight began killing the valley's crops, a young scout named Clara chose to cross the northern treeline. She braved uncharted swamps, avoided predatory shadow-hawks, and discovered an ancient automated irrigation outpost.",
|
||||||
|
correct_input: 2,
|
||||||
|
success_message: "CORRECT: This is the Rising Action. The agricultural crisis forces the protagonist to break a societal taboo, initiating a journey filled with growing physical dangers.",
|
||||||
|
failure_message: "INCORRECT: This is the Rising Action. It catalogs the trials and steps taken by the character to solve a larger problem threatening her home.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "Inside the crumbling facility's central chamber, the security core activated, targeting Clara with red laser arrays. Remembering her father's old engineering notes, she severed the main power conduits with an axe, shutting down the lasers permanently.",
|
||||||
|
correct_input: 3,
|
||||||
|
success_message: "CORRECT: This is the Climax. The character faces an immediate, life-or-death mechanical threat, demanding a quick decision to achieve her ultimate goal.",
|
||||||
|
failure_message: "INCORRECT: This is the Climax. It marks the absolute peak of peril where the success of the mission hangs completely in balance.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "The ancient computer terminal flickered, rebooted, and launched an automated water purifying cycle down into the valley. Clara gathered her gear from the floor, breathing a massive sigh of relief as the defensive turrets retracted back into the ceiling.",
|
||||||
|
correct_input: 4,
|
||||||
|
success_message: "CORRECT: This is the Falling Action. The defense system has been disarmed, the water supply is rescued, and the character can collect herself safely.",
|
||||||
|
failure_message: "INCORRECT: This is the Falling Action. The main obstacle is cleared, showing the immediate positive effects of the successful climax.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "Clara returned home to find clean, fresh water filling the valley canals. The old borders were dismantled, and the people of Crestview began exploring the wider world beyond the forest, guided by their new maps.",
|
||||||
|
correct_input: 5,
|
||||||
|
success_message: "CORRECT: This is the Resolution. It shows a complete structural shift in the community's way of life, resulting in a progressive and open new era.",
|
||||||
|
failure_message: "INCORRECT: This is the Resolution. The initial problem is thoroughly solved, and a permanent, updated way of living takes root.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "The interstellar exploration barge, The Wanderer, drifted silently through the outer ring of the Orion Nebula. Capt. Thomas had spent six long, uneventful months logging cosmic radiation anomalies from the ship's bridge.",
|
||||||
|
correct_input: 1,
|
||||||
|
success_message: "CORRECT: This is the Exposition. It introduces the space setting, outlines the monotonous routine of the pilot, and sets up a quiet environment before an event.",
|
||||||
|
failure_message: "INCORRECT: This is the Exposition. This section frames the isolation and standard background environment of the interstellar mission before any conflict arises.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "An uncharted rogue asteroid collided with the primary engine block. Thomas crawled through smoky maintenance tubes, patched leaking plasma pipelines, and hand-cranked the backup magnetic shields while automated warnings blared across every console.",
|
||||||
|
correct_input: 2,
|
||||||
|
success_message: "CORRECT: This is the Rising Action. An unexpected strike disrupts the ship's path, forcing the protagonist into a chain of high-stress repairs to survive.",
|
||||||
|
failure_message: "INCORRECT: This is the Rising Action. This part depicts the accumulation of mechanical emergencies and the character's step-by-step struggle to keep the vessel intact.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "With the computer failing, Thomas manually aimed the auxiliary thruster nozzle toward a nearby gravity well, igniting the remaining fuel block to fling the spacecraft clear of a collapsing black hole's event horizon.",
|
||||||
|
correct_input: 3,
|
||||||
|
success_message: "CORRECT: This is the Climax. It represents the ultimate mechanical and cosmic threshold where one critical maneuver determines survival or complete obliteration.",
|
||||||
|
failure_message: "INCORRECT: This is the Climax. This is the absolute peak of kinetic action and catastrophic pressure within the sci-fi plotline.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "The crushing gravitational forces slowly eased up as the ship pulled away into stable space. Thomas collapsed back into his pilot chair, wiping sweat from his forehead as the ship's internal alert sirens drifted down to a quiet hum.",
|
||||||
|
correct_input: 4,
|
||||||
|
success_message: "CORRECT: This is the Falling Action. The vessel has escaped total destruction, the physical pressure releases, and the immediate panic drops away.",
|
||||||
|
failure_message: "INCORRECT: This is the Falling Action. It portrays the physical and mental de-escalation of the scene immediately following a successful escape.",
|
||||||
|
},
|
||||||
|
StoryNode {
|
||||||
|
text: "The ship entered a secure planetary port for structural retrofitting. Thomas received a distinguished citation for bravery, looking forward to a long, well-deserved shore leave on a peaceful green homeworld.",
|
||||||
|
correct_input: 5,
|
||||||
|
success_message: "CORRECT: This is the Resolution. The mission is officially wrapped up, the protagonist is safe on land, and a permanent state of rest is achieved.",
|
||||||
|
failure_message: "INCORRECT: This is the Resolution. It brings a definite closing frame to the journey, outlining a safe and stable retirement of the conflict.",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
struct StoryPartGame {
|
||||||
|
question_order: [usize; TOTAL_QUESTIONS],
|
||||||
|
current: usize,
|
||||||
|
score: usize,
|
||||||
|
answered: bool,
|
||||||
|
was_correct: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_wrapped(start_row: usize, text: &str, color: ColorCode, max_width: usize) -> usize {
|
||||||
|
let bytes = text.as_bytes();
|
||||||
|
let len = bytes.len();
|
||||||
|
let mut row = start_row;
|
||||||
|
let mut pos = 0;
|
||||||
|
|
||||||
|
while pos < len && row < vga_buffer::HEIGHT {
|
||||||
|
let remaining = len - pos;
|
||||||
|
let line_len = if remaining <= max_width {
|
||||||
|
remaining
|
||||||
|
} else {
|
||||||
|
let mut break_pos = pos + max_width;
|
||||||
|
while break_pos > pos && bytes[break_pos] != b' ' {
|
||||||
|
break_pos -= 1;
|
||||||
|
}
|
||||||
|
if break_pos == pos {
|
||||||
|
max_width
|
||||||
|
} else {
|
||||||
|
break_pos - pos
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let line = &text[pos..pos + line_len];
|
||||||
|
let line = line.trim_end();
|
||||||
|
if !line.is_empty() {
|
||||||
|
let col = (vga_buffer::WIDTH - line.len()) / 2;
|
||||||
|
vga_buffer::write_str(row, col, line, color);
|
||||||
|
}
|
||||||
|
row += 1;
|
||||||
|
|
||||||
|
pos += line_len;
|
||||||
|
if pos < len && bytes[pos] == b' ' {
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
row
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_number(row: usize, col: usize, num: usize, color: ColorCode) -> usize {
|
||||||
|
let mut n = num;
|
||||||
|
let mut c = col;
|
||||||
|
if n == 0 {
|
||||||
|
vga_buffer::write_char(row, c, b'0', color);
|
||||||
|
return c + 1;
|
||||||
|
}
|
||||||
|
let mut digits = [b'0'; 10];
|
||||||
|
let mut len = 0;
|
||||||
|
while n > 0 {
|
||||||
|
digits[len] = b'0' + (n % 10) as u8;
|
||||||
|
n /= 10;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
for i in (0..len).rev() {
|
||||||
|
vga_buffer::write_char(row, c, digits[i], color);
|
||||||
|
c += 1;
|
||||||
|
}
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_question(game: &StoryPartGame) {
|
||||||
|
vga_buffer::clear_screen(Color::Black);
|
||||||
|
|
||||||
|
let white = ColorCode::new(Color::White, Color::Black);
|
||||||
|
let yellow = ColorCode::new(Color::Yellow, Color::Black);
|
||||||
|
let cyan = ColorCode::new(Color::Cyan, Color::Black);
|
||||||
|
let dgray = ColorCode::new(Color::DarkGray, Color::Black);
|
||||||
|
|
||||||
|
let title = "STORY PART GAME";
|
||||||
|
let col = (vga_buffer::WIDTH - title.len()) / 2;
|
||||||
|
vga_buffer::write_str(0, col, title, yellow);
|
||||||
|
|
||||||
|
let mut c = 2;
|
||||||
|
let p1 = "QUESTION ";
|
||||||
|
vga_buffer::write_str(1, c, p1, cyan);
|
||||||
|
c += p1.len();
|
||||||
|
c = write_number(1, c, game.current + 1, cyan);
|
||||||
|
let p2 = " OF ";
|
||||||
|
vga_buffer::write_str(1, c, p2, cyan);
|
||||||
|
c += p2.len();
|
||||||
|
c = write_number(1, c, TOTAL_QUESTIONS, cyan);
|
||||||
|
let p3 = " SCORE: ";
|
||||||
|
vga_buffer::write_str(1, c, p3, cyan);
|
||||||
|
c += p3.len();
|
||||||
|
write_number(1, c, game.score, cyan);
|
||||||
|
|
||||||
|
let node = &NODES[game.question_order[game.current]];
|
||||||
|
let text_end = render_wrapped(3, node.text, white, 74);
|
||||||
|
|
||||||
|
let choice_start = text_end + 1;
|
||||||
|
for i in 0..5 {
|
||||||
|
let label = CHOICE_LABELS[i];
|
||||||
|
let mut buf = [0u8; 64];
|
||||||
|
let prefix = match i {
|
||||||
|
0 => b"1. ",
|
||||||
|
1 => b"2. ",
|
||||||
|
2 => b"3. ",
|
||||||
|
3 => b"4. ",
|
||||||
|
_ => b"5. ",
|
||||||
|
};
|
||||||
|
let mut len = 0;
|
||||||
|
for &b in prefix {
|
||||||
|
buf[len] = b;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
for &b in label.as_bytes() {
|
||||||
|
buf[len] = b;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
let line = core::str::from_utf8(&buf[..len]).unwrap();
|
||||||
|
let col = (vga_buffer::WIDTH - line.len()) / 2;
|
||||||
|
vga_buffer::write_str(choice_start + i, col, line, white);
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = "PRESS 1-5 TO CHOOSE ~ TO EXIT";
|
||||||
|
let col = (vga_buffer::WIDTH - msg.len()) / 2;
|
||||||
|
vga_buffer::write_str(choice_start + 6, col, msg, dgray);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_feedback(game: &StoryPartGame) {
|
||||||
|
vga_buffer::clear_screen(Color::Black);
|
||||||
|
|
||||||
|
let white = ColorCode::new(Color::White, Color::Black);
|
||||||
|
let yellow = ColorCode::new(Color::Yellow, Color::Black);
|
||||||
|
let dgray = ColorCode::new(Color::DarkGray, Color::Black);
|
||||||
|
|
||||||
|
let node = &NODES[game.question_order[game.current]];
|
||||||
|
|
||||||
|
let result_color = if game.was_correct {
|
||||||
|
ColorCode::new(Color::Green, Color::Black)
|
||||||
|
} else {
|
||||||
|
ColorCode::new(Color::Red, Color::Black)
|
||||||
|
};
|
||||||
|
let result_text = if game.was_correct { "CORRECT!" } else { "INCORRECT!" };
|
||||||
|
let col = (vga_buffer::WIDTH - result_text.len()) / 2;
|
||||||
|
vga_buffer::write_str(1, col, result_text, result_color);
|
||||||
|
|
||||||
|
let msg = if game.was_correct {
|
||||||
|
node.success_message
|
||||||
|
} else {
|
||||||
|
node.failure_message
|
||||||
|
};
|
||||||
|
render_wrapped(3, msg, white, 74);
|
||||||
|
|
||||||
|
let correct_label = CHOICE_LABELS[node.correct_input - 1];
|
||||||
|
let mut buf = [0u8; 64];
|
||||||
|
let mut len = 0;
|
||||||
|
for &b in b"THE CORRECT ANSWER WAS: " {
|
||||||
|
buf[len] = b;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
for &b in correct_label.as_bytes() {
|
||||||
|
buf[len] = b;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
let answer_line = core::str::from_utf8(&buf[..len]).unwrap();
|
||||||
|
let col = (vga_buffer::WIDTH - len) / 2;
|
||||||
|
vga_buffer::write_str(21, col, answer_line, yellow);
|
||||||
|
|
||||||
|
let cont = "PRESS ANY KEY TO CONTINUE";
|
||||||
|
let col = (vga_buffer::WIDTH - cont.len()) / 2;
|
||||||
|
vga_buffer::write_str(23, col, cont, dgray);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_results(score: usize) {
|
||||||
|
vga_buffer::clear_screen(Color::Black);
|
||||||
|
|
||||||
|
let white = ColorCode::new(Color::White, Color::Black);
|
||||||
|
let yellow = ColorCode::new(Color::Yellow, Color::Black);
|
||||||
|
let dgray = ColorCode::new(Color::DarkGray, Color::Black);
|
||||||
|
|
||||||
|
let title = "GAME OVER!";
|
||||||
|
let col = (vga_buffer::WIDTH - title.len()) / 2;
|
||||||
|
vga_buffer::write_str(4, col, title, yellow);
|
||||||
|
|
||||||
|
let mut buf = [0u8; 64];
|
||||||
|
let mut len = 0;
|
||||||
|
for &b in b"FINAL SCORE: " {
|
||||||
|
buf[len] = b;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
let mut n = score;
|
||||||
|
let mut digs = [b'0'; 10];
|
||||||
|
let mut dl = 0;
|
||||||
|
if n == 0 {
|
||||||
|
digs[0] = b'0';
|
||||||
|
dl = 1;
|
||||||
|
} else {
|
||||||
|
while n > 0 {
|
||||||
|
digs[dl] = b'0' + (n % 10) as u8;
|
||||||
|
n /= 10;
|
||||||
|
dl += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in (0..dl).rev() {
|
||||||
|
buf[len] = digs[i];
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
buf[len] = b'/';
|
||||||
|
len += 1;
|
||||||
|
let mut m = TOTAL_QUESTIONS;
|
||||||
|
let mut digs2 = [b'0'; 10];
|
||||||
|
let mut dl2 = 0;
|
||||||
|
while m > 0 {
|
||||||
|
digs2[dl2] = b'0' + (m % 10) as u8;
|
||||||
|
m /= 10;
|
||||||
|
dl2 += 1;
|
||||||
|
}
|
||||||
|
for i in (0..dl2).rev() {
|
||||||
|
buf[len] = digs2[i];
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
let score_str = core::str::from_utf8(&buf[..len]).unwrap();
|
||||||
|
let col = (vga_buffer::WIDTH - len) / 2;
|
||||||
|
vga_buffer::write_str(7, col, score_str, white);
|
||||||
|
|
||||||
|
let pct = score * 100 / TOTAL_QUESTIONS;
|
||||||
|
let mut buf2 = [0u8; 64];
|
||||||
|
let mut len2 = 0;
|
||||||
|
for &b in b"PERCENTAGE: " {
|
||||||
|
buf2[len2] = b;
|
||||||
|
len2 += 1;
|
||||||
|
}
|
||||||
|
let mut n2 = pct;
|
||||||
|
let mut digs3 = [b'0'; 10];
|
||||||
|
let mut dl3 = 0;
|
||||||
|
if n2 == 0 {
|
||||||
|
digs3[0] = b'0';
|
||||||
|
dl3 = 1;
|
||||||
|
} else {
|
||||||
|
while n2 > 0 {
|
||||||
|
digs3[dl3] = b'0' + (n2 % 10) as u8;
|
||||||
|
n2 /= 10;
|
||||||
|
dl3 += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in (0..dl3).rev() {
|
||||||
|
buf2[len2] = digs3[i];
|
||||||
|
len2 += 1;
|
||||||
|
}
|
||||||
|
buf2[len2] = b'%';
|
||||||
|
len2 += 1;
|
||||||
|
let pct_str = core::str::from_utf8(&buf2[..len2]).unwrap();
|
||||||
|
let col = (vga_buffer::WIDTH - len2) / 2;
|
||||||
|
vga_buffer::write_str(8, col, pct_str, white);
|
||||||
|
|
||||||
|
let rating = if pct >= 90 {
|
||||||
|
"MASTER OF NARRATIVE STRUCTURE!"
|
||||||
|
} else if pct >= 70 {
|
||||||
|
"STRONG UNDERSTANDING OF PLOT!"
|
||||||
|
} else if pct >= 50 {
|
||||||
|
"DECENT FOUNDATION! KEEP STUDYING."
|
||||||
|
} else {
|
||||||
|
"REVIEW THE FIVE ACT STRUCTURE."
|
||||||
|
};
|
||||||
|
let col = (vga_buffer::WIDTH - rating.len()) / 2;
|
||||||
|
vga_buffer::write_str(10, col, rating, yellow);
|
||||||
|
|
||||||
|
let msg = "PRESS ANY KEY TO RETURN";
|
||||||
|
let col = (vga_buffer::WIDTH - msg.len()) / 2;
|
||||||
|
vga_buffer::write_str(14, col, msg, dgray);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if keyboard::poll_key().is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_story_part() {
|
||||||
|
let mut rng = Lcg::from_tsc();
|
||||||
|
|
||||||
|
let mut all = [0usize; TOTAL_QUESTIONS];
|
||||||
|
let mut i = 0;
|
||||||
|
while i < TOTAL_QUESTIONS {
|
||||||
|
all[i] = i;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
let mut j = TOTAL_QUESTIONS;
|
||||||
|
while j > 1 {
|
||||||
|
j -= 1;
|
||||||
|
let k = rng.next_range(j + 1);
|
||||||
|
let tmp = all[j];
|
||||||
|
all[j] = all[k];
|
||||||
|
all[k] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut game = StoryPartGame {
|
||||||
|
question_order: all,
|
||||||
|
current: 0,
|
||||||
|
score: 0,
|
||||||
|
answered: false,
|
||||||
|
was_correct: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
render_question(&game);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Some(action) = keyboard::poll_key() {
|
||||||
|
match action {
|
||||||
|
KeyAction::Tilde => return,
|
||||||
|
KeyAction::Digit(d) if d >= 1 && d <= 5 => {
|
||||||
|
let idx = (d - 1) as usize;
|
||||||
|
let node = &NODES[game.question_order[game.current]];
|
||||||
|
game.was_correct = idx + 1 == node.correct_input;
|
||||||
|
if game.was_correct {
|
||||||
|
game.score += 1;
|
||||||
|
sound::correct_beep();
|
||||||
|
} else {
|
||||||
|
sound::wrong_beep();
|
||||||
|
}
|
||||||
|
game.answered = true;
|
||||||
|
render_feedback(&game);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if keyboard::poll_key().is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
game.current += 1;
|
||||||
|
if game.current >= TOTAL_QUESTIONS {
|
||||||
|
sound::fanfare_beep();
|
||||||
|
show_results(game.score);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
game.answered = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+84
-24
@@ -110,30 +110,55 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_pixel(state: &mut FbState, x: usize, y: usize, r: u8, g: u8, b: u8) {
|
|
||||||
if x >= state.width || y >= state.height {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let offset = (y * state.stride + x) * state.bpp;
|
|
||||||
unsafe {
|
|
||||||
if state.is_bgr {
|
|
||||||
*state.ptr.add(offset) = b;
|
|
||||||
*state.ptr.add(offset + 1) = g;
|
|
||||||
*state.ptr.add(offset + 2) = r;
|
|
||||||
} else {
|
|
||||||
*state.ptr.add(offset) = r;
|
|
||||||
*state.ptr.add(offset + 1) = g;
|
|
||||||
*state.ptr.add(offset + 2) = b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill_rect(state: &mut FbState, x: usize, y: usize, w: usize, h: usize, r: u8, g: u8, b: u8) {
|
fn fill_rect(state: &mut FbState, x: usize, y: usize, w: usize, h: usize, r: u8, g: u8, b: u8) {
|
||||||
let x_end = (x + w).min(state.width);
|
let x_end = (x + w).min(state.width);
|
||||||
let y_end = (y + h).min(state.height);
|
let y_end = (y + h).min(state.height);
|
||||||
|
if x >= x_end || y >= y_end { return; }
|
||||||
|
let w_px = x_end - x;
|
||||||
|
let bpp = state.bpp;
|
||||||
|
let stride = state.stride;
|
||||||
|
unsafe {
|
||||||
|
let ptr = state.ptr;
|
||||||
|
if bpp == 4 {
|
||||||
|
let pixel = if state.is_bgr {
|
||||||
|
b as u32 | (g as u32) << 8 | (r as u32) << 16 | 0xFF000000
|
||||||
|
} else {
|
||||||
|
r as u32 | (g as u32) << 8 | (b as u32) << 16 | 0xFF000000
|
||||||
|
};
|
||||||
|
let buf = core::slice::from_raw_parts_mut(ptr as *mut u32, state.height * stride);
|
||||||
for py in y..y_end {
|
for py in y..y_end {
|
||||||
for px in x..x_end {
|
let start = py * stride + x;
|
||||||
write_pixel(state, px, py, r, g, b);
|
let end = start + w_px;
|
||||||
|
let mut i = start;
|
||||||
|
while i < end {
|
||||||
|
*buf.get_unchecked_mut(i) = pixel;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if state.is_bgr {
|
||||||
|
for py in y..y_end {
|
||||||
|
let mut off = (py * stride + x) * bpp;
|
||||||
|
let end = off + w_px * bpp;
|
||||||
|
while off < end {
|
||||||
|
*ptr.add(off) = b;
|
||||||
|
*ptr.add(off + 1) = g;
|
||||||
|
*ptr.add(off + 2) = r;
|
||||||
|
if bpp >= 4 { *ptr.add(off + 3) = 0xFF; }
|
||||||
|
off += bpp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for py in y..y_end {
|
||||||
|
let mut off = (py * stride + x) * bpp;
|
||||||
|
let end = off + w_px * bpp;
|
||||||
|
while off < end {
|
||||||
|
*ptr.add(off) = r;
|
||||||
|
*ptr.add(off + 1) = g;
|
||||||
|
*ptr.add(off + 2) = b;
|
||||||
|
if bpp >= 4 { *ptr.add(off + 3) = 0xFF; }
|
||||||
|
off += bpp;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,16 +201,45 @@ fn draw_glyph_at(
|
|||||||
let off_x = (cell_w - glw) / 2;
|
let off_x = (cell_w - glw) / 2;
|
||||||
let off_y = (cell_h - glh) / 2;
|
let off_y = (cell_h - glh) / 2;
|
||||||
|
|
||||||
|
if state.bpp == 4 {
|
||||||
|
let fg_pixel = if state.is_bgr {
|
||||||
|
fg_b as u32 | (fg_g as u32) << 8 | (fg_r as u32) << 16 | 0xFF000000
|
||||||
|
} else {
|
||||||
|
fg_r as u32 | (fg_g as u32) << 8 | (fg_b as u32) << 16 | 0xFF000000
|
||||||
|
};
|
||||||
|
let stride = state.stride;
|
||||||
|
let fw = state.width;
|
||||||
|
let fh = state.height;
|
||||||
|
unsafe {
|
||||||
|
let buf = core::slice::from_raw_parts_mut(state.ptr as *mut u32, fh * stride);
|
||||||
for row in 0..font::FONT_5X7_H {
|
for row in 0..font::FONT_5X7_H {
|
||||||
for col in 0..font::FONT_5X7_W {
|
for col in 0..font::FONT_5X7_W {
|
||||||
if glyph[row] & (1 << (4 - col)) != 0 {
|
if glyph[row] & (1 << (4 - col)) != 0 {
|
||||||
fill_rect(
|
let px = cell_x + off_x + col * scale;
|
||||||
state,
|
let py = cell_y + off_y + row * scale;
|
||||||
|
let dy_end = scale.min(fh.saturating_sub(py));
|
||||||
|
for dy in 0..dy_end {
|
||||||
|
let y = py + dy;
|
||||||
|
let start = y * stride + px;
|
||||||
|
let dx_end = scale.min(fw.saturating_sub(px));
|
||||||
|
for dx in 0..dx_end {
|
||||||
|
*buf.get_unchecked_mut(start + dx) = fg_pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for row in 0..font::FONT_5X7_H {
|
||||||
|
for col in 0..font::FONT_5X7_W {
|
||||||
|
if glyph[row] & (1 << (4 - col)) != 0 {
|
||||||
|
fill_rect(state,
|
||||||
cell_x + off_x + col * scale,
|
cell_x + off_x + col * scale,
|
||||||
cell_y + off_y + row * scale,
|
cell_y + off_y + row * scale,
|
||||||
scale, scale,
|
scale, scale,
|
||||||
fg_r, fg_g, fg_b,
|
fg_r, fg_g, fg_b);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,7 +248,13 @@ fn draw_glyph_at(
|
|||||||
pub fn clear_screen(bg: Color) {
|
pub fn clear_screen(bg: Color) {
|
||||||
let (r, g, b) = color_to_rgb(bg);
|
let (r, g, b) = color_to_rgb(bg);
|
||||||
with_fb(|state| {
|
with_fb(|state| {
|
||||||
|
if r == 0 && g == 0 && b == 0 {
|
||||||
|
unsafe {
|
||||||
|
core::ptr::write_bytes(state.ptr, 0, state.height * state.stride * state.bpp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
fill_rect(state, 0, 0, state.width, state.height, r, g, b);
|
fill_rect(state, 0, 0, state.width, state.height, r, g, b);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-475
@@ -1,475 +0,0 @@
|
|||||||
CPU Reset (CPU 0)
|
|
||||||
EAX=00000000 EBX=00000000 ECX=00000000 EDX=00060fb1
|
|
||||||
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000
|
|
||||||
EIP=0000fff0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0000 00000000 0000ffff 00009300
|
|
||||||
CS =f000 ffff0000 0000ffff 00009b00
|
|
||||||
SS =0000 00000000 0000ffff 00009300
|
|
||||||
DS =0000 00000000 0000ffff 00009300
|
|
||||||
FS =0000 00000000 0000ffff 00009300
|
|
||||||
GS =0000 00000000 0000ffff 00009300
|
|
||||||
LDT=0000 00000000 0000ffff 00008200
|
|
||||||
TR =0000 00000000 0000ffff 00008b00
|
|
||||||
GDT= 00000000 0000ffff
|
|
||||||
IDT= 00000000 0000ffff
|
|
||||||
CR0=60000010 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000000 CCD=00000000 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
|
|
||||||
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
|
|
||||||
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
|
|
||||||
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
|
|
||||||
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
|
|
||||||
XMM00=0000000000000000 0000000000000000 XMM01=0000000000000000 0000000000000000
|
|
||||||
XMM02=0000000000000000 0000000000000000 XMM03=0000000000000000 0000000000000000
|
|
||||||
XMM04=0000000000000000 0000000000000000 XMM05=0000000000000000 0000000000000000
|
|
||||||
XMM06=0000000000000000 0000000000000000 XMM07=0000000000000000 0000000000000000
|
|
||||||
CPU Reset (CPU 0)
|
|
||||||
EAX=00000000 EBX=00000000 ECX=00000000 EDX=00060fb1
|
|
||||||
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000
|
|
||||||
EIP=0000fff0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0000 00000000 0000ffff 00009300
|
|
||||||
CS =f000 ffff0000 0000ffff 00009b00
|
|
||||||
SS =0000 00000000 0000ffff 00009300
|
|
||||||
DS =0000 00000000 0000ffff 00009300
|
|
||||||
FS =0000 00000000 0000ffff 00009300
|
|
||||||
GS =0000 00000000 0000ffff 00009300
|
|
||||||
LDT=0000 00000000 0000ffff 00008200
|
|
||||||
TR =0000 00000000 0000ffff 00008b00
|
|
||||||
GDT= 00000000 0000ffff
|
|
||||||
IDT= 00000000 0000ffff
|
|
||||||
CR0=60000010 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000000 CCD=00000000 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
|
|
||||||
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
|
|
||||||
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
|
|
||||||
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
|
|
||||||
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
|
|
||||||
XMM00=0000000000000000 0000000000000000 XMM01=0000000000000000 0000000000000000
|
|
||||||
XMM02=0000000000000000 0000000000000000 XMM03=0000000000000000 0000000000000000
|
|
||||||
XMM04=0000000000000000 0000000000000000 XMM05=0000000000000000 0000000000000000
|
|
||||||
XMM06=0000000000000000 0000000000000000 XMM07=0000000000000000 0000000000000000
|
|
||||||
SMM: enter
|
|
||||||
EAX=00000000 EBX=00000000 ECX=02000000 EDX=02000628
|
|
||||||
ESI=0000000b EDI=02000000 EBP=06feb0e0 ESP=00006d5c
|
|
||||||
EIP=000ebede EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
|
|
||||||
CS =0008 00000000 ffffffff 00cf9b00 DPL=0 CS32 [-RA]
|
|
||||||
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
|
|
||||||
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
|
|
||||||
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
|
|
||||||
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
|
|
||||||
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
|
|
||||||
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
|
|
||||||
GDT= 000f6220 00000037
|
|
||||||
IDT= 000f625e 00000000
|
|
||||||
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000044 CCD=ffffffff CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: after RSM
|
|
||||||
EAX=00000000 EBX=00000000 ECX=02000000 EDX=02000628
|
|
||||||
ESI=0000000b EDI=02000000 EBP=06feb0e0 ESP=00006d5c
|
|
||||||
EIP=000ebede EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
|
|
||||||
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
|
|
||||||
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
|
|
||||||
GDT= 000f6220 00000037
|
|
||||||
IDT= 000f625e 00000000
|
|
||||||
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000044 CCD=00000000 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: enter
|
|
||||||
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=00006cff
|
|
||||||
ESI=00006cb8 EDI=06ffee71 EBP=00006c78 ESP=00006c78
|
|
||||||
EIP=00007d2a EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =da80 000da800 ffffffff 008f9300
|
|
||||||
CS =f000 000f0000 ffffffff 008f9b00
|
|
||||||
SS =0000 00000000 ffffffff 008f9300
|
|
||||||
DS =0000 00000000 ffffffff 008f9300
|
|
||||||
FS =0000 00000000 ffffffff 008f9300
|
|
||||||
GS =0000 00000000 ffffffff 008f9300
|
|
||||||
LDT=0000 00000000 0000ffff 00008200
|
|
||||||
TR =0000 00000000 0000ffff 00008b00
|
|
||||||
GDT= 00000000 00000000
|
|
||||||
IDT= 00000000 000003ff
|
|
||||||
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000000 CCD=00006c78 CCO=ADDL
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: after RSM
|
|
||||||
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=00006cff
|
|
||||||
ESI=00006cb8 EDI=06ffee71 EBP=00006c78 ESP=00006c78
|
|
||||||
EIP=000f7d2d EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
|
|
||||||
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
|
|
||||||
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
|
|
||||||
GDT= 000f6220 00000037
|
|
||||||
IDT= 000f625e 00000000
|
|
||||||
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000044 CCD=00000000 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: enter
|
|
||||||
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=06fffe88
|
|
||||||
ESI=000e9780 EDI=06ffee71 EBP=00006c78 ESP=00006c78
|
|
||||||
EIP=000f7d44 EFL=00000012 [----A--] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
|
|
||||||
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
|
|
||||||
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
|
|
||||||
GDT= 000f6220 00000037
|
|
||||||
IDT= 000f625e 00000000
|
|
||||||
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000008 CCD=00006c64 CCO=ADDL
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: after RSM
|
|
||||||
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=06fffe88
|
|
||||||
ESI=000e9780 EDI=06ffee71 EBP=00006c78 ESP=00006c78
|
|
||||||
EIP=00007d47 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =da80 000da800 ffffffff 00809300
|
|
||||||
CS =f000 000f0000 ffffffff 00809b00
|
|
||||||
SS =0000 00000000 ffffffff 00809300
|
|
||||||
DS =0000 00000000 ffffffff 00809300
|
|
||||||
FS =0000 00000000 ffffffff 00809300
|
|
||||||
GS =0000 00000000 ffffffff 00809300
|
|
||||||
LDT=0000 00000000 0000ffff 00008200
|
|
||||||
TR =0000 00000000 0000ffff 00008b00
|
|
||||||
GDT= 00000000 00000000
|
|
||||||
IDT= 00000000 000003ff
|
|
||||||
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000004 CCD=00000001 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: enter
|
|
||||||
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
|
|
||||||
ESI=000069a2 EDI=06ffee71 EBP=00006962 ESP=00006962
|
|
||||||
EIP=00007d2a EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =da80 000da800 ffffffff 008f9300
|
|
||||||
CS =f000 000f0000 ffffffff 008f9b00
|
|
||||||
SS =0000 00000000 ffffffff 008f9300
|
|
||||||
DS =0000 00000000 ffffffff 008f9300
|
|
||||||
FS =0000 00000000 ffffffff 008f9300
|
|
||||||
GS =ca00 000ca000 ffffffff 008f9300
|
|
||||||
LDT=0000 00000000 0000ffff 00008200
|
|
||||||
TR =0000 00000000 0000ffff 00008b00
|
|
||||||
GDT= 00000000 00000000
|
|
||||||
IDT= 00000000 000003ff
|
|
||||||
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000000 CCD=00006962 CCO=ADDL
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: after RSM
|
|
||||||
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
|
|
||||||
ESI=000069a2 EDI=06ffee71 EBP=00006962 ESP=00006962
|
|
||||||
EIP=000f7d2d EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
|
|
||||||
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
|
|
||||||
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
|
|
||||||
GDT= 000f6220 00000037
|
|
||||||
IDT= 000f625e 00000000
|
|
||||||
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000044 CCD=00000000 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: enter
|
|
||||||
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000005
|
|
||||||
ESI=00000000 EDI=06ffee71 EBP=00006962 ESP=00006962
|
|
||||||
EIP=000f7d44 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
|
|
||||||
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
|
|
||||||
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
|
|
||||||
GDT= 000f6220 00000037
|
|
||||||
IDT= 000f625e 00000000
|
|
||||||
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000008 CCD=0000694e CCO=ADDL
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: after RSM
|
|
||||||
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000005
|
|
||||||
ESI=00000000 EDI=06ffee71 EBP=00006962 ESP=00006962
|
|
||||||
EIP=00007d47 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =da80 000da800 ffffffff 00809300
|
|
||||||
CS =f000 000f0000 ffffffff 00809b00
|
|
||||||
SS =0000 00000000 ffffffff 00809300
|
|
||||||
DS =0000 00000000 ffffffff 00809300
|
|
||||||
FS =0000 00000000 ffffffff 00809300
|
|
||||||
GS =ca00 000ca000 ffffffff 00809300
|
|
||||||
LDT=0000 00000000 0000ffff 00008200
|
|
||||||
TR =0000 00000000 0000ffff 00008b00
|
|
||||||
GDT= 00000000 00000000
|
|
||||||
IDT= 00000000 000003ff
|
|
||||||
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000000 CCD=00000001 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: enter
|
|
||||||
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
|
|
||||||
ESI=0000699c EDI=06ffee71 EBP=0000695c ESP=0000695c
|
|
||||||
EIP=00007d2a EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =da80 000da800 ffffffff 00809300
|
|
||||||
CS =f000 000f0000 ffffffff 00809b00
|
|
||||||
SS =0000 00000000 ffffffff 00809300
|
|
||||||
DS =0000 00000000 ffffffff 00809300
|
|
||||||
FS =0000 00000000 ffffffff 00809300
|
|
||||||
GS =ca00 000ca000 ffffffff 00809300
|
|
||||||
LDT=0000 00000000 0000ffff 00008200
|
|
||||||
TR =0000 00000000 0000ffff 00008b00
|
|
||||||
GDT= 00000000 00000000
|
|
||||||
IDT= 00000000 000003ff
|
|
||||||
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000000 CCD=0000695c CCO=ADDL
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: after RSM
|
|
||||||
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
|
|
||||||
ESI=0000699c EDI=06ffee71 EBP=0000695c ESP=0000695c
|
|
||||||
EIP=000f7d2d EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
|
|
||||||
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
|
|
||||||
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
|
|
||||||
GDT= 000f6220 00000037
|
|
||||||
IDT= 000f625e 00000000
|
|
||||||
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000044 CCD=00000000 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: enter
|
|
||||||
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000003
|
|
||||||
ESI=06fd2080 EDI=06ffee71 EBP=0000695c ESP=0000695c
|
|
||||||
EIP=000f7d44 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
|
|
||||||
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
|
|
||||||
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
|
|
||||||
GDT= 000f6220 00000037
|
|
||||||
IDT= 000f625e 00000000
|
|
||||||
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000008 CCD=00006948 CCO=ADDL
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: after RSM
|
|
||||||
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000003
|
|
||||||
ESI=06fd2080 EDI=06ffee71 EBP=0000695c ESP=0000695c
|
|
||||||
EIP=00007d47 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =da80 000da800 ffffffff 00809300
|
|
||||||
CS =f000 000f0000 ffffffff 00809b00
|
|
||||||
SS =0000 00000000 ffffffff 00809300
|
|
||||||
DS =0000 00000000 ffffffff 00809300
|
|
||||||
FS =0000 00000000 ffffffff 00809300
|
|
||||||
GS =ca00 000ca000 ffffffff 00809300
|
|
||||||
LDT=0000 00000000 0000ffff 00008200
|
|
||||||
TR =0000 00000000 0000ffff 00008b00
|
|
||||||
GDT= 00000000 00000000
|
|
||||||
IDT= 00000000 000003ff
|
|
||||||
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000004 CCD=00000001 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: enter
|
|
||||||
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
|
|
||||||
ESI=000069a2 EDI=06ffee71 EBP=00006962 ESP=00006962
|
|
||||||
EIP=00007d2a EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =da80 000da800 ffffffff 00809300
|
|
||||||
CS =f000 000f0000 ffffffff 00809b00
|
|
||||||
SS =0000 00000000 ffffffff 00809300
|
|
||||||
DS =0000 00000000 ffffffff 00809300
|
|
||||||
FS =0000 00000000 ffffffff 00809300
|
|
||||||
GS =ca00 000ca000 ffffffff 00809300
|
|
||||||
LDT=0000 00000000 0000ffff 00008200
|
|
||||||
TR =0000 00000000 0000ffff 00008b00
|
|
||||||
GDT= 00000000 00000000
|
|
||||||
IDT= 00000000 000003ff
|
|
||||||
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000000 CCD=00006962 CCO=ADDL
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: after RSM
|
|
||||||
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
|
|
||||||
ESI=000069a2 EDI=06ffee71 EBP=00006962 ESP=00006962
|
|
||||||
EIP=000f7d2d EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
|
|
||||||
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
|
|
||||||
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
|
|
||||||
GDT= 000f6220 00000037
|
|
||||||
IDT= 000f625e 00000000
|
|
||||||
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000044 CCD=00000000 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: enter
|
|
||||||
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000005
|
|
||||||
ESI=00000000 EDI=06ffee71 EBP=00006962 ESP=00006962
|
|
||||||
EIP=000f7d44 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
|
|
||||||
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
|
|
||||||
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
|
|
||||||
GDT= 000f6220 00000037
|
|
||||||
IDT= 000f625e 00000000
|
|
||||||
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000008 CCD=0000694e CCO=ADDL
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: after RSM
|
|
||||||
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000005
|
|
||||||
ESI=00000000 EDI=06ffee71 EBP=00006962 ESP=00006962
|
|
||||||
EIP=00007d47 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =da80 000da800 ffffffff 00809300
|
|
||||||
CS =f000 000f0000 ffffffff 00809b00
|
|
||||||
SS =0000 00000000 ffffffff 00809300
|
|
||||||
DS =0000 00000000 ffffffff 00809300
|
|
||||||
FS =0000 00000000 ffffffff 00809300
|
|
||||||
GS =ca00 000ca000 ffffffff 00809300
|
|
||||||
LDT=0000 00000000 0000ffff 00008200
|
|
||||||
TR =0000 00000000 0000ffff 00008b00
|
|
||||||
GDT= 00000000 00000000
|
|
||||||
IDT= 00000000 000003ff
|
|
||||||
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000000 CCD=00000001 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: enter
|
|
||||||
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
|
|
||||||
ESI=0000699c EDI=06ffee71 EBP=0000695c ESP=0000695c
|
|
||||||
EIP=00007d2a EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =da80 000da800 ffffffff 00809300
|
|
||||||
CS =f000 000f0000 ffffffff 00809b00
|
|
||||||
SS =0000 00000000 ffffffff 00809300
|
|
||||||
DS =0000 00000000 ffffffff 00809300
|
|
||||||
FS =0000 00000000 ffffffff 00809300
|
|
||||||
GS =ca00 000ca000 ffffffff 00809300
|
|
||||||
LDT=0000 00000000 0000ffff 00008200
|
|
||||||
TR =0000 00000000 0000ffff 00008b00
|
|
||||||
GDT= 00000000 00000000
|
|
||||||
IDT= 00000000 000003ff
|
|
||||||
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000000 CCD=0000695c CCO=ADDL
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: after RSM
|
|
||||||
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
|
|
||||||
ESI=0000699c EDI=06ffee71 EBP=0000695c ESP=0000695c
|
|
||||||
EIP=000f7d2d EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
|
|
||||||
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
|
|
||||||
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
|
|
||||||
GDT= 000f6220 00000037
|
|
||||||
IDT= 000f625e 00000000
|
|
||||||
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000044 CCD=00000000 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: enter
|
|
||||||
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000003
|
|
||||||
ESI=06f32080 EDI=06ffee71 EBP=0000695c ESP=0000695c
|
|
||||||
EIP=000f7d44 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
|
|
||||||
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
|
|
||||||
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
|
|
||||||
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
|
|
||||||
GDT= 000f6220 00000037
|
|
||||||
IDT= 000f625e 00000000
|
|
||||||
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000008 CCD=00006948 CCO=ADDL
|
|
||||||
EFER=0000000000000000
|
|
||||||
SMM: after RSM
|
|
||||||
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000003
|
|
||||||
ESI=06f32080 EDI=06ffee71 EBP=0000695c ESP=0000695c
|
|
||||||
EIP=00007d47 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
|
|
||||||
ES =da80 000da800 ffffffff 00809300
|
|
||||||
CS =f000 000f0000 ffffffff 00809b00
|
|
||||||
SS =0000 00000000 ffffffff 00809300
|
|
||||||
DS =0000 00000000 ffffffff 00809300
|
|
||||||
FS =0000 00000000 ffffffff 00809300
|
|
||||||
GS =ca00 000ca000 ffffffff 00809300
|
|
||||||
LDT=0000 00000000 0000ffff 00008200
|
|
||||||
TR =0000 00000000 0000ffff 00008b00
|
|
||||||
GDT= 00000000 00000000
|
|
||||||
IDT= 00000000 000003ff
|
|
||||||
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
|
|
||||||
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
|
|
||||||
DR6=00000000ffff0ff0 DR7=0000000000000400
|
|
||||||
CCS=00000004 CCD=00000001 CCO=EFLAGS
|
|
||||||
EFER=0000000000000000
|
|
||||||
Servicing hardware INT=0x08
|
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
{"rustc_fingerprint":17979571215740175684,"outputs":{"6326266571342547659":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.a\nC:\\Users\\yulia\\.rustup\\toolchains\\nightly-x86_64-pc-windows-msvc\noff\n___\ndebug_assertions\nemscripten_wasm_eh\nfmt_debug=\"full\"\noverflow_checks\npanic=\"abort\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"x87\"\ntarget_has_atomic\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_has_atomic_primitive_alignment=\"16\"\ntarget_has_atomic_primitive_alignment=\"32\"\ntarget_has_atomic_primitive_alignment=\"64\"\ntarget_has_atomic_primitive_alignment=\"8\"\ntarget_has_atomic_primitive_alignment=\"ptr\"\ntarget_has_reliable_f128\ntarget_has_reliable_f16\ntarget_has_reliable_f16_math\ntarget_object_format=\"elf\"\ntarget_os=\"none\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nub_checks\n","stderr":"warning: dropping unsupported crate type `dylib` for target `x86_64-unknown-none`\n\nwarning: dropping unsupported crate type `cdylib` for target `x86_64-unknown-none`\n\nwarning: dropping unsupported crate type `proc-macro` for target `x86_64-unknown-none`\n\nwarning: 3 warnings emitted\n\n"},"14280398215498370689":{"success":true,"status":"","code":0,"stdout":"rustc 1.98.0-nightly (d595fce01 2026-06-02)\nbinary: rustc\ncommit-hash: d595fce01043347bf7f80e85b76dcc41b59a3e6e\ncommit-date: 2026-06-02\nhost: x86_64-pc-windows-msvc\nrelease: 1.98.0-nightly\nLLVM version: 22.1.6\n","stderr":""},"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\yulia\\.rustup\\toolchains\\nightly-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\nemscripten_wasm_eh\nfmt_debug=\"full\"\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"lahfsahf\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_feature=\"x87\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_has_atomic_primitive_alignment=\"128\"\ntarget_has_atomic_primitive_alignment=\"16\"\ntarget_has_atomic_primitive_alignment=\"32\"\ntarget_has_atomic_primitive_alignment=\"64\"\ntarget_has_atomic_primitive_alignment=\"8\"\ntarget_has_atomic_primitive_alignment=\"ptr\"\ntarget_has_reliable_f128\ntarget_has_reliable_f16\ntarget_has_reliable_f16_math\ntarget_object_format=\"coff\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"pc\"\nub_checks\nwindows\n","stderr":""}},"successes":{}}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Signature: 8a477f597d28d172789f06886806bc55
|
||||||
|
# This file is a cache directory tag created by cargo.
|
||||||
|
# For information about cache directory tags see https://bford.info/cachedir/
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
39fb8435f9a9b572
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[\"default\", \"std\"]","declared_features":"[\"backtrace\", \"default\", \"std\"]","target":1563897884725121975,"profile":10185441994092410962,"path":3040454749933930215,"deps":[[12478428894219133322,"build_script_build",false,3228896219581358326]],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\anyhow-448746f4dc7c73d4\\dep-lib-anyhow","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2ebcf139b2f09e16
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[\"default\", \"std\"]","declared_features":"[\"backtrace\", \"default\", \"std\"]","target":5408242616063297496,"profile":2225463790103693989,"path":10096794491780688900,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\anyhow-c2b2bbc82b37717e\\dep-build-script-build-script-build","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
f6a0bf180158cf2c
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"","declared_features":"","target":0,"profile":0,"path":0,"deps":[[12478428894219133322,"build_script_build",false,1630004763421686830]],"local":[{"RerunIfChanged":{"output":"debug\\build\\anyhow-e82d7206950644df\\output","paths":["src/nightly.rs"]}}],"rustflags":[],"config":0,"compile_kind":0}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
caabf8fc3e29520f
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[]","declared_features":"[\"i128\"]","target":9517688912158169860,"profile":10185441994092410962,"path":11908128863078194284,"deps":[[13548984313718623784,"serde",false,11886077514470579359]],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\bincode-a4a657ebac9f6166\\dep-lib-bincode","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
8c77d7bfa96418b0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[]","declared_features":"[\"arbitrary\", \"bytemuck\", \"example_generated\", \"serde\", \"serde_core\", \"std\"]","target":7691312148208718491,"profile":10185441994092410962,"path":10140360757900709517,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\bitflags-5ef5139c408a1282\\dep-lib-bitflags","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
40a754790151619d
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[\"default\"]","declared_features":"[\"compiler_builtins\", \"core\", \"default\", \"example_generated\", \"rustc-dep-of-std\"]","target":12919857562465245259,"profile":10185441994092410962,"path":11617663436478920972,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\bitflags-91402f742522ba1e\\dep-lib-bitflags","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
09f0ff598612832f
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[\"alloc\", \"atomic\", \"default\", \"std\"]","declared_features":"[\"alloc\", \"atomic\", \"default\", \"serde\", \"std\", \"testing\"]","target":15523958261975496690,"profile":10185441994092410962,"path":12550972486038902362,"deps":[[2901717918821536064,"funty",false,13330536037794653500],[4989309779925288624,"tap",false,4741840433733133465],[7533601061668075701,"wyz",false,10262504819920652965],[13404482562374806937,"radium",false,17059808365545106514]],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\bitvec-ec8786aa5ab748a8\\dep-lib-bitvec","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
c19ef9ca046c6bf7
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[\"bios\", \"default\", \"uefi\"]","declared_features":"[\"bios\", \"default\", \"uefi\"]","target":2835126046236718539,"profile":2225463790103693989,"path":16743340526586326593,"deps":[[16120145906303750382,"llvm_tools",false,4040276181147726773]],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\bootloader-4411ad2090d07266\\dep-build-script-build-script-build","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
e7afef7470648aae
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"","declared_features":"","target":0,"profile":0,"path":0,"deps":[[14060995142846132541,"build_script_build",false,17828462317717266113]],"local":[{"Precalculated":"0.11.15"}],"rustflags":[],"config":0,"compile_kind":0}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
20cc18ce7b1d03f9
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[\"bios\", \"default\", \"uefi\"]","declared_features":"[\"bios\", \"default\", \"uefi\"]","target":13195395103163152136,"profile":10185441994092410962,"path":12785248296520697740,"deps":[[779114543244679829,"mbrman",false,15283427482311718655],[995984767121273484,"gpt",false,16628868090897652735],[8578586876803397814,"serde_json",false,12025338656884309018],[8733079735558824486,"fatfs",false,14019061167740385005],[9723370144619655183,"tempfile",false,13697350754417250572],[11826740708254998817,"bootloader_boot_config",false,14607083958791435368],[12478428894219133322,"anyhow",false,8265699578918271801],[14060995142846132541,"build_script_build",false,12576975343546511335]],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\bootloader-b8760cfeb20857a1\\dep-lib-bootloader","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
68f4110878c9b6ca
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[]","declared_features":"[]","target":13987071512657762218,"profile":10185441994092410962,"path":13281582299073270774,"deps":[[13548984313718623784,"serde",false,11886077514470579359]],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\bootloader-boot-config-32d4cac68f794b1f\\dep-lib-bootloader_boot_config","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
d31ecd1c2a822d06
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[]","declared_features":"[]","target":2835126046236718539,"profile":15657897354478470176,"path":13438689814957725733,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\bootloader_api-657bcc0bdcbe5657\\dep-build-script-build-script-build","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
6c32a059d14b6844
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[]","declared_features":"[]","target":11134935318254218515,"profile":8557956707643402571,"path":95470513025842401,"deps":[[14060995142846132541,"bootloader",false,17943217757950102560]],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\builder-c0163acb54143a56\\dep-bin-builder","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
85cabb71e478b14b
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[\"std\"]","declared_features":"[\"default\", \"i128\", \"std\"]","target":8344828840634961491,"profile":10185441994092410962,"path":16259150052650528991,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\byteorder-7183a407b43f50d6\\dep-lib-byteorder","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3170cb57b1d062c9
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[]","declared_features":"[\"core\", \"rustc-dep-of-std\"]","target":13840298032947503755,"profile":10185441994092410962,"path":11859193525878169033,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\cfg-if-4d66968427371f75\\dep-lib-cfg_if","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
6e8f15a47126dd61
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[\"arch\", \"compiler-builtins\", \"default\", \"mem\", \"unmangled-names\"]","declared_features":"[\"arch\", \"c\", \"compiler-builtins\", \"default\", \"mem\", \"unmangled-names\", \"unstable-public-internals\"]","target":2835126046236718539,"profile":15657897354478470176,"path":12334206888044322955,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\compiler_builtins-2a9f8a72341263f3\\dep-build-script-build-script-build","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
a91ba55d45d45ce2
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[]","declared_features":"[]","target":4924338683985979974,"profile":10185441994092410962,"path":425270840798977162,"deps":[[17276112982712585484,"crc_catalog",false,5878752341955317199]],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\crc-4fc2f03a6f19626d\\dep-lib-crc","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
cf0dbea81b869551
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[]","declared_features":"[]","target":11450272957467397601,"profile":10185441994092410962,"path":6273559363555537734,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\crc-catalog-ead5addf74421a58\\dep-lib-crc_catalog","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
203f58f1d862d3a9
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[\"alloc\", \"default\", \"std\"]","declared_features":"[\"alloc\", \"default\", \"getrandom\", \"js\", \"std\"]","target":9543367341069791401,"profile":10185441994092410962,"path":7004734588812112552,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\fastrand-857fb03e19f06e05\\dep-lib-fastrand","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ed76a31bdbb58dc2
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"rustc":5817494548283694837,"features":"[\"alloc\", \"std\"]","declared_features":"[\"alloc\", \"chrono\", \"core_io\", \"default\", \"std\"]","target":15638540431404190697,"profile":10185441994092410962,"path":8908618958527765328,"deps":[[3712811570531045576,"byteorder",false,5454273546278390405],[10435729446543529114,"bitflags",false,11340434403468027712],[10554110433548904600,"log",false,16565071179598802380]],"local":[{"CheckDepInfo":{"dep_info":"debug\\.fingerprint\\fatfs-b99c85da4957f6e0\\dep-lib-fatfs","checksum":false}}],"rustflags":[],"config":9396254390672932401,"compile_kind":0}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This file has an mtime of when this was started.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3c254beee593ffb8
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user