Mirai's Miscellaneous Misadventures

M46 / core / dialogues.c

/* license: AGPLv3 or later */
/* copyright 2023 zamfofex */

#include <mimimi.h>

static void mimimi_box(struct mimimi_image *image, unsigned char stroke, unsigned char fill)
{
	static unsigned char colors[10][10] =
	{
		{0,0,0,0,0,0,0,0,0,1},
		{0,0,0,0,0,0,0,1,1,1},
		{0,0,0,0,0,1,1,1,1,1},
		{0,0,0,0,1,1,1,1,2,2},
		{0,0,0,1,1,1,2,2,2,2},
		{0,0,1,1,1,2,2,2,2,2},
		{0,0,1,1,2,2,2,2,2,2},
		{0,1,1,1,2,2,2,2,2,2},
		{0,1,1,2,2,2,2,2,2,2},
		{1,1,1,2,2,2,2,2,2,2},
	};
	
	int width, height;
	int x0, y0, x1, y1;
	unsigned char table[3];
	unsigned char color;
	
	width = image->width;
	height = image->height;
	
	table[0] = 0;
	table[1] = stroke;
	table[2] = fill;
	
	for (y1 = 0 ; y1 < height ; y1++)
	for (x1 = 0 ; x1 < width ; x1++)
		image->colors[x1 + y1 * width] = fill;
	
	for (x1 = 0 ; x1 < width ; x1++)
	{
		image->colors[x1 + 0 * width] = stroke;
		image->colors[x1 + 1 * width] = stroke;
		image->colors[x1 + (height - 1) * width] = stroke;
		image->colors[x1 + (height - 2) * width] = stroke;
	}
	
	for (y1 = 0 ; y1 < height ; y1++)
	{
		image->colors[0 + y1 * width] = stroke;
		image->colors[1 + y1 * width] = stroke;
		image->colors[(width - 1) + y1 * width] = stroke;
		image->colors[(width - 2) + y1 * width] = stroke;
	}
	
	for (y0 = 0 ; y0 < 10 ; y0++)
	for (x0 = 0 ; x0 < 10 ; x0++)
	{
		x1 = width - 1 - x0;
		y1 = height - 1 - y0;
		
		color = table[colors[y0][x0]];
		
		image->colors[x0 + y0 * width] = color;
		image->colors[x1 + y0 * width] = color;
		image->colors[x0 + y1 * width] = color;
		image->colors[x1 + y1 * width] = color;
	}
}

static void mimimi_portrait(struct mimimi_image *image, struct mimimi_model *model, unsigned char background)
{
	static unsigned char mask[6][6] =
	{
		{0,0,0,0,0,0},
		{0,0,0,0,1,1},
		{0,0,0,1,1,1},
		{0,0,1,1,1,1},
		{0,1,1,1,1,1},
		{0,1,1,1,1,1},
	};
	
	int width, height;
	struct mimimi_pose pose;
	int i;
	int x0, y0, x1, y1;
	
	width = image->width;
	height = image->height;
	
	pose.layers[0].slant = 0;
	pose.layers[0].y_angle = 42;
	pose.layers[0].z_angle = 0;
	
	pose.x = width / 2;
	pose.y = height / 2 + 16;
	pose.count = 1;
	
	mimimi_pose(image, model, &pose);
	
	for (i = 0 ; i < width * height ; i++)
	{
		if (image->colors[i] == 0)
			image->colors[i] = background;
	}
	
	for (y0 = 0 ; y0 < 6 ; y0++)
	for (x0 = 0 ; x0 < 6 ; x0++)
	{
		if (mask[y0][x0] != 0) continue;
		
		x1 = width - 1 - x0;
		y1 = height - 1 - y0;
		
		image->colors[x0 + y0 * width] = 0;
		image->colors[x1 + y0 * width] = 0;
		image->colors[x0 + y1 * width] = 0;
		image->colors[x1 + y1 * width] = 0;
	}
}

