Mirai's Miscellaneous Misadventures

M29 / core / controls.c

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

#include <mimimi/controls.h>
#include <mimimi/behaviors.h>
#include <mimimi/allocators.h>
#include <mimimi/sprites.h>
#include <mimimi/chapters.h>

struct mimimi_history_data
{
	struct mimimi_allocator *allocator;
	struct mimimi_behavior *behavior;
	struct mimimi_history *history;
	struct mimimi_chapter *chapter;
};

static void mimimi_history_behave(void *data0)
{
	struct mimimi_history_data *data = data0;
	data->history->left <<= 1;
	data->history->left |= data->chapter->left;
	data->history->right <<= 1;
	data->history->right |= data->chapter->right;
}

static void mimimi_history_finish(void *data)
{
	struct mimimi_history_data *history_data = data;
	mimimi_deallocate(history_data->allocator,history_data->behavior);
	mimimi_deallocate(history_data->allocator,history_data);
}

struct mimimi_behavior *mimimi_history(struct mimimi_history *history, struct mimimi_chapter *chapter, struct mimimi_allocator *allocator)
{
	struct mimimi_history_data *data = mimimi_allocate(allocator, sizeof *data);
	data->allocator = allocator;
	data->history = history;
	data->chapter = chapter;
	
	history->left = 0;
	history->right = 0;
	
	data->behavior = mimimi_allocate(allocator, sizeof *data->behavior);
	data->behavior->behave = &mimimi_history_behave;
	data->behavior->finish = &mimimi_history_finish;
	data->behavior->data = data;
	
	return data->behavior;
}

struct mimimi_controls_data
{
	struct mimimi_allocator *allocator;
	struct mimimi_behavior *behavior;
	struct mimimi_controls *controls;
	struct mimimi_walk *walk;
	struct mimimi_history *history;
};

static unsigned char mimimi_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 mimimi_control(void *data)
{
	struct mimimi_controls_data *controls_data = data;
	
	struct mimimi_walk *walk = controls_data->walk;
	struct mimimi_history *history = controls_data->history;
	
	struct mimimi_behavior *jump = controls_data->controls->jump;
	
	unsigned char left_oscillations = mimimi_count_oscillations(history->left);
	unsigned char right_oscillations = mimimi_count_oscillations(history->right);
	
	switch (walk->direction)
	{
	case 0:
		if ((history->left&1) != 0)
		{
			walk->direction = 1;
			if (left_oscillations > 1 && (history->left&2) == 0)
				(*jump->behave)(jump->data);
		}
		if ((history->right&1) != 0)
		{
			walk->direction = 2;
			if (right_oscillations > 1 && (history->right&2) == 0)
				(*jump->behave)(jump->data);
		}
		break;
	case 1:
		if ((history->left&1) == 0)
			walk->direction = 0;
		break;
	case 2:
		if ((history->right&1) == 0)
			walk->direction = 0;
		break;
	}
}

static void mimimi_controls_finish(void *data)
{
	struct mimimi_controls_data *controls_data = data;
	mimimi_deallocate(controls_data->allocator, controls_data->behavior);
	mimimi_deallocate(controls_data->allocator, controls_data);
}

struct mimimi_behavior *mimimi_controls(struct mimimi_controls *controls, struct mimimi_walk *walk, struct mimimi_history *history, struct mimimi_allocator *allocator)
{
	struct mimimi_controls_data *data = mimimi_allocate(allocator, sizeof *data);
	data->allocator = allocator;
	data->controls = controls;
	data->walk = walk;
	data->history = history;
	
	controls->jump = mimimi_noop;
	
	data->behavior = mimimi_allocate(allocator, sizeof *data->behavior);
	data->behavior->behave = &mimimi_control;
	data->behavior->finish = &mimimi_controls_finish;
	data->behavior->data = data;
	
	return data->behavior;
}

struct mimimi_jump_data
{
	struct mimimi_allocator *allocator;
	struct mimimi_behavior *behavior;
	struct mimimi_jump *jump;
	struct mimimi_physics *physics;
};

static void mimimi_jump_behave(void *data)
{
	struct mimimi_jump_data *jump_data = data;
	jump_data->physics->airborne = 1;
	jump_data->physics->dy = -jump_data->jump->strength;
}

static void mimimi_jump_finish(void *data)
{
	struct mimimi_jump_data *jump_data = data;
	mimimi_deallocate(jump_data->allocator, jump_data->behavior);
	mimimi_deallocate(jump_data->allocator, jump_data);
}

struct mimimi_behavior *mimimi_jump(struct mimimi_jump *jump, struct mimimi_physics *physics, struct mimimi_allocator *allocator)
{
	struct mimimi_jump_data *data = mimimi_allocate(allocator, sizeof *data);
	data->allocator = allocator;
	data->jump = jump;
	data->physics = physics;
	
	jump->strength = 56;
	
	data->behavior = mimimi_allocate(allocator, sizeof *data->behavior);
	data->behavior->behave = &mimimi_jump_behave;
	data->behavior->finish = &mimimi_jump_finish;
	data->behavior->data = data;
	
	return data->behavior;
}

struct mimimi_dash_data
{
	struct mimimi_allocator *allocator;
	struct mimimi_behavior *behavior;
	struct mimimi_dash *dash;
	struct mimimi_physics *physics;
	struct mimimi_walk *walk;
};

static void mimimi_dash_behave(void *data)
{
	struct mimimi_dash_data *dash_data = data;
	if (dash_data->walk->direction == 1)
		dash_data->physics->dx -= dash_data->dash->x_strength;
	if (dash_data->walk->direction == 2)
		dash_data->physics->dx += dash_data->dash->x_strength;
	dash_data->physics->dy -= dash_data->dash->y_strength;
}

static void mimimi_dash_finish(void *data)
{
	struct mimimi_dash_data *dash_data = data;
	mimimi_deallocate(dash_data->allocator, dash_data->behavior);
	mimimi_deallocate(dash_data->allocator, dash_data);
}

struct mimimi_behavior *mimimi_dash(struct mimimi_dash *dash, struct mimimi_physics *physics, struct mimimi_walk *walk, struct mimimi_allocator *allocator)
{
	struct mimimi_dash_data *data = mimimi_allocate(allocator, sizeof *data);
	data->allocator = allocator;
	data->dash = dash;
	data->physics = physics;
	data->walk = walk;
	
	dash->x_strength = 32;
	dash->y_strength = 16;
	
	data->behavior = mimimi_allocate(allocator, sizeof *data->behavior);
	data->behavior->behave = &mimimi_dash_behave;
	data->behavior->finish = &mimimi_dash_finish;
	data->behavior->data = data;
	
	return data->behavior;
}

struct mimimi_stationary
{
	struct mimimi_allocator *allocator;
	struct mimimi_behavior *behavior;
	int *direction;
	int *position;
	int *target;
};

static void mimimi_stationary_behave(void *data)
{
	struct mimimi_stationary *stationary = data;
	if (*stationary->position > *stationary->target)
		*stationary->direction = 1;
	else
		*stationary->direction = 2;
}

static void mimimi_stationary_finish(void *data)
{
	struct mimimi_stationary *stationary = data;
	mimimi_deallocate(stationary->allocator, stationary->behavior);
	mimimi_deallocate(stationary->allocator, stationary);
}

struct mimimi_behavior *mimimi_stationary(int *direction, int *position, int *target, struct mimimi_allocator *allocator)
{
	struct mimimi_stationary *data = mimimi_allocate(allocator, sizeof *data);
	data->allocator = allocator;
	data->direction = direction;
	data->position = position;
	data->target = target;
	
	data->behavior = mimimi_allocate(allocator, sizeof *data->behavior);
	data->behavior->behave = &mimimi_stationary_behave;
	data->behavior->finish = &mimimi_stationary_finish;
	data->behavior->data = data;
	
	return data->behavior;
}