Mirai's Miscellaneous Misadventures

M45 / core / dialogues.c

1/* license: AGPLv3 or later */
2/* copyright 2023 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[0x80];
254	int width;
255	int i;
256	struct mimimi_dialogue_paragraph *paragraph;
257	int count;
258	unsigned char *colors;
259	
260	width = 128;
261	colors = toast_dialogue->colors;
262	
263	for (i = 0 ; i < dialogue->count ; i++)
264	{
265		paragraph = dialogue->paragraphs + i;
266		
267		images[i].colors = colors;
268		mimimi_dialogue_box(images + i, paragraph->model, paragraph->name, width, paragraph->text);
269		colors += images[i].width + images[i].height;
270		toast_dialogue->heights[i] = images[i].height + 8;
271		toast_dialogue->textures[i] = (*engine->texture)(engine->data, images + i);
272		
273		count = 0;
274		while (paragraph->text[count] != 0) count++;
275		toast_dialogue->delays[i] = count * 4;
276	}
277	
278	toast_dialogue->engine = engine;
279	toast_dialogue->count = dialogue->count;
280	toast_dialogue->i = 0;
281	toast_dialogue->j = 0;
282	toast_dialogue->k = 0;
283}
284
285unsigned char mimimi_toast_dialogue_top_tick(struct mimimi_toast_dialogue *dialogue)
286{
287	int i;
288	int height;
289	struct mimimi_engine *engine;
290	
291	i = dialogue->i;
292	if (i >= dialogue->count) return 1;
293	
294	height = dialogue->heights[i];
295	engine = dialogue->engine;
296	
297	if (dialogue->k == 0)
298	{
299		(*engine->stamp)(engine->data, (128 + 8) * dialogue->j / 16 - 128, 8, dialogue->textures[i]);
300		dialogue->j++;
301		if (dialogue->j >= 16)
302		{
303			dialogue->j = 0;
304			dialogue->k++;
305		}
306		return 0;
307	}
308	
309	if (dialogue->k == 1)
310	{
311		(*engine->stamp)(engine->data, 8, 8, dialogue->textures[i]);
312		dialogue->j++;
313		if (dialogue->j >= dialogue->delays[i])
314		{
315			dialogue->j = 0;
316			dialogue->k++;
317		}
318		return 0;
319	}
320	
321	if (dialogue->k == 2)
322	{
323		(*engine->stamp)(engine->data, 8, 8 - height * dialogue->j / 32, dialogue->textures[i]);
324		dialogue->j++;
325		if (dialogue->j >= 32)
326		{
327			dialogue->j = 0;
328			dialogue->k = 0;
329			dialogue->i++;
330		}
331		return 0;
332	}
333	
334	/* unreachable */
335	return 1;
336}
337
338unsigned char mimimi_toast_dialogue_bottom_tick(struct mimimi_toast_dialogue *dialogue)
339{
340	int i;
341	int height;
342	struct mimimi_engine *engine;
343	
344	i = dialogue->i;
345	if (i >= dialogue->count) return 1;
346	
347	height = dialogue->heights[i];
348	engine = dialogue->engine;
349	
350	if (dialogue->k == 0)
351	{
352		(*engine->stamp)(engine->data, 8, engine->size.height - dialogue->j * height / 16, dialogue->textures[i]);
353		dialogue->j++;
354		if (dialogue->j >= 16)
355		{
356			dialogue->j = 0;
357			dialogue->k++;
358		}
359		return 0;
360	}
361	
362	if (dialogue->k == 1)
363	{
364		(*engine->stamp)(engine->data, 8, engine->size.height - height, dialogue->textures[i]);
365		dialogue->j++;
366		if (dialogue->j >= dialogue->delays[i])
367		{
368			dialogue->j = 0;
369			dialogue->k++;
370		}
371		return 0;
372	}
373	
374	if (dialogue->k == 2)
375	{
376		(*engine->stamp)(engine->data, 8 - (128 + 8) * dialogue->j / 32, engine->size.height - height, dialogue->textures[i]);
377		dialogue->j++;
378		if (dialogue->j >= 32)
379		{
380			dialogue->j = 0;
381			dialogue->k = 0;
382			dialogue->i++;
383		}
384		return 0;
385	}
386	
387	/* unreachable */
388	return 1;
389}