Spaces:
Sleeping
Sleeping
| use crate::core::logic::*; | |
| use crate::test_helpers::{create_test_state, load_real_db}; | |
| /// Verifies that O_DRAW and O_MOVE_TO_DISCARD correctly manipulate hand and deck using real card IDs. | |
| fn test_opcode_draw_discard() { | |
| let db = load_real_db(); // Use production DB | |
| let mut state = create_test_state(); | |
| state.ui.silent = true; | |
| // Use real card IDs: 121 (Eli), 124 (Rin) | |
| state.players[0].deck = vec![121, 124, 121, 124, 121].into(); | |
| let ctx = AbilityContext { | |
| player_id: 0, | |
| ..Default::default() | |
| }; | |
| // O_DRAW 2 | |
| let bc = vec![O_DRAW, 2, 0, 0, 0, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| assert_eq!(state.players[0].hand.len(), 2); | |
| assert_eq!(state.players[0].deck.len(), 3); | |
| assert!(state.players[0].hand.contains(&121) || state.players[0].hand.contains(&124)); | |
| // O_MOVE_TO_DISCARD 1 (attr 2 = Hand) | |
| // Pre-seed choice_index so it doesn't suspend, since inline bytecode can't be resumed | |
| let discard_ctx = AbilityContext { | |
| player_id: 0, | |
| choice_index: 0, | |
| ..Default::default() | |
| }; | |
| let bc = vec![O_MOVE_TO_DISCARD, 1, 2, 0, 6, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &discard_ctx); | |
| assert_eq!(state.players[0].hand.len(), 1); | |
| assert_eq!(state.players[0].discard.len(), 1); | |
| } | |
| /// Verifies that O_ADD_BLADES, O_ADD_HEARTS, and O_BOOST_SCORE correctly apply stat buffs. | |
| fn test_opcode_stats_boost() { | |
| let db = load_real_db(); | |
| let mut state = create_test_state(); | |
| state.players[0].stage[0] = 121; // Real card ID | |
| let ctx = AbilityContext { | |
| player_id: 0, | |
| area_idx: 0, | |
| ..Default::default() | |
| }; | |
| // O_ADD_BLADES 2 to SELF (Slot 4) | |
| let bc = vec![O_ADD_BLADES, 2, 0, 0, 4, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| assert_eq!(state.players[0].blade_buffs[0], 2); | |
| // O_ADD_HEARTS 3 (Pink=0) to SELF (Slot 4) | |
| let bc = vec![O_ADD_HEARTS, 3, 0, 0, 4, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| assert_eq!(state.players[0].heart_buffs[0].get_color_count(0), 3); | |
| // O_BOOST_SCORE 5 to SELF | |
| let bc = vec![O_BOOST_SCORE, 5, 0, 0, 0, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| assert_eq!(state.players[0].live_score_bonus, 5); | |
| } | |
| /// Verifies that O_SET_TAPPED can both tap and untap members. | |
| fn test_opcode_tap_untap() { | |
| let db = load_real_db(); | |
| let mut state = create_test_state(); | |
| state.players[0].stage[1] = 124; // Real card ID | |
| state.players[0].set_tapped(1, false); | |
| let ctx = AbilityContext { | |
| player_id: 0, | |
| area_idx: 1, | |
| ..Default::default() | |
| }; | |
| // O_SET_TAPPED 1 SELF | |
| let bc = vec![O_SET_TAPPED, 1, 0, 0, 4, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| assert!(state.players[0].is_tapped(1)); | |
| // O_SET_TAPPED 0 SELF | |
| let bc = vec![O_SET_TAPPED, 0, 0, 0, 4, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| assert!(!state.players[0].is_tapped(1)); | |
| } | |
| /// Verifies that conditional jumps (O_JUMP_F) work correctly based on card count in hand (C_COUNT_HAND). | |
| fn test_conditions_basic() { | |
| let db = load_real_db(); | |
| let mut state = create_test_state(); | |
| state.players[0].hand = vec![121, 124, 121].into(); | |
| let ctx = AbilityContext { | |
| player_id: 0, | |
| ..Default::default() | |
| }; | |
| let bc = vec![ | |
| C_COUNT_HAND, | |
| 3, | |
| 0, | |
| 0, | |
| 0, | |
| O_JUMP_IF_FALSE, | |
| 1, | |
| 0, | |
| 0, | |
| 0, | |
| O_DRAW, | |
| 1, | |
| 0, | |
| 0, | |
| 0, | |
| O_RETURN, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| ]; | |
| state.players[0].deck = vec![124].into(); | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| assert_eq!(state.players[0].hand.len(), 4); | |
| // C_COUNT_HAND GE 5 (False) -> Draw 1 | |
| let mut state = create_test_state(); | |
| state.players[0].hand = vec![121, 124, 121].into(); | |
| state.players[0].deck = vec![124].into(); | |
| let bc = vec![ | |
| C_COUNT_HAND, | |
| 5, | |
| 0, | |
| 0, | |
| 0, | |
| O_JUMP_IF_FALSE, | |
| 1, | |
| 0, | |
| 0, | |
| 0, | |
| O_DRAW, | |
| 1, | |
| 0, | |
| 0, | |
| 0, | |
| O_RETURN, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| ]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| assert_eq!(state.players[0].hand.len(), 3); | |
| } | |
| /// Verifies that O_LOOK_AND_CHOOSE correctly defaults to deck and moves remainder to discard using real data. | |
| fn test_look_and_choose_remainder() { | |
| let db = load_real_db(); | |
| let mut state = create_test_state(); | |
| state.players[0].deck = vec![121, 124, 121, 124, 121].into(); | |
| let ctx = AbilityContext { | |
| player_id: 0, | |
| ..Default::default() | |
| }; | |
| // O_LOOK_DECK 4 -> O_LOOK_AND_CHOOSE 1 to Hand (Source 6) | |
| let bc = vec![ | |
| O_LOOK_DECK, | |
| 4, | |
| 0, | |
| 0, | |
| 0, | |
| O_LOOK_AND_CHOOSE, | |
| 1, | |
| 0, | |
| 0, | |
| 6, | |
| O_RETURN, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| ]; | |
| // Execution 1: Reveal cards | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| assert_eq!(state.phase, Phase::Response); | |
| assert_eq!(state.players[0].looked_cards.len(), 4); | |
| assert_eq!(state.players[0].deck.len(), 1); | |
| // Simulated selection of index 1 | |
| let mut state2 = state.clone(); | |
| let mut ctx2 = state2 | |
| .interaction_stack | |
| .last() | |
| .expect("Missing pending_interaction") | |
| .ctx | |
| .clone(); | |
| ctx2.choice_index = 1; | |
| state2.resolve_bytecode_cref(&db, &bc, &ctx2); | |
| assert_eq!(state2.players[0].hand.len(), 1); | |
| assert_eq!(state2.players[0].deck.len(), 4); // 1 unlooked + 3 remainder | |
| assert_eq!(state2.players[0].looked_cards.len(), 0); | |
| // Execution 2: Skip selection (999) | |
| let mut state3 = state.clone(); | |
| let mut ctx3 = state3 | |
| .interaction_stack | |
| .last() | |
| .expect("Missing pending_interaction") | |
| .ctx | |
| .clone(); | |
| ctx3.choice_index = 999; | |
| state3.resolve_bytecode_cref(&db, &bc, &ctx3); | |
| assert_eq!(state3.players[0].hand.len(), 0); | |
| assert_eq!(state3.players[0].deck.len(), 5); // All 4 + 1 back to deck | |
| } | |