/
chnik
/
StratCore
Обзор
Документация
Войти
/
chnik
/
StratCore
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
1
CI/CD
Аналитика
Безопасность
master
engine/gui.asm
2 473 строки
66 KB
chnik
create: entity.asm, gui.asm, input.asm, macros.inc, sound.asm, types.inc, win64_entry.asm
15 май 2026, 16:00
Верифицирован
15 май 2026, 16:00
75001cf
Код
Авторство
О чём код?
; ============================================================ ; StratCore Engine - gui.asm ; Native Windows desktop interface built from standard controls. ; Built from native Windows controls. No console screen, no pseudo-shell. ; ============================================================ %include "engine/macros.inc" %include "engine/types.inc" %include "game/buildings.inc" ; ---------- WinAPI/control constants ---------- WM_DESTROY equ 0x0002 WM_COMMAND equ 0x0111 WM_TIMER equ 0x0113 WM_CTLCOLOREDIT equ 0x0133 WM_CTLCOLORSTATIC equ 0x0138 WM_SETFONT equ 0x0030 WM_KEYDOWN equ 0x0100 WM_CHAR equ 0x0102 VK_RETURN equ 0x0D GWLP_WNDPROC equ -4 WS_OVERLAPPEDWINDOW equ 0x00CF0000 WS_VISIBLE equ 0x10000000 WS_CHILD equ 0x40000000 WS_BORDER equ 0x00800000 WS_VSCROLL equ 0x00200000 WS_TABSTOP equ 0x00010000 ES_LEFT equ 0x0000 ES_MULTILINE equ 0x0004 ES_AUTOVSCROLL equ 0x0040 ES_READONLY equ 0x0800 LBS_NOTIFY equ 0x0001 LBS_HASSTRINGS equ 0x0040 LB_ADDSTRING equ 0x0180 LB_RESETCONTENT equ 0x0184 LB_GETCURSEL equ 0x0188 LB_SETCURSEL equ 0x0186 LBN_SELCHANGE equ 1 BS_PUSHBUTTON equ 0x00000000 BS_DEFPUSHBUTTON equ 0x00000001 CW_USEDEFAULT equ -2147483648 IDC_ARROW equ 32512 COLOR_WINDOW equ 5 COLOR_WINDOW_PLUS_1 equ 6 DEFAULT_GUI_FONT equ 17 TRANSPARENT equ 1 OPAQUE equ 2 CP_UTF8 equ 65001 FILE_APPEND_DATA equ 0x00000004 FILE_SHARE_READ equ 0x00000001 OPEN_ALWAYS equ 4 FILE_ATTRIBUTE_NORMAL equ 0x00000080 ID_CATEGORY_LIST equ 1001 ID_BUILDING_LIST equ 1002 ID_ACTIVE_LIST equ 1003 ID_ORDER_EDIT equ 1004 ID_EXECUTE equ 1005 ID_BTN_CATS equ 1006 ID_BTN_BUILD equ 1007 ID_BTN_DEMOLISH equ 1008 ID_BTN_INFO equ 1009 ID_BTN_NEXT equ 1010 ID_BTN_HELP equ 1011 ID_BTN_EXIT equ 1012 ID_BTN_ACTIONS equ 1013 ID_BTN_PAYMODE equ 1014 ID_POP_TITLE equ 1015 ID_POP_INFO equ 1016 %macro GUI_CREATE 9 sub rsp, 96 xor ecx, ecx lea rdx, [rel %2] lea r8, [rel %3] mov r9d, %4 mov qword [rsp + 32], %5 mov qword [rsp + 40], %6 mov qword [rsp + 48], %7 mov qword [rsp + 56], %8 mov rax, [rel gui_hwnd] mov [rsp + 64], rax mov qword [rsp + 72], %9 mov rax, [rel gui_hinstance] mov [rsp + 80], rax mov qword [rsp + 88], 0 call CreateWindowExW mov [rel %1], rax add rsp, 96 %endmacro section .data gui_class_name dw 'S','t','r','a','t','C','o','r','e','N','a','t','i','v','e','G','U','I',0 gui_window_title dw 'S','t','r','a','t','C','o','r','e',' ','/',' ','L','a','s','t',' ','W','a','r',0 ctl_static dw 'S','T','A','T','I','C',0 ctl_edit dw 'E','D','I','T',0 ctl_button dw 'B','U','T','T','O','N',0 ctl_listbox dw 'L','I','S','T','B','O','X',0 empty_w dw 0 winmm_dll_w dw 'w','i','n','m','m','.','d','l','l',0 proc_time_begin db 'timeBeginPeriod',0 proc_time_end db 'timeEndPeriod',0 u8_empty db 0 u8_metrics_prefix db "Память: ",0 u8_mem_tick db " Б | изменение памяти: ",0 u8_ops db " Б | ассемблерных операций: ",0 u8_frame db " | кадр: ",0 u8_tick db " | ход: ",0 u8_balance_title db "Баланс фракции",13,10,0 u8_food db "Пища: ",0 u8_water db " Вода: ",0 u8_ammo db " Патроны: ",0 u8_energy db " Энергия: ",0 u8_loyalty db " Лояльность: ",0 u8_space db " Место: ",0 u8_slash db "/",0 u8_free db " свободно: ",0 u8_active_title db "Активные постройки",0 u8_categories_title db "Области строительства",0 u8_catalog_title db "Постройки выбранной области",0 u8_details_title db "Карточка постройки",0 u8_message_title db "Сводка",0 u8_order_label db "Приказ фракции:",0 u8_btn_execute db "Выполнить",0 u8_btn_cats db "Обновить",0 u8_btn_build db "Построить x1",0 u8_btn_demolish db "Демонтировать x1",0 u8_btn_info db "Описание",0 u8_btn_next db "Ход",0 u8_btn_help db "Справка",0 u8_btn_exit db "Выход",0 u8_btn_actions db "Действия",0 u8_btn_pay_ammo db "Оплата: патроны",0 u8_btn_pay_labor db "Оплата: трудодни",0 u8_pop_title db "Население",0 u8_pop_head db "Население и структура",13,10,0 u8_pop_total db "Всего: ",0 u8_pop_slum db " | Бедняки: ",0 u8_pop_worker db " | Рабочие: ",0 u8_pop_military db " | Военные: ",0 u8_pop_elite db " | Элита: ",0 u8_pop_bureau db " | Бюрократы: ",0 u8_pop_needs_food db 13,10,"Пища: ",0 u8_pop_needs_water db " | Вода: ",0 u8_pop_needs_ammo db " | Патроны: ",0 u8_pop_needs_unit db " ед./нед.",0 u8_pop_turn_food db " | Расход: ",0 u8_pop_turn_water db " | ",0 u8_pop_turn_ammo db " | ",0 u8_pop_turn_unit db " ед.",0 u8_cat1 db "1. Продуктовые",0 u8_cat2 db "2. Транспортные",0 u8_cat3 db "3. Энергетические",0 u8_cat4 db "4. Торговые",0 u8_cat5 db "5. Развлекательные",0 u8_cat6 db "6. Производственные",0 u8_cat7 db "7. Военные",0 u8_cat8 db "8. Обслуживающие",0 u8_cat9 db "9. Укрепления и оборона",0 cat_table dq u8_cat1,u8_cat2,u8_cat3,u8_cat4,u8_cat5,u8_cat6,u8_cat7,u8_cat8,u8_cat9,0 u8_msg_ready db "Готово к управлению фракцией.",0 u8_msg_help db "Справка",13,10,"Выберите область слева, затем постройку в списке.",13,10,"Построить x1: строит выбранную постройку выбранным способом оплаты.",13,10,"Оплата: переключает патроны/трудодни.",13,10,"Действия: открывает прямые решения игрока.",13,10,"Ход: производство зданий минус потребности населения.",13,10,"Команды: помощь, действия, раздать патроны, построить 7 2, демонтаж 7, инфо 7, ход.",0 u8_msg_select_building db "Выберите постройку в списке выбранной области.",0 u8_msg_select_category db "Выберите область строительства слева.",0 u8_msg_order_empty db "Пустой приказ не выполнен.",0 u8_no_building db "Постройка не выбрана.",0 u8_details_hint db "Выберите область и постройку. Здесь появится карточка: стоимость, место, рабочие, баланс за ход и описание.",0 u8_active_empty db "Активных построек нет",0 u8_hash db "#",0 u8_x db " x",0 u8_pipe db " | ",0 u8_cost db "стоимость: ",0 u8_cost_ammo db "патроны ",0 u8_cost_labor db "трудодни ",0 u8_cost_sep db " / ",0 u8_size db "место: ",0 u8_workers db "рабочие: ",0 u8_count db "построено: ",0 u8_f db " Пщ ",0 u8_w db " Вд ",0 u8_a db " Птр ",0 u8_e db " Эн ",0 u8_newline db 13,10,0 u8_id db "ID: ",0 u8_name db "Название: ",0 u8_desc db "Описание: ",0 u8_flags db "Флаги: ",0 u8_cmd_build db "build ",0 u8_cmd_demolish db "demolish ",0 u8_cmd_next db "ход",0 u8_cmd_categories db "категории",0 u8_cmd_help db "помощь",0 u8_cmd_actions db "действия",0 u8_bytes_suffix db " Б",0 u8_kib_open db " (",0 u8_kib_suffix db " КиБ)",0 u8_unit_each db " ед.",0 u8_unit_per_turn db " ед./ход",0 u8_unit_ammo_cost db " патр.",0 u8_unit_space db " м2",0 u8_unit_people db " чел.",0 u8_unit_percent db "%",0 u8_unit_labor_cost db " труд.",0 u8_population db "Население: ",0 u8_labor_days db " Трудодни: ",0 u8_needs_sat db " Потребности: ",0 u8_refresh_ok db "Данные интерфейса обновлены.",0 u8_pay_ammo_ok db "Оплата строительства: патроны.",0 u8_pay_labor_ok db "Оплата строительства: трудодни.",0 u8_actions_head db "Действия игрока",13,10,0 u8_action_ammo_line db "1. раздать патроны: расход = население x норма патронов, лояльность +5.",13,10,0 u8_action_hint db "Команда: раздать патроны. Да, народ любит, когда его не только убеждают дубинкой.",0 u8_needs_head db "Скрытая карта потребностей",13,10,0 u8_needs_rates db "Норма на человека/ход: пища ",0 u8_needs_water db "; вода ",0 u8_needs_ammo db "; патроны ",0 u8_needs_total db 13,10,"Расход фракции/ход: пища ",0 u8_needs_sat_label db 13,10,"Удовлетворение: ",0 u8_needs_loyalty_label db "; лояльность: ",0 u8_needs_labor_label db "; трудодни: ",0 u8_needs_workers_label db 13,10,"Занятость зданий: ",0 u8_needs_unemployed_label db "; свободно населения: ",0 u8_needs_formula db 13,10,"Формула трудодней: (население - рабочие - солдаты) x лояльность x удовлетворение / 10000.",0 u8_life_level_label db 13,10,"Уровень жизни: ",0 u8_life_level_suffix db 0 u8_employment_head db "Занятость: ",0 u8_emp_food db "пища ",0 u8_emp_semicolon db "; ",0 u8_emp_water db "вода ",0 u8_emp_trade db "торговля ",0 u8_emp_ent db "развлечения ",0 u8_emp_mil db "военные ",0 u8_emp_prod db "промышленность ",0 u8_emp_build db "строительство ",0 u8_turn_head db "Ход ",0 u8_turn_done db " завершён.",13,10,"Изменение баланса: ",0 u8_turn_food db "пища ",0 u8_turn_water db "; вода ",0 u8_turn_ammo db "; патроны ",0 u8_turn_energy db "; энергия ",0 u8_turn_space db 13,10,"Место занято: ",0 u8_turn_workers db "; рабочих требуется: ",0 u8_turn_labor db 13,10,"Трудодни на следующий ход: ",0 u8_turn_needs db "; потребности закрыты на ",0 u8_turn_loyalty db "; лояльность ",0 u8_order_file db "orders_utf8.txt",0 u8_order_prefix db "Приказ сохранён: ",0 u8_order_empty db "Текст приказа после двоеточия пуст.",0 u8_file_turn db "Ход ",0 u8_colon_space db ": ",0 u8_crlf db 13,10,0 frame_counter dq 0 op_counter dq 0 mem_usage dq SimState_size mem_tick_delta dq 0 last_message_ptr dq u8_msg_ready section .bss gui_hinstance resq 1 gui_hwnd resq 1 gui_sim_state resq 1 gui_msg resb 48 gui_wndclass resb 80 gui_hwinmm resq 1 gui_p_time_begin resq 1 gui_p_time_end resq 1 h_title resq 1 h_metrics resq 1 h_balance resq 1 h_active_title resq 1 h_active_list resq 1 h_categories_title resq 1 h_category_list resq 1 h_catalog_title resq 1 h_building_list resq 1 h_details_title resq 1 h_details resq 1 h_message_title resq 1 h_message resq 1 h_order_label resq 1 h_order_edit resq 1 h_btn_execute resq 1 h_btn_cats resq 1 h_btn_build resq 1 h_btn_demolish resq 1 h_btn_info resq 1 h_btn_next resq 1 h_btn_help resq 1 h_btn_exit resq 1 h_btn_actions resq 1 h_btn_paymode resq 1 h_pop_title resq 1 h_pop_info resq 1 gui_old_edit_proc resq 1 gui_text_buffer resb 65536 gui_text_len resq 1 gui_wide_buffer resw 65536 render_buf resb 64 gui_command_tmp resb CMD_BUF_LEN gui_dynamic_message resb 4096 gui_order_message resb 2048 orders_written resd 1 last_message_is_order resd 1 gui_building_ids resd MAX_BUILDINGS gui_building_count resd 1 section .text global gui_init global gui_run global gui_update_stats global gui_add_ops global gui_set_message global gui_show_needs global gui_show_actions %ifdef WINDOWS extern GetModuleHandleW extern LoadCursorW extern RegisterClassExW extern CreateWindowExW extern ShowWindow extern UpdateWindow extern SetTimer extern KillTimer extern GetMessageW extern TranslateMessage extern DispatchMessageW extern DefWindowProcW extern PostQuitMessage extern DestroyWindow extern SendMessageW extern SetWindowTextW extern GetWindowTextW extern SetWindowLongPtrW extern CallWindowProcW extern MultiByteToWideChar extern WideCharToMultiByte extern LoadLibraryW extern GetProcAddress extern FreeLibrary extern CreateFileA extern WriteFile extern CloseHandle extern SetTextColor extern SetBkMode extern GetSysColorBrush extern InvalidateRect %endif extern input_process_command extern rules_turn extern rules_calc_labor_cost extern turn_workers_food extern turn_workers_water extern turn_workers_trade extern turn_workers_ent extern turn_workers_mil extern turn_workers_prod extern turn_workers_build ; ============================================================ ; Public API ; ============================================================ gui_init: mov qword [rel frame_counter], 0 mov qword [rel op_counter], 0 mov qword [rel mem_tick_delta], 0 mov dword [rel last_message_is_order], 0 lea rax, [rel u8_msg_ready] mov [rel last_message_ptr], rax ret gui_set_message: push rbp mov rbp, rsp test rdi, rdi jz .done mov dword [rel last_message_is_order], 0 mov [rel last_message_ptr], rdi mov rax, [rel h_message] test rax, rax jz .done mov rsi, rdi mov rdi, rax call gui_set_control_text_utf8 .done: pop rbp ret gui_add_ops: add [rel op_counter], rdi ret gui_update_stats: push rbp mov rbp, rsp call gui_update_metrics pop rbp ret ; gui_run(rdi = SimState*) gui_run: push rbp mov rbp, rsp push rbx push r12 mov [rel gui_sim_state], rdi sub rsp, 32 xor ecx, ecx call GetModuleHandleW add rsp, 32 mov [rel gui_hinstance], rax mov rbx, rax sub rsp, 32 xor ecx, ecx mov edx, IDC_ARROW call LoadCursorW add rsp, 32 mov r12, rax lea rdi, [rel gui_wndclass] mov rcx, 80 xor eax, eax rep stosb mov dword [rel gui_wndclass + 0], 80 mov dword [rel gui_wndclass + 4], 3 lea rax, [rel gui_wndproc] mov [rel gui_wndclass + 8], rax mov [rel gui_wndclass + 24], rbx mov [rel gui_wndclass + 40], r12 mov qword [rel gui_wndclass + 48], COLOR_WINDOW_PLUS_1 lea rax, [rel gui_class_name] mov [rel gui_wndclass + 64], rax sub rsp, 32 lea rcx, [rel gui_wndclass] call RegisterClassExW add rsp, 32 sub rsp, 96 xor ecx, ecx lea rdx, [rel gui_class_name] lea r8, [rel gui_window_title] mov r9d, WS_OVERLAPPEDWINDOW | WS_VISIBLE mov qword [rsp + 32], CW_USEDEFAULT mov qword [rsp + 40], CW_USEDEFAULT mov qword [rsp + 48], 1180 mov qword [rsp + 56], 900 mov qword [rsp + 64], 0 mov qword [rsp + 72], 0 mov rax, [rel gui_hinstance] mov [rsp + 80], rax mov qword [rsp + 88], 0 call CreateWindowExW add rsp, 96 mov [rel gui_hwnd], rax test rax, rax jz .done call gui_create_controls call gui_refresh_all sub rsp, 32 mov rcx, [rel gui_hwnd] mov edx, 1 call ShowWindow mov rcx, [rel gui_hwnd] call UpdateWindow add rsp, 32 call gui_timer_resolution_begin sub rsp, 32 mov rcx, [rel gui_hwnd] mov edx, 1 mov r8d, 1 xor r9d, r9d call SetTimer add rsp, 32 .msg_loop: sub rsp, 32 lea rcx, [rel gui_msg] xor edx, edx xor r8d, r8d xor r9d, r9d call GetMessageW add rsp, 32 test eax, eax jle .done sub rsp, 32 lea rcx, [rel gui_msg] call TranslateMessage lea rcx, [rel gui_msg] call DispatchMessageW add rsp, 32 jmp .msg_loop .done: call gui_timer_resolution_end lea rsp, [rbp - 16] pop r12 pop rbx pop rbp ret ; ============================================================ ; Multimedia timer resolution through run-time loading. ; Static imports from winmm.dll are intentionally avoided: GoLink can ; otherwise bind timeBeginPeriod/timeEndPeriod to the wrong DLL on some ; builds, and Windows kills the process before the first instruction. ; ============================================================ gui_timer_resolution_begin: push rbp mov rbp, rsp and rsp, -16 sub rsp, 32 lea rcx, [rel winmm_dll_w] call LoadLibraryW mov [rel gui_hwinmm], rax test rax, rax jz .done mov rcx, rax lea rdx, [rel proc_time_begin] call GetProcAddress mov [rel gui_p_time_begin], rax mov rcx, [rel gui_hwinmm] lea rdx, [rel proc_time_end] call GetProcAddress mov [rel gui_p_time_end], rax mov rax, [rel gui_p_time_begin] test rax, rax jz .done mov ecx, 1 call rax .done: mov rsp, rbp pop rbp ret gui_timer_resolution_end: push rbp mov rbp, rsp and rsp, -16 sub rsp, 32 mov rax, [rel gui_p_time_end] test rax, rax jz .free mov ecx, 1 call rax .free: mov rcx, [rel gui_hwinmm] test rcx, rcx jz .done call FreeLibrary mov qword [rel gui_hwinmm], 0 mov qword [rel gui_p_time_begin], 0 mov qword [rel gui_p_time_end], 0 .done: mov rsp, rbp pop rbp ret ; ============================================================ ; Window procedure ; ============================================================ gui_wndproc: push rbp mov rbp, rsp push rbx push r12 push r13 push r14 mov rbx, rcx mov r12, rdx mov r13, r8 mov r14, r9 mov [rel gui_hwnd], rbx cmp r12d, WM_CTLCOLORSTATIC je .ctlcolor cmp r12d, WM_CTLCOLOREDIT je .ctlcolor cmp r12d, WM_DESTROY je .destroy cmp r12d, WM_TIMER je .timer cmp r12d, WM_COMMAND je .command cmp r12d, WM_KEYDOWN je .keydown jmp .default .ctlcolor: mov rax, [rel h_message] cmp r14, rax jne .default sub rsp, 32 mov rcx, r13 cmp dword [rel last_message_is_order], 0 jne .ctl_yellow xor edx, edx jmp .ctl_set .ctl_yellow: mov edx, 000000FFh .ctl_set: call SetTextColor mov rcx, r13 mov edx, OPAQUE call SetBkMode mov ecx, COLOR_WINDOW call GetSysColorBrush add rsp, 32 jmp .out .default: sub rsp, 32 mov rcx, rbx mov rdx, r12 mov r8, r13 mov r9, r14 call DefWindowProcW add rsp, 32 jmp .out .timer: inc qword [rel frame_counter] mov rdi, 1 call gui_add_ops call gui_update_metrics xor eax, eax jmp .out .command: mov eax, r13d and eax, 0FFFFh cmp eax, ID_CATEGORY_LIST je .category_list cmp eax, ID_BUILDING_LIST je .building_list cmp eax, ID_EXECUTE je .execute cmp eax, ID_BTN_CATS je .btn_cats cmp eax, ID_BTN_BUILD je .btn_build cmp eax, ID_BTN_DEMOLISH je .btn_demolish cmp eax, ID_BTN_INFO je .btn_info cmp eax, ID_BTN_NEXT je .btn_next cmp eax, ID_BTN_HELP je .btn_help cmp eax, ID_BTN_ACTIONS je .btn_actions cmp eax, ID_BTN_PAYMODE je .btn_paymode cmp eax, ID_BTN_EXIT je .btn_exit xor eax, eax jmp .out .keydown: cmp r13d, VK_RETURN jne .default mov eax, r14d cmp eax, [rel h_order_edit] jne .default call gui_execute_edit_command xor eax, eax jmp .out .category_list: mov eax, r13d shr eax, 16 cmp eax, LBN_SELCHANGE jne .out_zero call gui_on_category_select call gui_refresh_catalog_and_details xor eax, eax jmp .out .building_list: mov eax, r13d shr eax, 16 cmp eax, LBN_SELCHANGE jne .out_zero call gui_on_building_select call gui_update_details xor eax, eax jmp .out .execute: call gui_execute_edit_command xor eax, eax jmp .out .btn_cats: lea rdi, [rel u8_refresh_ok] call gui_set_message call gui_refresh_all xor eax, eax jmp .out .btn_build: lea rdi, [rel u8_cmd_build] call gui_command_selected_id xor eax, eax jmp .out .btn_demolish: lea rdi, [rel u8_cmd_demolish] call gui_command_selected_id xor eax, eax jmp .out .btn_info: mov rdi, [rel gui_sim_state] cmp dword [rdi + SimState.selected_building_id], 0 jne .btn_info_ok lea rdi, [rel u8_msg_select_building] call gui_set_message call gui_update_message xor eax, eax jmp .out .btn_info_ok: mov eax, [rdi + SimState.selected_building_id] mov dword [rdi + SimState.ui_mode], UI_MODE_INFO mov edi, eax call gui_find_building_by_id test rax, rax jz .btn_info_missing mov rbx, rax call gui_build_details_text call gui_set_dynamic_from_text call gui_update_details xor eax, eax jmp .out .btn_info_missing: lea rdi, [rel u8_msg_select_building] call gui_set_message call gui_update_message xor eax, eax jmp .out .btn_next: lea rdi, [rel u8_cmd_next] call gui_run_command_utf8 xor eax, eax jmp .out .btn_help: lea rdi, [rel u8_msg_help] call gui_set_message call gui_update_message xor eax, eax jmp .out .btn_actions: lea rdi, [rel u8_cmd_actions] call gui_run_command_utf8 xor eax, eax jmp .out .btn_paymode: call gui_toggle_payment_mode xor eax, eax jmp .out .btn_exit: sub rsp, 32 mov rcx, [rel gui_hwnd] call DestroyWindow add rsp, 32 xor eax, eax jmp .out .destroy: sub rsp, 32 mov rcx, rbx mov edx, 1 call KillTimer xor ecx, ecx call PostQuitMessage add rsp, 32 xor eax, eax jmp .out .out_zero: xor eax, eax .out: lea rsp, [rbp - 32] pop r14 pop r13 pop r12 pop rbx pop rbp ret ; ============================================================ ; Subclassed EDIT procedure — intercepts Enter key ; ============================================================ gui_edit_proc: push rbp mov rbp, rsp push rbx push r12 push r13 push r14 mov rbx, rcx ; hwnd mov r12d, edx ; uMsg mov r13, r8 ; wParam mov r14, r9 ; lParam cmp r12d, WM_CHAR jne .pass_through cmp r13d, VK_RETURN jne .pass_through ; Enter pressed — execute command call gui_execute_edit_command xor eax, eax jmp .out .pass_through: ; Pass to original procedure sub rsp, 32 mov rcx, [rel gui_old_edit_proc] mov rdx, rbx mov r8d, r12d mov r9, r13 mov [rsp + 32], r14 call CallWindowProcW add rsp, 32 .out: pop r14 pop r13 pop r12 pop rbx pop rbp ret ; ============================================================ ; Control creation and refresh ; ============================================================ gui_create_controls: push rbp mov rbp, rsp push rbx sub rsp, 8 GUI_CREATE h_title, ctl_static, empty_w, WS_CHILD|WS_VISIBLE, 14, 12, 900, 24, 0 GUI_CREATE h_metrics, ctl_static, empty_w, WS_CHILD|WS_VISIBLE, 14, 38,1100, 24, 0 GUI_CREATE h_balance, ctl_static, empty_w, WS_CHILD|WS_VISIBLE, 14, 78,1100, 70, 0 GUI_CREATE h_active_title, ctl_static, empty_w, WS_CHILD|WS_VISIBLE, 14, 160, 250, 22, 0 GUI_CREATE h_active_list, ctl_listbox, empty_w, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|LBS_NOTIFY|LBS_HASSTRINGS, 14,185,520,105, ID_ACTIVE_LIST GUI_CREATE h_pop_title, ctl_static, empty_w, WS_CHILD|WS_VISIBLE, 14, 295, 250, 22, 0 GUI_CREATE h_pop_info, ctl_edit, empty_w, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|ES_MULTILINE|ES_READONLY|ES_AUTOVSCROLL, 14,320,520,70, 0 GUI_CREATE h_categories_title, ctl_static, empty_w, WS_CHILD|WS_VISIBLE, 14, 398, 250, 22, 0 GUI_CREATE h_category_list, ctl_listbox, empty_w, WS_CHILD|WS_VISIBLE|WS_BORDER|LBS_NOTIFY|LBS_HASSTRINGS, 14,423,250,160, ID_CATEGORY_LIST GUI_CREATE h_catalog_title, ctl_static, empty_w, WS_CHILD|WS_VISIBLE, 280, 398, 360, 22, 0 GUI_CREATE h_building_list, ctl_listbox, empty_w, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|LBS_NOTIFY|LBS_HASSTRINGS,280,423,480,160, ID_BUILDING_LIST GUI_CREATE h_details_title, ctl_static, empty_w, WS_CHILD|WS_VISIBLE, 776, 398, 320, 22, 0 GUI_CREATE h_details, ctl_edit, empty_w, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|ES_MULTILINE|ES_READONLY|ES_AUTOVSCROLL, 776,423,370,160, 0 GUI_CREATE h_message_title, ctl_static, empty_w, WS_CHILD|WS_VISIBLE, 14, 595, 250, 22, 0 GUI_CREATE h_message, ctl_edit, empty_w, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|ES_MULTILINE|ES_READONLY|ES_AUTOVSCROLL, 14, 620,1132, 80, 0 GUI_CREATE h_order_label, ctl_static, empty_w, WS_CHILD|WS_VISIBLE, 14, 715, 200, 22, 0 GUI_CREATE h_order_edit, ctl_edit, empty_w, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP|ES_LEFT, 150,711,620,28, ID_ORDER_EDIT ; Подклассирование EDIT для перехвата Enter sub rsp, 32 mov rcx, [rel h_order_edit] mov edx, GWLP_WNDPROC lea r8, [rel gui_edit_proc] call SetWindowLongPtrW mov [rel gui_old_edit_proc], rax add rsp, 32 GUI_CREATE h_btn_execute, ctl_button, empty_w, WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_DEFPUSHBUTTON, 782,711,110,28, ID_EXECUTE GUI_CREATE h_btn_paymode, ctl_button, empty_w, WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_PUSHBUTTON, 904,711,190,28, ID_BTN_PAYMODE GUI_CREATE h_btn_cats, ctl_button, empty_w, WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_PUSHBUTTON, 14,750,105,30, ID_BTN_CATS GUI_CREATE h_btn_build, ctl_button, empty_w, WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_PUSHBUTTON, 132,750,130,30, ID_BTN_BUILD GUI_CREATE h_btn_demolish, ctl_button, empty_w, WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_PUSHBUTTON, 276,750,160,30, ID_BTN_DEMOLISH GUI_CREATE h_btn_info, ctl_button, empty_w, WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_PUSHBUTTON, 450,750,110,30, ID_BTN_INFO GUI_CREATE h_btn_next, ctl_button, empty_w, WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_PUSHBUTTON, 574,750,90,30, ID_BTN_NEXT GUI_CREATE h_btn_help, ctl_button, empty_w, WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_PUSHBUTTON, 678,750,105,30, ID_BTN_HELP GUI_CREATE h_btn_actions, ctl_button, empty_w, WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_PUSHBUTTON, 797,750,110,30, ID_BTN_ACTIONS GUI_CREATE h_btn_exit, ctl_button, empty_w, WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_PUSHBUTTON,1030,750,105,30, ID_BTN_EXIT ; Captions are UTF-8 internally, then converted to UTF-16 for controls. mov rdi, [rel h_title] lea rsi, [rel u8_window_caption] call gui_set_control_text_utf8 mov rdi, [rel h_pop_title] lea rsi, [rel u8_pop_title] call gui_set_control_text_utf8 call gui_update_population mov rdi, [rel h_active_title] lea rsi, [rel u8_active_title] call gui_set_control_text_utf8 mov rdi, [rel h_categories_title] lea rsi, [rel u8_categories_title] call gui_set_control_text_utf8 mov rdi, [rel h_catalog_title] lea rsi, [rel u8_catalog_title] call gui_set_control_text_utf8 mov rdi, [rel h_details_title] lea rsi, [rel u8_details_title] call gui_set_control_text_utf8 mov rdi, [rel h_message_title] lea rsi, [rel u8_message_title] call gui_set_control_text_utf8 mov rdi, [rel h_order_label] lea rsi, [rel u8_order_label] call gui_set_control_text_utf8 mov rdi, [rel h_btn_execute] lea rsi, [rel u8_btn_execute] call gui_set_control_text_utf8 call gui_update_paymode_caption mov rdi, [rel h_btn_cats] lea rsi, [rel u8_btn_cats] call gui_set_control_text_utf8 mov rdi, [rel h_btn_build] lea rsi, [rel u8_btn_build] call gui_set_control_text_utf8 mov rdi, [rel h_btn_demolish] lea rsi, [rel u8_btn_demolish] call gui_set_control_text_utf8 mov rdi, [rel h_btn_info] lea rsi, [rel u8_btn_info] call gui_set_control_text_utf8 mov rdi, [rel h_btn_next] lea rsi, [rel u8_btn_next] call gui_set_control_text_utf8 mov rdi, [rel h_btn_help] lea rsi, [rel u8_btn_help] call gui_set_control_text_utf8 mov rdi, [rel h_btn_actions] lea rsi, [rel u8_btn_actions] call gui_set_control_text_utf8 mov rdi, [rel h_btn_exit] lea rsi, [rel u8_btn_exit] call gui_set_control_text_utf8 call gui_populate_categories lea rsp, [rbp - 8] pop rbx pop rbp ret section .data u8_window_caption db "StratCore: управление фракцией / Last War",0 section .text gui_refresh_all: push rbp mov rbp, rsp call gui_update_metrics call gui_update_balance call gui_update_population call gui_update_active_list call gui_update_catalog call gui_update_details call gui_update_message call gui_update_paymode_caption pop rbp ret gui_refresh_catalog_and_details: push rbp mov rbp, rsp call gui_update_catalog call gui_update_details call gui_update_message pop rbp ret ; ============================================================ ; Payment mode and hidden population screens ; ============================================================ gui_update_paymode_caption: push rbx mov rdi, [rel h_btn_paymode] test rdi, rdi jz .done mov rbx, [rel gui_sim_state] test rbx, rbx jz .ammo cmp dword [rbx + SimState.build_payment_mode], PAY_MODE_LABOR je .labor .ammo: lea rsi, [rel u8_btn_pay_ammo] jmp .set .labor: lea rsi, [rel u8_btn_pay_labor] .set: call gui_set_control_text_utf8 .done: pop rbx ret gui_toggle_payment_mode: push rbx mov rbx, [rel gui_sim_state] test rbx, rbx jz .done cmp dword [rbx + SimState.build_payment_mode], PAY_MODE_LABOR je .to_ammo mov dword [rbx + SimState.build_payment_mode], PAY_MODE_LABOR lea rdi, [rel u8_pay_labor_ok] call gui_set_message jmp .refresh .to_ammo: mov dword [rbx + SimState.build_payment_mode], PAY_MODE_AMMO lea rdi, [rel u8_pay_ammo_ok] call gui_set_message .refresh: call gui_update_paymode_caption call gui_update_catalog call gui_update_details call gui_update_message .done: pop rbx ret gui_show_actions: call gui_buf_reset lea rsi, [rel u8_actions_head] call gui_append_cstr lea rsi, [rel u8_action_ammo_line] call gui_append_cstr lea rsi, [rel u8_action_hint] call gui_append_cstr call gui_set_dynamic_from_text ret gui_show_needs: push rbx push r12 mov rbx, [rel gui_sim_state] test rbx, rbx jz .done lea r12, [rbx + SimState.entities] call gui_buf_reset lea rsi, [rel u8_needs_head] call gui_append_cstr lea rsi, [rel u8_population] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.population] call gui_append_i64 lea rsi, [rel u8_unit_people] call gui_append_cstr lea rsi, [rel u8_newline] call gui_append_cstr lea rsi, [rel u8_needs_rates] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.need_food_pp] call gui_append_i64 lea rsi, [rel u8_needs_water] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.need_water_pp] call gui_append_i64 lea rsi, [rel u8_needs_ammo] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.need_ammo_pp] call gui_append_i64 lea rsi, [rel u8_needs_total] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.population] imul rax, [r12 + Entity.resources + FactionResources.need_food_pp] call gui_append_i64 lea rsi, [rel u8_needs_water] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.population] imul rax, [r12 + Entity.resources + FactionResources.need_water_pp] call gui_append_i64 lea rsi, [rel u8_needs_ammo] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.population] imul rax, [r12 + Entity.resources + FactionResources.need_ammo_pp] call gui_append_i64 lea rsi, [rel u8_needs_sat_label] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.needs_sat] call gui_append_i64 lea rsi, [rel u8_unit_percent] call gui_append_cstr lea rsi, [rel u8_needs_loyalty_label] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.loyalty] call gui_append_i64 lea rsi, [rel u8_unit_percent] call gui_append_cstr lea rsi, [rel u8_needs_labor_label] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.labor_days] call gui_append_i64 lea rsi, [rel u8_unit_labor_cost] call gui_append_cstr lea rsi, [rel u8_needs_workers_label] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.workers] call gui_append_i64 lea rsi, [rel u8_unit_people] call gui_append_cstr lea rsi, [rel u8_needs_unemployed_label] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.unemployed] call gui_append_i64 lea rsi, [rel u8_unit_people] call gui_append_cstr ; Life level display lea rsi, [rel u8_newline] call gui_append_cstr lea rsi, [rel u8_life_level_label] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.life_level] call gui_append_i64 lea rsi, [rel u8_life_level_suffix] call gui_append_cstr ; Employment breakdown lea rsi, [rel u8_newline] call gui_append_cstr lea rsi, [rel u8_employment_head] call gui_append_cstr lea rsi, [rel u8_emp_food] call gui_append_cstr mov rax, [rel turn_workers_food] call gui_append_i64 lea rsi, [rel u8_emp_semicolon] call gui_append_cstr lea rsi, [rel u8_emp_water] call gui_append_cstr mov rax, [rel turn_workers_water] call gui_append_i64 lea rsi, [rel u8_emp_semicolon] call gui_append_cstr lea rsi, [rel u8_emp_trade] call gui_append_cstr mov rax, [rel turn_workers_trade] call gui_append_i64 lea rsi, [rel u8_emp_semicolon] call gui_append_cstr lea rsi, [rel u8_emp_ent] call gui_append_cstr mov rax, [rel turn_workers_ent] call gui_append_i64 lea rsi, [rel u8_emp_semicolon] call gui_append_cstr lea rsi, [rel u8_emp_mil] call gui_append_cstr mov rax, [rel turn_workers_mil] call gui_append_i64 lea rsi, [rel u8_emp_semicolon] call gui_append_cstr lea rsi, [rel u8_emp_prod] call gui_append_cstr mov rax, [rel turn_workers_prod] call gui_append_i64 lea rsi, [rel u8_emp_semicolon] call gui_append_cstr lea rsi, [rel u8_emp_build] call gui_append_cstr mov rax, [rel turn_workers_build] call gui_append_i64 lea rsi, [rel u8_needs_formula] call gui_append_cstr call gui_set_dynamic_from_text .done: pop r12 pop rbx ret gui_append_build_cost_active: push rbx mov rbx, rbx lea rsi, [rel u8_cost] call gui_append_cstr mov rax, [rel gui_sim_state] test rax, rax jz .ammo cmp dword [rax + SimState.build_payment_mode], PAY_MODE_LABOR je .labor .ammo: mov rax, [rbx + Building.ammo_cost] call gui_append_i64 lea rsi, [rel u8_unit_ammo_cost] call gui_append_cstr jmp .done .labor: mov rdi, [rbx + Building.ammo_cost] mov rsi, [rbx + Building.size] mov rdx, [rbx + Building.workers] call rules_calc_labor_cost call gui_append_i64 lea rsi, [rel u8_unit_labor_cost] call gui_append_cstr .done: pop rbx ret gui_append_build_cost_both: push rbx lea rsi, [rel u8_cost] call gui_append_cstr mov rax, [rbx + Building.ammo_cost] call gui_append_i64 lea rsi, [rel u8_unit_ammo_cost] call gui_append_cstr lea rsi, [rel u8_cost_sep] call gui_append_cstr mov rdi, [rbx + Building.ammo_cost] mov rsi, [rbx + Building.size] mov rdx, [rbx + Building.workers] call rules_calc_labor_cost call gui_append_i64 lea rsi, [rel u8_unit_labor_cost] call gui_append_cstr pop rbx ret ; ============================================================ ; UI update functions ; ============================================================ gui_update_metrics: push rbx call gui_buf_reset lea rsi, [rel u8_metrics_prefix] call gui_append_cstr mov rax, [rel mem_usage] call gui_append_i64 lea rsi, [rel u8_kib_open] call gui_append_cstr mov rax, [rel mem_usage] shr rax, 10 call gui_append_i64 lea rsi, [rel u8_kib_suffix] call gui_append_cstr lea rsi, [rel u8_mem_tick] call gui_append_cstr mov rax, [rel mem_tick_delta] call gui_append_i64 lea rsi, [rel u8_ops] call gui_append_cstr mov rax, [rel op_counter] call gui_append_i64 lea rsi, [rel u8_frame] call gui_append_cstr mov rax, [rel frame_counter] call gui_append_i64 lea rsi, [rel u8_tick] call gui_append_cstr mov rbx, [rel gui_sim_state] test rbx, rbx jz .no_tick mov rax, [rbx + SimState.market + MarketState.turn_number] call gui_append_i64 .no_tick: mov rdi, [rel h_metrics] lea rsi, [rel gui_text_buffer] call gui_set_control_text_utf8 pop rbx ret gui_update_balance: push rbx mov rbx, [rel gui_sim_state] test rbx, rbx jz .done add rbx, SimState.entities call gui_buf_reset lea rsi, [rel u8_balance_title] call gui_append_cstr lea rsi, [rel u8_population] call gui_append_cstr mov rax, [rbx + Entity.resources + FactionResources.population] call gui_append_i64 lea rsi, [rel u8_unit_people] call gui_append_cstr lea rsi, [rel u8_labor_days] call gui_append_cstr mov rax, [rbx + Entity.resources + FactionResources.labor_days] call gui_append_i64 lea rsi, [rel u8_unit_labor_cost] call gui_append_cstr lea rsi, [rel u8_needs_sat] call gui_append_cstr mov rax, [rbx + Entity.resources + FactionResources.needs_sat] call gui_append_i64 lea rsi, [rel u8_unit_percent] call gui_append_cstr lea rsi, [rel u8_newline] call gui_append_cstr lea rsi, [rel u8_food] call gui_append_cstr mov rax, [rbx + Entity.resources + FactionResources.food] call gui_append_i64 lea rsi, [rel u8_unit_each] call gui_append_cstr mov rax, [rel gui_sim_state] mov rax, [rax + SimState.last_food_delta] call gui_append_delta_paren lea rsi, [rel u8_water] call gui_append_cstr mov rax, [rbx + Entity.resources + FactionResources.water] call gui_append_i64 lea rsi, [rel u8_unit_each] call gui_append_cstr mov rax, [rel gui_sim_state] mov rax, [rax + SimState.last_water_delta] call gui_append_delta_paren lea rsi, [rel u8_ammo] call gui_append_cstr mov rax, [rbx + Entity.resources + FactionResources.ammo] call gui_append_i64 lea rsi, [rel u8_unit_each] call gui_append_cstr mov rax, [rel gui_sim_state] mov rax, [rax + SimState.last_ammo_delta] call gui_append_delta_paren lea rsi, [rel u8_energy] call gui_append_cstr mov rax, [rbx + Entity.resources + FactionResources.energy] call gui_append_i64 lea rsi, [rel u8_unit_each] call gui_append_cstr mov rax, [rel gui_sim_state] mov rax, [rax + SimState.last_energy_delta] call gui_append_delta_paren lea rsi, [rel u8_loyalty] call gui_append_cstr mov rax, [rbx + Entity.resources + FactionResources.loyalty] call gui_append_i64 lea rsi, [rel u8_unit_percent] call gui_append_cstr lea rsi, [rel u8_space] call gui_append_cstr mov rax, [rbx + Entity.resources + FactionResources.used_space] call gui_append_i64 lea rsi, [rel u8_unit_space] call gui_append_cstr lea rsi, [rel u8_slash] call gui_append_cstr mov rax, [rbx + Entity.resources + FactionResources.territory] call gui_append_i64 lea rsi, [rel u8_unit_space] call gui_append_cstr lea rsi, [rel u8_free] call gui_append_cstr mov rax, [rbx + Entity.resources + FactionResources.territory] sub rax, [rbx + Entity.resources + FactionResources.used_space] call gui_append_i64 lea rsi, [rel u8_unit_space] call gui_append_cstr mov rdi, [rel h_balance] lea rsi, [rel gui_text_buffer] call gui_set_control_text_utf8 .done: pop rbx ret gui_update_population: push rbx push r12 mov rbx, [rel gui_sim_state] test rbx, rbx jz .done lea r12, [rbx + SimState.entities] call gui_buf_reset lea rsi, [rel u8_pop_head] call gui_append_cstr lea rsi, [rel u8_pop_total] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.population] call gui_append_i64 lea rsi, [rel u8_pop_slum] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.social_slum] call gui_append_i64 lea rsi, [rel u8_pop_worker] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.social_worker] call gui_append_i64 lea rsi, [rel u8_pop_military] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.social_military] call gui_append_i64 lea rsi, [rel u8_pop_elite] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.social_elite] call gui_append_i64 lea rsi, [rel u8_pop_bureau] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.bureaucrats] call gui_append_i64 lea rsi, [rel u8_pop_needs_food] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.need_food_pp] call gui_append_i64 lea rsi, [rel u8_pop_needs_unit] call gui_append_cstr lea rsi, [rel u8_pop_turn_food] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.population] imul rax, [r12 + Entity.resources + FactionResources.need_food_pp] xor edx, edx mov rcx, 100 div rcx call gui_append_i64 lea rsi, [rel u8_pop_turn_unit] call gui_append_cstr lea rsi, [rel u8_pop_needs_water] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.need_water_pp] call gui_append_i64 lea rsi, [rel u8_pop_needs_unit] call gui_append_cstr lea rsi, [rel u8_pop_turn_water] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.population] imul rax, [r12 + Entity.resources + FactionResources.need_water_pp] xor edx, edx mov rcx, 100 div rcx call gui_append_i64 lea rsi, [rel u8_pop_turn_unit] call gui_append_cstr lea rsi, [rel u8_pop_needs_ammo] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.need_ammo_pp] call gui_append_i64 lea rsi, [rel u8_pop_needs_unit] call gui_append_cstr lea rsi, [rel u8_pop_turn_ammo] call gui_append_cstr mov rax, [r12 + Entity.resources + FactionResources.population] imul rax, [r12 + Entity.resources + FactionResources.need_ammo_pp] xor edx, edx mov rcx, 100 div rcx call gui_append_i64 lea rsi, [rel u8_pop_turn_unit] call gui_append_cstr lea rsi, [rel u8_newline] call gui_append_cstr mov rdi, [rel h_pop_info] lea rsi, [rel gui_text_buffer] call gui_set_control_text_utf8 .done: pop r12 pop rbx ret gui_update_message: push rbp mov rbp, rsp mov rdi, [rel h_message] mov rsi, [rel last_message_ptr] test rsi, rsi jnz .ok lea rsi, [rel u8_empty] .ok: call gui_set_control_text_utf8 pop rbp ret gui_update_active_list: push rbx push r12 push r13 mov rdi, [rel h_active_list] call gui_listbox_reset mov rbx, [rel gui_sim_state] test rbx, rbx jz .done add rbx, SimState.entities + Entity.buildings xor r13d, r13d mov r12d, MAX_BUILDINGS .loop: cmp qword [rbx + Building.active_count], 0 jle .next call gui_build_active_line mov rdi, [rel h_active_list] lea rsi, [rel gui_text_buffer] call gui_listbox_add_utf8 inc r13d .next: add rbx, Building_size dec r12d jnz .loop test r13d, r13d jnz .done mov rdi, [rel h_active_list] lea rsi, [rel u8_active_empty] call gui_listbox_add_utf8 .done: pop r13 pop r12 pop rbx ret gui_populate_categories: push rbx mov rdi, [rel h_category_list] call gui_listbox_reset lea rbx, [rel cat_table] .loop: mov rsi, [rbx] test rsi, rsi jz .done mov rdi, [rel h_category_list] call gui_listbox_add_utf8 add rbx, 8 jmp .loop .done: pop rbx ret gui_update_catalog: push rbp mov rbp, rsp push rbx push r12 push r13 push r14 mov rdi, [rel h_building_list] call gui_listbox_reset mov dword [rel gui_building_count], 0 mov r14, [rel gui_sim_state] test r14, r14 jz .done mov eax, [r14 + SimState.selected_category] test eax, eax jnz .has_cat mov dword [r14 + SimState.selected_category], 1 mov eax, 1 .has_cat: mov r13d, eax lea rbx, [r14 + SimState.entities + Entity.buildings] mov r12d, MAX_BUILDINGS .loop: movzx eax, byte [rbx + Building.category] cmp eax, r13d jne .next call gui_build_catalog_line mov rdi, [rel h_building_list] lea rsi, [rel gui_text_buffer] call gui_listbox_add_utf8 mov ecx, [rel gui_building_count] movzx eax, word [rbx + Building.id] lea rdx, [rel gui_building_ids] mov [rdx + rcx*4], eax inc ecx mov [rel gui_building_count], ecx .next: add rbx, Building_size dec r12d jnz .loop .done: lea rsp, [rbp - 32] pop r14 pop r13 pop r12 pop rbx pop rbp ret gui_update_details: push rbx mov rbx, [rel gui_sim_state] test rbx, rbx jz .hint mov eax, [rbx + SimState.selected_building_id] test eax, eax jz .hint mov edi, eax call gui_find_building_by_id test rax, rax jz .hint mov rbx, rax call gui_build_details_text mov rdi, [rel h_details] lea rsi, [rel gui_text_buffer] call gui_set_control_text_utf8 pop rbx ret .hint: mov rdi, [rel h_details] lea rsi, [rel u8_details_hint] call gui_set_control_text_utf8 pop rbx ret ; ============================================================ ; Command execution ; ============================================================ gui_execute_edit_command: push rbx mov rbx, [rel gui_sim_state] test rbx, rbx jz .done sub rsp, 32 mov rcx, [rel h_order_edit] lea rdx, [rel gui_wide_buffer] mov r8d, CMD_BUF_LEN call GetWindowTextW add rsp, 32 sub rsp, 64 mov ecx, CP_UTF8 xor edx, edx lea r8, [rel gui_wide_buffer] mov r9d, -1 lea rax, [rbx + SimState.player_cmd] mov [rsp + 32], rax mov qword [rsp + 40], CMD_BUF_LEN mov qword [rsp + 48], 0 mov qword [rsp + 56], 0 call WideCharToMultiByte add rsp, 64 cmp eax, 1 jg .has_text lea rdi, [rel u8_msg_order_empty] call gui_set_message jmp .clear_and_refresh .has_text: dec eax mov [rbx + SimState.cmd_len], eax lea rsi, [rbx + SimState.player_cmd] .order_skip_left: mov al, [rsi] cmp al, ' ' je .order_advance cmp al, 9 je .order_advance jmp .order_check .order_advance: inc rsi jmp .order_skip_left .order_check: cmp byte [rsi], ':' jne .normal_command inc rsi .order_skip_after_colon: mov al, [rsi] cmp al, ' ' je .order_advance_after_colon cmp al, 9 je .order_advance_after_colon jmp .order_dispatch .order_advance_after_colon: inc rsi jmp .order_skip_after_colon .order_dispatch: mov rdi, rbx call gui_handle_free_order jmp .clear_and_refresh .normal_command: mov rdi, rbx call input_process_command test rax, rax jz .clear_and_refresh mov rdi, rbx call rules_turn call gui_set_turn_summary .clear_and_refresh: sub rsp, 32 mov rcx, [rel h_order_edit] lea rdx, [rel empty_w] call SetWindowTextW add rsp, 32 call gui_refresh_all .done: pop rbx ret gui_run_command_utf8: push rbx mov rbx, [rel gui_sim_state] test rbx, rbx jz .done mov rsi, rdi lea rdi, [rbx + SimState.player_cmd] mov rcx, CMD_BUF_LEN - 1 call gui_copy_cstr_limited mov [rbx + SimState.cmd_len], eax mov rdi, rbx call input_process_command test rax, rax jz .refresh mov rdi, rbx call rules_turn call gui_set_turn_summary .refresh: call gui_refresh_all .done: pop rbx ret gui_command_selected_id: push rbp mov rbp, rsp push rbx push r12 mov r12, rdi mov rbx, [rel gui_sim_state] test rbx, rbx jz .nope mov eax, [rbx + SimState.selected_building_id] test eax, eax jz .nope call gui_buf_reset_tmp mov rsi, r12 call gui_tmp_append_cstr mov eax, [rbx + SimState.selected_building_id] call gui_tmp_append_i64 mov al, ' ' call gui_tmp_append_char mov al, '1' call gui_tmp_append_char lea rdi, [rel gui_command_tmp] call gui_run_command_utf8 jmp .done .nope: lea rdi, [rel u8_msg_select_building] call gui_set_message call gui_update_message .done: lea rsp, [rbp - 16] pop r12 pop rbx pop rbp ret ; ============================================================ ; Selection handlers ; ============================================================ gui_on_category_select: push rbx mov rbx, [rel gui_sim_state] test rbx, rbx jz .done sub rsp, 32 mov rcx, [rel h_category_list] mov edx, LB_GETCURSEL xor r8d, r8d xor r9d, r9d call SendMessageW add rsp, 32 cmp eax, -1 je .done inc eax mov [rbx + SimState.selected_category], eax mov dword [rbx + SimState.ui_mode], UI_MODE_CATEGORIES mov dword [rbx + SimState.selected_building_id], 0 .done: pop rbx ret gui_on_building_select: push rbx mov rbx, [rel gui_sim_state] test rbx, rbx jz .done sub rsp, 32 mov rcx, [rel h_building_list] mov edx, LB_GETCURSEL xor r8d, r8d xor r9d, r9d call SendMessageW add rsp, 32 cmp eax, -1 je .done mov ecx, [rel gui_building_count] cmp eax, ecx jae .done lea rdx, [rel gui_building_ids] mov eax, [rdx + rax*4] mov [rbx + SimState.selected_building_id], eax mov dword [rbx + SimState.ui_mode], UI_MODE_INFO .done: pop rbx ret ; ============================================================ ; Building text rows ; ============================================================ gui_build_active_line: call gui_buf_reset lea rsi, [rel u8_hash] call gui_append_cstr movzx rax, word [rbx + Building.id] call gui_append_i64 lea rsi, [rel u8_x] call gui_append_cstr mov rax, [rbx + Building.active_count] call gui_append_i64 lea rsi, [rel u8_pipe] call gui_append_cstr lea rsi, [rbx + Building.name] call gui_append_cstr lea rsi, [rel u8_pipe] call gui_append_cstr lea rsi, [rel u8_f] call gui_append_cstr mov rax, [rbx + Building.food_delta] call gui_append_signed_with_plus lea rsi, [rel u8_w] call gui_append_cstr mov rax, [rbx + Building.water_delta] call gui_append_signed_with_plus lea rsi, [rel u8_a] call gui_append_cstr mov rax, [rbx + Building.ammo_delta] call gui_append_signed_with_plus lea rsi, [rel u8_e] call gui_append_cstr mov rax, [rbx + Building.energy_delta] call gui_append_signed_with_plus ret gui_build_catalog_line: call gui_buf_reset lea rsi, [rel u8_hash] call gui_append_cstr movzx rax, word [rbx + Building.id] call gui_append_i64 lea rsi, [rel u8_pipe] call gui_append_cstr lea rsi, [rbx + Building.name] call gui_append_cstr lea rsi, [rel u8_pipe] call gui_append_cstr call gui_append_build_cost_active lea rsi, [rel u8_pipe] call gui_append_cstr lea rsi, [rel u8_size] call gui_append_cstr mov rax, [rbx + Building.size] call gui_append_i64 lea rsi, [rel u8_unit_space] call gui_append_cstr lea rsi, [rel u8_pipe] call gui_append_cstr lea rsi, [rel u8_workers] call gui_append_cstr mov rax, [rbx + Building.workers] call gui_append_i64 lea rsi, [rel u8_unit_people] call gui_append_cstr lea rsi, [rel u8_pipe] call gui_append_cstr lea rsi, [rel u8_f] call gui_append_cstr mov rax, [rbx + Building.food_delta] call gui_append_signed_with_plus lea rsi, [rel u8_w] call gui_append_cstr mov rax, [rbx + Building.water_delta] call gui_append_signed_with_plus lea rsi, [rel u8_a] call gui_append_cstr mov rax, [rbx + Building.ammo_delta] call gui_append_signed_with_plus lea rsi, [rel u8_e] call gui_append_cstr mov rax, [rbx + Building.energy_delta] call gui_append_signed_with_plus ret gui_build_details_text: call gui_buf_reset lea rsi, [rel u8_id] call gui_append_cstr movzx rax, word [rbx + Building.id] call gui_append_i64 lea rsi, [rel u8_newline] call gui_append_cstr lea rsi, [rel u8_name] call gui_append_cstr lea rsi, [rbx + Building.name] call gui_append_cstr lea rsi, [rel u8_newline] call gui_append_cstr call gui_append_build_cost_both lea rsi, [rel u8_newline] call gui_append_cstr lea rsi, [rel u8_size] call gui_append_cstr mov rax, [rbx + Building.size] call gui_append_i64 lea rsi, [rel u8_unit_space] call gui_append_cstr lea rsi, [rel u8_newline] call gui_append_cstr lea rsi, [rel u8_workers] call gui_append_cstr mov rax, [rbx + Building.workers] call gui_append_i64 lea rsi, [rel u8_unit_people] call gui_append_cstr lea rsi, [rel u8_newline] call gui_append_cstr lea rsi, [rel u8_count] call gui_append_cstr mov rax, [rbx + Building.active_count] call gui_append_i64 lea rsi, [rel u8_newline] call gui_append_cstr lea rsi, [rel u8_f] call gui_append_cstr mov rax, [rbx + Building.food_delta] call gui_append_signed_with_plus lea rsi, [rel u8_w] call gui_append_cstr mov rax, [rbx + Building.water_delta] call gui_append_signed_with_plus lea rsi, [rel u8_a] call gui_append_cstr mov rax, [rbx + Building.ammo_delta] call gui_append_signed_with_plus lea rsi, [rel u8_e] call gui_append_cstr mov rax, [rbx + Building.energy_delta] call gui_append_signed_with_plus lea rsi, [rel u8_newline] call gui_append_cstr lea rsi, [rel u8_desc] call gui_append_cstr lea rsi, [rbx + Building.description] call gui_append_cstr ret ; ============================================================ ; Dynamic messages, free-form orders and turn summaries ; ============================================================ gui_set_dynamic_from_text: push rbp mov rbp, rsp lea rdi, [rel gui_dynamic_message] lea rsi, [rel gui_text_buffer] mov rcx, 4095 call gui_copy_cstr_limited lea rdi, [rel gui_dynamic_message] call gui_set_message pop rbp ret gui_set_order_message: mov dword [rel last_message_is_order], 1 lea rax, [rel gui_order_message] mov [rel last_message_ptr], rax mov rdi, [rel h_message] test rdi, rdi jz .done lea rsi, [rel gui_order_message] call gui_set_control_text_utf8 .done: ret gui_copy_order_text: push rbx push r12 xor ebx, ebx cmp byte [rsi], '"' jne .copy inc rsi .copy: cmp ebx, 2046 jae .stop mov al, [rsi] test al, al jz .stop cmp al, 13 je .stop cmp al, 10 je .stop cmp al, '"' jne .store mov dl, [rsi + 1] test dl, dl jz .stop .store: lea r12, [rel gui_order_message] mov [r12 + rbx], al inc rbx inc rsi jmp .copy .stop: lea r12, [rel gui_order_message] .trim_tail: test ebx, ebx jz .finish mov al, [r12 + rbx - 1] cmp al, ' ' je .trim_one cmp al, 9 je .trim_one cmp al, '"' je .trim_one jmp .finish .trim_one: dec ebx jmp .trim_tail .finish: mov byte [r12 + rbx], 0 mov eax, ebx pop r12 pop rbx ret gui_handle_free_order: push rbp mov rbp, rsp push rbx mov rbx, rdi call gui_copy_order_text test eax, eax jnz .has_order lea rdi, [rel u8_order_empty] call gui_set_message jmp .done .has_order: call gui_buf_reset lea rsi, [rel u8_file_turn] call gui_append_cstr mov rax, [rbx + SimState.market + MarketState.turn_number] call gui_append_i64 lea rsi, [rel u8_colon_space] call gui_append_cstr lea rsi, [rel gui_order_message] call gui_append_cstr lea rsi, [rel u8_crlf] call gui_append_cstr call gui_write_order_line call gui_set_order_message .done: pop rbx pop rbp ret gui_write_order_line: push rbp mov rbp, rsp push rbx and rsp, -16 sub rsp, 64 lea rcx, [rel u8_order_file] mov edx, FILE_APPEND_DATA mov r8d, FILE_SHARE_READ xor r9d, r9d mov qword [rsp + 32], OPEN_ALWAYS mov qword [rsp + 40], FILE_ATTRIBUTE_NORMAL mov qword [rsp + 48], 0 call CreateFileA cmp rax, -1 je .done_aligned mov rbx, rax mov rcx, rbx lea rdx, [rel gui_text_buffer] mov r8d, [rel gui_text_len] lea r9, [rel orders_written] mov qword [rsp + 32], 0 call WriteFile mov rcx, rbx call CloseHandle .done_aligned: lea rsp, [rbp - 8] pop rbx pop rbp ret gui_set_turn_summary: push rbx mov rbx, [rel gui_sim_state] test rbx, rbx jz .done call gui_buf_reset lea rsi, [rel u8_turn_head] call gui_append_cstr mov rax, [rbx + SimState.market + MarketState.turn_number] call gui_append_i64 lea rsi, [rel u8_turn_done] call gui_append_cstr lea rsi, [rel u8_turn_food] call gui_append_cstr mov rax, [rbx + SimState.last_food_delta] call gui_append_signed_with_plus lea rsi, [rel u8_unit_per_turn] call gui_append_cstr lea rsi, [rel u8_turn_water] call gui_append_cstr mov rax, [rbx + SimState.last_water_delta] call gui_append_signed_with_plus lea rsi, [rel u8_unit_per_turn] call gui_append_cstr lea rsi, [rel u8_turn_ammo] call gui_append_cstr mov rax, [rbx + SimState.last_ammo_delta] call gui_append_signed_with_plus lea rsi, [rel u8_unit_per_turn] call gui_append_cstr lea rsi, [rel u8_turn_energy] call gui_append_cstr mov rax, [rbx + SimState.last_energy_delta] call gui_append_signed_with_plus lea rsi, [rel u8_unit_per_turn] call gui_append_cstr lea rsi, [rel u8_turn_space] call gui_append_cstr mov rax, [rbx + SimState.entities + Entity.resources + FactionResources.used_space] call gui_append_i64 lea rsi, [rel u8_unit_space] call gui_append_cstr lea rsi, [rel u8_turn_workers] call gui_append_cstr mov rax, [rbx + SimState.last_workers_req] call gui_append_i64 lea rsi, [rel u8_unit_people] call gui_append_cstr lea rsi, [rel u8_turn_labor] call gui_append_cstr mov rax, [rbx + SimState.last_labor_days] call gui_append_i64 lea rsi, [rel u8_unit_labor_cost] call gui_append_cstr lea rsi, [rel u8_turn_needs] call gui_append_cstr mov rax, [rbx + SimState.last_needs_sat] call gui_append_i64 lea rsi, [rel u8_unit_percent] call gui_append_cstr lea rsi, [rel u8_turn_loyalty] call gui_append_cstr mov rax, [rbx + SimState.last_loyalty_delta] call gui_append_signed_with_plus call gui_set_dynamic_from_text .done: pop rbx ret ; ============================================================ ; Data lookup ; ============================================================ gui_find_building_by_id: push rbx push r12 mov r12d, edi mov rbx, [rel gui_sim_state] test rbx, rbx jz .not_found add rbx, SimState.entities + Entity.buildings mov ecx, MAX_BUILDINGS .loop: movzx eax, word [rbx + Building.id] cmp eax, r12d je .found add rbx, Building_size dec ecx jnz .loop .not_found: xor eax, eax jmp .done .found: mov rax, rbx .done: pop r12 pop rbx ret ; ============================================================ ; WinAPI text/list helpers ; ============================================================ gui_set_control_text_utf8: push rbp mov rbp, rsp push rbx mov rbx, rdi and rsp, -16 sub rsp, 64 mov ecx, CP_UTF8 xor edx, edx mov r8, rsi mov r9d, -1 lea rax, [rel gui_wide_buffer] mov [rsp + 32], rax mov qword [rsp + 40], 65535 call MultiByteToWideChar mov rcx, rbx lea rdx, [rel gui_wide_buffer] call SetWindowTextW mov rcx, rbx xor edx, edx mov r8d, 1 call InvalidateRect lea rsp, [rbp - 8] pop rbx pop rbp ret gui_listbox_reset: push rbp mov rbp, rsp and rsp, -16 sub rsp, 32 mov rcx, rdi mov edx, LB_RESETCONTENT xor r8d, r8d xor r9d, r9d call SendMessageW mov rsp, rbp pop rbp ret gui_listbox_add_utf8: push rbp mov rbp, rsp push rbx mov rbx, rdi and rsp, -16 sub rsp, 64 mov ecx, CP_UTF8 xor edx, edx mov r8, rsi mov r9d, -1 lea rax, [rel gui_wide_buffer] mov [rsp + 32], rax mov qword [rsp + 40], 65535 call MultiByteToWideChar mov rcx, rbx mov edx, LB_ADDSTRING xor r8d, r8d lea r9, [rel gui_wide_buffer] call SendMessageW lea rsp, [rbp - 8] pop rbx pop rbp ret ; ============================================================ ; UTF-8 text builder ; ============================================================ gui_buf_reset: mov qword [rel gui_text_len], 0 mov byte [rel gui_text_buffer], 0 ret gui_append_cstr: push rbx push rdx mov rbx, [rel gui_text_len] .loop: mov al, [rsi] test al, al jz .done lea rdx, [rel gui_text_buffer] mov [rdx + rbx], al inc rbx inc rsi jmp .loop .done: lea rdx, [rel gui_text_buffer] mov byte [rdx + rbx], 0 mov [rel gui_text_len], rbx pop rdx pop rbx ret gui_append_char: push rbx mov rbx, [rel gui_text_len] lea rdx, [rel gui_text_buffer] mov [rdx + rbx], al inc rbx mov byte [rdx + rbx], 0 mov [rel gui_text_len], rbx pop rbx ret gui_append_signed_with_plus: test rax, rax jl .neg push rax mov al, '+' call gui_append_char pop rax call gui_append_i64 ret .neg: call gui_append_i64 ret gui_append_delta_paren: push rax mov al, ' ' call gui_append_char mov al, '(' call gui_append_char pop rax call gui_append_signed_with_plus lea rsi, [rel u8_unit_per_turn] call gui_append_cstr mov al, ')' call gui_append_char ret gui_append_i64: push rbx push rcx push rdx push rsi mov rbx, rax test rbx, rbx jge .positive mov al, '-' call gui_append_char neg rbx .positive: lea rsi, [rel render_buf + 63] mov byte [rsi], 0 dec rsi cmp rbx, 0 jne .digits mov byte [rsi], '0' jmp .emit .digits: mov rax, rbx .loop: xor edx, edx mov ecx, 10 div rcx add dl, '0' mov [rsi], dl dec rsi mov rbx, rax test rax, rax jnz .loop inc rsi .emit: call gui_append_cstr pop rsi pop rdx pop rcx pop rbx ret ; Temporary command buffer builder. gui_buf_reset_tmp: mov byte [rel gui_command_tmp], 0 mov qword [rel gui_text_len], 0 ret gui_tmp_append_cstr: push rbx mov rbx, [rel gui_text_len] .loop: mov al, [rsi] test al, al jz .done lea rdx, [rel gui_command_tmp] mov [rdx + rbx], al inc rbx inc rsi jmp .loop .done: lea rdx, [rel gui_command_tmp] mov byte [rdx + rbx], 0 mov [rel gui_text_len], rbx pop rbx ret gui_tmp_append_char: push rbx mov rbx, [rel gui_text_len] lea rdx, [rel gui_command_tmp] mov [rdx + rbx], al inc rbx mov byte [rdx + rbx], 0 mov [rel gui_text_len], rbx pop rbx ret gui_tmp_append_i64: push rbx push rcx push rdx push rsi mov rbx, rax lea rsi, [rel render_buf + 63] mov byte [rsi], 0 dec rsi cmp rbx, 0 jne .digits mov byte [rsi], '0' jmp .emit .digits: mov rax, rbx .loop: xor edx, edx mov ecx, 10 div rcx add dl, '0' mov [rsi], dl dec rsi mov rbx, rax test rax, rax jnz .loop inc rsi .emit: call gui_tmp_append_cstr pop rsi pop rdx pop rcx pop rbx ret ; rdi=dest, rsi=src, rcx=max bytes not counting zero. returns eax=len. gui_copy_cstr_limited: push rbx xor ebx, ebx .loop: cmp rbx, rcx jae .stop mov al, [rsi + rbx] test al, al jz .stop mov [rdi + rbx], al inc rbx jmp .loop .stop: mov byte [rdi + rbx], 0 mov eax, ebx pop rbx ret