Mirai's Miscellaneous Misadventures

M2 / game.c

#include "game.h"

// types

struct mirai_sprite_type
{
	struct mirai_image *image;
	int radius;
	int height;
};

struct mirai_sprite
{
	struct mirai_sprite_type *type;
	
	int x, y;
	// speed
	int dx, dy;
	// acceleration
	int ax;
	
	char airborne;
	char moving_direction;
	
	int attack_y;
	int attack_width;
	int attack_height;
	int attack_time;
	
	void (*behave)(struct mirai_game *game, struct mirai_sprite *sprite);
};


// constants

static int mirai_gravity = 4;

static struct mirai_image mirai_mirai_image = {20, 34};
static struct mirai_image mirai_ground_image = {16, 16};
static struct mirai_sprite_type mirai_mirai = {&mirai_mirai_image, 80, 544};

static char mirai_ground[][20] =
{
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1},
	{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
};


// state

struct mirai_game
{
	struct mirai_engine engine;
	struct mirai_sprite sprites[256];
	unsigned char sprite_count;
	
	unsigned int left_history:16;
	unsigned int right_history:16;
};


// functions

static void mirai_physics(struct mirai_sprite *sprite)
{
	sprite->dx += sprite->ax;
	sprite->x += sprite->dx;
	sprite->y += sprite->dy;
	
	int x0 = sprite->x;
	int x1 = sprite->type->radius;
	int y0 = sprite->y;
	int y1 = sprite->type->height;
	
	if (mirai_ground[(x0 + x1) / 128][(y0 - 127) / 128] != 0)
	{
		if (mirai_ground[(x0 + x1) / 128][(y0 - y1 / 2) / 128] == 0)
		{
			sprite->y = (y0 / 128 - 1) * 128;
		}
		else
		{
			sprite->dx = 0;
			sprite->x = (x0 + x1) / 128 * 128 - x1;
		}
	}
	if (mirai_ground[(x0 - x1) / 128][(y0 - 127) / 128] != 0)
	{
		if (mirai_ground[(x0 - x1) / 128][(y0 - y1 / 2) / 128] == 0)
		{
			sprite->y = (y0 / 128 - 1) * 128;
		}
		else
		{
			sprite->dx = 0;
			sprite->x = ((x0 - x1) / 128 + 1) * 128 + x1;
		}
	}
	
	x0 = sprite->x;
	y0 = sprite->y;
	
	if (mirai_ground[(x0 - x1) / 128][y0 / 128] != 0 || mirai_ground[(x0 + x1 - 1) / 128][y0 / 128] != 0)
	{
		sprite->y = y0 / 128 * 128;
		sprite->dy = 0;
		sprite->airborne = 0;
	}
	else if (!sprite->airborne)
	{
		if (mirai_ground[x0 / 128][y0 / 128 + 1] == 0)
			sprite->airborne = 1;
		else
			sprite->y = (y0 / 128 + 1) * 128;
	}
}

static void mirai_ground_physics(struct mirai_sprite *sprite)
{
	sprite->dx *= 1;
	sprite->dx /= 2;
	sprite->dy = 0;
	mirai_physics(sprite);
}

static void mirai_airborne_physics(struct mirai_sprite *sprite)
{
	sprite->dx *= 3;
	sprite->dx /= 4;
	sprite->dy += mirai_gravity;
	mirai_physics(sprite);
}

static void mirai_jump(struct mirai_sprite *sprite)
{
	if (sprite->airborne) return;
	sprite->airborne = 1;
	sprite->dy = -64;
}

static void mirai_damage(struct mirai_game *game, struct mirai_sprite *sprite)
{
	struct mirai_image area = {sprite->attack_width / 16, sprite->attack_height / 8};
	int x = sprite->x / 8 + sprite->attack_width / 32;
	int y = (sprite->y + sprite->attack_y) / 8;
	
	if (sprite->moving_direction == 1) x -= sprite->attack_width / 16;
	
	(*game->engine.stamp)(game->engine.data, x, y, &area);
}

static void mirai_attack(struct mirai_game *game, struct mirai_sprite *sprite)
{
	sprite->attack_y = -16;
	sprite->attack_width = 512;
	sprite->attack_height = 256;
	mirai_damage(game, sprite);
}

