Prototype game engine for Heroes of Might & Magic, featuring a gameplay plot-twist...
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

510 lines
26KB

  1. -- Copyright (c) 2024 - Ognjen 'xolatile' Milan Robovic
  2. --
  3. -- GNU General Public Licence (version 3 or later)
  4. with core;
  5. package body ui is
  6. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  7. type element is (
  8. none,
  9. main_background,
  10. --
  11. corner_upper_left, border_upper, corner_upper_right,
  12. border_left, border_right,
  13. corner_lower_left, border_lower, corner_lower_right,
  14. --
  15. tiny_corner_upper_left, tiny_border_upper, tiny_corner_upper_right,
  16. tiny_border_left, tiny_border_right,
  17. tiny_corner_lower_left, tiny_border_lower, tiny_corner_lower_right,
  18. --
  19. frame_upper_left, frame_upper, frame_upper_right,
  20. frame_left, frame_middle, frame_right,
  21. frame_lower_left, frame_lower, frame_lower_right,
  22. --
  23. icon_upper_left, icon_upper, icon_upper_right,
  24. icon_left, icon_right,
  25. icon_lower_left, icon_lower, icon_lower_right,
  26. --
  27. text_upper_left, text_upper, text_upper_right,
  28. text_left, text_middle, text_right,
  29. text_lower_left, text_lower, text_lower_right,
  30. --
  31. cursor, icon, overicon, icon_selected,
  32. --
  33. fill_bar_left, fill_bar_horizontal, fill_bar_right, fill_horizontal,
  34. --
  35. scroll_bar_lower, scroll_bar_middle, scroll_bar_upper,
  36. --
  37. title_bar_left, title_bar_middle, title_bar_right
  38. );
  39. ------------------------------------------------------------------------------------------
  40. type rectangle is record
  41. x, y, width, height : integer;
  42. end record;
  43. ------------------------------------------------------------------------------------------
  44. structure_limit : constant natural := 12;
  45. sprite : array (style, element) of core.sprite;
  46. font : array (style) of core.font;
  47. structure_array : array (0 .. structure_limit) of structure;
  48. structure_count : natural := 0;
  49. ------------------------------------------------------------------------------------------
  50. function cursor_inside (x, y, width, height : in integer) return boolean is
  51. begin
  52. --~return (core.cursor.x > x and core.cursor.x < x + width and core.cursor.y > y and core.cursor.y < y + height);
  53. if core.cursor.x > x and core.cursor.x < x + width
  54. and core.cursor.y > y and core.cursor.y < y + height then
  55. return true;
  56. else
  57. return false;
  58. end if;
  59. end cursor_inside;
  60. ------------------------------------------------------------------------------------------
  61. procedure draw (index : in element := none;
  62. x : in integer := 0;
  63. y : in integer := 0;
  64. width : in integer := 0;
  65. height : in integer := 0) is
  66. save_zoom : natural := core.zoom;
  67. begin
  68. core.zoom := 1;
  69. core.draw (sprite (active, index), x, y, 0, 0, width, height);
  70. core.zoom := save_zoom;
  71. end draw;
  72. ------------------------------------------------------------------------------------------
  73. procedure draw_horizontally (index : in element; x, y, width : in integer; action : core.pointer := core.idle'access) is
  74. step : constant integer := sprite (active, index).width;
  75. begin
  76. for move in 0 .. width / step - 1 loop
  77. draw (index, x + move * step,y);
  78. end loop;
  79. --
  80. if width mod step > 0 then
  81. draw (index, x + (width / step) * step, y, width mod step, sprite (active, index).height);
  82. end if;
  83. --
  84. --~if core.cursor.x > x and core.cursor.x < x + width
  85. --~and core.cursor.y > y and core.cursor.y < y + sprite (active, index).height
  86. --~and core.cursor_mode = 1 then
  87. --~action.all;
  88. --~core.cursor_mode := 0;
  89. --~end if;
  90. end draw_horizontally;
  91. ------------------------------------------------------------------------------------------
  92. procedure draw_vertically (index : in element; x, y, height : in integer; action : core.pointer := core.idle'access) is
  93. step : constant integer := sprite (active, index).height;
  94. begin
  95. for move in 0 .. height / step - 1 loop
  96. draw (index, x, y + move * step);
  97. end loop;
  98. --
  99. if height mod step > 0 then
  100. draw (index, x, y + (height / step) * step, sprite (active, index).width, height mod step);
  101. end if;
  102. --
  103. --~if core.cursor.x > x and core.cursor.x < x + sprite (active, index).width
  104. --~and core.cursor.y > y and core.cursor.y < y + height
  105. --~and core.cursor_mode = 1 then
  106. --~action.all;
  107. --~core.cursor_mode := 0;
  108. --~end if;
  109. end draw_vertically;
  110. ------------------------------------------------------------------------------------------
  111. procedure draw_background (index : in element; x, y, width, height : in integer; action : core.pointer := core.idle'access) is
  112. base_width : integer := sprite (active, index).width;
  113. base_height : integer := sprite (active, index).height;
  114. crop_width : integer := width mod base_width;
  115. crop_height : integer := height mod base_height;
  116. begin
  117. for move_y in 0 .. height / base_height - 1 loop
  118. for move_x in 0 .. width / base_width - 1 loop
  119. draw (index, x + move_x * base_width, y + move_y * base_height);
  120. end loop;
  121. --
  122. if width mod base_width > 0 then
  123. draw (index, x + width - crop_width, y + move_y * base_height, crop_width, base_height);
  124. end if;
  125. end loop;
  126. --
  127. for move_x in 0 .. width / base_width - 1 loop
  128. if height mod base_height > 0 then
  129. draw (index, x + move_x * base_width, y + height - crop_height, base_width, crop_height);
  130. end if;
  131. end loop;
  132. --
  133. if width mod base_width > 0 and height mod base_height > 0 then
  134. draw (index, x + width - crop_width, y + height - crop_height, crop_width, crop_height);
  135. end if;
  136. end draw_background;
  137. ------------------------------------------------------------------------------------------
  138. procedure draw_structure (data : in structure) is
  139. offset : constant integer := core.icon / 4;
  140. orients : natural := 0;
  141. --
  142. frame_data : rectangle := (others => 0);
  143. button_data : rectangle := (others => 0);
  144. begin
  145. for index in 0 .. data.gui_n - 1 loop
  146. if data.gui_list (index).kind = gui_orient then
  147. orients := orients + 1;
  148. end if;
  149. end loop;
  150. --
  151. frame_data.width := (if data.resize then 320 else data.width) * (orients + 1) - offset * orients;
  152. frame_data.height := (if data.resize then data.gui_n * (core.icon + 2 * offset) + 2 * core.icon else data.height) / (orients + 1) + offset * orients;
  153. frame_data.x := (if data.center then (core.window_width - frame_data.width) / 2 else data.x);
  154. frame_data.y := (if data.center then (core.window_height - frame_data.height) / 2 else data.y);
  155. button_data.width := frame_data.width / (orients + 1) - 2 * core.icon;
  156. button_data.height := core.icon + 2 * offset;
  157. button_data.x := frame_data.x + core.icon;
  158. button_data.y := frame_data.y + core.icon;
  159. --
  160. draw_tiny_menu (frame_data.x, frame_data.y, frame_data.width, frame_data.height);
  161. draw_title_bar (frame_data.x, frame_data.y, frame_data.width, data.title);
  162. --
  163. for x in 0 .. data.gui_n - 1 loop
  164. case data.gui_list (x).kind is
  165. when gui_button =>
  166. draw_frame (data.gui_list (x).info, button_data.x, button_data.y, button_data.width, button_data.height);
  167. draw_icon (data.gui_list (x).image, data.gui_list (x).info, button_data.x + offset, button_data.y + offset);
  168. write (data.gui_list (x).text, button_data.x + offset + core.icon, button_data.y + offset + 2);
  169. --~if cursor_inside (at_x, at_y, new_width - 2 * offset - core.icon - 2, 3 * core.icon / 2) then
  170. --~draw (cursor, at_x + new_width - 96, at_y);
  171. --~end if;
  172. button_data.y := button_data.y + button_data.height;
  173. when gui_orient =>
  174. button_data.x := button_data.x + frame_data.width / (orients + 1) - 2 * core.icon + offset;
  175. button_data.y := frame_data.y + core.icon;
  176. when others => null;
  177. end case;
  178. end loop;
  179. --
  180. if orients > 0 then
  181. draw_scroll_bar (frame_data.x + frame_data.width - 2 * core.icon, frame_data.y + core.icon, frame_data.height - 2 * core.icon, 4);
  182. end if;
  183. end draw_structure;
  184. ------------------------------------------------------------------------------------------
  185. procedure configure is
  186. procedure load_ui (index : in style; folder_path : in string) is
  187. begin
  188. font (index) := core.import_font ("./sprite/ui/" & folder_path & "/font.png", 24, 0);
  189. --
  190. for this in element loop
  191. sprite (index, this) := core.import_sprite ("./sprite/ui/" & folder_path & "/" & core.lowercase (element'image (this)) & ".png", 1, 1);
  192. end loop;
  193. end load_ui;
  194. begin
  195. core.echo (core.comment, "Configuring UI components...");
  196. --
  197. for index in style loop
  198. load_ui (index, core.lowercase (style'image (index)));
  199. end loop;
  200. end configure;
  201. ------------------------------------------------------------------------------------------
  202. procedure synchronize is
  203. begin
  204. for index in 0 .. structure_count - 1 loop
  205. if core.signal_mode = core.signal_code'pos (structure_array (index).toggle) then
  206. structure_array (index).show := (if structure_array (index).show then false else true);
  207. end if;
  208. --
  209. if structure_array (index).show then
  210. draw_structure (structure_array (index));
  211. end if;
  212. end loop;
  213. end synchronize;
  214. ------------------------------------------------------------------------------------------
  215. procedure write (text : in string; x, y : in integer) is
  216. begin
  217. core.write (text, x, y, font (active));
  218. end write;
  219. ------------------------------------------------------------------------------------------
  220. procedure draw_icon (data : in core.sprite; description : in string; x, y : in integer; action : core.pointer := core.idle'access) is
  221. save_zoom : natural := core.zoom;
  222. begin
  223. draw (icon, x, y);
  224. --
  225. core.zoom := 1;
  226. core.draw (data, x, y);
  227. core.zoom := save_zoom;
  228. --
  229. if cursor_inside (x, y, core.icon, core.icon) then
  230. draw (icon_selected, x, y);
  231. --
  232. core.write_text_box (description);
  233. --
  234. if core.cursor_mode = 1 then
  235. action.all;
  236. core.cursor_mode := 0;
  237. end if;
  238. end if;
  239. end draw_icon;
  240. ------------------------------------------------------------------------------------------
  241. procedure draw_overicon (data : in core.sprite; description : in string; x, y : in integer; action : core.pointer := core.idle'access) is
  242. save_zoom : natural := core.zoom;
  243. begin
  244. core.zoom := 1;
  245. core.draw (data, x, y);
  246. core.zoom := save_zoom;
  247. --
  248. draw (overicon, x, y);
  249. end draw_overicon;
  250. ------------------------------------------------------------------------------------------
  251. procedure draw_text_box (text : in string) is
  252. width : constant integer := 144;
  253. height : constant integer := 72;
  254. x : constant integer := (core.window_width - width) / 2;
  255. y : constant integer := (core.window_height - height) / 2;
  256. offset : constant integer := sprite (active, text_middle).width;
  257. begin
  258. draw_background (text_middle, x + offset, y + offset, width - 2 * offset, height - 2 * offset);
  259. --
  260. draw_horizontally (text_upper, x + offset, y, width - 2 * offset);
  261. draw_horizontally (text_lower, x + offset, y + height - offset, width - 2 * offset);
  262. draw_vertically (text_left, x, y + offset, height - 2 * offset);
  263. draw_vertically (text_right, x + width - offset, y + offset, height - 2 * offset);
  264. --
  265. draw (text_upper_left, x, y);
  266. draw (text_upper_right, x + width - offset, y);
  267. draw (text_lower_left, x, y + height - offset);
  268. draw (text_lower_right, x + width - offset, y + height - offset);
  269. --
  270. write (text, x, y);
  271. end draw_text_box;
  272. ------------------------------------------------------------------------------------------
  273. --~procedure a is begin core.echo (core.warning, "Heyo world!"); end a;
  274. procedure draw_help_box (x, y, width, height : in integer; action : core.pointer := core.idle'access) is
  275. offset : constant integer := sprite (active, text_middle).width;
  276. begin
  277. draw_background (text_middle, x + offset, y + offset, width - 2 * offset, height - 2 * offset);
  278. --
  279. draw_horizontally (text_upper, x + offset, y, width - 2 * offset);
  280. draw_horizontally (text_lower, x + offset, y + height - offset, width - 2 * offset);
  281. draw_vertically (text_left, x, y + offset, height - 2 * offset);
  282. draw_vertically (text_right, x + width - offset, y + offset, height - 2 * offset);
  283. --
  284. draw (text_upper_left, x, y);
  285. draw (text_upper_right, x + width - offset, y);
  286. draw (text_lower_left, x, y + height - offset);
  287. draw (text_lower_right, x + width - offset, y + height - offset);
  288. --
  289. core.write (core.read_text_box, x, y, font (active));
  290. --
  291. --~core.block_queue ((x, y, width, height, 2, a'access));
  292. end draw_help_box;
  293. ------------------------------------------------------------------------------------------
  294. procedure draw_frame (description : in string; x, y, width, height : in integer; action : core.pointer := core.idle'access) is
  295. offset_x : constant integer := sprite (active, frame_middle).width;
  296. offset_y : constant integer := sprite (active, frame_middle).height;
  297. begin
  298. if height < core.icon or width < core.icon then
  299. return;
  300. end if;
  301. --
  302. draw_background (frame_middle, x + offset_x, y + offset_y, width - 2 * offset_x, height - 2 * offset_y);
  303. --
  304. draw_horizontally (frame_upper, x + offset_x, y, width - 2 * offset_x);
  305. draw_horizontally (frame_lower, x + offset_x, y + height - offset_y, width - 2 * offset_x);
  306. draw_vertically (frame_left, x, y + offset_y, height - 2 * offset_y);
  307. draw_vertically (frame_right, x + width - offset_x, y + offset_y, height - 2 * offset_y);
  308. --
  309. draw (frame_upper_left, x, y);
  310. draw (frame_upper_right, x + width - sprite (active, frame_upper_right).width, y);
  311. draw (frame_lower_left, x, y + height - sprite (active, frame_lower_left).height);
  312. draw (frame_lower_right, x + width - sprite (active, frame_lower_right).width, y + height - sprite (active, frame_lower_right).height);
  313. end draw_frame;
  314. ------------------------------------------------------------------------------------------
  315. procedure draw_title_bar (x, y, width : in integer; title : in string) is
  316. middle_width : constant integer := width - sprite (active, title_bar_left).width - sprite (active, title_bar_right).width;
  317. begin
  318. draw (title_bar_left, x, y - sprite (active, title_bar_left).height);
  319. draw (title_bar_right, x + middle_width + sprite (active, title_bar_left).width, y - sprite (active, title_bar_right).height);
  320. --
  321. draw_horizontally (title_bar_middle, x + sprite (active, title_bar_left).width, y - sprite (active, title_bar_middle).height, middle_width);
  322. --
  323. core.write (title, x + sprite (active, title_bar_left).width / 2 + 20, y - sprite (active, title_bar_middle).height / 2 - 6, font (active));
  324. end draw_title_bar;
  325. ------------------------------------------------------------------------------------------
  326. procedure draw_fill_bar (x, y, width : in integer; fill : in float) is
  327. middle_width : constant integer := width - sprite (active, fill_bar_left).width - sprite (active, fill_bar_right).width;
  328. fill_width : constant integer := integer (float (middle_width) * fill);
  329. begin
  330. draw (fill_bar_left, x, y - sprite (active, fill_bar_left).height);
  331. draw (fill_bar_right, x + middle_width + sprite (active, fill_bar_left).width, y - sprite (active, fill_bar_right).height);
  332. --
  333. draw_horizontally (fill_bar_horizontal, x + sprite (active, fill_bar_left).width, y - sprite (active, fill_bar_horizontal).height, middle_width);
  334. draw_horizontally (fill_horizontal, x + sprite (active, fill_bar_left).width, y - sprite (active, fill_bar_horizontal).height, fill_width);
  335. end draw_fill_bar;
  336. ------------------------------------------------------------------------------------------
  337. procedure draw_scroll_bar (x, y, height, offset : in integer) is
  338. middle_height : constant integer := height - sprite (active, scroll_bar_upper).height - sprite (active, scroll_bar_lower).height;
  339. begin
  340. draw (scroll_bar_upper, x, y);
  341. draw (scroll_bar_lower, x, y + middle_height + sprite (active, scroll_bar_upper).height);
  342. --
  343. draw_vertically (scroll_bar_middle, x, y + sprite (active, scroll_bar_upper).height, middle_height);
  344. end draw_scroll_bar;
  345. ------------------------------------------------------------------------------------------
  346. procedure draw_menu (x, y, width, height : in integer) is
  347. offset : constant integer := sprite (active, none).width;
  348. begin
  349. declare upper : constant integer := width - sprite (active, corner_upper_left).width - sprite (active, corner_upper_right).width;
  350. lower : constant integer := width - sprite (active, corner_lower_left).width - sprite (active, corner_lower_right).width;
  351. left : constant integer := height - sprite (active, corner_upper_left).height - sprite (active, corner_lower_left).height;
  352. right : constant integer := height - sprite (active, corner_upper_right).height - sprite (active, corner_lower_right).height;
  353. begin
  354. draw_horizontally (border_upper, x + sprite (active, corner_upper_left).width, y, upper, action => core.move_camera_up'access);
  355. draw_horizontally (border_lower, x + sprite (active, corner_lower_left).width, y + height - sprite (active, border_lower).height, lower, action => core.move_camera_down'access);
  356. draw_vertically (border_left, x, y + sprite (active, corner_upper_left).height, left, action => core.move_camera_left'access);
  357. draw_vertically (border_right, x + width - sprite (active, border_right).width, y + sprite (active, corner_upper_right).height, right, action => core.move_camera_right'access);
  358. end;
  359. --
  360. draw (corner_upper_left, x, y);
  361. draw (corner_upper_right, x + width - sprite (active, corner_upper_right).width, y);
  362. draw (corner_lower_left, x, y + height - sprite (active, corner_lower_left).height);
  363. draw (corner_lower_right, x + width - sprite (active, corner_lower_right).width, y + height - sprite (active, corner_lower_right).height);
  364. end draw_menu;
  365. ------------------------------------------------------------------------------------------
  366. procedure draw_tiny_menu (x, y, width, height : in integer) is
  367. offset : constant integer := sprite (active, none).width;
  368. begin
  369. draw_background (main_background, x + offset, y + offset, width - 2 * offset, height - 2 * offset);
  370. --
  371. declare upper : constant integer := width - sprite (active, tiny_corner_upper_left).width - sprite (active, tiny_corner_upper_right).width;
  372. lower : constant integer := width - sprite (active, tiny_corner_lower_left).width - sprite (active, tiny_corner_lower_right).width;
  373. left : constant integer := height - sprite (active, tiny_corner_upper_left).height - sprite (active, tiny_corner_lower_left).height;
  374. right : constant integer := height - sprite (active, tiny_corner_upper_right).height - sprite (active, tiny_corner_lower_right).height;
  375. begin
  376. draw_horizontally (tiny_border_upper, x + sprite (active, tiny_corner_upper_left).width, y, upper);
  377. draw_horizontally (tiny_border_lower, x + sprite (active, tiny_corner_lower_left).width, y + height - sprite (active, tiny_border_lower).height, lower);
  378. draw_vertically (tiny_border_left, x, y + sprite (active, tiny_corner_upper_left).height, left);
  379. draw_vertically (tiny_border_right, x + width - sprite (active, tiny_border_right).width, y + sprite (active, tiny_corner_upper_right).height, right);
  380. end;
  381. --
  382. draw (tiny_corner_upper_left, x, y);
  383. draw (tiny_corner_upper_right, x + width - sprite (active, tiny_corner_upper_right).width, y);
  384. draw (tiny_corner_lower_left, x, y + height - sprite (active, tiny_corner_lower_left).height);
  385. draw (tiny_corner_lower_right, x + width - sprite (active, tiny_corner_lower_right).width, y + height - sprite (active, tiny_corner_lower_right).height);
  386. end draw_tiny_menu;
  387. ------------------------------------------------------------------------------------------
  388. procedure draw_icon_menu (description : in string; x, y, width, height : in integer; action : core.pointer := core.idle'access) is
  389. offset_x : constant integer := sprite (active, icon_upper_left).width;
  390. offset_y : constant integer := sprite (active, icon_upper_left).height;
  391. begin
  392. if height < 2 * sprite (active, icon_upper_left).height
  393. or width < 2 * sprite (active, icon_upper_left).width then
  394. return;
  395. end if;
  396. --
  397. draw_horizontally (icon_upper, x + offset_x, y, width - 2 * offset_x);
  398. draw_horizontally (icon_lower, x + offset_x, y + height - offset_y, width - 2 * offset_x);
  399. draw_vertically (icon_left, x, y + offset_y, height - 2 * offset_y);
  400. draw_vertically (icon_right, x + width - offset_x, y + offset_y, height - 2 * offset_y);
  401. --
  402. draw (icon_upper_left, x, y);
  403. draw (icon_upper_right, x + width - sprite (active, icon_upper_right).width, y);
  404. draw (icon_lower_left, x, y + height - sprite (active, icon_lower_left).height);
  405. draw (icon_lower_right, x + width - sprite (active, icon_lower_right).width, y + height - sprite (active, icon_lower_right).height);
  406. end draw_icon_menu;
  407. ------------------------------------------------------------------------------------------
  408. procedure draw_state_box (x, y : in integer) is
  409. begin
  410. ui.write ("Cursor X:" & core.cursor.x'image, x, y + 0);
  411. ui.write ("Cursor Y:" & core.cursor.y'image, x, y + 32);
  412. ui.write ("Cursor Mode:" & core.cursor_mode'image, x, y + 64);
  413. ui.write ("Camera X:" & core.camera.x'image, x, y + 96);
  414. ui.write ("Camera Y:" & core.camera.y'image, x, y + 128);
  415. ui.write ("Global Time:" & core.global_time'image, x, y + 160);
  416. ui.write ("Gameplay Time:" & core.gameplay_time'image, x, y + 192);
  417. ui.write ("Animation Time:" & core.animation_time'image, x, y + 224);
  418. ui.write ("Framerate:" & core.framerate'image, x, y + 256);
  419. end draw_state_box;
  420. ------------------------------------------------------------------------------------------
  421. procedure add_structure (data : in structure) is
  422. begin
  423. structure_array (structure_count) := data;
  424. structure_array (structure_count).gui_list := new gui_array (0 .. structure_array (structure_count).gui_n - 1);
  425. structure_array (structure_count).gui_n := 0;
  426. --
  427. core.increment (structure_count);
  428. end add_structure;
  429. ------------------------------------------------------------------------------------------
  430. procedure add_structure_button (icon : in core.sprite; text : in core.short_string; description : in core.long_string := "") is
  431. begin
  432. structure_array (structure_count - 1).gui_list (structure_array (structure_count - 1).gui_n).kind := gui_button;
  433. structure_array (structure_count - 1).gui_list (structure_array (structure_count - 1).gui_n).text := text;
  434. structure_array (structure_count - 1).gui_list (structure_array (structure_count - 1).gui_n).info := description;
  435. structure_array (structure_count - 1).gui_list (structure_array (structure_count - 1).gui_n).number := 0;
  436. structure_array (structure_count - 1).gui_list (structure_array (structure_count - 1).gui_n).image := icon;
  437. --
  438. core.increment (structure_array (structure_count - 1).gui_n);
  439. end add_structure_button;
  440. ------------------------------------------------------------------------------------------
  441. procedure add_structure_orient is
  442. begin
  443. structure_array (structure_count - 1).gui_list (structure_array (structure_count - 1).gui_n).kind := gui_orient;
  444. --
  445. core.increment (structure_array (structure_count - 1).gui_n);
  446. end add_structure_orient;
  447. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  448. end ui;