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.

488 lines
25KB

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