static void mirai_rapid_attack(struct mirai_game *game, struct mirai_sprite *sprite)
{
	sprite->attack_y = -128;
	sprite->attack_width = sprite->attack_time * 16 + 64;
	sprite->attack_height = 64;
	if (sprite->attack_time != 0x7F) sprite->attack_time++;
	mirai_damage(game, sprite);
}

static unsigned char mirai_count_oscillations(unsigned int history)
{
	unsigned char count = 0;
	unsigned char prev = history&1;
	for (int i = 0 ; i < 16 ; i++)
	{
		if ((history&1) != prev) count++;
		prev = history&1;
		history >>= 1;
	}
	return count;
}

static void mirai_controls(struct mirai_game *game, struct mirai_sprite *sprite)
{
	unsigned char left_oscillations = mirai_count_oscillations(game->left_history);
	unsigned char right_oscillations = mirai_count_oscillations(game->right_history);
	
	switch (sprite->moving_direction)
	{
	case 0:
		if (left_oscillations > 0 || game->left_history != 0)
			sprite->moving_direction = 1;
		if (right_oscillations > 0)
			sprite->moving_direction = 2;
		else if (game->right_history != 0)
			sprite->moving_direction = 2;
		break;
	case 1:
		if ((game->left_history&1) == 0)
			sprite->moving_direction = 0;
		else if (left_oscillations > 1)
			mirai_jump(sprite);
		if ((game->right_history&1) != 0)
			if (right_oscillations == 0)
				mirai_rapid_attack(game, sprite);
			else
				sprite->attack_time = 0;
		if ((game->right_history&1) != 0 && (game->right_history&2) == 0)
			mirai_attack(game, sprite);
		break;
	case 2:
		if ((game->right_history&1) == 0)
			sprite->moving_direction = 0;
		else if (right_oscillations > 1)
			mirai_jump(sprite);
		if ((game->left_history&1) != 0)
			if (left_oscillations == 0)
				mirai_rapid_attack(game, sprite);
			else
				sprite->attack_time = 0;
		if ((game->left_history&1) != 0 && (game->left_history&2) == 0)
			mirai_attack(game, sprite);
		break;
	}
}

static void mirai_spawn(struct mirai_game *game, int x, int y, struct mirai_sprite_type *type, void (*behave)(struct mirai_game *game, struct mirai_sprite *sprite))
{
	struct mirai_sprite *sprite = game->sprites + game->sprite_count;
	game->sprite_count++;
	sprite->type = type;
	sprite->x = x;
	sprite->y = y;
	sprite->dx = 0;
	sprite->dy = 0;
	sprite->ax = 0;
	sprite->airborne = 1;
	sprite->moving_direction = 0;
	sprite->attack_time = 0;
	sprite->behave = behave;
}


// constant exports

int mirai_game_size = sizeof (struct mirai_game);
int mirai_width = 512;
int mirai_height = 256;


// function exports

void mirai_start(struct mirai_game *game, struct mirai_engine *engine)
{
	game->engine = *engine;
	
	game->left_history = 0;
	game->right_history = 0;
	game->sprite_count = 0;
	
	mirai_spawn(game, 500, 1500, &mirai_mirai, &mirai_controls);
}

void mirai_step(struct mirai_game *game, struct mirai_keys keys)
{
	game->left_history <<= 1;
	game->left_history |= keys.left;
	game->right_history <<= 1;
	game->right_history |= keys.right;
	
	for (int i = 0 ; i < sizeof mirai_ground / sizeof *mirai_ground ; i++)
	for (int j = 0 ; j < sizeof *mirai_ground / sizeof **mirai_ground ; j++)
	{
		if (mirai_ground[i][j])
		{
			(*game->engine.stamp)(game->engine.data, i * 16 + 8, j * 16 + 16, &mirai_ground_image);
		}
	}
	
	for (int i = 0 ; i < game->sprite_count ; i++)
	{
		struct mirai_sprite *sprite = game->sprites + i;
		
		if (game->sprites->airborne == 0)
			mirai_ground_physics(sprite);
		else
			mirai_airborne_physics(sprite);
		
		(*sprite->behave)(game, sprite);
		
		game->sprites[i].ax = 0;
		if (sprite->moving_direction == 1)
			sprite->ax = -8;
		if (sprite->moving_direction == 2)
			sprite->ax = 8;
		
		(*game->engine.stamp)(game->engine.data, sprite->x / 8, sprite->y / 8, sprite->type->image);
	}
}