diff --git a/data/Tupfile.lua b/data/Tupfile.lua index 2a2122b97..7a3e79898 100644 --- a/data/Tupfile.lua +++ b/data/Tupfile.lua @@ -771,6 +771,7 @@ tup.append_table(extra_files, { {"kolibrios/games/marblematch3", VAR_PROGS .. "/games/marblematch3/marblematch3"}, {"kolibrios/games/nsider", VAR_PROGS .. "/games/nsider/nsider"}, {"kolibrios/games/fridge/", VAR_PROGS .. "/games/fridge/fridge"}, + {"kolibrios/games/solitare", VAR_PROGS .. "/games/solitare/solitare"}, {"kolibrios/develop/lua/lua", VAR_CONTRIB .. "/other/lua-5.2.0/lua"}, {"kolibrios/develop/lua/calc.lua", "../contrib/other/lua-5.2.0/calc.lua"}, {"kolibrios/develop/lua/console.lua", "../contrib/other/lua-5.2.0/console.lua"}, diff --git a/programs/games/solitare/License free.txt b/programs/games/solitare/License free.txt new file mode 100644 index 000000000..61899af79 --- /dev/null +++ b/programs/games/solitare/License free.txt @@ -0,0 +1,45 @@ +IMPORTANT NOTICE: This license only applies if you downloaded this content as +an unsubscribed user. If you are a premium user (ie, you pay a subscription) +you are bound to the license terms described in the accompanying file +"License premium.txt". + +--------------------- + +You must attribute the image to its author: + +In order to use a content or a part of it, you must attribute it to rawpixel.com / Freepik, +so we will be able to continue creating new graphic resources every day. + + +How to attribute it? + +For websites: + +Please, copy this code on your website to accredit the author: +Designed by rawpixel.com / Freepik + +For printing: + +Paste this text on the final work so the authorship is known. +- For example, in the acknowledgements chapter of a book: +"Designed by rawpixel.com / Freepik" + + +You are free to use this image: + +- For both personal and commercial projects and to modify it. +- In a website or presentation template or application or as part of your design. + +You are not allowed to: + +- Sub-license, resell or rent it. +- Include it in any online or offline archive or database. + +The full terms of the license are described in section 7 of the Freepik +terms of use, available online in the following link: + + http://www.freepik.com/terms_of_use + +The terms described in the above link have precedence over the terms described +in the present document. In case of disagreement, the Freepik Terms of Use +will prevail. diff --git a/programs/games/solitare/Tupfile.lua b/programs/games/solitare/Tupfile.lua new file mode 100644 index 000000000..6aa6f3884 --- /dev/null +++ b/programs/games/solitare/Tupfile.lua @@ -0,0 +1,6 @@ +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("solitare.asm", FASM .. " %f %o " .. tup.getconfig("KPACK_CMD"), "solitare") diff --git a/programs/games/solitare/jd.bmp b/programs/games/solitare/jd.bmp new file mode 100644 index 000000000..0ba9f893b Binary files /dev/null and b/programs/games/solitare/jd.bmp differ diff --git a/programs/games/solitare/jh.bmp b/programs/games/solitare/jh.bmp new file mode 100644 index 000000000..0a5ca1864 Binary files /dev/null and b/programs/games/solitare/jh.bmp differ diff --git a/programs/games/solitare/jp.bmp b/programs/games/solitare/jp.bmp new file mode 100644 index 000000000..af422ad5b Binary files /dev/null and b/programs/games/solitare/jp.bmp differ diff --git a/programs/games/solitare/jt.bmp b/programs/games/solitare/jt.bmp new file mode 100644 index 000000000..72cb477c1 Binary files /dev/null and b/programs/games/solitare/jt.bmp differ diff --git a/programs/games/solitare/kd.bmp b/programs/games/solitare/kd.bmp new file mode 100644 index 000000000..d687ef916 Binary files /dev/null and b/programs/games/solitare/kd.bmp differ diff --git a/programs/games/solitare/kh.bmp b/programs/games/solitare/kh.bmp new file mode 100644 index 000000000..a92279b08 Binary files /dev/null and b/programs/games/solitare/kh.bmp differ diff --git a/programs/games/solitare/kp.bmp b/programs/games/solitare/kp.bmp new file mode 100644 index 000000000..0ef181e69 Binary files /dev/null and b/programs/games/solitare/kp.bmp differ diff --git a/programs/games/solitare/kt.bmp b/programs/games/solitare/kt.bmp new file mode 100644 index 000000000..dff881013 Binary files /dev/null and b/programs/games/solitare/kt.bmp differ diff --git a/programs/games/solitare/qd.bmp b/programs/games/solitare/qd.bmp new file mode 100644 index 000000000..8fb8712dd Binary files /dev/null and b/programs/games/solitare/qd.bmp differ diff --git a/programs/games/solitare/qh.bmp b/programs/games/solitare/qh.bmp new file mode 100644 index 000000000..cda415734 Binary files /dev/null and b/programs/games/solitare/qh.bmp differ diff --git a/programs/games/solitare/qp.bmp b/programs/games/solitare/qp.bmp new file mode 100644 index 000000000..5264e808f Binary files /dev/null and b/programs/games/solitare/qp.bmp differ diff --git a/programs/games/solitare/qt.bmp b/programs/games/solitare/qt.bmp new file mode 100644 index 000000000..92753dce5 Binary files /dev/null and b/programs/games/solitare/qt.bmp differ diff --git a/programs/games/solitare/solitare.asm b/programs/games/solitare/solitare.asm new file mode 100644 index 000000000..d38bb1978 --- /dev/null +++ b/programs/games/solitare/solitare.asm @@ -0,0 +1,1597 @@ +; SPDX-License-Identifier: GPL-2.0-only +; Program - Solitaire card game +; Copyright (C) 2026 KolibriOS team +; Author: Max +; Graphics: Designed by rawpixel.com / Freepik (see 'License free.txt') +; http://www.freepik.com + +use32 +org 0x0 + +db 'MENUET01' +dd 0x01 +dd start +dd i_end +dd mem_end +dd stacktop +dd 0, 0 + +include '../../macros.inc' +include '../../KOSfuncs.inc' + +; --- CONSTANTS --- +CARD_W equ 70 +CARD_H equ 100 +STACK_OFFSET equ 25 +WIN_W equ 760 +WIN_H equ 560 +BPP equ 3 +STRIDE equ WIN_W * BPP +BMP_W equ 40 +BMP_H equ 60 +BMP_BPP equ 3 +BMP_LINE equ (BMP_W * BMP_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 ; Dark gray/green for slots +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 100 +FOUNDATION_X equ 420 +FOUND_Y equ 20 +SCORE_Y equ 525 +BTN_REPLAY equ 100 +BTN_EXIT equ 101 + +start: + mcall SF_STYLE_SETTINGS, SSF_GET_SKIN_HEIGHT + mov [skin_h], eax + call init_game_state + +event_loop: + cmp byte [exit_request], 1 + je .force_exit + mcall SF_CHECK_EVENT + cmp eax, 1 + je on_redraw + cmp eax, 2 + je on_key + cmp eax, 3 + je on_button + cmp byte [game_won], 1 + je .win_animation + call handle_mouse + jmp .check_redraw + +.win_animation: + call update_cascade + mov byte [force_redraw], 1 + cmp byte [win_thread_active], 0 + jne .check_reset + inc dword [win_timer] + cmp dword [win_timer], 120 + 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 + 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 + 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 + +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 + +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 + mov eax, [win_proc_info + 34] + add eax, 200 + shl eax, 16 + add eax, 300 + mov [win_pos_x], eax + mov eax, [win_proc_info + 38] + add eax, 180 + 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: + mov byte [exit_request], 1 + mcall SF_TERMINATE_PROCESS + +int_to_string: + pushad + mov ebx, 10 + xor ecx, ecx + test eax, eax + jnz .l1 + mov byte [edi], '0' + inc edi + jmp .fin +.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 +.fin: + 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 + inc word [edi+2] + mov ax, [edi+2] + add [esi+CARD_Y], ax + mov ax, [edi] + add [esi+CARD_X], ax + 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 + mov [edi+2], ax +.no_y_bounce: + mov ax, [esi+CARD_X] + cmp ax, 0 + jg .not_left + mov word [esi+CARD_X], 0 + neg word [edi] +.not_left: + add ax, CARD_W + cmp ax, WIN_W + jl .no_x_bounce + mov word [esi+CARD_X], WIN_W - CARD_W + neg word [edi] +.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 + mcall SF_GET_SYS_TIME + push eax + and eax, 0x0F + sub eax, 7 + jnz .x_ok + inc eax +.x_ok: + mov [edi], ax + pop eax + shr eax, 4 + and eax, 0x07 + neg eax + sub eax, 4 + 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 + push esi + mov esi, eax + push ebx + push ecx + add ebx, 4 + add ecx, 4 + call draw_char_to_buf + pop ecx + pop ebx + pop esi + push esi + movzx eax, byte [esi+CARD_SUIT] + add eax, 13 + push ebx + push ecx + add ebx, 10 + add ecx, 4 + mov esi, eax + call draw_char_to_buf + pop ecx + pop ebx + pop esi + push esi + movzx eax, byte [esi+CARD_SUIT] + add eax, 13 + push ebx + push ecx + add ebx, CARD_W - 15 + add ecx, CARD_H - 11 + mov esi, eax + call draw_char_to_buf + pop ecx + pop ebx + pop esi + movzx eax, byte [esi+CARD_VALUE] + dec eax + push esi + mov esi, eax + push ebx + push ecx + add ebx, CARD_W - 9 + add ecx, CARD_H - 11 + call draw_char_to_buf + pop ecx + pop ebx + pop esi + pop esi + pop edx + + pushad + movzx eax, byte [esi+CARD_VALUE] + movzx edx, byte [esi+CARD_SUIT] + + movsx ebx, word [esi+CARD_X] + add ebx, 15 + movsx ecx, word [esi+CARD_Y] + add ecx, 20 + + cmp eax, 11 + je .face_jack + cmp eax, 12 + je .face_queen + cmp eax, 13 + je .face_king + jmp .patterns + +.face_jack: + cmp edx, 0 + je .set_jp + cmp edx, 1 + je .set_jd + cmp edx, 2 + je .set_jh + mov esi, db_jt + jmp .render_face + .set_jp: mov esi, db_jp + jmp .render_face + .set_jd: mov esi, db_jd + jmp .render_face + .set_jh: mov esi, db_jh + jmp .render_face + +.face_queen: + cmp edx, 0 + je .set_qp + cmp edx, 1 + je .set_qd + cmp edx, 2 + je .set_qh + mov esi, db_queen + jmp .render_face + .set_qp: mov esi, db_qp + jmp .render_face + .set_qd: mov esi, db_qd + jmp .render_face + .set_qh: mov esi, db_qh + jmp .render_face + +.face_king: + cmp edx, 0 + je .set_kp + cmp edx, 1 + je .set_kd + cmp edx, 2 + je .set_kh + mov esi, db_kt + jmp .render_face + .set_kp: mov esi, db_kp + jmp .render_face + .set_kd: mov esi, db_kkaro + jmp .render_face + .set_kh: mov esi, db_kh + jmp .render_face + +.render_face: + call draw_bitmap_to_buf + jmp .face_done + +.patterns: + mov edx, COL_RED + cmp byte [esi+CARD_SUIT], 2 + jb .set_center_color + mov edx, COL_BLACK +.set_center_color: + dec eax + mov eax, [pattern_offsets + eax*4] + add eax, card_patterns + mov ebp, eax +.pat_loop: + mov al, [ebp] + cmp al, 0xFF + je .face_done + push edx + movzx ebx, byte [ebp] + add bx, word [esi+CARD_X] + movzx ecx, byte [ebp+1] + add cx, word [esi+CARD_Y] + movzx eax, byte [esi+CARD_SUIT] + add eax, 13 + push esi + mov esi, eax + call draw_char_to_buf + pop esi + pop edx + add ebp, 2 + jmp .pat_loop + +.face_done: + popad + popad + ret + +draw_bitmap_to_buf: + pushad + add ecx, BMP_H + dec ecx + mov dword [temp_counter2], BMP_H +.row_loop: + cmp ecx, 0 + jl .skip_row + cmp ecx, WIN_H + jge .skip_row + push ebx + mov eax, ecx + imul eax, STRIDE + add eax, screen_buffer + mov ebp, BMP_W +.pixel_loop: + cmp ebx, 0 + jl .next_p + cmp ebx, WIN_W + jge .next_p + lea edi, [ebx*2 + ebx] + mov dl, [esi] + mov [eax+edi], dl + mov dl, [esi+1] + mov [eax+edi+1], dl + mov dl, [esi+2] + mov [eax+edi+2], dl +.next_p: + add esi, 3 + inc ebx + dec ebp + jnz .pixel_loop + pop ebx + jmp .row_finish +.skip_row: + add esi, BMP_LINE +.row_finish: + dec ecx + dec dword [temp_counter2] + jnz .row_loop + 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, 480 + 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 + ; Foundation slots (Top Right) + 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, 80 + dec dword [temp_counter2] + jnz .f_loop + + ; Stock slot (Top Left) + mov ebx, STOCK_X + mov ecx, FOUND_Y + mov edx, COL_SLOT + mov edi, CARD_W + mov ebp, CARD_H + call fill_rect_buf + + ; Waste slot (Next to Stock) + mov ebx, WASTE_X + mov ecx, FOUND_Y + mov edx, COL_SLOT + mov edi, CARD_W + mov ebp, CARD_H + call fill_rect_buf + + ; Tableau slots (Bottom rows) + mov ebx, 40 + mov ecx, 140 + 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, 105 + 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, 35 + cmp eax, 70 + ja .next_f + movsx eax, word [esi+CARD_Y] + sub eax, FOUND_Y + add eax, 50 + cmp eax, 100 + ja .next_f + call check_foundation_logic + jc .found_match +.next_f: + add ebx, 80 + 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, 35 + cmp eax, 70 + ja .nx_col + movsx eax, word [esi+CARD_Y] + movsx edx, word [edi+CARD_Y] + sub eax, edx + add eax, 50 + cmp eax, 100 + 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, 35 + cmp eax, 70 + ja .nx_col + movsx eax, word [esi+CARD_Y] + sub eax, 140 + add eax, 50 + cmp eax, 100 + ja .nx_col + cmp byte [esi+CARD_VALUE], 13 + jne .nx_col + mov dx, 140 + jmp .apply_move +.nx_col: + add ebx, 105 + cmp ebx, 750 + 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, 105 + 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, CARD_STRUCT_SIZE + 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, 105 + cmp ebx, 750 + 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 + +; --- PRNG (Linear Congruential Generator) --- +; Uses constants from Numerical Recipes: a=1103515245, c=12345 +seed_rng: + mcall SF_GET_SYS_TIME + imul eax, eax, 1103515245 + add eax, 12345 + mov [rng_state], eax + ret + +; Returns pseudo-random value in eax +rng_next: + mov eax, [rng_state] + imul eax, eax, 1103515245 + add eax, 12345 + mov [rng_state], eax + shr eax, 16 + and eax, 0x7FFF + ret + +shuffle_deck: + call seed_rng + mov dword [temp_counter2], 51 +.sh: + call rng_next + 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, 140 + 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, 105 + 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 --- +title db 'Solitare V1.6', 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 + + +; ... Font Data ... +font_8x12: +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,11111000b +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 + +pattern_offsets: +dd p1-card_patterns, p2-card_patterns, p3-card_patterns, p4-card_patterns, p5-card_patterns +dd p6-card_patterns, p7-card_patterns, p8-card_patterns, p9-card_patterns, p10-card_patterns +card_patterns: +p1 db 32,46, 0xFF +p2 db 32,20, 32,72, 0xFF +p3 db 32,20, 32,46, 32,72, 0xFF +p4 db 18,20, 47,20, 18,72, 47,72, 0xFF +p5 db 18,20, 47,20, 18,72, 47,72, 32,46, 0xFF +p6 db 18,20, 47,20, 18,46, 47,46, 18,72, 47,72, 0xFF +p7 db 18,20, 47,20, 18,46, 47,46, 18,72, 47,72, 32,33, 0xFF +p8 db 18,20, 47,20, 18,46, 47,46, 18,72, 47,72, 32,33, 32,59, 0xFF +p9 db 18,20, 47,20, 18,40, 47,40, 18,60, 47,60, 18,80, 47,80, 32,50, 0xFF +p10 db 18,20, 47,20, 18,40, 47,40, 18,60, 47,60, 18,80, 47,80, 32,30, 32,70, 0xFF + +align 4 +; --- Bitmap Data --- +; Suit mapping: 0=hearts(red), 1=diamonds(red), 2=clubs(black), 3=spades(black) +; File naming: h=hearts, d=diamonds, t=trefl/clubs, p=pik/spades +db_jp: file 'jh.bmp':54 +db_jh: file 'jt.bmp':54 +db_jd: file 'jd.bmp':54 +db_jt: file 'jp.bmp':54 +db_qp: file 'qh.bmp':54 +db_qh: file 'qt.bmp':54 +db_qd: file 'qd.bmp':54 +db_queen: file 'qp.bmp':54 +db_kp: file 'kh.bmp':54 +db_kh: file 'kt.bmp':54 +db_kkaro: file 'kd.bmp':54 +db_kt: file 'kp.bmp':54 + + +i_end: + +score rd 1 +game_won rb 1 +win_timer rd 1 +win_thread_active rb 1 +should_reset rb 1 +exit_request rb 1 +mouse_lock rd 1 +force_redraw rb 1 +temp_counter rd 1 +temp_counter2 rd 1 +temp_val rd 1 +mouse_off_x rw 1 +mouse_off_y rw 1 +temp_active rd 1 +temp_card_buf rd 3 +active_card_count rd 1 +active_stack rd 13 +skin_h rd 1 +cascade_timer rd 1 +active_cascade_idx rd 1 +win_pos_x rd 1 +win_pos_y rd 1 +rng_state rd 1 + +align 4 +deck_data: rb 52 * CARD_STRUCT_SIZE +card_vectors: rw 52 * 2 +win_proc_info: rb 1024 +score_str rb 16 + +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: