view examples/example-states/state-play.c @ 645:83781cc87fca

core: rework game stack state mechanism The current model was fundamentally broken as the state could continue its execution when calling mlk_game_pop from itself (e.g. in update). The current model uses a sjlj mechanism with mlk_game_push/pop being disallowed in special state function like end, finish, suspend.
author David Demelier <markand@malikania.fr>
date Sun, 04 Feb 2024 15:24:00 +0100
parents
children
line wrap: on
line source

/*
 * state-play.c -- very funny game
 *
 * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <mlk/core/alloc.h>
#include <mlk/core/event.h>
#include <mlk/core/game.h>
#include <mlk/core/image.h>
#include <mlk/core/painter.h>
#include <mlk/core/panic.h>
#include <mlk/core/state.h>
#include <mlk/core/texture.h>
#include <mlk/core/util.h>

#include <mlk/ui/align.h>
#include <mlk/ui/button.h>

#include <assets/images/dvd.h>

#include "state-menu.h"
#include "state-play.h"

#define BG         0x99c2dbff
#define BUTTON_H   (mlk_window.h / 20)
#define BUTTON_W   (mlk_window.w / 10)
#define LOGO       assets_images_dvd
#define LOGO_SPEED 100

#define PLAY(self) MLK_CONTAINER_OF(self, struct play, state)

struct play {
	struct mlk_button button_leave;
	struct mlk_texture logo;
	int x;
	int y;
	int dx;
	int dy;
	struct mlk_state state;
};

static void
state_play__start(struct mlk_state *self)
{
	struct play *play = PLAY(self);

	if (mlk_image_openmem(&play->logo, LOGO, sizeof (LOGO)) < 0)
		mlk_panic();

	mlk_align(MLK_ALIGN_CENTER,
	          &play->x,
	          &play->y,
	          play->logo.w,
	          play->logo.h,
	          0,
	          0,
	          mlk_window.w,
	          mlk_window.h
	);

	play->button_leave.text = "menu";
	play->button_leave.w = BUTTON_W;
	play->button_leave.h = BUTTON_H;

	mlk_align(MLK_ALIGN_BOTTOM,
	          &play->button_leave.x,
	          &play->button_leave.y,
	          play->button_leave.w,
	          play->button_leave.h,
	          0,
	          0,
	          mlk_window.w,
	          mlk_window.h - 20
	);

	play->dx = play->dy = 1;
}

static void
state_play__handle(struct mlk_state *self, const union mlk_event *event)
{
	struct play *play = PLAY(self);

	if (mlk_button_handle(&play->button_leave, event))
		mlk_game_pop();

	switch (event->type) {
	case MLK_EVENT_QUIT:
		mlk_game_quit();
		break;
	case MLK_EVENT_KEYUP:
		switch (event->key.key) {
		case MLK_KEY_ESCAPE:
			mlk_game_pop();
			break;
		default:
			break;
		}
		break;
	default:
		break;
	}
}

static void
state_play__update(struct mlk_state *self, unsigned int ticks)
{
	struct play *play = PLAY(self);

	play->x += play->dx * (LOGO_SPEED * ticks / 1000);
	play->y += play->dy * (LOGO_SPEED * ticks / 1000);

	if (play->x < 0) {
		play->dx = 1;
		play->x = 0;
	} else if (play->x + play->logo.w >= mlk_window.w) {
		play->dx = -1;
		play->x = mlk_window.w - play->logo.w;
	}

	if (play->y < 0) {
		play->dy = 1;
		play->y = 0;
	} else if (play->y + play->logo.h >= mlk_window.h) {
		play->dy = -1;
		play->y = mlk_window.h - play->logo.h;
	}

	mlk_button_update(&play->button_leave, ticks);
}

static void
state_play__draw(struct mlk_state *self)
{
	struct play *play = PLAY(self);

	mlk_painter_set_color(BG);
	mlk_painter_clear();
	mlk_texture_draw(&play->logo, play->x, play->y);
	mlk_button_draw(&play->button_leave);
	mlk_painter_present();
}

static void
state_play__finish(struct mlk_state *self)
{
	struct play *play = PLAY(self);

	mlk_texture_finish(&play->logo);
}

struct mlk_state *
state_play_new(void)
{
	struct play *play;

	play = mlk_alloc_new0(1, sizeof (*play));
	play->state.name = "play";
	play->state.start = state_play__start;
	play->state.handle = state_play__handle;
	play->state.update = state_play__update;
	play->state.draw = state_play__draw;
	play->state.finish = state_play__finish;

	return &play->state;
}