Mirai's Miscellaneous Misadventures

M51 / core / dialogues.c

1/* license: AGPLv3 or later */
2/* copyright 2024 zamfofex */
3
4#include <mimimi.h>
5
6static void mimimi_box(struct mimimi_image *image, unsigned char stroke, unsigned char fill)
7{
8	static unsigned char colors[10][10] =
9	{
10		{0,0,0,0,0,0,0,0,0,1},
11		{0,0,0,0,0,0,0,1,1,1},
12		{0,0,0,0,0,1,1,1,1,1},
13		{0,0,0,0,1,1,1,1,2,2},
14		{0,0,0,1,1,1,2,2,2,2},
15		{0,0,1,1,1,2,2,2,2,2},
16		{0,0,1,1,2,2,2,2,2,2},
17		{0,1,1,1,2,2,2,2,2,2},
18		{0,1,1,2,2,2,2,2,2,2},
19		{1,1,1,2,2,2,2,2,2,2},
20	};
21	
22	int width, height;
23	int x0, y0, x1, y1;
24	unsigned char table[3];
25	unsigned char color;
26	
27	width = image->width;
28	height = image->height;
29	
30	table[0] = 0;
31	table[1] = stroke;
32	table[2] = fill;
33	
34	for (y1 = 0 ; y1 < height ; y1++)
35	for (x1 = 0 ; x1 < width ; x1++)
36		image->colors[x1 + y1 * width] = fill;
37	
38	for (x1 = 0 ; x1 < width ; x1++)
39	{
40		image->colors[x1 + 0 * width] = stroke;
41		image->colors[x1 + 1 * width] = stroke;
42		image->colors[x1 + (height - 1) * width] = stroke;
43		image->colors[x1 + (height - 2) * width] = stroke;
44	}
45	
46	for (y1 = 0 ; y1 < height ; y1++)
47	{
48		image->colors[0 + y1 * width] = stroke;
49		image->colors[1 + y1 * width] = stroke;
50		image->colors[(width - 1) + y1 * width] = stroke;
51		image->colors[(width - 2) + y1 * width] = stroke;
52	}
53	
54	for (y0 = 0 ; y0 < 10 ; y0++)
55	for (x0 = 0 ; x0 < 10 ; x0++)
56	{
57		x1 = width - 1 - x0;
58		y1 = height - 1 - y0;
59		
60		color = table[colors[y0][x0]];
61		
62		image->colors[x0 + y0 * width] = color;
63		image->colors[x1 + y0 * width] = color;
64		image->colors[x0 + y1 * width] = color;
65		image->colors[x1 + y1 * width] = color;
66	}
67}
68
69static void mimimi_portrait(struct mimimi_image *image, struct mimimi_model *model, unsigned char background)
70{
71	static unsigned char mask[6][6] =
72	{
73		{0,0,0,0,0,0},
74		{0,0,0,0,1,1},
75		{0,0,0,1,1,1},
76		{0,0,1,1,1,1},
77		{0,1,1,1,1,1},
78		{0,1,1,1,1,1},
79	};
80	
81	int width, height;
82	struct mimimi_pose pose;
83	int i;
84	int x0, y0, x1, y1;
85	
86	width = image->width;
87	height = image->height;
88	
89	pose.layers[0].slant = 0;
90	pose.layers[0].y_angle = 42;
91	pose.layers[0].z_angle = 0;
92	
93	pose.x = width / 2;
94	pose.y = height / 2 + 16;
95	pose.count = 1;
96	
97	mimimi_pose(image, model, &pose);
98	
99	for (i = 0 ; i < width * height ; i++)
100	{
101		if (image->colors[i] == 0)
102			image->colors[i] = background;
103	}
104	
105	for (y0 = 0 ; y0 < 6 ; y0++)
106	for (x0 = 0 ; x0 < 6 ; x0++)
107	{
108		if (mask[y0][x0] != 0) continue;
109		
110		x1 = width - 1 - x0;
111		y1 = height - 1 - y0;
112		
113		image->colors[x0 + y0 * width] = 0;
114		image->colors[x1 + y0 * width] = 0;
115		image->colors[x0 + y1 * width] = 0;
116		image->colors[x1 + y1 * width] = 0;
117	}
118}
119
120static void mimimi_stamp(struct mimimi_image *target, int x0, int y0, struct mimimi_image *source)
121{
122	int x1, y1;
123	int x, y;
124	unsigned char color;
125	
126	for (y1 = 0 ; y1 < source->height ; y1++)
127	for (x1 = 0 ; x1 < source->width ; x1++)
128	{
129		x = x0 + x1;
130		y = y0 + y1;
131		
132		if (x < 0) continue;
133		if (y < 0) continue;
134		
135		if (x >= target->width) break;
136		if (y >= target->height) return;
137		
138		color = source->colors[x1 + y1 * source->width];
139		if (color == 0) continue;
140		
141		target->colors[x + y * target->width] = color;
142	}
143}
144
145static void mimimi_dialogue_box(struct mimimi_image *box, struct mimimi_model *model, char *name, int width, char *paragraph)
146{
147	char *text;
148	int count;
149	int height;
150	unsigned char colors[18 * 18];
151	struct mimimi_image portrait;
152	int n, i;
153	
154	text = paragraph;
155	count = 0;
156	while (*text != 0)
157	{
158		text = mimimi_skip_line(mimimi_font, text, width - 12);
159		count++;
160	}
161	
162	height = 30 + 14 * count;
163	
164	box->width = width;
165	box->height = height;
166	mimimi_box(box, 4, 13);
167	
168	portrait.width = 18;
169	portrait.height = 18;
170	portrait.colors = colors;
171	mimimi_portrait(&portrait, model, 8);
172	mimimi_stamp(box, 6, 6, &portrait);
173	
174	n = mimimi_draw_paragraph(mimimi_font, name, box, 28, 7, 4);
175	for (i = 28 ; i < n - 1 ; i++)
176		box->colors[i + 18 * box->width] = 4;
177	
178	mimimi_draw_text(mimimi_font, paragraph, width - 12, 14, box, 6, 24, 4);
179}
180
181void mimimi_strip_dialogue(struct mimimi_strip_dialogue *strip_dialogue, struct mimimi_dialogue *dialogue, struct mimimi_engine *engine)
182{
183	static int width = 128;
184	static int height = 2048;
185	
186	int i;
187	struct mimimi_dialogue_paragraph *paragraph;
188	void *texture;
189	struct mimimi_image image;
190	unsigned char colors[128 * 512];
191	int notch;
192	
193	notch = 0;
194	
195	strip_dialogue->image.width = width;
196	strip_dialogue->image.height = height;
197	strip_dialogue->image.colors = strip_dialogue->colors;
198	for (i = 0 ; i < width * height ; i++)
199		strip_dialogue->image.colors[i] = 0;
200	
201	for (i = 0 ; i < dialogue->count ; i++)
202	{
203		image.colors = colors;
204		paragraph = dialogue->paragraphs + i;
205		mimimi_dialogue_box(&image, paragraph->model, paragraph->name, width, paragraph->text);
206		mimimi_stamp(&strip_dialogue->image, 0, notch, &image);
207		notch += image.height + 8;
208		strip_dialogue->notches[i] = notch;
209	}
210	
211	texture = (*engine->texture)(engine->data, &strip_dialogue->image);
212	
213	strip_dialogue->engine = engine;
214	strip_dialogue->count = dialogue->count;
215	strip_dialogue->texture = texture;
216	strip_dialogue->held = 1;
217	strip_dialogue->i = 0;
218	strip_dialogue->x = 0;
219	strip_dialogue->y = 0;
220}
221
222unsigned char mimimi_strip_dialogue_tick(struct mimimi_strip_dialogue *dialogue, unsigned char held)
223{
224	struct mimimi_engine *engine;
225	int distance;
226	
227	if (dialogue->i >= dialogue->count) return 1;
228	
229	engine = dialogue->engine;
230	
231	if (dialogue->i < dialogue->count)
232	{
233		(*engine->stamp)(engine->data, 8, engine->size.height - dialogue->y, dialogue->texture);
234		
235		distance = dialogue->notches[dialogue->i] - dialogue->y;
236		if (distance > 0) dialogue->y += (distance - 1) / 8 + 1;
237		
238		if (dialogue->held == 0 && held != 0)
239			dialogue->i++;
240		dialogue->held = held;
241	}
242	else
243	{
244		if (dialogue->x >= 32) return 1;
245		(*engine->stamp)(engine->data, 8 - (128 + 8) * dialogue->x / 32, engine->size.height - dialogue->notches[dialogue->count - 1], dialogue->texture);
246	}
247	
248	return 0;
249}
250
251void mimimi_toast_dialogue(struct mimimi_toast_dialogue *toast_dialogue, struct mimimi_dialogue *dialogue, struct mimimi_engine *engine)
252{
253	struct mimimi_image *images;
254	int width;
255	int i;
256	struct mimimi_dialogue_paragraph *paragraph;
257	int count;
258	unsigned char *colors;
259	
260	width = 128;
261	images = toast_dialogue->images;
262	colors = toast_dialogue->colors;
263	
264	for (i = 0 ; i < dialogue->count ; i++)
265	{
266		paragraph = dialogue->paragraphs + i;
267		
268		images[i].colors = colors;
269		mimimi_dialogue_box(images + i, paragraph->model, paragraph->name, width, paragraph->text);
270		colors += images[i].width + images[i].height;
271		toast_dialogue->heights[i] = images[i].height + 8;
272		toast_dialogue->textures[i] = (*engine->texture)(engine->data, images + i);
273		
274		count = 0;
275		while (paragraph->text[count] != 0) count++;
276		toast_dialogue->delays[i] = count * 4;
277	}
278	
279	toast_dialogue->engine = engine;
280	toast_dialogue->count = dialogue->count;
281	toast_dialogue->i = 0;
282	toast_dialogue->j = 0;
283	toast_dialogue->k = 0;
284}
285
286unsigned char mimimi_toast_dialogue_top_tick(struct mimimi_toast_dialogue *dialogue)
287{
288	int i;
289	int height;
290	struct mimimi_engine *engine;
291	
292	i = dialogue->i;
293	if (i >= dialogue->count) return 1;
294	
295	height = dialogue->heights[i];
296	engine = dialogue->engine;
297	
298	if (dialogue->k == 0)
299	{
300		(*engine->stamp)(engine->data, (128 + 8) * dialogue->j / 16 - 128, 8, dialogue->textures[i]);
301		dialogue->j++;
302		if (dialogue->j >= 16)
303		{
304			dialogue->j = 0;
305			dialogue->k++;
306		}
307		return 0;
308	}
309	
310	if (dialogue->k == 1)
311	{
312		(*engine->stamp)(engine->data, 8, 8, dialogue->textures[i]);
313		dialogue->j++;
314		if (dialogue->j >= dialogue->delays[i])
315		{
316			dialogue->j = 0;
317			dialogue->k++;
318		}
319		return 0;
320	}
321	
322	if (dialogue->k == 2)
323	{
324		(*engine->stamp)(engine->data, 8, 8 - height * dialogue->j / 32, dialogue->textures[i]);
325		dialogue->j++;
326		if (dialogue->j >= 32)
327		{
328			dialogue->j = 0;
329			dialogue->k = 0;
330			dialogue->i++;
331		}
332		return 0;
333	}
334	
335	/* unreachable */
336	return 1;
337}
338
339unsigned char mimimi_toast_dialogue_bottom_tick(struct mimimi_toast_dialogue *dialogue)
340{
341	int i;
342	int height;
343	struct mimimi_engine *engine;
344	
345	i = dialogue->i;
346	if (i >= dialogue->count) return 1;
347	
348	height = dialogue->heights[i];
349	engine = dialogue->engine;
350	
351	if (dialogue->k == 0)
352	{
353		(*engine->stamp)(engine->data, 8, engine->size.height - dialogue->j * height / 16, dialogue->textures[i]);
354		dialogue->j++;
355		if (dialogue->j >= 16)
356		{
357			dialogue->j = 0;
358			dialogue->k++;
359		}
360		return 0;
361	}
362	
363	if (dialogue->k == 1)
364	{
365		(*engine->stamp)(engine->data, 8, engine->size.height - height, dialogue->textures[i]);
366		dialogue->j++;
367		if (dialogue->j >= dialogue->delays[i])
368		{
369			dialogue->j = 0;
370			dialogue->k++;
371		}
372		return 0;
373	}
374	
375	if (dialogue->k == 2)
376	{
377		(*engine->stamp)(engine->data, 8 - (128 + 8) * dialogue->j / 32, engine->size.height - height, dialogue->textures[i]);
378		dialogue->j++;
379		if (dialogue->j >= 32)
380		{
381			dialogue->j = 0;
382			dialogue->k = 0;
383			dialogue->i++;
384		}
385		return 0;
386	}
387	
388	/* unreachable */
389	return 1;
390}