Mirai's Miscellaneous Misadventures

M39 / core / effects.c

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

#include <mimimi/ground.h>
#include <mimimi/behaviors.h>
#include <mimimi/sprites.h>
#include <mimimi/allocators.h>
#include <mimimi/geometry.h>
#include <mimimi/compound-behaviors.h>

struct mimimi_conveyor
{
	struct mimimi_sprite *sprite;
	struct mimimi_ground *ground;
	unsigned char which;
	int speed;
	unsigned char was_airborne;
};

static void mimimi_conveyor_behave(void *data)
{
	struct mimimi_conveyor *conveyor_data = data;
	struct mimimi_sprite *sprite = conveyor_data->sprite;
	struct mimimi_ground *ground = conveyor_data->ground;
	unsigned char was_airborne = conveyor_data->was_airborne;
	conveyor_data->was_airborne = sprite->physics->airborne;
	
	if (sprite->physics->airborne != 0)
	{
		if (was_airborne == 0)
			sprite->physics->dx += conveyor_data->speed;
		return;
	}
	
	int x1 = (sprite->position->x - sprite->physics->width / 2) / 128;
	int x2 = (sprite->position->x + sprite->physics->width / 2) / 128;
	int y = (sprite->position->y + 64) / 128;
	
	if (mimimi_ground_tile(ground, x1, y) == conveyor_data->which || mimimi_ground_tile(ground, x2, y) == conveyor_data->which)
		sprite->position->x += conveyor_data->speed;
}

struct mimimi_behavior *mimimi_conveyor(struct mimimi_sprite *sprite, struct mimimi_ground *ground, unsigned char which, int speed, struct mimimi_allocator *allocator)
{
	struct mimimi_behavior *compound = mimimi_compound_behavior(allocator);
	
	struct mimimi_conveyor *conveyor = mimimi_compound_allocate(compound, sizeof *conveyor, allocator);
	conveyor->sprite = sprite;
	conveyor->ground = ground;
	conveyor->which = which;
	conveyor->speed = speed;
	conveyor->was_airborne = sprite->physics->airborne;
	
	mimimi_compound(compound, mimimi_function(conveyor, &mimimi_conveyor_behave, allocator));
	
	return compound;
}

struct mimimi_transformed_physics
{
	struct mimimi_physics *physics;
	struct mimimi_position *position;
	struct mimimi_ground *ground;
	struct mimimi_physics *other;
	struct mimimi_behavior *behavior;
	struct mimimi_position *transform;
	struct mimimi_position previous;
	struct mimimi_position other_position;
	unsigned char was_airborne;
};

static void mimimi_transformed_physics_behave(void *data)
{
	struct mimimi_transformed_physics *physics = data;
	
	int dx = physics->transform->x - physics->previous.x;
	int dy = physics->transform->y - physics->previous.y;
	physics->previous = *physics->transform;
	
	unsigned char was_airborne = physics->was_airborne;
	physics->was_airborne = physics->other->airborne;
	
	physics->other->dx = physics->physics->dx;
	physics->other->dy = physics->physics->dy;
	physics->other->width = physics->physics->width;
	physics->other->height = physics->physics->height;
	physics->other->gravity = 0;
	
	physics->other_position = *physics->position;
	physics->other_position.x -= physics->transform->x;
	physics->other_position.y -= physics->transform->y;
	
	if (physics->other->airborne == 0)
	{
		physics->other_position.x += dx;
		physics->other_position.y += dy;
	}
	
	if (physics->physics->airborne != 0) physics->other->airborne = 1;
	
	(*physics->behavior->behave)(physics->behavior->data);
	
	if (physics->other->airborne == 0) physics->physics->airborne = 0;
	
	*physics->position = physics->other_position;
	physics->position->x += physics->transform->x;
	physics->position->y += physics->transform->y;
	
	physics->physics->dx = physics->other->dx;
	physics->physics->dy = physics->other->dy;
	
	if (physics->other->airborne != 0 && was_airborne == 0)
	{
		physics->physics->dx += dx;
		physics->physics->dy += dy;
	}
}

struct mimimi_behavior *mimimi_positioned_physics(struct mimimi_physics *other, struct mimimi_position *position, struct mimimi_ground *ground, struct mimimi_position *transform, struct mimimi_allocator *allocator)
{
	struct mimimi_behavior *compound = mimimi_compound_behavior(allocator);
	
	struct mimimi_transformed_physics *physics = mimimi_compound_allocate(compound, sizeof *physics, allocator);
	physics->physics = other;
	physics->position = position;
	physics->ground = ground;
	physics->other = mimimi_compound_allocate(compound, sizeof *physics->other, allocator);
	physics->behavior = mimimi_collision_physics(physics->other, &physics->other_position, ground, allocator);
	physics->transform = transform;
	physics->previous = *transform;
	physics->was_airborne = other->airborne;
	
	mimimi_compound(compound, mimimi_function(physics, &mimimi_transformed_physics_behave, allocator));
	mimimi_compound(compound, mimimi_finalizer(physics->behavior->data, physics->behavior->finish, allocator));
	
	return compound;
}

struct mimimi_trampoline
{
	struct mimimi_sprite *sprite;
	struct mimimi_ground *ground;
	unsigned char which;
	int restitution;
	int dy;
};

static void mimimi_trampoline_behave(void *data)
{
	struct mimimi_trampoline *trampoline_data = data;
	struct mimimi_sprite *sprite = trampoline_data->sprite;
	struct mimimi_ground *ground = trampoline_data->ground;
	int dy = trampoline_data->dy;
	trampoline_data->dy = 0;
	
	if (sprite->physics->airborne != 0)
	{
		trampoline_data->dy = sprite->physics->dy;
		return;
	}
	
	if (dy == 0) return;
	
	int x1 = (sprite->position->x - sprite->physics->width / 2) / 128;
	int x2 = (sprite->position->x + sprite->physics->width / 2) / 128;
	int y = (sprite->position->y + 64) / 128;
	
	if (mimimi_ground_tile(ground, x1, y) != trampoline_data->which) return;
	if (mimimi_ground_tile(ground, x2, y) != trampoline_data->which) return;
	
	sprite->physics->airborne = 1;
	sprite->physics->dy -= dy * trampoline_data->restitution / 256;
}

struct mimimi_behavior *mimimi_trampoline(struct mimimi_sprite *sprite, struct mimimi_ground *ground, unsigned char which, int restitution, struct mimimi_allocator *allocator)
{
	struct mimimi_behavior *compound = mimimi_compound_behavior(allocator);
	
	struct mimimi_trampoline *trampoline = mimimi_compound_allocate(compound, sizeof *trampoline, allocator);
	trampoline->sprite = sprite;
	trampoline->ground = ground;
	trampoline->which = which;
	trampoline->restitution = restitution;
	trampoline->dy = sprite->physics->dy;
	
	mimimi_compound(compound, mimimi_function(trampoline, &mimimi_trampoline_behave, allocator));
	
	return compound;
}