summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-01 21:24:48 +0100
committersalmonik <salmonik@tuta.io>2026-11-01 21:24:48 +0100
commita5ea79739783682c46983d5b67b2b4b7b5338ccb (patch)
treed7e06cacfffdb173e8b27bcca426d6bbdd152ee3
parentv4.2.0: wire the GUI to the core, transport and all four games (diff)
downloadzagan-bet-a5ea79739783682c46983d5b67b2b4b7b5338ccb.tar.gz
zagan-bet-a5ea79739783682c46983d5b67b2b4b7b5338ccb.zip
v4.2.1: plain-language game copy and a balance fetch on login
The lobby and every game screen now speak to a player, not an operator: each game has one how-to sentence, and the table headers, limits, RTP, paytable and server-timing notes are gone. The account and balance are fetched whenever a session signs in, including stored-credential auto-login, so the header is filled before the lobby appears.
-rw-r--r--Cargo.toml2
-rw-r--r--docs/GUI-PLAN.md7
-rw-r--r--docs/TESTING.md5
-rw-r--r--docs/screenshots/gui-blackjack-1080p.pngbin60075 -> 47190 bytes
-rw-r--r--docs/screenshots/gui-crash-1080p.pngbin64377 -> 44892 bytes
-rw-r--r--docs/screenshots/gui-lobby-1080p.pngbin64944 -> 67881 bytes
-rw-r--r--docs/screenshots/gui-roulette-1080p.pngbin118143 -> 105976 bytes
-rw-r--r--docs/screenshots/gui-roulette-result-1080p.pngbin113253 -> 109944 bytes
-rw-r--r--docs/screenshots/gui-slots-1080p.pngbin78173 -> 56798 bytes
-rw-r--r--gui/Cargo.toml2
-rw-r--r--gui/src/app.rs79
-rw-r--r--gui/src/blackjack.rs36
-rw-r--r--gui/src/crash.rs47
-rw-r--r--gui/src/net.rs2
-rw-r--r--gui/src/roulette.rs38
-rw-r--r--gui/src/slots.rs56
16 files changed, 51 insertions, 223 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 23188da..179cb39 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,7 @@ members = ["gui"]
3 3
4[package] 4[package]
5name = "zagan-bet" 5name = "zagan-bet"
6version = "4.2.0" 6version = "4.2.1"
7edition = "2024" 7edition = "2024"
8license = "GPL-3.0-or-later" 8license = "GPL-3.0-or-later"
9 9
diff --git a/docs/GUI-PLAN.md b/docs/GUI-PLAN.md
index 4279626..89cf495 100644
--- a/docs/GUI-PLAN.md
+++ b/docs/GUI-PLAN.md
@@ -130,6 +130,9 @@ they show the same layout with the real session and the pinned action bars.
130- Sidebars pin their action block below the content, so SPIN, CLEAR, DEAL and 130- Sidebars pin their action block below the content, so SPIN, CLEAR, DEAL and
131 START never move as the bet slip or a hand grows. The embedded fonts have no 131 START never move as the bet slip or a hand grows. The embedded fonts have no
132 suit or arrow glyphs, so suits are shapes and navigation uses plain words. 132 suit or arrow glyphs, so suits are shapes and navigation uses plain words.
133- The lobby and the game screens speak plain language: one how-to sentence per
134 game, no RTP, table limits, deck rules or server-timing jargon. The account
135 and balance are fetched as soon as a session signs in.
133- The header balance is held at the staked amount while the reels or wheel 136- The header balance is held at the staked amount while the reels or wheel
134 turn, then jumps to the settled value: the GUI never reveals a payout before 137 turn, then jumps to the settled value: the GUI never reveals a payout before
135 the animation does. Refused spins cancel the animation and show the error. 138 the animation does. Refused spins cancel the animation and show the error.
@@ -152,8 +155,8 @@ Measured on Linux at 1080p with llvmpipe software GL:
152| --- | --- | 155| --- | --- |
153| Linux binary | 8.3 MB | 156| Linux binary | 8.3 MB |
154| Windows x86_64 binary | 6.8 MB | 157| Windows x86_64 binary | 6.8 MB |
155| Cold start to window | 61 ms | 158| Cold start to window | 72 ms |
156| Idle RSS | 131 MB | 159| Idle RSS | 130 MB |
157| Idle CPU | 0% | 160| Idle CPU | 0% |
158 161
159**Gate:** the screenshot set is ready for review. Remaining for the gate: 162**Gate:** the screenshot set is ready for review. Remaining for the gate:
diff --git a/docs/TESTING.md b/docs/TESTING.md
index 817cb3d..613cd08 100644
--- a/docs/TESTING.md
+++ b/docs/TESTING.md
@@ -126,8 +126,9 @@ terminal client; a saved login from one signs in the other.
1262. First contact: the fingerprint modal appears before any credentials. Cancel 1262. First contact: the fingerprint modal appears before any credentials. Cancel
127 leaves the connect screen; Trust continues to the login screen. 127 leaves the connect screen; Trust continues to the login screen.
1283. Register, then log in with "Remember this login". The lobby shows the host, 1283. Register, then log in with "Remember this login". The lobby shows the host,
129 ping, account, balance and the four games; quit and reconnect: the stored 129 ping, account, balance and the four games; the balance fills in immediately
130 login signs in without asking. 130 without entering a game. Quit and reconnect: the stored login signs in
131 without asking.
1314. Each game: place a bet or deal, play a round, and confirm the balance and the 1324. Each game: place a bet or deal, play a round, and confirm the balance and the
132 header update. Slots shows reels and win lines; roulette spins the wheel and 133 header update. Slots shows reels and win lines; roulette spins the wheel and
133 reports the payout; blackjack draws suits and settles; crash draws a smooth 134 reports the payout; blackjack draws suits and settles; crash draws a smooth
diff --git a/docs/screenshots/gui-blackjack-1080p.png b/docs/screenshots/gui-blackjack-1080p.png
index b0fcc19..361f987 100644
--- a/docs/screenshots/gui-blackjack-1080p.png
+++ b/docs/screenshots/gui-blackjack-1080p.png
Binary files differ
diff --git a/docs/screenshots/gui-crash-1080p.png b/docs/screenshots/gui-crash-1080p.png
index 245a3a1..da72f1f 100644
--- a/docs/screenshots/gui-crash-1080p.png
+++ b/docs/screenshots/gui-crash-1080p.png
Binary files differ
diff --git a/docs/screenshots/gui-lobby-1080p.png b/docs/screenshots/gui-lobby-1080p.png
index a258967..8bfe8c8 100644
--- a/docs/screenshots/gui-lobby-1080p.png
+++ b/docs/screenshots/gui-lobby-1080p.png
Binary files differ
diff --git a/docs/screenshots/gui-roulette-1080p.png b/docs/screenshots/gui-roulette-1080p.png
index 1d7af3d..703ea57 100644
--- a/docs/screenshots/gui-roulette-1080p.png
+++ b/docs/screenshots/gui-roulette-1080p.png
Binary files differ
diff --git a/docs/screenshots/gui-roulette-result-1080p.png b/docs/screenshots/gui-roulette-result-1080p.png
index fe4c9cd..c1ffa63 100644
--- a/docs/screenshots/gui-roulette-result-1080p.png
+++ b/docs/screenshots/gui-roulette-result-1080p.png
Binary files differ
diff --git a/docs/screenshots/gui-slots-1080p.png b/docs/screenshots/gui-slots-1080p.png
index 9d79321..0982114 100644
--- a/docs/screenshots/gui-slots-1080p.png
+++ b/docs/screenshots/gui-slots-1080p.png
Binary files differ
diff --git a/gui/Cargo.toml b/gui/Cargo.toml
index d3d9c36..5418794 100644
--- a/gui/Cargo.toml
+++ b/gui/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "zagan-bet-gui" 2name = "zagan-bet-gui"
3version = "4.2.0" 3version = "4.2.1"
4edition = "2024" 4edition = "2024"
5license = "GPL-3.0-or-later" 5license = "GPL-3.0-or-later"
6description = "Desktop frontend for zagan-bet" 6description = "Desktop frontend for zagan-bet"
diff --git a/gui/src/app.rs b/gui/src/app.rs
index f0efd3e..af64e5e 100644
--- a/gui/src/app.rs
+++ b/gui/src/app.rs
@@ -49,6 +49,7 @@ pub struct ZaganApp {
49 notices: VecDeque<Notice>, 49 notices: VecDeque<Notice>,
50 awaiting: Option<Instant>, 50 awaiting: Option<Instant>,
51 need_info: bool, 51 need_info: bool,
52 was_logged_in: bool,
52 pending_login: Option<(String, String)>, 53 pending_login: Option<(String, String)>,
53 /// Displayed balance while a result animation runs: stake deducted, no 54 /// Displayed balance while a result animation runs: stake deducted, no
54 /// winnings yet. The view already knows the outcome; the player must not. 55 /// winnings yet. The view already knows the outcome; the player must not.
@@ -62,7 +63,6 @@ struct Session {
62 host: String, 63 host: String,
63 fingerprint: Option<String>, 64 fingerprint: Option<String>,
64 ping_ms: f32, 65 ping_ms: f32,
65 server_version: String,
66 latest: String, 66 latest: String,
67 url: String, 67 url: String,
68 user: Option<String>, 68 user: Option<String>,
@@ -186,6 +186,7 @@ impl ZaganApp {
186 notices: VecDeque::new(), 186 notices: VecDeque::new(),
187 awaiting: None, 187 awaiting: None,
188 need_info: false, 188 need_info: false,
189 was_logged_in: false,
189 pending_login: None, 190 pending_login: None,
190 balance_hold: None, 191 balance_hold: None,
191 balance: None, 192 balance: None,
@@ -348,7 +349,7 @@ impl ZaganApp {
348 reply, 349 reply,
349 }); 350 });
350 } 351 }
351 Event::Connected(connected) => self.connected(ctx, connected), 352 Event::Connected(connected) => self.connected(connected),
352 Event::Failed { 353 Event::Failed {
353 addr, 354 addr,
354 failure, 355 failure,
@@ -376,7 +377,7 @@ impl ZaganApp {
376 } 377 }
377 } 378 }
378 379
379 fn connected(&mut self, ctx: &Context, connected: Connected) { 380 fn connected(&mut self, connected: Connected) {
380 self.connect.pending = false; 381 self.connect.pending = false;
381 self.connect.error = None; 382 self.connect.error = None;
382 self.core.set_connected(true); 383 self.core.set_connected(true);
@@ -393,19 +394,14 @@ impl ZaganApp {
393 host: connected.addr, 394 host: connected.addr,
394 fingerprint: connected.fingerprint, 395 fingerprint: connected.fingerprint,
395 ping_ms: connected.elapsed_ms, 396 ping_ms: connected.elapsed_ms,
396 server_version: connected.server_version,
397 latest: connected.latest, 397 latest: connected.latest,
398 url: connected.url, 398 url: connected.url,
399 user: connected.user, 399 user: connected.user,
400 }); 400 });
401 401
402 self.need_info = self.session.as_ref().is_some_and(|s| s.user.is_some()); 402 self.need_info = false;
403 self.was_logged_in = false;
403 self.account = None; 404 self.account = None;
404 if self.need_info {
405 let effects = self.core.command(Command::Info);
406 self.apply(ctx, effects);
407 self.need_info = false;
408 }
409 } 405 }
410 406
411 fn failed(&mut self, addr: String, failure: Failure, message: String) { 407 fn failed(&mut self, addr: String, failure: Failure, message: String) {
@@ -511,7 +507,6 @@ impl ZaganApp {
511 let command = if registering { 507 let command = if registering {
512 Command::Register { user, credential } 508 Command::Register { user, credential }
513 } else { 509 } else {
514 self.need_info = true;
515 if self.login.remember { 510 if self.login.remember {
516 self.pending_login = Some((user.clone(), credential.clone())); 511 self.pending_login = Some((user.clone(), credential.clone()));
517 } 512 }
@@ -522,19 +517,26 @@ impl ZaganApp {
522 } 517 }
523 518
524 fn tick(&mut self, ctx: &Context) { 519 fn tick(&mut self, ctx: &Context) {
525 // Request deadline, mirroring the terminal's socket timeout. 520 // Fetch the account and balance once each time a session signs in.
526 if self.need_info 521 let signed_in = self
527 && self 522 .core
528 .session 523 .view()
529 .as_ref() 524 .as_ref()
530 .is_some_and(|session| session.user.is_some()) 525 .is_some_and(|view| matches!(view, View::Lobby(lobby) if lobby.user.is_some()));
531 && self.account.is_none() 526 if signed_in != self.was_logged_in {
532 { 527 self.was_logged_in = signed_in;
528 if signed_in {
529 self.need_info = true;
530 }
531 }
532 if self.need_info && signed_in {
533 let effects = self.core.command(Command::Info); 533 let effects = self.core.command(Command::Info);
534 self.apply(ctx, effects); 534 self.apply(ctx, effects);
535 self.need_info = false; 535 self.need_info = false;
536 } 536 }
537 537
538 // Request deadline, mirroring the terminal's socket timeout.
539
538 if let Some(sent) = self.awaiting { 540 if let Some(sent) = self.awaiting {
539 if sent.elapsed() > REQUEST_TIMEOUT { 541 if sent.elapsed() > REQUEST_TIMEOUT {
540 self.awaiting = None; 542 self.awaiting = None;
@@ -1351,13 +1353,7 @@ fn lobby_screen(ui: &mut Ui, app: &mut ZaganApp, lobby: &LobbyView) {
1351 ui.add_space(theme::SPACE_2); 1353 ui.add_space(theme::SPACE_2);
1352 ui.horizontal(|ui| { 1354 ui.horizontal(|ui| {
1353 if let Some(session) = &app.session { 1355 if let Some(session) = &app.session {
1354 widgets::note( 1356 widgets::note(ui, &format!("Signed in to {}", session.host));
1355 ui,
1356 &format!(
1357 "Signed in to {} · server v{}",
1358 session.host, session.server_version
1359 ),
1360 );
1361 } 1357 }
1362 ui.with_layout(Layout::right_to_left(Align::Center), |ui| { 1358 ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
1363 if widgets::ghost(ui, "Forget saved login") { 1359 if widgets::ghost(ui, "Forget saved login") {
@@ -1396,15 +1392,9 @@ fn game_card(ui: &mut Ui, app: &mut ZaganApp, key: &str, name: &str) {
1396 .font(theme::sans(22.0)) 1392 .font(theme::sans(22.0))
1397 .color(theme::TEXT), 1393 .color(theme::TEXT),
1398 ); 1394 );
1399 ui.add_space(2.0);
1400 widgets::note(ui, tagline(key));
1401 ui.add_space(theme::SPACE_1); 1395 ui.add_space(theme::SPACE_1);
1402 ui.label( 1396 widgets::note(ui, how_to(key));
1403 RichText::new(meta(key)) 1397 ui.add_space(theme::SPACE_4);
1404 .font(theme::mono(10.0))
1405 .color(theme::TEXT_FAINT),
1406 );
1407 ui.add_space(theme::SPACE_3);
1408 if widgets::primary(ui, "PLAY", true, 110.0, 36.0) { 1398 if widgets::primary(ui, "PLAY", true, 110.0, 36.0) {
1409 let effects = app.core.command(Command::Play { 1399 let effects = app.core.command(Command::Play {
1410 game: key.to_string(), 1400 game: key.to_string(),
@@ -1415,22 +1405,13 @@ fn game_card(ui: &mut Ui, app: &mut ZaganApp, key: &str, name: &str) {
1415 }); 1405 });
1416} 1406}
1417 1407
1418fn tagline(game: &str) -> &'static str { 1408/// One plain sentence: how to play, not the house's rules sheet.
1419 match game { 1409fn how_to(game: &str) -> &'static str {
1420 "slots" => "Five reels, five paylines, one lever.",
1421 "roulette" => "The European wheel, thirty-seven pockets.",
1422 "blackjack" => "Six decks, dealer stands on seventeen. Pays 3:2.",
1423 "crash" => "The multiplier rises. Cash out before it busts.",
1424 _ => "",
1425 }
1426}
1427
1428fn meta(game: &str) -> &'static str {
1429 match game { 1410 match game {
1430 "slots" => "MIN 10 KR · RTP 96%", 1411 "slots" => "Spin the reels. Three or more matching symbols from the left win.",
1431 "roulette" => "MIN 10 KR · SINGLE ZERO", 1412 "roulette" => "Put chips on the table, then spin. Straight numbers pay the most.",
1432 "blackjack" => "MIN 10 KR · S17 · 3:2", 1413 "blackjack" => "Get closer to 21 than the dealer without going over.",
1433 "crash" => "MIN 10 KR · 1% EDGE", 1414 "crash" => "Start the round, then cash out before the multiplier crashes.",
1434 _ => "", 1415 _ => "",
1435 } 1416 }
1436} 1417}
diff --git a/gui/src/blackjack.rs b/gui/src/blackjack.rs
index d42f000..bbe96fa 100644
--- a/gui/src/blackjack.rs
+++ b/gui/src/blackjack.rs
@@ -70,11 +70,7 @@ fn sidebar(ui: &mut Ui, view: &BlackjackView) -> Option<Command> {
70 .font(theme::sans(22.0)) 70 .font(theme::sans(22.0))
71 .color(theme::TEXT), 71 .color(theme::TEXT),
72 ); 72 );
73 ui.label( 73 widgets::note(ui, "Get close to 21 without going over.");
74 eframe::egui::RichText::new("6 decks · dealer stands on 17 · 3:2")
75 .font(theme::sans(12.0))
76 .color(theme::TEXT_DIM),
77 );
78 74
79 ui.add_space(theme::SPACE_5); 75 ui.add_space(theme::SPACE_5);
80 ui.label(theme::label("Bet")); 76 ui.label(theme::label("Bet"));
@@ -104,12 +100,6 @@ fn sidebar(ui: &mut Ui, view: &BlackjackView) -> Option<Command> {
104 ui.add_space(theme::SPACE_3); 100 ui.add_space(theme::SPACE_3);
105 widgets::error(ui, error); 101 widgets::error(ui, error);
106 } 102 }
107
108 ui.add_space(theme::SPACE_4);
109 ui.separator();
110 ui.add_space(theme::SPACE_2);
111 widgets::note(ui, "Blackjack pays 3:2. The dealer stands on soft 17.");
112 widgets::note(ui, "Double only on the first two cards.");
113 }); 103 });
114 }, 104 },
115 ); 105 );
@@ -189,29 +179,7 @@ fn result_text(result: &str, winnings: u64) -> (String, Color32) {
189} 179}
190 180
191fn board(ui: &mut Ui, view: &BlackjackView) { 181fn board(ui: &mut Ui, view: &BlackjackView) {
192 ui.horizontal(|ui| { 182 ui.add_space(theme::SPACE_2);
193 ui.label(
194 eframe::egui::RichText::new("Table 01")
195 .font(theme::sans(15.0))
196 .color(theme::TEXT),
197 );
198 ui.label(
199 eframe::egui::RichText::new("Blackjack · six decks")
200 .font(theme::sans(12.0))
201 .color(theme::TEXT_DIM),
202 );
203 ui.with_layout(
204 eframe::egui::Layout::right_to_left(eframe::egui::Align::Center),
205 |ui| {
206 ui.label(
207 eframe::egui::RichText::new("3:2 · S17")
208 .font(theme::mono(12.0))
209 .color(theme::TEXT_FAINT),
210 );
211 },
212 );
213 });
214 ui.add_space(theme::SPACE_5);
215 183
216 let Some(hand) = &view.hand else { 184 let Some(hand) = &view.hand else {
217 centered_note(ui, "Place a bet and deal a hand."); 185 centered_note(ui, "Place a bet and deal a hand.");
diff --git a/gui/src/crash.rs b/gui/src/crash.rs
index 14197ae..08113a8 100644
--- a/gui/src/crash.rs
+++ b/gui/src/crash.rs
@@ -119,11 +119,7 @@ fn sidebar(ui: &mut Ui, view: &CrashView, fx: &mut Fx) -> Option<Command> {
119 .font(theme::sans(22.0)) 119 .font(theme::sans(22.0))
120 .color(theme::TEXT), 120 .color(theme::TEXT),
121 ); 121 );
122 ui.label( 122 widgets::note(ui, "Cash out before the multiplier crashes.");
123 eframe::egui::RichText::new("Rise · cash out · don't bust")
124 .font(theme::sans(12.0))
125 .color(theme::TEXT_DIM),
126 );
127 123
128 ui.add_space(theme::SPACE_5); 124 ui.add_space(theme::SPACE_5);
129 ui.label(theme::label("Bet")); 125 ui.label(theme::label("Bet"));
@@ -152,14 +148,6 @@ fn sidebar(ui: &mut Ui, view: &CrashView, fx: &mut Fx) -> Option<Command> {
152 ui.add_space(theme::SPACE_3); 148 ui.add_space(theme::SPACE_3);
153 widgets::error(ui, error); 149 widgets::error(ui, error);
154 } 150 }
155
156 ui.add_space(theme::SPACE_4);
157 ui.separator();
158 ui.add_space(theme::SPACE_2);
159 widgets::note(
160 ui,
161 "The crash point is server-timed. The curve here is display only.",
162 );
163 }); 151 });
164 }, 152 },
165 ); 153 );
@@ -231,29 +219,7 @@ fn status(ui: &mut Ui, view: &CrashView) {
231} 219}
232 220
233fn chart(ui: &mut Ui, view: &CrashView, fx: &Fx) { 221fn chart(ui: &mut Ui, view: &CrashView, fx: &Fx) {
234 ui.horizontal(|ui| { 222 ui.add_space(theme::SPACE_2);
235 ui.label(
236 eframe::egui::RichText::new("Table 01")
237 .font(theme::sans(15.0))
238 .color(theme::TEXT),
239 );
240 ui.label(
241 eframe::egui::RichText::new("Crash · server-timed")
242 .font(theme::sans(12.0))
243 .color(theme::TEXT_DIM),
244 );
245 ui.with_layout(
246 eframe::egui::Layout::right_to_left(eframe::egui::Align::Center),
247 |ui| {
248 ui.label(
249 eframe::egui::RichText::new("Cash out any time before the crash")
250 .font(theme::mono(11.0))
251 .color(theme::TEXT_FAINT),
252 );
253 },
254 );
255 });
256 ui.add_space(theme::SPACE_4);
257 223
258 let available = ui.available_size(); 224 let available = ui.available_size();
259 let size = vec2(available.x, (available.y - 70.0).max(260.0)); 225 let size = vec2(available.x, (available.y - 70.0).max(260.0));
@@ -379,15 +345,6 @@ fn chart(ui: &mut Ui, view: &CrashView, fx: &Fx) {
379 } 345 }
380 } 346 }
381 } 347 }
382
383 ui.add_space(theme::SPACE_4);
384 ui.label(
385 eframe::egui::RichText::new(
386 "The crash point is drawn from the server; the multiplier here is display only.",
387 )
388 .font(theme::sans(11.0))
389 .color(theme::TEXT_FAINT),
390 );
391} 348}
392 349
393fn map_y(plot: Rect, multiplier: f64, max_multiplier: f64) -> f32 { 350fn map_y(plot: Rect, multiplier: f64, max_multiplier: f64) -> f32 {
diff --git a/gui/src/net.rs b/gui/src/net.rs
index d2bc7e4..7cba604 100644
--- a/gui/src/net.rs
+++ b/gui/src/net.rs
@@ -46,7 +46,6 @@ pub struct Connected {
46 pub user: Option<String>, 46 pub user: Option<String>,
47 pub auth_error: Option<String>, 47 pub auth_error: Option<String>,
48 pub fingerprint: Option<String>, 48 pub fingerprint: Option<String>,
49 pub server_version: String,
50 pub latest: String, 49 pub latest: String,
51 pub url: String, 50 pub url: String,
52 pub elapsed_ms: f32, 51 pub elapsed_ms: f32,
@@ -162,7 +161,6 @@ fn worker(
162 user: connected.user, 161 user: connected.user,
163 auth_error: connected.auth_error, 162 auth_error: connected.auth_error,
164 fingerprint, 163 fingerprint,
165 server_version: connected.server_version,
166 latest: connected.latest, 164 latest: connected.latest,
167 url: connected.url, 165 url: connected.url,
168 elapsed_ms: connected.elapsed.as_secs_f64() as f32 * 1000.0, 166 elapsed_ms: connected.elapsed.as_secs_f64() as f32 * 1000.0,
diff --git a/gui/src/roulette.rs b/gui/src/roulette.rs
index c791693..0100d1e 100644
--- a/gui/src/roulette.rs
+++ b/gui/src/roulette.rs
@@ -730,11 +730,7 @@ fn sidebar(
730 .font(theme::sans(22.0)) 730 .font(theme::sans(22.0))
731 .color(theme::TEXT), 731 .color(theme::TEXT),
732 ); 732 );
733 ui.label( 733 super::widgets::note(ui, "Pick numbers or colours, then spin.");
734 eframe::egui::RichText::new("European · single zero")
735 .font(theme::sans(12.0))
736 .color(theme::TEXT_DIM),
737 );
738 734
739 ui.add_space(theme::SPACE_4); 735 ui.add_space(theme::SPACE_4);
740 let wheel_size = ui.available_width().min(210.0); 736 let wheel_size = ui.available_width().min(210.0);
@@ -906,29 +902,7 @@ fn sidebar(
906fn board(ui: &mut Ui, view: &RouletteView, fx: &Fx) -> Option<zagan_bet::client::view::Command> { 902fn board(ui: &mut Ui, view: &RouletteView, fx: &Fx) -> Option<zagan_bet::client::view::Command> {
907 use zagan_bet::client::view::Command; 903 use zagan_bet::client::view::Command;
908 904
909 ui.horizontal(|ui| { 905 ui.add_space(theme::SPACE_2);
910 ui.label(
911 eframe::egui::RichText::new("Table 01")
912 .font(theme::sans(15.0))
913 .color(theme::TEXT),
914 );
915 ui.label(
916 eframe::egui::RichText::new("European · single zero")
917 .font(theme::sans(12.0))
918 .color(theme::TEXT_DIM),
919 );
920 ui.with_layout(
921 eframe::egui::Layout::right_to_left(eframe::egui::Align::Center),
922 |ui| {
923 ui.label(
924 eframe::egui::RichText::new("Limits 10 – 500 Kr")
925 .font(theme::mono(12.0))
926 .color(theme::TEXT_FAINT),
927 );
928 },
929 );
930 });
931 ui.add_space(theme::SPACE_4);
932 906
933 let strip_height = 104.0; 907 let strip_height = 104.0;
934 let available = ui.available_size(); 908 let available = ui.available_size();
@@ -968,11 +942,9 @@ fn board(ui: &mut Ui, view: &RouletteView, fx: &Fx) -> Option<zagan_bet::client:
968 }); 942 });
969 ui.add_space(theme::SPACE_2); 943 ui.add_space(theme::SPACE_2);
970 ui.label( 944 ui.label(
971 eframe::egui::RichText::new( 945 eframe::egui::RichText::new("Click to place a chip · right-click to take one back")
972 "Click to place · right or middle click takes back · C clears · space spins", 946 .font(theme::sans(12.0))
973 ) 947 .color(theme::TEXT_FAINT),
974 .font(theme::sans(12.0))
975 .color(theme::TEXT_FAINT),
976 ); 948 );
977 command 949 command
978} 950}
diff --git a/gui/src/slots.rs b/gui/src/slots.rs
index 6574626..d2a288a 100644
--- a/gui/src/slots.rs
+++ b/gui/src/slots.rs
@@ -109,11 +109,7 @@ fn sidebar(ui: &mut Ui, view: &SlotsView, fx: &mut Fx) -> Option<Command> {
109 .font(theme::sans(22.0)) 109 .font(theme::sans(22.0))
110 .color(theme::TEXT), 110 .color(theme::TEXT),
111 ); 111 );
112 ui.label( 112 widgets::note(ui, "Match three or more symbols from the left.");
113 eframe::egui::RichText::new("5 reels · 5 paylines")
114 .font(theme::sans(12.0))
115 .color(theme::TEXT_DIM),
116 );
117 113
118 ui.add_space(theme::SPACE_5); 114 ui.add_space(theme::SPACE_5);
119 ui.label(theme::label("Bet")); 115 ui.label(theme::label("Bet"));
@@ -128,32 +124,6 @@ fn sidebar(ui: &mut Ui, view: &SlotsView, fx: &mut Fx) -> Option<Command> {
128 ui.add_space(theme::SPACE_3); 124 ui.add_space(theme::SPACE_3);
129 widgets::error(ui, error); 125 widgets::error(ui, error);
130 } 126 }
131
132 ui.add_space(theme::SPACE_4);
133 ui.separator();
134 ui.add_space(theme::SPACE_3);
135 ui.label(theme::label("Paytable"));
136 ui.add_space(theme::SPACE_2);
137 ui.label(
138 eframe::egui::RichText::new("Pays bet × symbol × shape")
139 .font(theme::sans(11.0))
140 .color(theme::TEXT_FAINT),
141 );
142 ui.horizontal(|ui| {
143 ui.spacing_mut().item_spacing.x = theme::SPACE_2;
144 for (symbol, multiplier) in zagan_bet::slots::SYMBOL_MULTIPLIERS {
145 ui.label(
146 eframe::egui::RichText::new(format!("{symbol}{multiplier}"))
147 .font(theme::mono(12.0))
148 .color(symbol_color(symbol)),
149 );
150 }
151 });
152 ui.label(
153 eframe::egui::RichText::new("3×1 4×3 5×14")
154 .font(theme::mono(12.0))
155 .color(theme::TEXT_DIM),
156 );
157 }); 127 });
158 }, 128 },
159 ); 129 );
@@ -240,29 +210,7 @@ fn status(ui: &mut Ui, view: &SlotsView, spinning: bool) {
240} 210}
241 211
242fn board(ui: &mut Ui, view: &SlotsView, fx: &Fx) { 212fn board(ui: &mut Ui, view: &SlotsView, fx: &Fx) {
243 ui.horizontal(|ui| { 213 ui.add_space(theme::SPACE_2);
244 ui.label(
245 eframe::egui::RichText::new("Machine 01")
246 .font(theme::sans(15.0))
247 .color(theme::TEXT),
248 );
249 ui.label(
250 eframe::egui::RichText::new("5 reels · 5 paylines")
251 .font(theme::sans(12.0))
252 .color(theme::TEXT_DIM),
253 );
254 ui.with_layout(
255 eframe::egui::Layout::right_to_left(eframe::egui::Align::Center),
256 |ui| {
257 ui.label(
258 eframe::egui::RichText::new("Bet 10 – 500 Kr")
259 .font(theme::mono(12.0))
260 .color(theme::TEXT_FAINT),
261 );
262 },
263 );
264 });
265 ui.add_space(theme::SPACE_5);
266 214
267 let now = ui.input(|input| input.time); 215 let now = ui.input(|input| input.time);
268 let spinning = fx.spinning(view, now); 216 let spinning = fx.spinning(view, now);