static void mimimi_stamp(struct mimimi_image *target, int x0, int y0, struct mimimi_image *source)
{
	int x1, y1;
	int x, y;
	unsigned char color;
	
	for (y1 = 0 ; y1 < source->height ; y1++)
	for (x1 = 0 ; x1 < source->width ; x1++)
	{
		x = x0 + x1;
		y = y0 + y1;
		
		if (x < 0) continue;
		if (y < 0) continue;
		
		if (x >= target->width) break;
		if (y >= target->height) return;
		
		color = source->colors[x1 + y1 * source->width];
		if (color == 0) continue;
		
		target->colors[x + y * target->width] = color;
	}
}

static void mimimi_dialogue_box(struct mimimi_image *box, struct mimimi_model *model, char *name, int width, char *paragraph)
{
	char *text;
	int count;
	int height;
	unsigned char colors[18 * 18];
	struct mimimi_image portrait;
	int n, i;
	
	text = paragraph;
	count = 0;
	while (*text != 0)
	{
		text = mimimi_skip_line(mimimi_font, text, width - 12);
		count++;
	}
	
	height = 30 + 14 * count;
	
	box->width = width;
	box->height = height;
	mimimi_box(box, 4, 13);
	
	portrait.width = 18;
	portrait.height = 18;
	portrait.colors = colors;
	mimimi_portrait(&portrait, model, 8);
	mimimi_stamp(box, 6, 6, &portrait);
	
	n = mimimi_draw_paragraph(mimimi_font, name, box, 28, 7, 4);
	for (i = 28 ; i < n - 1 ; i++)
		box->colors[i + 18 * box->width] = 4;
	
	mimimi_draw_text(mimimi_font, paragraph, width - 12, 14, box, 6, 24, 4);
}

void mimimi_strip_dialogue(struct mimimi_strip_dialogue *strip_dialogue, struct mimimi_dialogue *dialogue, struct mimimi_engine *engine)
{
	static int width = 128;
	static int height = 2048;
	
	int i;
	struct mimimi_dialogue_paragraph *paragraph;
	void *texture;
	struct mimimi_image image;
	unsigned char colors[128 * 512];
	int notch;
	
	notch = 0;
	
	strip_dialogue->image.width = width;
	strip_dialogue->image.height = height;
	strip_dialogue->image.colors = strip_dialogue->colors;
	for (i = 0 ; i < width * height ; i++)
		strip_dialogue->image.colors[i] = 0;
	
	for (i = 0 ; i < dialogue->count ; i++)
	{
		image.colors = colors;
		paragraph = dialogue->paragraphs + i;
		mimimi_dialogue_box(&image, paragraph->model, paragraph->name, width, paragraph->text);
		mimimi_stamp(&strip_dialogue->image, 0, notch, &image);
		notch += image.height + 8;
		strip_dialogue->notches[i] = notch;
	}
	
	texture = (*engine->texture)(engine->data, &strip_dialogue->image);
	
	strip_dialogue->engine = engine;
	strip_dialogue->count = dialogue->count;
	strip_dialogue->texture = texture;
	strip_dialogue->held = 1;
	strip_dialogue->i = 0;
	strip_dialogue->x = 0;
	strip_dialogue->y = 0;
}

unsigned char mimimi_strip_dialogue_tick(struct mimimi_strip_dialogue *dialogue, unsigned char held)
{
	struct mimimi_engine *engine;
	int distance;
	
	if (dialogue->i >= dialogue->count) return 1;
	
	engine = dialogue->engine;
	
	if (dialogue->i < dialogue->count)
	{
		(*engine->stamp)(engine->data, 8, engine->size.height - dialogue->y, dialogue->texture);
		
		distance = dialogue->notches[dialogue->i] - dialogue->y;
		if (distance > 0) dialogue->y += (distance - 1) / 8 + 1;
		
		if (dialogue->held == 0 && held != 0)
			dialogue->i++;
		dialogue->held = held;
	}
	else
	{
		if (dialogue->x >= 32) return 1;
		(*engine->stamp)(engine->data, 8 - (128 + 8) * dialogue->x / 32, engine->size.height - dialogue->notches[dialogue->count - 1], dialogue->texture);
	}
	
	return 0;
}

void mimimi_toast_dialogue(struct mimimi_toast_dialogue *toast_dialogue, struct mimimi_dialogue *dialogue, struct mimimi_engine *engine)
{
	struct mimimi_image images[0x80];
	int width;
	int i;
	struct mimimi_dialogue_paragraph *paragraph;
	int count;
	unsigned char *colors;
	
	width = 128;
	colors = toast_dialogue->colors;
	
	for (i = 0 ; i < dialogue->count ; i++)
	{
		paragraph = dialogue->paragraphs + i;
		
		images[i].colors = colors;
		mimimi_dialogue_box(images + i, paragraph->model, paragraph->name, width, paragraph->text);
		colors += images[i].width + images[i].height;
		toast_dialogue->heights[i] = images[i].height + 8;
		toast_dialogue->textures[i] = (*engine->texture)(engine->data, images + i);
		
		count = 0;
		while (paragraph->text[count] != 0) count++;
		toast_dialogue->delays[i] = count * 4;
	}
	
	toast_dialogue->engine = engine;
	toast_dialogue->count = dialogue->count;
	toast_dialogue->i = 0;
	toast_dialogue->j = 0;
	toast_dialogue->k = 0;
}

unsigned char mimimi_toast_dialogue_top_tick(struct mimimi_toast_dialogue *dialogue)
{
	int i;
	int height;
	struct mimimi_engine *engine;
	
	i = dialogue->i;
	if (i >= dialogue->count) return 1;
	
	height = dialogue->heights[i];
	engine = dialogue->engine;
	
	if (dialogue->k == 0)
	{
		(*engine->stamp)(engine->data, (128 + 8) * dialogue->j / 16 - 128, 8, dialogue->textures[i]);
		dialogue->j++;
		if (dialogue->j >= 16)
		{
			dialogue->j = 0;
			dialogue->k++;
		}
		return 0;
	}
	
	if (dialogue->k == 1)
	{
		(*engine->stamp)(engine->data, 8, 8, dialogue->textures[i]);
		dialogue->j++;
		if (dialogue->j >= dialogue->delays[i])
		{
			dialogue->j = 0;
			dialogue->k++;
		}
		return 0;
	}
	
	if (dialogue->k == 2)
	{
		(*engine->stamp)(engine->data, 8, 8 - height * dialogue->j / 32, dialogue->textures[i]);
		dialogue->j++;
		if (dialogue->j >= 32)
		{
			dialogue->j = 0;
			dialogue->k = 0;
			dialogue->i++;
		}
		return 0;
	}
	
	/* unreachable */
	return 1;
}

unsigned char mimimi_toast_dialogue_bottom_tick(struct mimimi_toast_dialogue *dialogue)
{
	int i;
	int height;
	struct mimimi_engine *engine;
	
	i = dialogue->i;
	if (i >= dialogue->count) return 1;
	
	height = dialogue->heights[i];
	engine = dialogue->engine;
	
	if (dialogue->k == 0)
	{
		(*engine->stamp)(engine->data, 8, engine->size.height - dialogue->j * height / 16, dialogue->textures[i]);
		dialogue->j++;
		if (dialogue->j >= 16)
		{
			dialogue->j = 0;
			dialogue->k++;
		}
		return 0;
	}
	
	if (dialogue->k == 1)
	{
		(*engine->stamp)(engine->data, 8, engine->size.height - height, dialogue->textures[i]);
		dialogue->j++;
		if (dialogue->j >= dialogue->delays[i])
		{
			dialogue->j = 0;
			dialogue->k++;
		}
		return 0;
	}
	
	if (dialogue->k == 2)
	{
		(*engine->stamp)(engine->data, 8 - (128 + 8) * dialogue->j / 32, engine->size.height - height, dialogue->textures[i]);
		dialogue->j++;
		if (dialogue->j >= 32)
		{
			dialogue->j = 0;
			dialogue->k = 0;
			dialogue->i++;
		}
		return 0;
	}
	
	/* unreachable */
	return 1;
}