diff --git a/data/Tupfile.lua b/data/Tupfile.lua index b5dce6c1c..bab1c92cd 100644 --- a/data/Tupfile.lua +++ b/data/Tupfile.lua @@ -506,6 +506,7 @@ tup.append_table(img_files, { {"GAMES/TANKS", VAR_PROGS .. "/games/tanks/tanks"}, {"GAMES/TETRIS", VAR_PROGS .. "/games/tetris/tetris"}, {"GAMES/C4", VAR_PROGS .. "/games/c4/c4"}, + {"GAMES/SOLITARE", VAR_PROGS .. "/games/solitare/solitare"}, {"LIB/ARCHIVER.OBJ", VAR_PROGS .. "/fs/kfar/trunk/kfar_arc/kfar_arc.obj"}, {"LIB/BOX_LIB.OBJ", VAR_PROGS .. "/develop/libraries/box_lib/trunk/box_lib.obj"}, {"LIB/BUF2D.OBJ", VAR_PROGS .. "/develop/libraries/buf2d/trunk/buf2d.obj"}, diff --git a/programs/games/solitare/Tupfile.lua b/programs/games/solitare/Tupfile.lua new file mode 100644 index 000000000..1d55488e1 --- /dev/null +++ b/programs/games/solitare/Tupfile.lua @@ -0,0 +1,7 @@ +if tup.getconfig("NO_FASM") ~= "" then return end +HELPERDIR = (tup.getconfig("HELPERDIR") == "") and "../.." or tup.getconfig("HELPERDIR") +tup.include(HELPERDIR .. "/use_fasm.lua") +add_include(tup.getvariantdir()) + +tup.rule("echo lang fix " .. ((tup.getconfig("LANG") == "") and "en_US" or tup.getconfig("LANG")) .. " > %o", {"lang.inc"}) +tup.rule("solitare.asm", FASM .. " %f %o " .. tup.getconfig("KPACK_CMD"), "solitare") diff --git a/programs/games/solitare/solitare.asm b/programs/games/solitare/solitare.asm new file mode 100644 index 000000000..f2347325e --- /dev/null +++ b/programs/games/solitare/solitare.asm @@ -0,0 +1,1409 @@ +; Solitare V1.5.3 FINAL +; Platform: KolibriOS +; Author: Max +; Format: FASM (Flat Assembler) + +format binary as 'run' +use32 +org 0x0 + +; --- EXTERNAL INCLUDES --- +include '..\..\macros.inc' +include '..\..\KOSfuncs.inc' + +; --- GAME CONSTANTS --- +CARD_W equ 50 +CARD_H equ 70 +STACK_OFFSET equ 20 +WIN_W equ 750 +WIN_H equ 560 +BPP equ 3 +STRIDE equ WIN_W * BPP + +COL_TABLE equ 0x207020 +COL_REVERSE equ 0x0000AA +COL_WHITE equ 0xFFFFFF +COL_RED equ 0xCC0000 +COL_BLACK equ 0x000000 +COL_SLOT equ 0x185018 + +CARD_STRUCT_SIZE equ 12 +CARD_VALUE equ 0 +CARD_SUIT equ 1 +CARD_STATE equ 2 +CARD_LOC equ 3 +CARD_X equ 4 +CARD_Y equ 6 +CARD_OLD_X equ 8 +CARD_OLD_Y equ 10 + +STOCK_X equ 20 +WASTE_X equ 80 +FOUNDATION_X equ 350 +FOUND_Y equ 20 +SCORE_Y equ 535 + +BTN_REPLAY equ 100 +BTN_EXIT equ 101 + +; --- HEADER --- +db 'MENUET01' +dd 0x01 +dd start +dd i_end +dd mem_end +dd stacktop +dd 0, 0 + +start: + mov esp, stacktop + + ; Get system skin height for proper coordinate calculation + mcall SF_STYLE_SETTINGS, SSF_GET_SKIN_HEIGHT + mov [skin_h], eax + + call init_game_state + +event_loop: + ; --- GLOBAL EXIT CHECK --- + cmp byte [exit_request], 1 + je .force_exit + + mcall SF_CHECK_EVENT + + cmp eax, 1 ; Redraw event + je on_redraw + cmp eax, 2 ; Key event + je on_key + cmp eax, 3 ; Button event + je on_button + + ; If win condition met, run animation + cmp byte [game_won], 1 + je .win_animation + + call handle_mouse + jmp .check_redraw + +.win_animation: + call update_cascade + mov byte [force_redraw], 1 + + ; Logic to spawn win thread once + cmp byte [win_thread_active], 0 + jne .check_reset + inc dword [win_timer] + cmp dword [win_timer], 120 ; Delay before popup + jl .check_reset + + mov byte [win_thread_active], 1 + call create_win_thread + +.check_reset: + cmp byte [should_reset], 1 + jne .no_reset + call init_game_state + call draw_main_window +.no_reset: + mcall SF_SLEEP, 1 + jmp update_screen_only + +.check_redraw: + cmp dword [active_card_count], 0 + ja update_screen_only + cmp byte [force_redraw], 1 + je update_screen_only + + mcall SF_SLEEP, 1 + jmp event_loop + +.force_exit: + mcall SF_TERMINATE_PROCESS + +on_key: + mcall SF_GET_KEY + jmp event_loop + +on_button: + mcall SF_GET_BUTTON + shr eax, 8 + cmp eax, 1 ; Close button + jne event_loop + + mcall SF_TERMINATE_PROCESS + +on_redraw: + call draw_main_window + mov byte [force_redraw], 1 + jmp update_screen_only + +update_screen_only: + mov byte [force_redraw], 0 + call prepare_buffer + + ; Render buffer to window + mov edx, 4 shl 16 + add dx, word [skin_h] + mcall SF_PUT_IMAGE, screen_buffer, WIN_W shl 16 + WIN_H + jmp event_loop + +; --- WIN WINDOW THREAD LOGIC --- + +create_win_thread: + mcall SF_CREATE_THREAD, SSF_CREATE_THREAD, win_thread_entry, win_stack_top + ret + +win_thread_entry: + mcall SF_THREAD_INFO, win_proc_info, -1 + + ; Center win window relative to main window + mov eax, [win_proc_info + 34] + add eax, 220 + shl eax, 16 + add eax, 300 + mov [win_pos_x], eax + + mov eax, [win_proc_info + 38] + add eax, 200 + shl eax, 16 + add eax, 180 + mov [win_pos_y], eax + +.redr: + mcall SF_REDRAW, SSF_BEGIN_DRAW + mcall SF_CREATE_WINDOW, [win_pos_x], [win_pos_y], 0x34FFFFFF, 0x80505050, title_win + mcall SF_DRAW_TEXT, 70 shl 16 + 35, 0x80000000, msg_congrats + mcall SF_DRAW_TEXT, 100 shl 16 + 65, 0x80000000, msg_score_lbl + mov eax, [score] + mov edi, score_str + call int_to_string + mcall SF_DRAW_TEXT, 160 shl 16 + 65, 0x80000000, score_str + + mcall SF_DEFINE_BUTTON, 40 shl 16 + 100, 100 shl 16 + 30, BTN_REPLAY, 0xCCCCCC + mcall SF_DRAW_TEXT, 66 shl 16 + 110, 0x80000000, txt_replay + mcall SF_DEFINE_BUTTON, 160 shl 16 + 100, 100 shl 16 + 30, BTN_EXIT, 0xCCCCCC + mcall SF_DRAW_TEXT, 194 shl 16 + 110, 0x80000000, txt_exit + mcall SF_REDRAW, SSF_END_DRAW + +.wait_ev: + mcall SF_WAIT_EVENT + cmp eax, 1 + je .redr + cmp eax, 3 + jne .wait_ev + mcall SF_GET_BUTTON + shr eax, 8 + + cmp eax, BTN_EXIT + je .trigger_exit + cmp eax, BTN_REPLAY + jne .wait_ev + +.replay_game: + mov byte [should_reset], 1 + mcall SF_TERMINATE_PROCESS + jmp .wait_ev + +.trigger_exit: + ; Signal the main thread to terminate the entire process + mov byte [exit_request], 1 + mcall SF_TERMINATE_PROCESS + +; --- GAME ENGINE LOGIC --- + +init_game_state: + call init_deck + call shuffle_deck + call layout_tableau + mov dword [score], 0 + mov byte [game_won], 0 + mov dword [win_timer], 0 + mov byte [win_thread_active], 0 + mov byte [should_reset], 0 + mov byte [exit_request], 0 + mov dword [active_cascade_idx], -1 + mov dword [cascade_timer], 0 + ret + +draw_main_window: + mcall SF_REDRAW, SSF_BEGIN_DRAW + mov ebx, 100 shl 16 + (WIN_W + 9) + mov eax, [skin_h] + add eax, WIN_H + 5 + mov ecx, 100 shl 16 + mov cx, ax + mcall SF_CREATE_WINDOW, , , 0x14000000 or COL_TABLE, 0, title + mcall SF_REDRAW, SSF_END_DRAW + ret + +int_to_string: + pushad + mov ebx, 10 + xor ecx, ecx +.l1: xor edx, edx + div ebx + add dl, '0' + push edx + inc ecx + test eax, eax + jnz .l1 +.l2: pop edx + mov [edi], dl + inc edi + loop .l2 + mov byte [edi], 0 + popad + ret + +update_cascade: + pushad + inc dword [cascade_timer] + cmp dword [cascade_timer], 3 + jl .move_active + mov dword [cascade_timer], 0 + cmp dword [active_cascade_idx], 51 + jge .move_active + inc dword [active_cascade_idx] +.move_active: + mov ecx, [active_cascade_idx] + cmp ecx, 0 + jl .done +.l: + push ecx + imul esi, ecx, CARD_STRUCT_SIZE + add esi, deck_data + imul edi, ecx, 4 + add edi, card_vectors + + ; Apply Gravity (Vertical velocity increases) + inc word [edi+2] + + ; Update Y Position + mov ax, [edi+2] + add [esi+CARD_Y], ax + + ; Update X Position + mov ax, [edi] + add [esi+CARD_X], ax + + ; Bounce off the bottom + mov ax, [esi+CARD_Y] + add ax, CARD_H + cmp ax, WIN_H + jl .no_y_bounce + mov word [esi+CARD_Y], WIN_H - CARD_H + mov ax, [edi+2] + neg ax + sar ax, 1 ; Dampen vertical bounce (energy loss) + mov [edi+2], ax +.no_y_bounce: + + ; Bounce off the left wall + mov ax, [esi+CARD_X] + cmp ax, 0 + jg .not_left + mov word [esi+CARD_X], 0 + neg word [edi] ; Reverse X velocity +.not_left: + + ; Bounce off the right wall + add ax, CARD_W + cmp ax, WIN_W + jl .no_x_bounce + mov word [esi+CARD_X], WIN_W - CARD_W + neg word [edi] ; Reverse X velocity +.no_x_bounce: + + pop ecx + dec ecx + jns .l +.done: + popad + ret + +init_cascade_vectors: + pushad + xor ecx, ecx +.l: + imul edi, ecx, 4 + add edi, card_vectors + + ; Get pseudo-random value from system time + mcall SF_GET_SYS_TIME + push eax + + ; Random Horizontal Velocity (-7 to +8 range) + and eax, 0x0F + sub eax, 7 + jnz .x_ok + inc eax ; Avoid 0 horizontal speed for variety +.x_ok: + mov [edi], ax + + ; Random Initial Vertical Kick (Bounce up) + pop eax + shr eax, 4 + and eax, 0x07 ; 0 to 7 range + neg eax ; Make it negative (moving UP) + sub eax, 4 ; Ensure a minimum upward kick + mov [edi+2], ax + + inc ecx + cmp ecx, 52 + jl .l + popad + ret + +handle_mouse: + pushad + mcall SF_MOUSE_GET, SSF_WINDOW_POSITION + mov ebp, eax + shr ebp, 16 + sub ebp, 5 + and eax, 0xFFFF + sub eax, [skin_h] + mov edi, eax + mcall SF_MOUSE_GET, SSF_BUTTON + test eax, 1 + jnz .dragging_state + cmp dword [mouse_lock], 1 + jne .exit + mov dword [mouse_lock], 0 + cmp dword [active_card_count], 0 + je .exit + call check_stack_move + mov dword [active_card_count], 0 + mov byte [force_redraw], 1 + popad + ret +.dragging_state: + cmp dword [mouse_lock], 1 + je .no_deal_click + cmp ebp, STOCK_X + jb .find_card + cmp ebp, STOCK_X+CARD_W + jae .find_card + cmp edi, FOUND_Y + jb .find_card + cmp edi, FOUND_Y+CARD_H + jae .find_card + mov dword [mouse_lock], 1 + call deal_from_stock + mov byte [force_redraw], 1 + popad + ret +.find_card: + mov ebx, 51 +.f_loop: + imul edx, ebx, CARD_STRUCT_SIZE + add edx, deck_data + movsx esi, word [edx+CARD_X] + movsx ecx, word [edx+CARD_Y] + cmp ebp, esi + jb .next_s + add esi, CARD_W + cmp ebp, esi + jae .next_s + cmp edi, ecx + jb .next_s + add ecx, CARD_H + cmp edi, ecx + jae .next_s + test byte [edx+CARD_STATE], 1 + jz .next_s + mov dword [mouse_lock], 1 + mov [active_stack], ebx + mov dword [active_card_count], 1 + mov ax, word [edx+CARD_X] + sub ax, bp + mov [mouse_off_x], ax + mov ax, word [edx+CARD_Y] + sub ax, di + mov [mouse_off_y], ax + mov eax, ebx + call build_active_stack + popad + ret +.next_s: + dec ebx + jns .f_loop + popad + ret +.no_deal_click: + cmp dword [active_card_count], 0 + je .exit_drag + xor ecx, ecx +.d_lp: + cmp ecx, [active_card_count] + jae .exit_drag + mov eax, [active_stack + ecx*4] + imul eax, CARD_STRUCT_SIZE + add eax, deck_data + mov dx, [mouse_off_x] + add dx, bp + mov [eax+CARD_X], dx + mov dx, [mouse_off_y] + add dx, di + push eax + mov eax, ecx + imul eax, STACK_OFFSET + add dx, ax + pop eax + mov [eax+CARD_Y], dx + inc ecx + jmp .d_lp +.exit_drag: + popad + ret +.exit: + popad + ret + +prepare_buffer: + mov edi, screen_buffer + mov ecx, WIN_W * WIN_H + mov eax, COL_TABLE +.clear_loop: + mov [edi], al + mov [edi+1], ah + push eax + shr eax, 16 + mov byte [edi+2], al + pop eax + add edi, 3 + dec ecx + jnz .clear_loop + call draw_slots + call render_score_ui + xor ecx, ecx +.draw_loop: + call is_card_active + jc .skip + push ecx + imul esi, ecx, CARD_STRUCT_SIZE + add esi, deck_data + call draw_card_to_buffer + pop ecx +.skip: + inc ecx + cmp ecx, 52 + jl .draw_loop + xor ecx, ecx +.draw_active: + cmp ecx, [active_card_count] + jae .done_rendering + push ecx + mov eax, [active_stack + ecx*4] + imul esi, eax, CARD_STRUCT_SIZE + add esi, deck_data + call draw_card_to_buffer + pop ecx + inc ecx + jmp .draw_active +.done_rendering: + ret + +draw_card_to_buffer: + pushad + movsx ebx, word [esi+CARD_X] + movsx ecx, word [esi+CARD_Y] + mov edx, COL_BLACK + mov edi, CARD_W + mov ebp, CARD_H + call fill_rect_buf + inc ebx + inc ecx + mov edx, COL_WHITE + mov edi, CARD_W-2 + mov ebp, CARD_H-2 + call fill_rect_buf + test byte [esi+CARD_STATE], 1 + jnz .face_up + add ebx, 4 + add ecx, 4 + mov edx, COL_REVERSE + mov edi, CARD_W-10 + mov ebp, CARD_H-10 + call fill_rect_buf + popad + ret +.face_up: + mov edx, COL_RED + cmp byte [esi+CARD_SUIT], 2 + jb .is_red + mov edx, COL_BLACK +.is_red: + push edx + push esi + movzx eax, byte [esi+CARD_VALUE] + dec eax + mov esi, eax + add ebx, 3 + add ecx, 3 + call draw_char_to_buf + pop esi + push esi + movzx eax, byte [esi+CARD_SUIT] + add eax, 13 + add ebx, 6 + mov esi, eax + call draw_char_to_buf + pop esi + pop edx + push edx + push esi + movsx ebx, word [esi+CARD_X] + add ebx, CARD_W - 8 + movsx ecx, word [esi+CARD_Y] + add ecx, CARD_H - 10 + movzx eax, byte [esi+CARD_VALUE] + dec eax + mov esi, eax + call draw_char_to_buf + pop esi + push esi + movzx eax, byte [esi+CARD_SUIT] + add eax, 13 + sub ebx, 6 + mov esi, eax + call draw_char_to_buf + pop esi + pop edx + popad + ret + +fill_rect_buf: + pushad + mov esi, ebp +.yl: + cmp ecx, 0 + jl .skip_line + cmp ecx, WIN_H + jge .skip_line + push ecx + push ebx + mov eax, ecx + imul eax, STRIDE + add eax, screen_buffer + mov dword [temp_counter], edi +.xl: + cmp ebx, 0 + jl .skip_pixel + cmp ebx, WIN_W + jge .skip_pixel + push eax + mov ecx, ebx + imul ecx, 3 + add eax, ecx + mov [eax], dl + mov [eax+1], dh + push edx + shr edx, 16 + mov [eax+2], dl + pop edx + pop eax +.skip_pixel: + inc ebx + dec dword [temp_counter] + jnz .xl + pop ebx + pop ecx +.skip_line: + inc ecx + dec esi + jnz .yl + popad + ret + +draw_char_to_buf: + pushad + imul esi, 7 + add esi, font_5x7 + mov ebp, 7 +.y_loop: + push ebx + push ecx + mov al, [esi] + mov ah, 10000000b + mov dword [temp_counter], 5 +.x_loop: + test al, ah + jz .skip_pixel + cmp ebx, 0 + jl .skip_pixel + cmp ebx, WIN_W + jge .skip_pixel + cmp ecx, 0 + jl .skip_pixel + cmp ecx, WIN_H + jge .skip_pixel + push eax + mov eax, ecx + imul eax, STRIDE + push ebx + imul ebx, 3 + add eax, ebx + pop ebx + add eax, screen_buffer + mov [eax], dl + mov [eax+1], dh + push edx + shr edx, 16 + mov [eax+2], dl + pop edx + pop eax +.skip_pixel: + shr ah, 1 + inc ebx + dec dword [temp_counter] + jnz .x_loop + pop ecx + pop ebx + inc esi + inc ecx + dec ebp + jnz .y_loop + popad + ret + +draw_large_char_to_buf: + pushad + imul esi, 12 + add esi, font_8x12 + mov ebp, 12 +.y_loop: + push ebx + push ecx + mov al, [esi] + mov ah, 10000000b + mov dword [temp_counter], 8 +.x_loop: + test al, ah + jz .skip_pixel + cmp ebx, 0 + jl .skip_pixel + cmp ebx, WIN_W + jge .skip_pixel + cmp ecx, 0 + jl .skip_pixel + cmp ecx, WIN_H + jge .skip_pixel + push eax + mov eax, ecx + imul eax, STRIDE + push ebx + imul ebx, 3 + add eax, ebx + pop ebx + add eax, screen_buffer + mov [eax], dl + mov [eax+1], dh + push edx + shr edx, 16 + mov [eax+2], dl + pop edx + pop eax +.skip_pixel: + shr ah, 1 + inc ebx + dec dword [temp_counter] + jnz .x_loop + pop ecx + pop ebx + inc esi + inc ecx + dec ebp + jnz .y_loop + popad + ret + +render_score_ui: + mov edx, COL_BLACK + mov ebx, 530 + mov ecx, SCORE_Y + xor esi, esi + call draw_large_char_to_buf + add ebx, 10 + mov esi, 1 + call draw_large_char_to_buf + add ebx, 10 + mov esi, 2 + call draw_large_char_to_buf + add ebx, 10 + mov esi, 3 + call draw_large_char_to_buf + add ebx, 10 + mov esi, 4 + call draw_large_char_to_buf + add ebx, 20 + mov eax, [score] + test eax, eax + jns .pos + neg eax + push eax + mov ecx, SCORE_Y + mov esi, 5 + call draw_large_char_to_buf + add ebx, 10 + pop eax +.pos: + mov edi, 10 + xor ecx, ecx +.dec_loop: + xor edx, edx + div edi + push edx + inc ecx + test eax, eax + jnz .dec_loop +.print_dec: + pop edx + add edx, 6 + push ecx + mov ecx, SCORE_Y + mov esi, edx + mov edx, COL_BLACK + call draw_large_char_to_buf + add ebx, 10 + pop ecx + loop .print_dec + ret + +draw_slots: + pushad + mov ebx, FOUNDATION_X + mov ecx, FOUND_Y + mov dword [temp_counter2], 4 +.f_loop: + mov edx, COL_SLOT + mov edi, CARD_W + mov ebp, CARD_H + call fill_rect_buf + add ebx, 60 + dec dword [temp_counter2] + jnz .f_loop + mov ebx, 40 + mov ecx, 110 + mov dword [temp_counter2], 7 +.t_loop: + mov edx, COL_SLOT + mov edi, CARD_W + mov ebp, CARD_H + call fill_rect_buf + add ebx, 100 + dec dword [temp_counter2] + jnz .t_loop + popad + ret + +is_card_active: + push eax + push ebx + xor ebx, ebx +.l: + cmp ebx, [active_card_count] + jae .no + mov eax, [active_stack + ebx*4] + cmp eax, ecx + je .yes + inc ebx + jmp .l +.yes: + pop ebx + pop eax + stc + ret +.no: + pop ebx + pop eax + clc + ret + +build_active_stack: + pushad + imul esi, eax, CARD_STRUCT_SIZE + add esi, deck_data + mov bx, [esi+CARD_X] + mov dx, [esi+CARD_Y] + movzx ebp, dx + add ebp, STACK_OFFSET +.y_search: + xor ecx, ecx +.l: + imul edi, ecx, CARD_STRUCT_SIZE + add edi, deck_data + cmp word [edi+CARD_X], bx + jne .nx + cmp word [edi+CARD_Y], bp + jne .nx + mov edx, [active_card_count] + mov [active_stack + edx*4], ecx + inc dword [active_card_count] + add ebp, STACK_OFFSET + jmp .y_search +.nx: + inc ecx + cmp ecx, 52 + jl .l +.done: + xor ecx, ecx +.save: + cmp ecx, [active_card_count] + jae .fin + mov eax, [active_stack + ecx*4] + imul eax, CARD_STRUCT_SIZE + add eax, deck_data + mov dx, [eax+CARD_X] + mov [eax+CARD_OLD_X], dx + mov dx, [eax+CARD_Y] + mov [eax+CARD_OLD_Y], dx + inc ecx + jmp .save +.fin: + popad + ret + +check_stack_move: + pushad + mov esi, [active_stack] + imul esi, CARD_STRUCT_SIZE + add esi, deck_data + cmp dword [active_card_count], 1 + jne .tableau_check + mov ebx, FOUNDATION_X + xor ecx, ecx +.f_lp: + movsx eax, word [esi+CARD_X] + sub eax, ebx + add eax, 25 + cmp eax, 50 + ja .next_f + movsx eax, word [esi+CARD_Y] + sub eax, FOUND_Y + add eax, 30 + cmp eax, 60 + ja .next_f + call check_foundation_logic + jc .found_match +.next_f: + add ebx, 60 + inc ecx + cmp ecx, 4 + jl .f_lp +.tableau_check: + mov ebx, 40 +.col_lp: + xor ecx, ecx + mov dword [temp_val], -1 + mov dword [temp_counter], 0 +.find_l: + imul edi, ecx, CARD_STRUCT_SIZE + add edi, deck_data + push ecx + call is_card_active + pop ecx + jc .sk_l + cmp word [edi+CARD_X], bx + jne .sk_l + movzx eax, word [edi+CARD_Y] + cmp eax, [temp_counter] + jl .sk_l + mov [temp_counter], eax + mov [temp_val], ecx +.sk_l: + inc ecx + cmp ecx, 52 + jl .find_l + cmp dword [temp_val], -1 + je .empty_col + mov ecx, [temp_val] + imul edi, ecx, CARD_STRUCT_SIZE + add edi, deck_data + movsx eax, word [esi+CARD_X] + movsx edx, word [edi+CARD_X] + sub eax, edx + add eax, 25 + cmp eax, 50 + ja .nx_col + movsx eax, word [esi+CARD_Y] + movsx edx, word [edi+CARD_Y] + sub eax, edx + add eax, 30 + cmp eax, 60 + ja .nx_col + mov al, [esi+CARD_SUIT] + mov dl, [edi+CARD_SUIT] + shr al, 1 + shr dl, 1 + cmp al, dl + je .nx_col + mov al, [esi+CARD_VALUE] + mov dl, [edi+CARD_VALUE] + inc al + cmp al, dl + jne .nx_col + mov dx, word [edi+CARD_Y] + add dx, STACK_OFFSET + jmp .apply_move +.empty_col: + movsx eax, word [esi+CARD_X] + sub eax, ebx + add eax, 25 + cmp eax, 50 + ja .nx_col + movsx eax, word [esi+CARD_Y] + sub eax, 110 + add eax, 30 + cmp eax, 60 + ja .nx_col + cmp byte [esi+CARD_VALUE], 13 + jne .nx_col + mov dx, 110 + jmp .apply_move +.nx_col: + add ebx, 100 + cmp ebx, 740 + jl .col_lp + xor ecx, ecx +.back: + cmp ecx, [active_card_count] + jae .out + mov eax, [active_stack + ecx*4] + imul eax, CARD_STRUCT_SIZE + add eax, deck_data + mov dx, [eax+CARD_OLD_X] + mov [eax+CARD_X], dx + mov dx, [eax+CARD_OLD_Y] + mov [eax+CARD_Y], dx + inc ecx + jmp .back +.out: + popad + ret +.found_match: + test byte [esi+CARD_STATE], 0x80 + jnz .already_scored + add dword [score], 10 + or byte [esi+CARD_STATE], 0x80 +.already_scored: + mov word [esi+CARD_X], bx + mov word [esi+CARD_Y], FOUND_Y + mov byte [esi+CARD_LOC], 8 + pushad + mov eax, [active_stack] + mov [temp_active], eax + call move_single_to_top + popad + call auto_reveal_all + call check_win + popad + ret +.apply_move: + xor ecx, ecx + push eax + push edx + mov eax, ebx + sub eax, 40 + xor edx, edx + mov edi, 100 + div edi + mov [temp_val], eax + pop edx + pop eax + mov [temp_counter2], edx +.sticky_loop: + cmp ecx, [active_card_count] + jae .sticky_done + mov eax, [active_stack + ecx*4] + imul eax, CARD_STRUCT_SIZE + add eax, deck_data + mov word [eax+CARD_X], bx + mov dx, word [temp_counter2] + mov word [eax+CARD_Y], dx + mov dl, byte [temp_val] + mov byte [eax+CARD_LOC], dl + add word [temp_counter2], STACK_OFFSET + inc ecx + jmp .sticky_loop +.sticky_done: + xor ecx, ecx +.top_loop: + cmp ecx, [active_card_count] + jae .ok + push ecx + mov eax, [active_stack + ecx*4] + mov [temp_active], eax + call move_single_to_top + call update_active_stack_after_move + pop ecx + inc ecx + jmp .top_loop +.ok: + call auto_reveal_all + popad + ret + +update_active_stack_after_move: + pushad + mov edx, [temp_active] + xor ecx, ecx +.up_lp: + cmp ecx, [active_card_count] + jae .up_dn + mov eax, [active_stack + ecx*4] + cmp eax, edx + jbe .no_chg + dec eax + mov [active_stack + ecx*4], eax +.no_chg: + inc ecx + jmp .up_lp +.up_dn: + popad + ret + +check_foundation_logic: + pushad + xor eax, eax + mov dword [temp_val], 0 + mov dword [temp_counter], -1 + xor ebp, ebp +.find_top: + imul edi, ebp, CARD_STRUCT_SIZE + add edi, deck_data + cmp word [edi+CARD_X], bx + jne .nx_c + cmp word [edi+CARD_Y], FOUND_Y + jne .nx_c + movzx edx, byte [edi+CARD_VALUE] + cmp edx, [temp_val] + jle .nx_c + mov [temp_val], edx + mov [temp_counter], ebp +.nx_c: + inc ebp + cmp ebp, 52 + jl .find_top + mov al, [esi+CARD_VALUE] + mov dl, [esi+CARD_SUIT] + cmp dword [temp_counter], -1 + jne .not_empty + cmp al, 1 + je .valid + jmp .invalid +.not_empty: + mov edi, [temp_counter] + imul edi, 12 + add edi, deck_data + cmp dl, [edi+CARD_SUIT] + jne .invalid + mov dl, [edi+CARD_VALUE] + inc dl + cmp al, dl + je .valid +.invalid: + popad + clc + ret +.valid: + popad + stc + ret + +check_win: + pushad + xor ecx, ecx + xor eax, eax +.l: + imul esi, ecx, CARD_STRUCT_SIZE + add esi, deck_data + cmp byte [esi+CARD_LOC], 8 + jne .no + inc eax +.no: + inc ecx + cmp ecx, 52 + jl .l + cmp eax, 52 + jne .out + cmp byte [game_won], 0 + jne .out + add dword [score], 100 + mov byte [game_won], 1 + call init_cascade_vectors +.out: + popad + ret + +auto_reveal_all: + pushad + mov ebx, 40 +.col_loop: + xor esi, esi + mov dword [temp_val], -1 + xor ecx, ecx +.find_lowest: + imul edi, ecx, CARD_STRUCT_SIZE + add edi, deck_data + cmp word [edi+CARD_X], bx + jne .next_card + movzx eax, word [edi+CARD_Y] + cmp eax, [temp_val] + jle .next_card + mov [temp_val], eax + mov esi, edi +.next_card: + inc ecx + cmp ecx, 52 + jl .find_lowest + cmp dword [temp_val], -1 + je .next_col + or byte [esi+CARD_STATE], 1 +.next_col: + add ebx, 100 + cmp ebx, 740 + jl .col_loop + popad + ret + +move_single_to_top: + mov eax, [temp_active] + cmp eax, 51 + je .done + imul esi, eax, CARD_STRUCT_SIZE + add esi, deck_data + mov edi, temp_card_buf + mov ecx, 3 + cld + rep movsd + mov eax, [temp_active] + inc eax + imul esi, eax, CARD_STRUCT_SIZE + add esi, deck_data + imul edi, [temp_active], CARD_STRUCT_SIZE + add edi, deck_data + mov ecx, 51 + sub ecx, [temp_active] + imul ecx, 3 + rep movsd + mov esi, temp_card_buf + mov edi, deck_data + (51 * CARD_STRUCT_SIZE) + mov ecx, 3 + rep movsd +.done: ret + +deal_from_stock: + pushad + mov ecx, 51 +.f: + imul esi, ecx, CARD_STRUCT_SIZE + add esi, deck_data + cmp byte [esi+CARD_LOC], 7 + jne .n + test byte [esi+CARD_STATE], 1 + jnz .n +.o: + or byte [esi+CARD_STATE], 1 + mov byte [esi+CARD_LOC], 9 + mov word [esi+CARD_X], WASTE_X + mov word [esi+CARD_Y], FOUND_Y + mov [temp_active], ecx + call move_single_to_top + popad + ret +.n: dec ecx + jns .f + call reset_stock + popad + ret + +reset_stock: + sub dword [score], 20 + xor ecx, ecx +.l: + imul esi, ecx, CARD_STRUCT_SIZE + add esi, deck_data + cmp byte [esi+CARD_LOC], 9 + jne .s + and byte [esi+CARD_STATE], 0xFE + mov byte [esi+CARD_LOC], 7 + mov word [esi+CARD_X], STOCK_X + mov word [esi+CARD_Y], FOUND_Y +.s: inc ecx + cmp ecx, 52 + jl .l + ret + +init_deck: + mov edi, deck_data + xor eax, eax +.s: xor ebx, ebx +.r: inc ebx + mov byte [edi+CARD_VALUE], bl + mov byte [edi+CARD_SUIT], al + mov byte [edi+CARD_STATE], 0 + mov byte [edi+CARD_LOC], 7 + add edi, CARD_STRUCT_SIZE + cmp ebx, 13 + jne .r + inc eax + cmp eax, 4 + jne .s + ret + +shuffle_deck: + mov dword [temp_counter2], 51 +.sh: + mcall SF_GET_SYS_TIME + and eax, 0xFFFF + xor edx, edx + mov ebx, [temp_counter2] + inc ebx + div ebx + imul esi, [temp_counter2], CARD_STRUCT_SIZE + add esi, deck_data + imul edi, edx, CARD_STRUCT_SIZE + add edi, deck_data + mov ecx, 3 +.sw: + mov eax, [esi] + mov edx, [edi] + mov [esi], edx + mov [edi], eax + add esi, 4 + add edi, 4 + loop .sw + dec dword [temp_counter2] + jnz .sh + ret + +layout_tableau: + mov edi, deck_data + mov ebx, 40 + xor edx, edx +.cols: + mov ecx, 110 + mov eax, edx + inc eax + mov ebp, eax +.cards: + mov word [edi+CARD_X], bx + mov word [edi+CARD_Y], cx + mov byte [edi+CARD_LOC], dl + dec ebp + jnz .h + or byte [edi+CARD_STATE], 1 + jmp .nx +.h: + and byte [edi+CARD_STATE], 0xFE +.nx: + add edi, CARD_STRUCT_SIZE + add ecx, STACK_OFFSET + cmp edx, 6 + je .cl + cmp ebp, 0 + jne .cards + add ebx, 100 + inc edx + jmp .cols +.cl: + cmp ebp, 0 + jne .cards +.sf: + cmp edi, deck_data + (52 * CARD_STRUCT_SIZE) + jae .fin + mov word [edi+CARD_X], STOCK_X + mov word [edi+CARD_Y], FOUND_Y + mov byte [edi+CARD_LOC], 7 + and byte [edi+CARD_STATE], 0xFE + add edi, CARD_STRUCT_SIZE + jmp .sf +.fin: ret + +; --- DATA SECTION --- +title db 'Solitare V1.5.3', 0 +title_win db 'Winner!', 0 +msg_congrats db 'Congrats You Win', 0 +msg_score_lbl db 'SCORE: ', 0 +txt_replay db 'REPLAY', 0 +txt_exit db 'EXIT', 0 +score_str rb 16 +score dd 0 +game_won db 0 +win_timer dd 0 +win_thread_active db 0 +should_reset db 0 +exit_request db 0 ; GLOBAL EXIT FLAG +mouse_lock dd 0 +force_redraw db 0 +temp_counter dd 0 +temp_counter2 dd 0 +temp_val dd 0 +mouse_off_x dw 0 +mouse_off_y dw 0 +temp_active dd 0 +temp_card_buf rd 3 +active_card_count dd 0 +active_stack rd 13 +skin_h dd 0 +cascade_timer dd 0 +active_cascade_idx dd -1 +win_pos_x dd 0 +win_pos_y dd 0 + +align 4 +card_vectors: rw 52 * 2 ; (X velocity, Y velocity) per card +win_proc_info: rb 1024 + +font_8x12: +; 0-4 (SCRE-) 5(-) 6-15 (0-9) +db 01111100b,10000010b,10000000b,10000000b,01111100b,00000010b,00000010b,10000010b,01111100b,00000000b,00000000b,00000000b +db 01111100b,10000010b,10000000b,10000000b,10000000b,10000000b,10000000b,10000010b,01111100b,00000000b,00000000b,00000000b +db 01111100b,10000010b,10000010b,10000010b,10000010b,10000010b,10000010b,10000010b,01111100b,00000000b,00000000b,00000000b +db 11111100b,10000010b,10000010b,10000010b,11111100b,10010000b,10001000b,10000100b,10000010b,00000000b,00000000b,00000000b +db 11111110b,10000000b,10000000b,10000000b,11111100b,10000000b,10000000b,10000000b,11111110b,00000000b,00000000b,00000000b +db 00000000b,00000000b,00000000b,00000000b,11111111b,00000000b,00000000b,00000000b,00000000b,00000000b,00000000b,00000000b +db 00111100b,01000010b,10000001b,10000001b,10000001b,10000001b,10000001b,01000010b,00111100b,00000000b,00000000b,00000000b +db 00011000b,00111000b,00001000b,00001000b,00001000b,00001000b,00001000b,00001000b,00111100b,00000000b,00000000b,00000000b +db 01111100b,10000010b,00000010b,00000010b,01111100b,10000000b,10000000b,10000000b,11111110b,00000000b,00000000b,00000000b +db 01111100b,10000010b,00000010b,00000010b,00111100b,00000010b,00000010b,10000010b,01111100b,00000000b,00000000b,00000000b +db 00001000b,00011000b,00101000b,01001000b,10001000b,11111111b,00001000b,00001000b,00001000b,00000000b,00000000b,00000000b +db 11111110b,10000000b,10000000b,11111100b,00000010b,00000010b,00000010b,10000010b,01111100b,00000000b,00000000b,00000000b +db 01111100b,10000010b,10000000b,11111100b,10000010b,10000010b,10000010b,10000010b,01111100b,00000000b,00000000b,00000000b +db 11111110b,00000010b,00000100b,00001000b,00010000b,00100000b,00100000b,00100000b,00100000b,00000000b,00000000b,00000000b +db 01111100b,10000010b,10000010b,10000010b,01111100b,10000010b,10000010b,10000010b,01111100b,00000000b,00000000b,00000000b +db 01111100b,10000010b,10000010b,10000010b,10000010b,01111110b,00000010b,10000010b,01111100b,00000000b,00000000b,00000000b + +font_5x7: +db 01110000b,10001000b,10001000b,11111000b,10001000b,10001000b,10001000b +db 11110000b,00001000b,00010000b,00100000b,01000000b,10000000b,11111000b +db 11110000b,00001000b,00001000b,01110000b,00001000b,00001000b,11110000b +db 10001000b,10001000b,10001000b,11111000b,00001000b,00001000b,00001000b +db 11111000b,10000000b,10000000b,11110000b,00001000b,00001000b,11111000b +db 01110000b,10000000b,10000000b,11110000b,10001000b,10001000b,01110000b +db 11111000b,00001000b,00010000b,00100000b,01000000b,01000000b,01000000b +db 01110000b,10001000b,10001000b,01110000b,10001000b,10001000b,01110000b +db 01110000b,10001000b,10001000b,01111000b,00001000b,00001000b,01110000b +db 10111000b,10101000b,10101000b,10101000b,10101000b,10101000b,10111000b +db 00011000b,00001000b,00001000b,00001000b,00001000b,10001000b,01110000b +db 01110000b,10001000b,10001000b,10001000b,10101000b,10010000b,01101000b +db 10001000b,10010000b,10100000b,11000000b,10100000b,10010000b,10001000b +db 01010000b,11111000b,11111000b,01110000b,00100000b,00000000b,00000000b +db 00100000b,01110000b,11111000b,11111000b,01110000b,00100000b,00000000b +db 00100000b,00100000b,11111000b,11111000b,00100000b,00100000b,01110000b +db 00100000b,01110000b,11111000b,11111000b,01110000b,00100000b,01110000b + +align 4 +deck_data: rb 52 * CARD_STRUCT_SIZE +i_end: + +align 64 +stack_bottom: rb 16384 +stacktop: + +align 64 +win_stack_bottom: rb 8192 +win_stack_top: + +screen_buffer: rb WIN_W * WIN_H * 3 + 32768 +mem_end: