Mirai's Miscellaneous Misadventures

M42 / test / chapter.c

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

#include <mimimi/test.h>
#include <mimimi/chapters.h>
#include <mimimi/characters.h>
#include <mimimi/appearances.h>
#include <mimimi/allocators.h>
#include <mimimi/ground.h>
#include <mimimi/compound-behaviors.h>
#include <mimimi/geometry.h>
#include <mimimi/assets.h>
#include <mimimi/engines.h>
#include <mimimi/controls.h>
#include <mimimi/sprites.h>
#include <mimimi/cameras.h>
#include <mimimi/behaviors.h>
#include <mimimi/font.h>
#include <mimimi/fonts.h>
#include <mimimi/animations.h>
#include <mimimi/collisions.h>
#include <mimimi/version.h>
#include <mimimi/text.h>

#include "ground.c"

static void mimimi_test_animation_set(struct mimimi_animation_set *set, struct mimimi_allocator *allocator)
{
	int animation_count = 8;
	int image_count = 8;
	int width = 48;
	int height = 48;
	
	int total_animation_count = 3 + animation_count;
	int total_image_count = 2 + image_count * animation_count + image_count;
	
	struct mimimi_animation *animations = mimimi_allocate(allocator, total_animation_count * sizeof *animations);
	struct mimimi_image *images = mimimi_allocate(allocator, total_image_count * sizeof *images);
	
	set->standing = animations;
	set->standing_animation_count = animation_count;
	animations += animation_count;
	
	for (int i = 0 ; i < animation_count ; i++)
	{
		struct mimimi_animation *animation = set->standing + i;
		animation->count = image_count;
		animation->images = images;
		images += image_count;
		
		for (int i = 0 ; i < image_count ; i++)
		{
			struct mimimi_image *image = animation->images + i;
			image->width = width;
			image->height = height;
			image->colors = mimimi_allocate(allocator, width * height);
			for (int i = 0 ; i < width * height ; i++) image->colors[i] = 0;
		}
	}
	
	struct mimimi_animation *knocked = animations++;
	knocked->count = 1;
	knocked->images = images++;
	knocked->images[0].width = width;
	knocked->images[0].height = height;
	knocked->images[0].colors = mimimi_allocate(allocator, width * height);
	for (int i = 0 ; i < width * height ; i++) knocked->images[0].colors[i] = 0;
	
	struct mimimi_animation *falling = animations++;
	falling->count = 1;
	falling->images = images++;
	falling->images[0].width = width;
	falling->images[0].height = height;
	falling->images[0].colors = mimimi_allocate(allocator, width * height);
	for (int i = 0 ; i < width * height ; i++) falling->images[0].colors[i] = 0;
	
	set->knocked = knocked;
	set->falling = falling;
	
	set->jumping = animations++;
	set->jumping->count = image_count;
	set->jumping->images = images;
	images += image_count;
	
	for (int i = 0 ; i < image_count ; i++)
	{
		struct mimimi_image *image = set->jumping->images + i;
		image->width = width;
		image->height = height;
		image->colors = mimimi_allocate(allocator, width * height);
		for (int i = 0 ; i < width * height ; i++) image->colors[i] = 0;
	}
}

static struct mimimi_appearance *mimimi_test_appearance(struct mimimi_allocator *allocator)
{
	struct mimimi_appearance *appearance = mimimi_allocate(allocator, sizeof *appearance);
	
	mimimi_test_animation_set(&appearance->left, allocator);
	mimimi_test_animation_set(&appearance->right, allocator);
	
	return appearance;
}

struct mimimi_test_sprite
{
	int x, y;
	struct mimimi_model *model;
};

static void mimimi_test_player(struct mimimi_behavior *compound, struct mimimi_chapter *chapter, struct mimimi_sprite *player, struct mimimi_allocator *allocator)
{
	struct mimimi_allocator *compound_allocator = mimimi_compound_allocator(compound, allocator);
	
	struct mimimi_history *history = mimimi_allocate(compound_allocator, sizeof *history);
	mimimi_compound(compound, mimimi_history(history, chapter, allocator));
	
	struct mimimi_jump *jump = mimimi_allocate(compound_allocator, sizeof *jump);
	struct mimimi_behavior *jump_behavior = mimimi_jump(jump, player->physics, compound_allocator);
	
	struct mimimi_controls *controls = mimimi_allocate(compound_allocator, sizeof *controls);
	struct mimimi_behavior *controls_behavior = mimimi_controls(controls, player->walk, history, allocator);
	
	controls->jump = mimimi_choice(&player->physics->airborne, mimimi_noop, jump_behavior, compound_allocator);
	mimimi_compound(compound, mimimi_choice(&player->awake, controls_behavior, mimimi_noop, allocator));
}

static struct mimimi_image *mimimi_test_background(struct mimimi_ground *ground, unsigned char *colors, struct mimimi_allocator *allocator)
{
	struct mimimi_image *background = mimimi_allocate(allocator, sizeof *background);
	background->colors = mimimi_allocate(allocator, ground->width * 16 * ground->height * 16);
	
	background->width = ground->width * 16;
	background->height = ground->height * 16;
	
	for (int y = 0 ; y < ground->height * 16 ; y++)
	for (int x = 0 ; x < ground->width * 16 ; x++)
	{
		unsigned char color = colors[mimimi_ground_tile(ground, x / 16, y / 16)];
		background->colors[x + y * ground->width * 16] = color;
	}
	
	return background;
}

struct mimimi_chapter *mimimi_test(struct mimimi_engine *engine)
{
	static struct mimimi_ground *ground = &mimimi_test_ground;
	
	struct mimimi_behavior *compound = mimimi_compound_behavior(engine->allocator);
	struct mimimi_allocator *compound_allocator = mimimi_compound_allocator(compound, engine->allocator);
	
	struct mimimi_chapter *chapter = mimimi_allocate(compound_allocator, sizeof *chapter);
	chapter->behavior = compound;
	
	struct mimimi_position *camera = mimimi_allocate(compound_allocator, sizeof *camera);
	
	struct mimimi_test_sprite infos[] =
	{
		{110, 36, mimimi_mango},
		{108, 36, mimimi_pepper},
	};
	
	int count = sizeof infos / sizeof *infos;
	
	struct mimimi_position **positions = mimimi_allocate(compound_allocator, count * sizeof *positions);
	mimimi_compound(compound, mimimi_collision(count, positions, 128, 256, 512, 64, engine->allocator));
	
	int *directions = mimimi_allocate(compound_allocator, count * sizeof *directions);
	
	struct mimimi_sprite **sprites = mimimi_allocate(engine->allocator, count * sizeof *sprites);
	
	for (int i = 0 ; i < count ; i++)
	{
		struct mimimi_test_sprite *info = infos + i;
		
		struct mimimi_sprite *sprite = mimimi_sprite(ground, info->x * 128, info->y * 128, 80, 250, engine->allocator);
		mimimi_compound(compound, sprite->behavior);
		sprites[i] = sprite;
		
		if (i == 0)
		{
			mimimi_test_player(compound, chapter, sprite, engine->allocator);
			mimimi_compound(compound, mimimi_camera(camera, sprite->position, 0, -512, engine->allocator));
		}
		else
		{
			struct mimimi_jump *jump = mimimi_allocate(compound_allocator, sizeof *jump);
			struct mimimi_behavior *jump_behavior = mimimi_jump(jump, sprite->physics, compound_allocator);
			
			struct mimimi_path_finding *path_finding = mimimi_allocate(compound_allocator, sizeof *path_finding);
			mimimi_compound(compound, mimimi_find_path(path_finding, ground, sprites[i - 1]->position, sprite, engine->allocator));
			
			path_finding->jump = jump;
			path_finding->jump_behavior = jump_behavior;
		}
		
		positions[i] = sprite->position;
	}
	
	static unsigned char ground_colors[] = {0x0D, 0x05};
	struct mimimi_image *background = mimimi_test_background(ground, ground_colors, compound_allocator);
	
	mimimi_compound(compound, mimimi_background(engine, camera, (*engine->texture)(engine->data, background), 0, 0, 0, engine->allocator));
	
	for (int i = 0 ; i < count ; i++)
	{
		struct mimimi_test_sprite *info = infos + i;
		struct mimimi_sprite *sprite = sprites[i];
		
		struct mimimi_appearance *appearance = mimimi_test_appearance(compound_allocator);
		mimimi_appearance(appearance, info->model, 24, 48, engine->allocator);
		
		struct mimimi_video_appearance *video_appearance = mimimi_allocate(compound_allocator, sizeof *video_appearance);
		mimimi_video_appearance(video_appearance, appearance, engine, compound_allocator);
		
		mimimi_compound(compound, mimimi_display(engine, camera, sprite, video_appearance, directions + i, 24, 48, engine->allocator));
	}
	
	mimimi_deallocate(engine->allocator, sprites);
	
	struct mimimi_image *overlay = mimimi_allocate(compound_allocator, sizeof *overlay);
	overlay->width = 256;
	overlay->height = 64;
	overlay->colors = mimimi_allocate(compound_allocator, overlay->width * overlay->height);
	
	for (int i = 0 ; i < overlay->width * overlay->height ; i++)
		overlay->colors[i] = 0;
	
	struct mimimi_allocator *text_allocator = mimimi_temporary_allocator(engine->allocator);
	
	unsigned char *name = mimimi_utf8("mimimi game engine ", text_allocator);
	unsigned char *version = mimimi_utf8(mimimi_version, text_allocator);
	
	int name_count = mimimi_count_text(name);
	int version_count = mimimi_count_text(version);
	int text_count = name_count + version_count;
	
	unsigned char *text = mimimi_allocate(text_allocator, text_count + 1);
	for (int i = 0 ; i < name_count ; i++) text[i] = name[i];
	for (int i = 0 ; i < version_count ; i++) text[name_count + i] = version[i];
	text[text_count] = 0;
	
	mimimi_draw_text(overlay, 16, 16, overlay->width - 32, 16, text, mimimi_font, 0x76);
	
	void *overlay_texture = (*engine->texture)(engine->data, overlay);
	mimimi_compound(compound, mimimi_overlay(engine, overlay_texture, 0, 0, engine->allocator));
	
	mimimi_finish_temporary_allocator(text_allocator);
	
	return chapter;
}