Added load and draw image

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2025-08-11 11:27:18 +0200
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2025-08-11 11:27:18 +0200
Commit 321a8063c025c8a1c20aa16e9a3a28d8065294eb (patch)
-rw-r--r-- README.md 18
-rw-r--r-- main.c 104
-rw-r--r-- tests/test.lua 24
3 files changed, 116 insertions, 30 deletions
diff --git a/README.md b/README.md
1
**Bidi** is a lightweight framework and fantasy console designed for creating
1
**Bidi** is a lightweight framework and fantasy console designed for creating
2
tiny video games, perfect for learning and having fun.  It takes heavy
2
tiny video games, perfect for learning and having fun.  It takes heavy
3
inspiration from [LÖVE](https://www.love2d.org/),
3
inspiration from [LÖVE](https://www.love2d.org/),
4
[Pico8](https://www.lexaloffle.com/pico-8.php) and [Atari
4
[Pico8](https://www.lexaloffle.com/pico-8.php), [Atari
5
2600](https://en.wikipedia.org/wiki/Atari_2600).
5
2600](https://en.wikipedia.org/wiki/Atari_2600) and [Windows
  
6
3.1](https://en.wikipedia.org/wiki/Windows_3.1).
6
  
7
  
7
You develop your games in Lua and you are only allowed to have one Lua file
8
You develop your games in Lua and you are only allowed to have one Lua file
8
where all your code lives. You can then load external assets like images and
9
where all your code lives. You can then load external assets like images and
...
75
| `draw_circle`    | `number center_x`, `number center_y`, `number radius`, `color color`                        |          |
76
| `draw_circle`    | `number center_x`, `number center_y`, `number radius`, `color color`                        |          |
76
| `draw_ellipse`   | `number center_x`, `number center_y`, `number radius_h`, `number radius_v`, `color color`   |          |
77
| `draw_ellipse`   | `number center_x`, `number center_y`, `number radius_h`, `number radius_v`, `color color`   |          |
77
| `draw_triangle`  | `number x1`, `number y1`, `number x2`, `number y2`, `number x3`, `number y3`, `color color` |          |
78
| `draw_triangle`  | `number x1`, `number y1`, `number x2`, `number y2`, `number x3`, `number y3`, `color color` |          |
78
| `load_image`     | `TODO`                                                                                      |          |
79
| `load_image`     | `string filepath`                                                                           | `string` |
  
80
| `draw_image`     | `string uid`, `number x`, `number y`                                                        |          |
79
| `load_audio`     | `TODO`                                                                                      |          |
81
| `load_audio`     | `TODO`                                                                                      |          |
80
| `button_down`    | `button button`                                                                             | `bool`   |
82
| `button_down`    | `button button`                                                                             | `bool`   |
81
| `button_pressed` | `button button`                                                                             | `bool`   |
83
| `button_pressed` | `button button`                                                                             | `bool`   |
...
137
  
139
  
138
- https://hardwaretester.com/gamepad
140
- https://hardwaretester.com/gamepad
139
- https://www.raylib.com/examples/core/loader.html?name=core_input_gamepad
141
- https://www.raylib.com/examples/core/loader.html?name=core_input_gamepad
  
142
  
  
143
## Interesting games
  
144
  
  
145
- https://www.old-games.com/download/3873/aethra-s-chronicles
  
146
- https://www.old-games.com/download/10746/wintrek-1992
  
147
- https://www.old-games.com/download/3887/bard-s-tale-1
  
148
- https://www.old-games.com/download/3902/castle-of-the-winds
  
149
- https://www.old-games.com/download/3923/decker
  
150
- https://www.old-games.com/download/3920/darkspyre
  
151
- https://www.old-games.com/download/6089/exile-3-ruined-world
140
  
152
  
141
## Development references
153
## Development references
142
  
154
  
...
diff --git a/main.c b/main.c
...
3
#include <getopt.h>
3
#include <getopt.h>
4
#include <stdlib.h>
4
#include <stdlib.h>
5
#include <time.h>
5
#include <time.h>
  
6
#include <string.h>
6
  
7
  
7
#include "raylib.h"
8
#include "raylib.h"
8
#include "lua.h"
9
#include "lua.h"
...
24
#define XBOX_ALIAS_2 "x-box"
25
#define XBOX_ALIAS_2 "x-box"
25
#define PS_ALIAS "playstation"
26
#define PS_ALIAS "playstation"
26
  
27
  
27
typedef struct {
28
typedef struct ExternalImage {
28
	char uid[UID_LENGTH + 1];
29
	char uid[UID_LENGTH + 1];
29
	const char* filepath;
30
	Texture2D texture;
  
31
	struct ExternalImage *next;
30
} ExternalImage;
32
} ExternalImage;
31
  
33
  
32
typedef struct {
34
typedef struct {
33
} ExternalAudio;
35
	ExternalImage *head;
  
36
	ExternalImage *tail;
  
37
	int count;
  
38
} ImageList;
34
  
39
  
35
typedef struct {
40
typedef struct {
36
	Font font;
41
	Font font;
37
	int font_size;
42
	int font_size;
38
	Camera2D camera;
43
	Camera2D camera;
39
	ExternalImage *images;
44
	ImageList images;
40
	ExternalAudio *audio;
  
41
} Context;
45
} Context;
42
  
46
  
43
// Setting up global context.
47
// Setting up global context.
44
Context ctx = {0};
48
Context ctx = {0};
45
  
49
  
46
static void generate_uid(char *uid) {
50
static void generate_uid(char *str, size_t length) {
47
	const char *chars = "0123456789abcdef";
51
	static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
48
	for (size_t i = 0; i < UID_LENGTH; i++) {
52
	size_t charset_size = sizeof(charset) - 1; // exclude null terminator
49
		uid[i] = chars[rand() % 16];
53
  
  
54
	for (size_t i = 0; i < length; i++) {
  
55
		int key = rand() % charset_size;
  
56
		str[i] = charset[key];
50
	}
57
	}
51
	uid[UID_LENGTH + 1] = '\0';
58
	str[length] = '\0';
  
59
}
  
60
  
  
61
void init_image_list(ImageList *list) {
  
62
	list->head = NULL;
  
63
	list->tail = NULL;
  
64
	list->count = 0;
52
}
65
}
53
  
66
  
54
static int lua_getfield_int(lua_State *L, int index, const char *key) {
67
static int lua_getfield_int(lua_State *L, int index, const char *key) {
...
67
  
80
  
68
static int load_embedded_module(lua_State *L, const char *module_code, size_t code_len, const char *module_name) {
81
static int load_embedded_module(lua_State *L, const char *module_code, size_t code_len, const char *module_name) {
69
	if (luaL_loadbuffer(L, module_code, code_len, module_name) || lua_pcall(L, 0, 1, 0)) {
82
	if (luaL_loadbuffer(L, module_code, code_len, module_name) || lua_pcall(L, 0, 1, 0)) {
70
		fprintf(stderr, "Error loading %s: %s\n", module_name, lua_tostring(L, -1));
83
		TraceLog(LOG_FATAL, "Error loading %s: %s\n", module_name, lua_tostring(L, -1));
71
		return 0;
84
		return 0;
72
	}
85
	}
73
	lua_setglobal(L, module_name);
86
	lua_setglobal(L, module_name);
...
380
}
393
}
381
  
394
  
382
static int l_load_image(lua_State *L) {
395
static int l_load_image(lua_State *L) {
  
396
	const char *filepath = luaL_checkstring(L, 1);
  
397
  
  
398
	ExternalImage *img = malloc(sizeof(ExternalImage));
  
399
	if (!img) {
  
400
		TraceLog(LOG_FATAL, "Out of memory!");
  
401
		return 0;
  
402
	}
  
403
  
  
404
	Image image = LoadImage(filepath);
  
405
	if (!image.data) {
  
406
		TraceLog(LOG_FATAL, "Failed to load image: %s\n", filepath);
  
407
		free(img);
  
408
		return 0;
  
409
	}
  
410
  
  
411
	img->texture = LoadTextureFromImage(image);
  
412
	UnloadImage(image);
  
413
  
  
414
	generate_uid(img->uid, UID_LENGTH);
  
415
	img->next = NULL;
  
416
  
  
417
	if (ctx.images.tail) {
  
418
		ctx.images.tail->next = img;
  
419
		ctx.images.tail = img;
  
420
	} else {
  
421
		ctx.images.head = img;
  
422
		ctx.images.tail = img;
  
423
	}
  
424
	ctx.images.count++;
  
425
  
  
426
	TraceLog(LOG_WARNING, "[adding]\t %s (%d)", img->uid, strlen(img->uid));
  
427
  
  
428
	lua_pushstring(L, img->uid);
  
429
	return 1;
  
430
}
  
431
  
  
432
static int l_load_audio(lua_State *L) {
383
	return 0;
433
	return 0;
384
}
434
}
385
  
435
  
386
static int l_load_audio(lua_State *L) {
436
static int l_draw_image(lua_State *L) {
  
437
	const char *uid = luaL_checkstring(L, 1);
  
438
	int x = luaL_checknumber(L, 2);
  
439
	int y = luaL_checknumber(L, 3);
  
440
  
  
441
	ExternalImage *current = ctx.images.head;
  
442
	while (current) {
  
443
		if (strncmp(current->uid, uid, UID_LENGTH) == 0) {
  
444
			/* TraceLog(LOG_WARNING, "[match]\t %s (%d)", uid, strlen(uid)); */
  
445
			DrawTexture(current->texture, x, y, WHITE);
  
446
			break;
  
447
		}
  
448
		current = current->next;
  
449
	}
  
450
  
387
	return 0;
451
	return 0;
388
}
452
}
389
  
453
  
...
404
  
468
  
405
int main(int argc, char *argv[]) {
469
int main(int argc, char *argv[]) {
406
	srand(time(NULL));
470
	srand(time(NULL));
  
471
	init_image_list(&ctx.images);
407
  
472
  
408
	TraceLogLevel debug_level = LOG_WARNING;
473
	TraceLogLevel debug_level = LOG_WARNING;
409
	const char *run_file = NULL;
474
	const char *run_file = NULL;
...
472
		lua_register(L, "get_width", l_get_width);
537
		lua_register(L, "get_width", l_get_width);
473
		lua_register(L, "get_height", l_get_height);
538
		lua_register(L, "get_height", l_get_height);
474
  
539
  
  
540
		lua_register(L, "load_image", l_load_image);
  
541
		lua_register(L, "load_audio", l_load_audio);
  
542
  
  
543
		lua_register(L, "button_down", l_button_down);
  
544
		lua_register(L, "button_pressed", l_button_pressed);
  
545
  
475
		lua_register(L, "draw_info", l_draw_info);
546
		lua_register(L, "draw_info", l_draw_info);
476
		lua_register(L, "draw_rect", l_draw_rect);
547
		lua_register(L, "draw_rect", l_draw_rect);
477
		lua_register(L, "draw_text", l_draw_text);
548
		lua_register(L, "draw_text", l_draw_text);
...
480
		lua_register(L, "draw_circle", l_draw_circle);
551
		lua_register(L, "draw_circle", l_draw_circle);
481
		lua_register(L, "draw_ellipse", l_draw_ellipse);
552
		lua_register(L, "draw_ellipse", l_draw_ellipse);
482
		lua_register(L, "draw_triangle", l_draw_triangle);
553
		lua_register(L, "draw_triangle", l_draw_triangle);
483
  
554
		lua_register(L, "draw_image", l_draw_image);
484
		lua_register(L, "load_image", l_load_image);
  
485
		lua_register(L, "load_audio", l_load_audio);
  
486
  
  
487
		lua_register(L, "button_down", l_button_down);
  
488
		lua_register(L, "button_pressed", l_button_pressed);
  
489
  
555
  
490
		// Interpreting and running input file Lua script.
556
		// Interpreting and running input file Lua script.
491
		if (luaL_loadfile(L, run_file) || lua_pcall(L, 0, 0, 0)) {
557
		if (luaL_loadfile(L, run_file) || lua_pcall(L, 0, 0, 0)) {
492
			fprintf(stderr, "Error: %s\n", lua_tostring(L, -1));
558
			TraceLog(LOG_FATAL, "Error: %s\n", lua_tostring(L, -1));
493
			return 1;
559
			return 1;
494
		}
560
		}
495
  
561
  
...
diff --git a/tests/test.lua b/tests/test.lua
1
math.randomseed(os.time())
1
math.randomseed(os.time())
2
  
2
  
  
3
open_window(800, 800, "Sample Window")
  
4
set_fps(60)
  
5
  
3
test_button_square = { x = 400, y = 400 }
6
test_button_square = { x = 400, y = 400 }
4
test_button_color = color.RED
7
test_button_color = color.RED
5
test_button_speed = 200
8
test_button_speed = 200
6
  
  
7
test_camera_position = { x = 0, y = 0 }
9
test_camera_position = { x = 0, y = 0 }
8
test_camera_speed = 200
10
test_camera_speed = 200
9
  
  
10
test_images_asset1 = load_image("tests/icons/icon_1.png")
11
test_images_asset1 = load_image("tests/icons/icon_1.png")
11
  
12
test_images_asset2 = load_image("tests/icons/icon_2.png")
12
open_window(800, 800, "Sample Window")
  
13
set_fps(60)
  
14
  
13
  
15
function test_api()
14
function test_api()
16
	draw_rect(100, 100, 300, 200, color.YELLOW)
15
	draw_rect(100, 100, 300, 200, color.YELLOW)
...
26
function get_random_color()
25
function get_random_color()
27
	local keys = {}
26
	local keys = {}
28
	for k in pairs(color) do
27
	for k in pairs(color) do
29
		table.insert(keys, k)  -- Collect all keys
28
		table.insert(keys, k)
30
	end
29
	end
31
	local randomKey = keys[math.random(1, #keys)]  -- Select a random key
30
	local randomKey = keys[math.random(1, #keys)]
32
	return color[randomKey]  -- Return the corresponding color
31
	return color[randomKey]
33
end
32
end
34
  
33
  
35
function test_json()
34
function test_json()
...
140
	draw_text("This text doesn't move!", 10, 10, 20, color.VIOLET)
139
	draw_text("This text doesn't move!", 10, 10, 20, color.VIOLET)
141
end
140
end
142
  
141
  
  
142
function test_images()
  
143
	draw_text(string.format("uid: %s", test_images_asset1), 50, 50, 20, color.VIOLET)
  
144
	draw_text(string.format("uid: %s", test_images_asset2), 50, 80, 20, color.VIOLET)
  
145
  
  
146
	draw_image(test_images_asset1, 150, 150)
  
147
	draw_image(test_images_asset2, 200, 200)
  
148
end
  
149
  
143
while window_running() do
150
while window_running() do
144
	start_drawing()
151
	start_drawing()
145
	clear_window(color.BLACK)
152
	clear_window(color.BLACK)
...
148
	-- test_api()
155
	-- test_api()
149
	-- test_buttons()
156
	-- test_buttons()
150
	-- test_camera()
157
	-- test_camera()
  
158
	-- test_images()
151
  
159
  
152
	draw_info()
160
	draw_info()
153
	stop_drawing()
161
	stop_drawing()
...