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.

582 lines
24KB

  1. -- Copyright (c) 2024 - Ognjen 'xolatile' Milan Robovic
  2. --
  3. -- GNU General Public Licence (version 3 or later)
  4. with ray;
  5. package body core is
  6. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  7. type terminal_colour is (
  8. grey, red, green, yellow, blue, pink,
  9. cyan, white
  10. );
  11. type terminal_effect is (
  12. normal, bold, italic, underline, blink, invert
  13. );
  14. ------------------------------------------------------------------------------------------
  15. type texture_data_array is array (natural range <>) of ray.texture;
  16. type sound_data_array is array (natural range <>) of ray.sound;
  17. type font_data_array is array (natural range <>) of ray.font;
  18. ------------------------------------------------------------------------------------------
  19. texture_count : integer := 0;
  20. sound_count : integer := 0;
  21. font_count : integer := 0;
  22. texture_array : access texture_data_array;
  23. sound_array : access sound_data_array;
  24. font_array : access font_data_array;
  25. game_icon : ray.image;
  26. ------------------------------------------------------------------------------------------
  27. procedure terminal (colour : in terminal_colour := white;
  28. effect : in terminal_effect := normal) is
  29. format : string := character'val (27) & "[" & character'val (terminal_effect'pos (effect) + 48) & ";3" & character'val (terminal_colour'pos (colour) + 48) & "m";
  30. begin
  31. put (format);
  32. end terminal;
  33. ------------------------------------------------------------------------------------------
  34. function "=" (a, b : in signal_code) return boolean is begin return natural (signal_code'pos (a)) = natural (signal_code'pos (b)); end "=";
  35. function "=" (a, b : in cursor_code) return boolean is begin return natural (cursor_code'pos (a)) = natural (cursor_code'pos (b)); end "=";
  36. function "/" (a, b : in signal_code) return boolean is begin return natural (signal_code'pos (a)) /= natural (signal_code'pos (b)); end "/";
  37. function "/" (a, b : in cursor_code) return boolean is begin return natural (cursor_code'pos (a)) /= natural (cursor_code'pos (b)); end "/";
  38. ------------------------------------------------------------------------------------------
  39. function "+" (data : in point; modifier : in natural) return point is
  40. this : point := data;
  41. begin
  42. this.value := (if (this.value + modifier) > this.limit then this.limit else (this.value + modifier));
  43. --
  44. return this;
  45. end "+";
  46. ------------------------------------------------------------------------------------------
  47. function "-" (data : in point; modifier : in natural) return point is
  48. this : point := data;
  49. begin
  50. this.value := (if (this.value - modifier) <= 0 then 0 else (this.value - modifier));
  51. --
  52. return this;
  53. end "-";
  54. ------------------------------------------------------------------------------------------
  55. function "*" (data : in point; modifier : in natural) return point is
  56. this : point := data;
  57. begin
  58. this.value := (if (this.value * modifier) > this.limit then this.limit else (this.value * modifier));
  59. --
  60. return this;
  61. end "*";
  62. ------------------------------------------------------------------------------------------
  63. function "/" (data : in point; modifier : in natural) return point is
  64. this : point := data;
  65. begin
  66. this.value := (if (this.value / modifier) <= 0 then 0 else (this.value / modifier));
  67. --
  68. return this;
  69. end "/";
  70. ------------------------------------------------------------------------------------------
  71. function "+" (data : in string) return unstring is begin return core.unbound (data); end "+";
  72. function "-" (data : in unstring) return string is begin return core.bound (data); end "-";
  73. ------------------------------------------------------------------------------------------
  74. procedure echo (status : in echo_status;
  75. text : in string) is
  76. begin
  77. if not echo_mark (status) then
  78. return;
  79. end if;
  80. --
  81. put ("[");
  82. case status is
  83. when failure => terminal (red, bold); put ("Failure"); terminal;
  84. when warning => terminal (yellow, bold); put ("Warning"); terminal;
  85. when success => terminal (green, bold); put ("Success"); terminal;
  86. when comment => terminal (grey, bold); put ("Comment"); terminal;
  87. when import => terminal (cyan, bold); put (" <-- "); terminal;
  88. when export => terminal (blue, bold); put (" --> "); terminal;
  89. end case;
  90. put ("] ");
  91. --
  92. put_line (text);
  93. end echo;
  94. ------------------------------------------------------------------------------------------
  95. procedure echo_when (condition : in boolean; status : in echo_status; text : in string) is
  96. begin
  97. if condition then
  98. echo (status, text);
  99. end if;
  100. end echo_when;
  101. ------------------------------------------------------------------------------------------
  102. procedure dash is
  103. begin
  104. terminal (grey, bold);
  105. put ("------------------------------------------------------------------------------------------");
  106. put ("------------------------------------------------------------------------------------------");
  107. terminal;
  108. new_line;
  109. end dash;
  110. ------------------------------------------------------------------------------------------
  111. procedure semi_dash is
  112. begin
  113. terminal (grey, bold);
  114. put (" ");
  115. put ("------------------------------------------------------------------------------------------");
  116. terminal;
  117. new_line;
  118. end semi_dash;
  119. ------------------------------------------------------------------------------------------
  120. function time return float is
  121. begin
  122. return float (ray.get_time);
  123. end time;
  124. ------------------------------------------------------------------------------------------
  125. function compress (data : in address; size : in integer; used : out integer) return address is
  126. begin
  127. return address (ray.compress (ray.pointer (data), size, used));
  128. end compress;
  129. ------------------------------------------------------------------------------------------
  130. function decompress (data : in address; size : in integer; used : out integer) return address is
  131. begin
  132. return address (ray.decompress (ray.pointer (data), size, used));
  133. end decompress;
  134. ------------------------------------------------------------------------------------------
  135. function c_string (ada_string : string) return string is
  136. begin
  137. return (ada_string & character'val (0));
  138. end c_string;
  139. ------------------------------------------------------------------------------------------
  140. function random (minimum, maximum : in integer) return integer is
  141. begin
  142. return ray.get_random (minimum, maximum);
  143. end random;
  144. ------------------------------------------------------------------------------------------
  145. procedure clip (value : in out integer; minimum, maximum : in integer) is
  146. begin
  147. if value < minimum then value := minimum; end if;
  148. if value > maximum then value := maximum; end if;
  149. end clip;
  150. ------------------------------------------------------------------------------------------
  151. function lowercase (text : in string) return string is
  152. result : string (1 .. text'length);
  153. begin
  154. for index in text'range loop
  155. if text (index) in 'A' .. 'Z' then
  156. result (index) := character'val (character'pos (text (index)) + 32);
  157. else
  158. result (index) := text (index);
  159. end if;
  160. end loop;
  161. --
  162. return result;
  163. end lowercase;
  164. ------------------------------------------------------------------------------------------
  165. function window_width return integer is begin return ray.get_screen_width; end window_width;
  166. function window_height return integer is begin return ray.get_screen_height; end window_height;
  167. ------------------------------------------------------------------------------------------
  168. function center_x (object : in integer) return integer is begin return (window_width - object) / 2; end center_x;
  169. function center_y (object : in integer) return integer is begin return (window_height - object) / 2; end center_y;
  170. ------------------------------------------------------------------------------------------
  171. function cursor_inside (x, y, width, height : in integer) return boolean is
  172. begin
  173. return cursor.x > x and cursor.x < x + width * zoom and cursor.y > y and cursor.y < y + height * zoom;
  174. end cursor_inside;
  175. ------------------------------------------------------------------------------------------
  176. function import_sprite (file_path : in string; frames, states : in integer) return sprite is
  177. this : sprite;
  178. begin
  179. texture_array (texture_count) := ray.load_texture (c_string (file_path));
  180. --
  181. texture_count := texture_count + 1;
  182. this.index := texture_count - 1;
  183. this.width := texture_array (this.index).width / frames;
  184. this.height := texture_array (this.index).height / states;
  185. this.frames := frames;
  186. this.states := states;
  187. --
  188. if this.width = 0 or this.height = 0 then
  189. echo (warning, "Sprite not imported: " & file_path);
  190. end if;
  191. --
  192. return this;
  193. end import_sprite;
  194. ------------------------------------------------------------------------------------------
  195. function import_font (file_path : in string; scale, space : in integer) return font is
  196. this : font;
  197. begin
  198. font_array (font_count) := ray.load_font (c_string (file_path));
  199. --
  200. font_count := font_count + 1;
  201. this.index := font_count - 1;
  202. this.scale := scale;
  203. this.space := space;
  204. --
  205. return this;
  206. end import_font;
  207. ------------------------------------------------------------------------------------------
  208. function import_song (file_path : in string) return song is
  209. this : song;
  210. begin
  211. sound_array (sound_count) := ray.load_sound (c_string (file_path));
  212. --
  213. sound_count := sound_count + 1;
  214. this.index := sound_count - 1;
  215. --
  216. return this;
  217. end import_song;
  218. ------------------------------------------------------------------------------------------
  219. procedure import_text (data : in out string_box_data; file_path : in string) is
  220. begin
  221. data.text := to_unbounded_string (to_ada (ray.load_text (c_string (file_path)).all)) & character'val (0);
  222. end import_text;
  223. ------------------------------------------------------------------------------------------
  224. procedure create_image (width, height : in integer) is
  225. begin
  226. global_image := ray.image_colour (width, height, (0, 0, 0, 255));
  227. end create_image;
  228. ------------------------------------------------------------------------------------------
  229. procedure draw_pixel (x, y : in integer; tint : in colour) is
  230. new_tint : ray.colour := (ray.colour_range (tint.r), ray.colour_range (tint.g), ray.colour_range (tint.b), ray.colour_range (tint.a));
  231. begin
  232. ray.image_pixel (global_image, x, y, new_tint);
  233. end draw_pixel;
  234. ------------------------------------------------------------------------------------------
  235. procedure render_image (data : in sprite; x, y, u, v, width, height : in integer) is
  236. temporary : ray.image;
  237. begin
  238. temporary := ray.image_import (texture_array (data.index));
  239. --
  240. ray.image_render (data => global_image,
  241. copy => temporary,
  242. from => (float (u), float (v), float (width), float (height)),
  243. to => (float (x), float (y), float (width), float (height)));
  244. --
  245. ray.image_delete (temporary);
  246. end render_image;
  247. ------------------------------------------------------------------------------------------
  248. procedure export_image (file_path : in string) is
  249. ignore : integer;
  250. begin
  251. ignore := ray.image_export (global_image, c_string (file_path));
  252. --
  253. ray.image_delete (global_image);
  254. end export_image;
  255. ------------------------------------------------------------------------------------------
  256. procedure draw (data : in sprite := (others => 0);
  257. x : in integer := 0;
  258. y : in integer := 0;
  259. u : in integer := 0;
  260. v : in integer := 0;
  261. width : in integer := 0;
  262. height : in integer := 0;
  263. ignore : in boolean := false;
  264. state : in animation := idle;
  265. factor : in integer := zoom;
  266. tint : in colour := (others => 255)) is
  267. new_width : constant float := float ((if width = 0 then data.width else width));
  268. new_height : constant float := float ((if height = 0 then data.height else height));
  269. --
  270. new_tint : ray.colour := (ray.colour_range (tint.r), ray.colour_range (tint.g), ray.colour_range (tint.b), ray.colour_range (tint.a));
  271. begin
  272. ray.draw_texture (data => texture_array (data.index),
  273. uv => (x => float (if ignore then u else (animation_time mod data.frames) * data.width),
  274. y => float (if ignore then v else (animation'pos (state) mod data.states) * data.height),
  275. width => new_width,
  276. height => new_height),
  277. view => (x => float (x),
  278. y => float (y),
  279. width => new_width * float (factor),
  280. height => new_height * float (factor)),
  281. tint => new_tint);
  282. end draw;
  283. ------------------------------------------------------------------------------------------
  284. procedure draw_horizontally (data : in sprite; x, y, width, factor : in integer; tint : in colour := (others => 255)) is
  285. begin
  286. for move in 0 .. width / data.width - 1 loop
  287. draw (data, x + move * data.width, y, tint => tint, factor => factor);
  288. end loop;
  289. --
  290. if width mod data.width > 0 then
  291. draw (data, x + (width / data.width) * data.width, y, 0, 0, width mod data.width, data.height, tint => tint, factor => factor);
  292. end if;
  293. end draw_horizontally;
  294. ------------------------------------------------------------------------------------------
  295. procedure draw_vertically (data : in sprite; x, y, height, factor : in integer; tint : in colour := (others => 255)) is
  296. begin
  297. for move in 0 .. height / data.height - 1 loop
  298. draw (data, x, y + move * data.height, tint => tint, factor => factor);
  299. end loop;
  300. --
  301. if height mod data.height > 0 then
  302. draw (data, x, y + (height / data.height) * data.height, 0, 0, data.width, height mod data.height, tint => tint, factor => factor);
  303. end if;
  304. end draw_vertically;
  305. ------------------------------------------------------------------------------------------
  306. procedure write (text : in string := "";
  307. x : in integer := 0;
  308. y : in integer := 0;
  309. tint : in colour := (others => 255);
  310. size : in integer := 0;
  311. data : in font := (others => 0)) is
  312. new_tint : ray.colour := (ray.colour_range (tint.r), ray.colour_range (tint.g), ray.colour_range (tint.b), ray.colour_range (tint.a));
  313. begin
  314. ray.draw_text (data => font_array (data.index),
  315. text => c_string (text),
  316. view => (float (x), float (y)),
  317. scale => (if size = 0 then float (font_array (data.index).base) else float (size)),
  318. space => float (font_array (data.index).pad),
  319. tint => new_tint);
  320. end write;
  321. ------------------------------------------------------------------------------------------
  322. procedure play (index : in integer) is begin ray.play_sound (sound_array (index)); end play;
  323. procedure stop (index : in integer) is begin ray.stop_sound (sound_array (index)); end stop;
  324. ------------------------------------------------------------------------------------------
  325. procedure overlay is
  326. begin
  327. ray.draw_rectangle (0, 0, window_width, window_height, (0, 0, 0, 127));
  328. end overlay;
  329. ------------------------------------------------------------------------------------------
  330. function read_help_box return string is begin return to_string (help_box.text); end read_help_box;
  331. function read_text_box return string is begin return to_string (text_box.text); end read_text_box;
  332. ------------------------------------------------------------------------------------------
  333. procedure write_help_box (text : in string) is begin help_box.text := to_unbounded_string (text); end write_help_box;
  334. procedure write_text_box (text : in string) is begin text_box.text := to_unbounded_string (text); end write_text_box;
  335. ------------------------------------------------------------------------------------------
  336. procedure save_point (here : in io.file_type; data : in point) is
  337. begin
  338. core.io.write (here, data.value);
  339. core.io.write (here, data.limit);
  340. end save_point;
  341. ------------------------------------------------------------------------------------------
  342. procedure load_point (here : in core.io.file_type; data : out point) is
  343. begin
  344. core.io.read (here, data.value);
  345. core.io.read (here, data.limit);
  346. end load_point;
  347. ------------------------------------------------------------------------------------------
  348. procedure increment (value : in out integer; super : in natural := 1) is begin value := value + super; end increment;
  349. procedure decrement (value : in out integer; super : in natural := 1) is begin value := value - super; end decrement;
  350. ------------------------------------------------------------------------------------------
  351. procedure idle_skip is null;
  352. ------------------------------------------------------------------------------------------
  353. procedure toggle_fullscreen is
  354. begin
  355. ray.toggle_fullscreen;
  356. end toggle_fullscreen;
  357. ------------------------------------------------------------------------------------------
  358. procedure initialize is
  359. begin
  360. echo (comment, "Initializing core components...");
  361. --
  362. engine_active := true;
  363. texture_array := new texture_data_array (0 .. 1600);
  364. sound_array := new sound_data_array (0 .. 4);
  365. font_array := new font_data_array (0 .. 16);
  366. --
  367. echo (comment, "-- Setting trace log level to none (ignoring all default Raylib logs).");
  368. ray.set_trace_log_level (ray.log_none);
  369. --
  370. ray.set_window_flags (ray.flag_window_resizable);
  371. --
  372. echo (comment, "-- Initializing Raylib window data...");
  373. echo (comment, "-- -- Window title : Xorana");
  374. echo (comment, "-- -- Window width : 1800");
  375. echo (comment, "-- -- Window height : 900");
  376. --
  377. ray.open_window (1800, 900, c_string ("Xorana"));
  378. ray.hide_cursor;
  379. --
  380. ray.set_window_minimal_size (640, 480);
  381. ray.set_window_maximal_size (3840, 2160);
  382. --
  383. echo (comment, "-- Initializing Raylib audio device data...");
  384. --~ray.open_audio_device;
  385. --
  386. game_icon := ray.load_image (c_string (folder & "/ui/game_icon.png"));
  387. --
  388. ray.window_icon (game_icon);
  389. --
  390. --ray.randomization (19970725);
  391. ray.set_target_fps (60);
  392. --
  393. echo (success, "Initialized core components.");
  394. end initialize;
  395. ------------------------------------------------------------------------------------------
  396. procedure deinitialize is
  397. begin
  398. echo (comment, "Deinitializing core components...");
  399. --
  400. engine_active := false;
  401. --
  402. echo (comment, "-- -- Unloading Raylib" & texture_count'image & " textures.");
  403. echo (comment, "-- -- Unloading Raylib" & sound_count'image & " sounds.");
  404. echo (comment, "-- -- Unloading Raylib" & font_count'image & " fonts.");
  405. --
  406. for index in 0 .. texture_count - 1 loop ray.unload_texture (texture_array (index)); end loop;
  407. for index in 0 .. sound_count - 1 loop ray.unload_sound (sound_array (index)); end loop;
  408. for index in 0 .. font_count - 1 loop ray.unload_font (font_array (index)); end loop;
  409. --
  410. echo (comment, "-- Deinitializing Raylib audio device data...");
  411. --~ray.close_audio_device;
  412. --
  413. ray.unload_image (game_icon);
  414. --
  415. echo (comment, "-- Deinitializing Raylib window data...");
  416. ray.close_window;
  417. ray.show_cursor;
  418. --
  419. echo (success, "Deinitialized core components.");
  420. end deinitialize;
  421. ------------------------------------------------------------------------------------------
  422. procedure synchronize is
  423. signal : integer := signal_code'pos (signal_none);
  424. begin
  425. global_time := global_time + 1;
  426. global_time := global_time mod (gameplay_framerate * animation_framerate);
  427. gameplay_time := global_time mod (gameplay_framerate);
  428. animation_time := global_time / (gameplay_framerate / animation_framerate);
  429. framerate := integer (ray.get_fps);
  430. signal := ray.get_key_pressed;
  431. cursor.x := ray.get_mouse_x;
  432. cursor.y := ray.get_mouse_y;
  433. wheel := wheel + ray.mouse_wheel_move;
  434. end_turn := false;
  435. --
  436. ray.end_drawing;
  437. --
  438. if ray.exit_key_is_pressed then
  439. engine_active := false;
  440. end if;
  441. --
  442. if ray.mouse_button_is_pressed (ray.mouse_button_left) then cursor_mode := cursor_left; end if;
  443. if ray.mouse_button_is_pressed (ray.mouse_button_right) then cursor_mode := cursor_right; end if;
  444. if ray.mouse_button_is_pressed (ray.mouse_button_middle) then cursor_mode := cursor_middle; end if;
  445. if ray.mouse_button_is_released (ray.mouse_button_left) then cursor_mode := cursor_none; end if;
  446. if ray.mouse_button_is_released (ray.mouse_button_right) then cursor_mode := cursor_none; end if;
  447. if ray.mouse_button_is_released (ray.mouse_button_middle) then cursor_mode := cursor_none; end if;
  448. --
  449. case signal is
  450. when 48 .. 57 => signal_mode := signal_code'val (signal - 48 + signal_code'pos (signal_0));
  451. when 65 .. 90 => signal_mode := signal_code'val (signal - 65 + signal_code'pos (signal_a));
  452. when 290 .. 301 => signal_mode := signal_code'val (signal - 290 + signal_code'pos (signal_f1));
  453. when 320 .. 329 => signal_mode := signal_code'val (signal - 320 + signal_code'pos (signal_kp_0));
  454. --
  455. when 0 => signal_mode := signal_none;
  456. when 32 => signal_mode := signal_space;
  457. when 96 => signal_mode := signal_grave;
  458. when 340 => signal_mode := signal_left_shift;
  459. when 341 => signal_mode := signal_left_control;
  460. when 333 => signal_mode := signal_kp_subtract;
  461. when 334 => signal_mode := signal_kp_add;
  462. when 256 => signal_mode := signal_escape;
  463. when 257 => signal_mode := signal_enter;
  464. when 258 => signal_mode := signal_tab;
  465. when 259 => signal_mode := signal_backspace;
  466. when 262 => signal_mode := signal_right;
  467. when 263 => signal_mode := signal_left;
  468. when 264 => signal_mode := signal_down;
  469. when 265 => signal_mode := signal_up;
  470. when others => signal_mode := signal_none;
  471. end case;
  472. --
  473. ray.begin_drawing;
  474. --
  475. ray.clear_background ((0, 0, 0, 255));
  476. end synchronize;
  477. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  478. end core;