[RELEASE] u22-v04

This version is submitted for U22 final presentation. (squashed 158 commits)
This commit is contained in:
2020-10-09 00:00:00 +00:00
parent 84c3a02b9a
commit 80b3b82332
277 changed files with 12154 additions and 13836 deletions

View File

@@ -1,28 +1,26 @@
add_library(loplayer
action.c
camera.c
combat.c
controller.c
entity.c
event.c
hud.c
menu.c
player.c
status.c
popup.c
stance.c
)
target_benum_sources(loplayer
stance.h
)
target_crial_sources(loplayer
event.crial
player.crial
)
target_link_libraries(loplayer
msgpackc
conv
glyphas
math
memory
mpkutil
lobullet
locommon
loeffect
loentity
loresource
loshader
)

View File

@@ -1,880 +0,0 @@
#include "./action.h"
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <msgpack.h>
#include "util/math/algorithm.h"
#include "util/memory/memory.h"
#include "util/mpkutil/get.h"
#include "util/mpkutil/pack.h"
#include "core/lobullet/base.h"
#include "core/lobullet/linear.h"
#include "core/lobullet/pool.h"
#include "core/locommon/easing.h"
#include "core/locommon/ticker.h"
#include "core/loeffect/effect.h"
#include "core/loeffect/stance.h"
#include "core/loentity/store.h"
#include "core/loresource/set.h"
#include "core/loresource/sound.h"
#include "core/loresource/text.h"
#include "./camera.h"
#include "./combat.h"
#include "./controller.h"
#include "./entity.h"
#include "./event.h"
#include "./hud.h"
#include "./menu.h"
#include "./status.h"
struct loplayer_action_t {
loresource_set_t* res;
const locommon_ticker_t* ticker;
lobullet_pool_t* bullets;
loentity_store_t* entities;
loplayer_event_t* event;
loplayer_status_t* status;
loplayer_entity_t* entity;
const loplayer_controller_t* controller;
loplayer_combat_t* combat;
loplayer_camera_t* camera;
loplayer_hud_t* hud;
loplayer_menu_t* menu;
union {
struct {
} stand;
struct {
float direction;
} moving;
struct {
float direction;
} dodge;
struct {
} combat;
struct {
} shoot;
struct {
} dead;
struct {
bool invincible;
} menu;
} state;
uint64_t since;
void
(*execute)(
loplayer_action_t* action
);
void
(*pack)(
const loplayer_action_t* action,
msgpack_packer* packer
);
};
#define LOPLAYER_ACTION_STATE_EACH_(PROC) do { \
PROC(stand); \
PROC(moving); \
PROC(dodge); \
PROC(combat); \
PROC(shoot); \
PROC(dead); \
PROC(menu); \
} while (0)
static void
loplayer_action_start_stand_state_(
loplayer_action_t* action
);
static void
loplayer_action_start_moving_state_(
loplayer_action_t* action,
float direction
);
static void
loplayer_action_start_dodge_state_(
loplayer_action_t* action,
float direction
);
static void
loplayer_action_start_combat_state_(
loplayer_action_t* action
);
static bool
loplayer_action_start_shoot_state_(
loplayer_action_t* action
);
static void
loplayer_action_start_dead_state_(
loplayer_action_t* action
);
static void
loplayer_action_start_menu_state_(
loplayer_action_t* action,
bool invincible
);
static void loplayer_action_affect_bullet_(loplayer_action_t* action) {
assert(action != NULL);
if (action->status->bullet_immune_until > action->ticker->time) {
return;
}
if (loplayer_entity_affect_bullet(action->entity)) {
action->status->bullet_immune_until = action->ticker->time + 200;
}
}
static bool loplayer_action_shoot_bullet_(loplayer_action_t* action) {
assert(action != NULL);
static const float consume = .05f;
float* f = &action->status->recipient.faith;
if (*f <= 0) return false;
vec2_t v;
locommon_position_sub(
&v, &action->controller->looking, &action->entity->super.super.pos);
const float vlen = vec2_length(&v);
if (vlen == 0) {
v = vec2(action->entity->direction, 0);
} else {
vec2_diveq(&v, vec2_length(&v));
}
/* TODO(catfoot): diffusion */
vec2_muleq(&v, 1.f);
vec2_addeq(&v, &action->entity->last_velocity);
lobullet_base_t* b = lobullet_pool_create(action->bullets);
lobullet_linear_light_build(b, (&(lobullet_linear_param_t) {
.owner = action->entity->super.super.id,
.pos = action->entity->super.super.pos,
.size = vec2(.015f, .015f),
.velocity = v,
.acceleration = vec2(0, -.1f),
.color = vec4(.8f, .8f, .8f, .8f),
.duration = 2000,
.knockback = .1f,
.effect = loeffect_immediate_damage(
action->status->recipient.status.attack/2),
}));
loentity_store_add(action->entities, &b->super.super);
*f -= consume;
if (*f < 0) *f = 0;
return true;
}
static void loplayer_action_show_tutorial_after_death_(loplayer_action_t* action) {
assert(action != NULL);
# define text_(name) loresource_text_get(action->res->lang, name)
# define popup_(name) \
loplayer_menu_popup( \
action->menu, \
text_("tutorial_title_"name), \
text_("tutorial_text_" name))
switch (action->status->recipient.last_damage) {
case LOEFFECT_ID_IMMEDIATE_DAMAGE:
popup_("dead_by_combat");
break;
case LOEFFECT_ID_CURSE:
popup_("dead_by_curse");
break;
case LOEFFECT_ID_LOST:
popup_("dead_by_lost");
break;
default:
return;
}
# undef popup_
# undef text_
loplayer_action_start_menu_popup_state(action);
}
static void loplayer_action_execute_stand_state_(loplayer_action_t* action) {
assert(action != NULL);
const float max_acceleration_ = action->entity->on_ground? 2.0f: 0.5f;
loplayer_entity_aim(action->entity, &action->controller->looking);
if (action->status->recipient.madness <= 0) {
loplayer_action_start_dead_state_(action);
return;
}
loplayer_action_affect_bullet_(action);
if (loplayer_combat_accept_all_attacks(action->combat)) {
loplayer_action_start_combat_state_(action);
return;
}
switch (action->controller->action) {
case LOPLAYER_CONTROLLER_ACTION_NONE:
break;
case LOPLAYER_CONTROLLER_ACTION_ATTACK:
if (loplayer_action_start_shoot_state_(action)) return;
break;
case LOPLAYER_CONTROLLER_ACTION_DODGE:
loplayer_action_start_dodge_state_(action, action->entity->direction);
return;
case LOPLAYER_CONTROLLER_ACTION_GUARD:
break;
case LOPLAYER_CONTROLLER_ACTION_UNGUARD:
break;
case LOPLAYER_CONTROLLER_ACTION_MENU:
if (action->entity->movement.x == 0) {
loplayer_menu_show_status(action->menu);
loplayer_action_start_menu_state_(action, false /* INVINCIBLE */);
return;
}
break;
}
switch (action->controller->movement) {
case LOPLAYER_CONTROLLER_MOVEMENT_NONE:
break;
case LOPLAYER_CONTROLLER_MOVEMENT_JUMP:
if (action->entity->on_ground) {
action->entity->gravity += action->status->recipient.status.jump;
}
break;
case LOPLAYER_CONTROLLER_MOVEMENT_WALK_LEFT:
case LOPLAYER_CONTROLLER_MOVEMENT_DASH_LEFT:
loplayer_action_start_moving_state_(action, -1);
return;
case LOPLAYER_CONTROLLER_MOVEMENT_WALK_RIGHT:
case LOPLAYER_CONTROLLER_MOVEMENT_DASH_RIGHT:
loplayer_action_start_moving_state_(action, 1);
return;
}
const float t = (action->ticker->time - action->since)%2000/1000.0f - 1;
action->entity->motion.time = t*t*(3-2*MATH_ABS(t));
action->entity->motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
action->entity->motion.to = LOSHADER_CHARACTER_MOTION_ID_STAND2;
locommon_easing_linear_float(
&action->entity->movement.x,
0,
max_acceleration_ * action->ticker->delta_f);
}
static void loplayer_action_pack_stand_state_(
const loplayer_action_t* action, msgpack_packer* packer) {
assert(action != NULL);
msgpack_pack_map(packer, 1);
mpkutil_pack_str(packer, "name");
mpkutil_pack_str(packer, "stand");
}
static bool loplayer_action_unpack_stand_state_(
loplayer_action_t* action, const msgpack_object_map* root) {
assert(action != NULL);
return root != NULL;
}
static void loplayer_action_start_stand_state_(loplayer_action_t* action) {
assert(action != NULL);
action->since = action->ticker->time;
action->execute = loplayer_action_execute_stand_state_;
action->pack = loplayer_action_pack_stand_state_;
action->camera->state = LOPLAYER_CAMERA_STATE_DEFAULT;
loplayer_hud_show(action->hud);
}
static void loplayer_action_execute_moving_state_(loplayer_action_t* action) {
assert(action != NULL);
static const float backwalk_attenuation_ = 0.8f;
static const float dash_speed_ = 1.4f;
const float max_acceleration_ = action->entity->on_ground? 2.4f: 0.8f;
const float dir = action->state.moving.direction;
loplayer_entity_aim(action->entity, &action->controller->looking);
if (action->status->recipient.madness <= 0) {
loplayer_action_start_dead_state_(action);
return;
}
loplayer_action_affect_bullet_(action);
if (loplayer_combat_accept_all_attacks(action->combat)) {
loplayer_action_start_combat_state_(action);
return;
}
switch (action->controller->action) {
case LOPLAYER_CONTROLLER_ACTION_NONE:
break;
case LOPLAYER_CONTROLLER_ACTION_ATTACK:
if (loplayer_action_start_shoot_state_(action)) return;
break;
case LOPLAYER_CONTROLLER_ACTION_DODGE:
loplayer_action_start_dodge_state_(action, dir);
return;
case LOPLAYER_CONTROLLER_ACTION_GUARD:
break;
case LOPLAYER_CONTROLLER_ACTION_UNGUARD:
break;
case LOPLAYER_CONTROLLER_ACTION_MENU:
break;
}
float max_speed = action->status->recipient.status.speed;
float control_dir = dir;
switch (action->controller->movement) {
case LOPLAYER_CONTROLLER_MOVEMENT_NONE:
loplayer_action_start_stand_state_(action);
return;
case LOPLAYER_CONTROLLER_MOVEMENT_JUMP:
if (action->entity->on_ground) {
action->entity->gravity += action->status->recipient.status.jump;
}
return;
case LOPLAYER_CONTROLLER_MOVEMENT_WALK_LEFT:
control_dir = -1;
break;
case LOPLAYER_CONTROLLER_MOVEMENT_WALK_RIGHT:
control_dir = 1;
break;
case LOPLAYER_CONTROLLER_MOVEMENT_DASH_LEFT:
max_speed *= dash_speed_;
control_dir = -1;
break;
case LOPLAYER_CONTROLLER_MOVEMENT_DASH_RIGHT:
max_speed *= dash_speed_;
control_dir = 1;
break;
}
if (control_dir * dir < 0) {
loplayer_action_start_stand_state_(action);
return;
}
if (dir * action->entity->direction < 0) {
max_speed *= backwalk_attenuation_;
}
if (action->entity->on_ground) {
const int32_t p = 70/max_speed;
const float t = (action->ticker->time - action->since)%p*2.0f/p - 1;
action->entity->motion.time = MATH_ABS(t);
action->entity->motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
action->entity->motion.to = LOSHADER_CHARACTER_MOTION_ID_WALK;
}
locommon_easing_linear_float(
&action->entity->movement.x,
max_speed*dir,
max_acceleration_ * action->ticker->delta_f);
}
static void loplayer_action_pack_moving_state_(
const loplayer_action_t* action, msgpack_packer* packer) {
assert(action != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, 2);
mpkutil_pack_str(packer, "name");
mpkutil_pack_str(packer, "moving");
mpkutil_pack_str(packer, "direction");
msgpack_pack_double(packer, action->state.moving.direction);
}
static bool loplayer_action_unpack_moving_state_(
loplayer_action_t* action, const msgpack_object_map* root) {
assert(action != NULL);
const msgpack_object* direction =
mpkutil_get_map_item_by_str(root, "direction");
if (!mpkutil_get_float(direction, &action->state.moving.direction)) {
return false;
}
return true;
}
static void loplayer_action_start_moving_state_(
loplayer_action_t* action, float dir) {
assert(action != NULL);
assert(MATH_FLOAT_VALID(dir));
action->since = action->ticker->time;
action->execute = loplayer_action_execute_moving_state_;
action->pack = loplayer_action_pack_moving_state_;
action->state = (typeof(action->state)) {
.moving = {
.direction = dir,
},
};
action->camera->state = LOPLAYER_CAMERA_STATE_DEFAULT;
loplayer_hud_show(action->hud);
}
static void loplayer_action_execute_dodge_state_(loplayer_action_t* action) {
assert(action != NULL);
static const uint64_t duration_ = 200;
static const float start_speed_ = 0.6f;
static const float end_speed_ = 0.1f;
if (action->since + duration_ <= action->ticker->time) {
loplayer_combat_drop_all_attacks(action->combat);
loplayer_action_start_stand_state_(action);
return;
}
const float dir = action->state.dodge.direction;
vec2_t* v = &action->entity->movement;
const float t = (action->ticker->time - action->since)*1.0f/duration_;
const float r = 1 - powf(1-t, 1.5);
v->x = (r * (start_speed_-end_speed_) + end_speed_) * dir;
v->y = 0;
action->entity->motion.time = 1-powf(1-t, 2);
action->entity->motion.from = LOSHADER_CHARACTER_MOTION_ID_WALK;
action->entity->motion.to = LOSHADER_CHARACTER_MOTION_ID_STAND1;
}
static void loplayer_action_pack_dodge_state_(
const loplayer_action_t* action, msgpack_packer* packer) {
assert(action != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, 2);
mpkutil_pack_str(packer, "name");
mpkutil_pack_str(packer, "dodge");
mpkutil_pack_str(packer, "direction");
msgpack_pack_double(packer, action->state.dodge.direction);
}
static bool loplayer_action_unpack_dodge_state_(
loplayer_action_t* action, const msgpack_object_map* root) {
assert(action != NULL);
const msgpack_object* direction =
mpkutil_get_map_item_by_str(root, "direction");
if (!mpkutil_get_float(direction, &action->state.moving.direction)) {
return false;
}
return true;
}
static void loplayer_action_start_dodge_state_(
loplayer_action_t* action, float dir) {
assert(action != NULL);
assert(MATH_FLOAT_VALID(dir));
action->since = action->ticker->time;
action->state = (typeof(action->state)) {
.moving = {
.direction = dir,
},
};
action->execute = loplayer_action_execute_dodge_state_;
action->pack = loplayer_action_pack_dodge_state_;
action->camera->state = LOPLAYER_CAMERA_STATE_DEFAULT;
loplayer_hud_show(action->hud);
loplayer_combat_drop_all_attacks(action->combat);
loresource_sound_play(action->res->sound, "dodge");
}
static void loplayer_action_execute_combat_state_(loplayer_action_t* action) {
assert(action != NULL);
if (action->status->recipient.madness <= 0) {
loplayer_action_start_dead_state_(action);
return;
}
loplayer_action_affect_bullet_(action);
if (!loplayer_combat_accept_all_attacks(action->combat)) {
loplayer_action_start_stand_state_(action);
return;
}
switch (action->controller->action) {
case LOPLAYER_CONTROLLER_ACTION_NONE:
break;
case LOPLAYER_CONTROLLER_ACTION_ATTACK:
break;
case LOPLAYER_CONTROLLER_ACTION_DODGE:
loplayer_action_start_dodge_state_(action, action->entity->direction);
return;
case LOPLAYER_CONTROLLER_ACTION_GUARD:
loplayer_combat_guard(action->combat);
break;
case LOPLAYER_CONTROLLER_ACTION_UNGUARD:
loplayer_combat_unguard(action->combat);
break;
case LOPLAYER_CONTROLLER_ACTION_MENU:
break;
}
action->entity->gravity = 0;
const float klen = vec2_length(&action->entity->knockback);
if (klen > .1f) vec2_muleq(&action->entity->knockback, .1f/klen);
}
static void loplayer_action_pack_combat_state_(
const loplayer_action_t* action, msgpack_packer* packer) {
assert(action != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, 1);
mpkutil_pack_str(packer, "name");
mpkutil_pack_str(packer, "combat");
}
static bool loplayer_action_unpack_combat_state_(
loplayer_action_t* action, const msgpack_object_map* root) {
assert(action != NULL);
return root != NULL;
}
static void loplayer_action_start_combat_state_(loplayer_action_t* action) {
assert(action != NULL);
action->since = action->ticker->time;
action->execute = loplayer_action_execute_combat_state_;
action->pack = loplayer_action_pack_combat_state_;
action->entity->movement = vec2(0, 0);
action->camera->state = LOPLAYER_CAMERA_STATE_COMBAT;
loplayer_hud_show(action->hud);
}
static void loplayer_action_execute_shoot_state_(loplayer_action_t* action) {
assert(action != NULL);
static const uint64_t duration = 300;
static const float max_acceleration = 1.f;
loplayer_entity_aim(action->entity, &action->controller->looking);
if (action->status->recipient.madness <= 0) {
loplayer_action_start_dead_state_(action);
return;
}
loplayer_action_affect_bullet_(action);
if (loplayer_combat_accept_all_attacks(action->combat)) {
loplayer_action_start_combat_state_(action);
return;
}
if (action->since+duration <= action->ticker->time) {
if (loplayer_action_shoot_bullet_(action)) {
loresource_sound_play(action->res->sound, "player_shoot");
}
loplayer_action_start_stand_state_(action);
return;
}
const float a = max_acceleration * action->ticker->delta_f;
locommon_easing_linear_float(&action->entity->movement.x, 0, a);
locommon_easing_linear_float(&action->entity->movement.y, 0, a);
}
static void loplayer_action_pack_shoot_state_(
const loplayer_action_t* action, msgpack_packer* packer) {
assert(action != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, 1);
mpkutil_pack_str(packer, "name");
mpkutil_pack_str(packer, "shoot");
}
static bool loplayer_action_unpack_shoot_state_(
loplayer_action_t* action, const msgpack_object_map* root) {
assert(action != NULL);
return root != NULL;
}
static bool loplayer_action_start_shoot_state_(loplayer_action_t* action) {
assert(action != NULL);
if (!loeffect_stance_set_has(
&action->status->stances, LOEFFECT_STANCE_ID_REVOLUTIONER)) {
return false;
}
action->since = action->ticker->time;
action->execute = loplayer_action_execute_shoot_state_;
action->pack = loplayer_action_pack_shoot_state_;
action->camera->state = LOPLAYER_CAMERA_STATE_DEFAULT;
loplayer_hud_show(action->hud);
loresource_sound_play(action->res->sound, "player_trigger");
return true;
}
static void loplayer_action_execute_dead_state_(loplayer_action_t* action) {
assert(action != NULL);
static const uint64_t duration_ = 3000;
if (action->since + duration_ <= action->ticker->time) {
loplayer_entity_move(action->entity, &action->status->respawn_pos);
loplayer_status_reset(action->status);
loplayer_combat_drop_all_attacks(action->combat);
loplayer_action_start_stand_state_(action);
loplayer_action_show_tutorial_after_death_(action);
loplayer_event_abort(action->event);
return;
}
}
static void loplayer_action_pack_dead_state_(
const loplayer_action_t* action, msgpack_packer* packer) {
assert(action != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, 1);
mpkutil_pack_str(packer, "name");
mpkutil_pack_str(packer, "dead");
}
static bool loplayer_action_unpack_dead_state_(
loplayer_action_t* action, const msgpack_object_map* root) {
assert(action != NULL);
return root != NULL;
}
static void loplayer_action_start_dead_state_(loplayer_action_t* action) {
assert(action != NULL);
action->since = action->ticker->time;
action->execute = loplayer_action_execute_dead_state_;
action->pack = loplayer_action_pack_dead_state_;
action->entity->movement = vec2(0, 0);
action->camera->state = LOPLAYER_CAMERA_STATE_DEAD;
loplayer_hud_hide(action->hud);
loplayer_combat_drop_all_attacks(action->combat);
/* Deny all event requests. */
loplayer_event_abort(action->event);
loplayer_event_take_control(action->event, action->entity->super.super.id);
}
static void loplayer_action_execute_menu_state_(loplayer_action_t* action) {
assert(action != NULL);
if (action->status->recipient.madness <= 0) {
loplayer_menu_hide(action->menu);
loplayer_action_start_dead_state_(action);
return;
}
loplayer_action_affect_bullet_(action);
if (!action->state.menu.invincible &&
loplayer_combat_accept_all_attacks(action->combat)) {
loplayer_menu_hide(action->menu);
loplayer_action_start_combat_state_(action);
return;
}
switch (action->controller->action) {
case LOPLAYER_CONTROLLER_ACTION_NONE:
break;
case LOPLAYER_CONTROLLER_ACTION_ATTACK:
break;
case LOPLAYER_CONTROLLER_ACTION_DODGE:
break;
case LOPLAYER_CONTROLLER_ACTION_GUARD:
break;
case LOPLAYER_CONTROLLER_ACTION_UNGUARD:
break;
case LOPLAYER_CONTROLLER_ACTION_MENU:
loplayer_menu_hide(action->menu);
loplayer_action_start_stand_state_(action);
return;
}
}
static void loplayer_action_pack_menu_state_(
const loplayer_action_t* action, msgpack_packer* packer) {
assert(action != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, 2);
mpkutil_pack_str(packer, "name");
mpkutil_pack_str(packer, "menu");
mpkutil_pack_str(packer, "invincible");
mpkutil_pack_bool(packer, action->state.menu.invincible);
}
static bool loplayer_action_unpack_menu_state_(
loplayer_action_t* action, const msgpack_object_map* root) {
assert(action != NULL);
const msgpack_object* invincible =
mpkutil_get_map_item_by_str(root, "invincible");
if (!mpkutil_get_bool(invincible, &action->state.menu.invincible)) {
return false;
}
return true;
}
static void loplayer_action_start_menu_state_(
loplayer_action_t* action, bool invincible) {
assert(action != NULL);
action->since = action->ticker->time;
action->execute = loplayer_action_execute_menu_state_;
action->pack = loplayer_action_pack_menu_state_;
action->entity->movement = vec2(0, 0);
action->state.menu = (typeof(action->state.menu)) {
.invincible = invincible,
};
action->camera->state = LOPLAYER_CAMERA_STATE_MENU;
}
loplayer_action_t* loplayer_action_new(
loresource_set_t* res,
const locommon_ticker_t* ticker,
lobullet_pool_t* bullets,
loentity_store_t* entities,
loplayer_event_t* event,
loplayer_status_t* status,
loplayer_entity_t* entity,
loplayer_combat_t* combat,
const loplayer_controller_t* controller,
loplayer_camera_t* camera,
loplayer_hud_t* hud,
loplayer_menu_t* menu) {
assert(res != NULL);
assert(ticker != NULL);
assert(bullets != NULL);
assert(entities != NULL);
assert(event != NULL);
assert(status != NULL);
assert(entity != NULL);
assert(combat != NULL);
assert(controller != NULL);
assert(camera != NULL);
assert(hud != NULL);
assert(menu != NULL);
loplayer_action_t* action = memory_new(sizeof(*action));
*action = (typeof(*action)) {
.res = res,
.ticker = ticker,
.bullets = bullets,
.entities = entities,
.event = event,
.status = status,
.entity = entity,
.combat = combat,
.controller = controller,
.camera = camera,
.hud = hud,
.menu = menu,
};
loplayer_action_start_stand_state_(action);
return action;
}
void loplayer_action_delete(loplayer_action_t* action) {
if (action == NULL) return;
memory_delete(action);
}
void loplayer_action_start_menu_popup_state(loplayer_action_t* action) {
assert(action != NULL);
loplayer_action_start_menu_state_(action, true /* invincible */);
}
void loplayer_action_execute(loplayer_action_t* action) {
assert(action != NULL);
assert(action->execute != NULL);
action->execute(action);
}
void loplayer_action_pack(const loplayer_action_t* action, msgpack_packer* packer) {
assert(action != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, 2);
mpkutil_pack_str(packer, "since");
msgpack_pack_uint64(packer, action->since);
assert(action->pack != NULL);
mpkutil_pack_str(packer, "state");
action->pack(action, packer);
}
bool loplayer_action_unpack(
loplayer_action_t* action, const msgpack_object* obj) {
assert(action != NULL);
if (obj == NULL) return false;
const msgpack_object_map* root = mpkutil_get_map(obj);
const msgpack_object* since = mpkutil_get_map_item_by_str(root, "since");
if (!mpkutil_get_uint64(since, &action->since)) return false;
const msgpack_object_map* state = mpkutil_get_map(
mpkutil_get_map_item_by_str(root, "state"));
bool state_loaded = false;
const msgpack_object* name = mpkutil_get_map_item_by_str(state, "name");
const char* v;
size_t len;
if (!mpkutil_get_str(name, &v, &len)) {
loplayer_action_start_stand_state_(action);
state_loaded = true;
}
# define unpack_state_(name_) do { \
if (!state_loaded && strncmp(v, #name_, len) == 0 && #name_[len] == 0) { \
action->execute = loplayer_action_execute_##name_##_state_; \
action->pack = loplayer_action_pack_##name_##_state_; \
if (!loplayer_action_unpack_##name_##_state_(action, state)) { \
loplayer_action_start_stand_state_(action); \
} \
state_loaded = true; \
} \
} while (0)
LOPLAYER_ACTION_STATE_EACH_(unpack_state_);
# undef unpack_state_
return true;
}

View File

@@ -1,64 +0,0 @@
#pragma once
#include <stdbool.h>
#include <msgpack.h>
#include "core/lobullet/pool.h"
#include "core/locommon/ticker.h"
#include "core/loresource/set.h"
#include "./camera.h"
#include "./combat.h"
#include "./controller.h"
#include "./entity.h"
#include "./event.h"
#include "./hud.h"
#include "./menu.h"
#include "./status.h"
struct loplayer_action_t;
typedef struct loplayer_action_t loplayer_action_t;
loplayer_action_t* /* OWNERSHIP */
loplayer_action_new(
loresource_set_t* res,
const locommon_ticker_t* ticker,
lobullet_pool_t* bullets,
loentity_store_t* entities,
loplayer_event_t* event,
loplayer_status_t* status,
loplayer_entity_t* entity,
loplayer_combat_t* combat,
const loplayer_controller_t* controller,
loplayer_camera_t* camera,
loplayer_hud_t* hud,
loplayer_menu_t* menu
);
void
loplayer_action_delete(
loplayer_action_t* action /* OWNERSHIP */
);
void
loplayer_action_start_menu_popup_state(
loplayer_action_t* action
);
void
loplayer_action_execute(
loplayer_action_t* action
);
void
loplayer_action_pack(
const loplayer_action_t* action,
msgpack_packer* packer
);
bool
loplayer_action_unpack(
loplayer_action_t* action,
const msgpack_object* obj /* NULLABLE */
);

View File

@@ -4,135 +4,43 @@
#include <stdbool.h>
#include <stddef.h>
#include "util/math/algorithm.h"
#include "util/math/matrix.h"
#include "util/math/vector.h"
#include "util/mpkutil/get.h"
#include "util/mpkutil/pack.h"
#include "core/locommon/easing.h"
#include "core/locommon/position.h"
#include "core/locommon/screen.h"
#include "core/locommon/ticker.h"
#include "./entity.h"
#include "./event.h"
#include "./status.h"
#define LOPLAYER_CAMERA_STATE_EACH_(PROC) do { \
PROC(DEFAULT, default); \
PROC(COMBAT, combat); \
PROC(DEAD, dead); \
PROC(MENU, menu); \
} while (0)
static void loplayer_camera_bind_position_in_area_(
const loplayer_camera_t* camera,
locommon_position_t* pos,
const locommon_position_t* areapos,
const vec2_t* areasize) {
assert(camera != NULL);
assert(locommon_position_valid(pos));
assert(locommon_position_valid(areapos));
assert(vec2_valid(areasize));
vec2_t szoffset = camera->display_chunksz;
vec2_diveq(&szoffset, camera->scale);
vec2_t sz;
vec2_sub(&sz, areasize, &szoffset);
vec2_t v;
locommon_position_sub(&v, pos, areapos);
# define fix_coordinate_(axis) do { \
if (sz.axis > 0) { \
if (MATH_ABS(v.axis) > sz.axis) v.axis = MATH_SIGN(v.axis)*sz.axis; \
} else { \
v.axis = 0; \
} \
} while (0)
fix_coordinate_(x);
fix_coordinate_(y);
# undef fix_coordinate_
*pos = *areapos;
vec2_addeq(&pos->fract, &v);
locommon_position_reduce(pos);
}
const char* loplayer_camera_state_stringify(loplayer_camera_state_t state) {
# define each_(NAME, name) do { \
if (state == LOPLAYER_CAMERA_STATE_##NAME) return #name; \
} while (0)
LOPLAYER_CAMERA_STATE_EACH_(each_);
assert(false);
return NULL;
# undef each_
}
bool loplayer_camera_state_unstringify(
loplayer_camera_state_t* state, const char* str, size_t len) {
assert(state != NULL);
# define each_(NAME, name) do { \
if (strncmp(str, #name, len) == 0 && #name[len] == 0) { \
*state = LOPLAYER_CAMERA_STATE_##NAME; \
return true; \
} \
} while (0)
LOPLAYER_CAMERA_STATE_EACH_(each_);
return false;
# undef each_
}
#include "core/loentity/entity.h"
#include "core/loshader/posteffect.h"
void loplayer_camera_initialize(
loplayer_camera_t* camera,
loshader_set_t* shaders,
const locommon_ticker_t* ticker,
const loplayer_event_t* event,
const loplayer_status_t* status,
const loplayer_entity_t* entity,
const mat4_t* proj) {
assert(camera != NULL);
assert(shaders != NULL);
assert(ticker != NULL);
assert(event != NULL);
assert(status != NULL);
assert(entity != NULL);
assert(mat4_valid(proj));
const locommon_screen_t* screen,
const locommon_ticker_t* ticker) {
assert(camera != NULL);
assert(screen != NULL);
assert(ticker != NULL);
mat4_t inv_proj;
mat4_inv(&inv_proj, proj);
mat4_t proj;
locommon_screen_build_projection_matrix(screen, &proj);
static const vec4_t chunk = vec4(1, 1, 0, 0);
vec4_t chunksz;
mat4_mul_vec4(&chunksz, &inv_proj, &chunk);
mat4_t iproj;
mat4_inv(&iproj, &proj);
vec4_t v;
mat4_mul_vec4(&v, &iproj, &vec4(1, 1, 0, 0));
*camera = (typeof(*camera)) {
.shaders = shaders,
.ticker = ticker,
.event = event,
.status = status,
.entity = entity,
.ticker = ticker,
.display_chunksz = chunksz.xy,
.base_brightness = 1,
.chunk_winsz = v.xy,
.matrix = mat4_scale(1, 1, 1),
.scale = 1.0f,
.cinesco = {
.color = vec4(0, 0, 0, 1),
.pos = locommon_position(0, 0, vec2(.5f, .5f)),
.scale = 1,
.posteffect = {
.brightness_whole = 1,
},
.state = LOPLAYER_CAMERA_STATE_DEFAULT,
.brightness = 1,
};
}
@@ -141,133 +49,9 @@ void loplayer_camera_deinitialize(loplayer_camera_t* camera) {
}
void loplayer_camera_update(loplayer_camera_t* camera) {
void loplayer_camera_build_matrix(const loplayer_camera_t* camera, mat4_t* m) {
assert(camera != NULL);
assert(m != NULL);
const float d = camera->ticker->delta_f;
const loplayer_status_t* stat = camera->status;
locommon_position_t target = camera->entity->super.super.pos;
/* ---- movement ---- */
const loplayer_event_param_t* e = loplayer_event_get_param(camera->event);
if (e != NULL && e->area_size.x > 0 && e->area_size.y > 0) {
loplayer_camera_bind_position_in_area_(
camera, &target, &e->area_pos, &e->area_size);
}
vec2_t dist;
locommon_position_sub(&dist, &target, &camera->pos);
if (vec2_pow_length(&dist) > 2) camera->pos = target;
locommon_easing_smooth_position(&camera->pos, &target, d*10);
# define ease_float_(name, ed, speed) \
locommon_easing_smooth_float(&camera->name, ed, d*(speed))
/* ---- cinema scope ---- */
ease_float_(cinesco.size, !!(e != NULL && e->cinescope)*.3f, 2);
/* ---- damage effect ---- */
const bool damaged =
stat->last_damage_time > 0 &&
stat->last_damage_time+500 > camera->ticker->time;
ease_float_(pe.raster, !!damaged*.5f, damaged? 5: 3);
/* ---- amnesia effect ---- */
const uint64_t amnesia_st = stat->recipient.effects.amnesia.begin;
const uint64_t amnesia_dur = stat->recipient.effects.amnesia.duration;
ease_float_(
pe.amnesia_displacement,
!!(amnesia_st+amnesia_dur > camera->ticker->time), 5);
/* ---- dying effect ---- */
const float dying = stat->dying_effect;
camera->pixsort = dying < .1f? dying/.1f: powf(1-(dying-.1f), 1.5f);
if (camera->pixsort < 0) camera->pixsort = 0;
/* ---- switch by current state ---- */
switch (camera->state) {
case LOPLAYER_CAMERA_STATE_DEFAULT:
ease_float_(scale, 1.0f, 10);
ease_float_(pe.whole_blur, 0.0f, 1);
ease_float_(pe.radial_displacement, 0.0f, 5);
ease_float_(pe.radial_fade, 0.5f, 3);
break;
case LOPLAYER_CAMERA_STATE_COMBAT:
ease_float_(scale, 1.5f, 8);
ease_float_(pe.whole_blur, 0.0f, 1);
ease_float_(pe.radial_displacement, 0.6f, 3);
ease_float_(pe.radial_fade, 0.8f, 1);
break;
case LOPLAYER_CAMERA_STATE_DEAD:
ease_float_(scale, 2.0f, 1);
ease_float_(pe.whole_blur, 1.0f, 1);
ease_float_(pe.radial_displacement, 0.3f, .7f);
ease_float_(pe.radial_fade, 1.5f, .7f);
break;
case LOPLAYER_CAMERA_STATE_MENU:
ease_float_(scale, 1.0f, 10);
ease_float_(pe.whole_blur, 0.9f, 1);
ease_float_(pe.radial_displacement, 0.0f, 10);
ease_float_(pe.radial_fade, 0.6f, 1);
break;
}
# undef ease_float_
/* ---- fixed params ---- */
camera->pe.brightness = camera->brightness;
/* ---- matrix ---- */
camera->matrix = mat4_scale(camera->scale, camera->scale, 1);
}
void loplayer_camera_draw(const loplayer_camera_t* camera) {
assert(camera != NULL);
loshader_pixsort_drawer_set_intensity(
camera->shaders->drawer.pixsort, camera->pixsort);
loshader_posteffect_drawer_set_param(
camera->shaders->drawer.posteffect, &camera->pe);
loshader_cinescope_drawer_set_param(
camera->shaders->drawer.cinescope, &camera->cinesco);
}
void loplayer_camera_pack(
const loplayer_camera_t* camera, msgpack_packer* packer) {
assert(camera != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, 2);
mpkutil_pack_str(packer, "state");
mpkutil_pack_str(packer, loplayer_camera_state_stringify(camera->state));
mpkutil_pack_str(packer, "pos");
locommon_position_pack(&camera->pos, packer);
}
bool loplayer_camera_unpack(
loplayer_camera_t* camera, const msgpack_object* obj) {
assert(camera != NULL);
if (obj == NULL) return false;
const msgpack_object_map* root = mpkutil_get_map(obj);
# define item_(v) mpkutil_get_map_item_by_str(root, v)
const char* v;
size_t len;
if (!mpkutil_get_str(item_("state"), &v, &len) ||
!loplayer_camera_state_unstringify(&camera->state, v, len)) {
return false;
}
if (!locommon_position_unpack(&camera->pos, item_("pos"))) {
return false;
}
# undef item_
return true;
*m = mat4_scale(camera->scale, camera->scale, 1);
}

View File

@@ -2,75 +2,34 @@
#include <stdbool.h>
#include <msgpack.h>
#include "util/math/matrix.h"
#include "util/math/vector.h"
#include "core/locommon/position.h"
#include "core/locommon/screen.h"
#include "core/locommon/ticker.h"
#include "core/loshader/cinescope.h"
#include "core/loentity/entity.h"
#include "core/loshader/posteffect.h"
#include "core/loshader/set.h"
#include "./entity.h"
#include "./event.h"
#include "./status.h"
typedef enum {
LOPLAYER_CAMERA_STATE_DEFAULT,
LOPLAYER_CAMERA_STATE_COMBAT,
LOPLAYER_CAMERA_STATE_DEAD,
LOPLAYER_CAMERA_STATE_MENU,
} loplayer_camera_state_t;
typedef struct {
/* injected deps */
loshader_set_t* shaders;
const locommon_ticker_t* ticker;
const loplayer_event_t* event;
const loplayer_status_t* status;
const loplayer_entity_t* entity;
/* immutable params */
vec2_t display_chunksz;
float base_brightness;
vec2_t chunk_winsz;
uint64_t corruption_since;
/* read-only mutable params */
locommon_position_t pos;
mat4_t matrix;
float scale;
float scale;
float pixsort;
loshader_posteffect_drawer_param_t pe;
loshader_cinescope_drawer_param_t cinesco;
/* public params */
loplayer_camera_state_t state;
float brightness;
loshader_posteffect_drawer_param_t posteffect;
} loplayer_camera_t;
const char*
loplayer_camera_state_stringify(
loplayer_camera_state_t state
);
bool
loplayer_camera_state_unstringify(
loplayer_camera_state_t* state,
const char* str,
size_t len
);
void
loplayer_camera_initialize(
loplayer_camera_t* camera,
loshader_set_t* shaders,
const locommon_ticker_t* ticker,
const loplayer_event_t* event,
const loplayer_status_t* status,
const loplayer_entity_t* entity,
const mat4_t* proj
const locommon_screen_t* screen,
const locommon_ticker_t* ticker
);
void
@@ -79,23 +38,7 @@ loplayer_camera_deinitialize(
);
void
loplayer_camera_update(
loplayer_camera_t* camera
);
void
loplayer_camera_draw(
const loplayer_camera_t* camera
);
void
loplayer_camera_pack(
loplayer_camera_build_matrix(
const loplayer_camera_t* camera,
msgpack_packer* packer
);
bool
loplayer_camera_unpack(
loplayer_camera_t* camera,
const msgpack_object* packer
mat4_t* m
);

View File

@@ -3,287 +3,72 @@
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "util/math/algorithm.h"
#include "util/memory/memory.h"
#include "util/mpkutil/get.h"
#include "util/mpkutil/pack.h"
#include "core/locommon/easing.h"
#include "core/locommon/msgpack.h"
#include "core/locommon/ticker.h"
#include "core/loeffect/effect.h"
#include "core/loentity/character.h"
#include "core/loentity/entity.h"
#include "core/loentity/store.h"
#include "core/loresource/sound.h"
#include "core/loshader/combat_ring.h"
#include "./entity.h"
#include "./status.h"
struct loplayer_combat_t {
/* injected deps */
loresource_sound_t* sound;
const locommon_ticker_t* ticker;
loshader_combat_ring_drawer_t* drawer;
loentity_store_t* entities;
loplayer_status_t* status;
loplayer_entity_t* entity;
/* temporary cache for drawing */
uint64_t ring_start;
uint64_t ring_end;
uint64_t guard_start;
uint64_t guard_end;
uint64_t attack_end;
float alpha;
/* params */
bool accepted;
size_t length;
loplayer_combat_attack_t attacks[1];
};
#define LOPLAYER_COMBAT_GUARD_ERROR_THRESHOLD 0.55f
#define LOPLAYER_COMBAT_ATTACK_PARAM_TO_PACK_EACH_(PROC) do { \
PROC("attacker", attacker); \
PROC("start", start); \
PROC("duration", duration); \
PROC("knockback", knockback); \
PROC("effect", effect); \
} while (0)
#define LOPLAYER_COMBAT_ATTACK_PARAM_TO_PACK_COUNT 5
static bool loplayer_combat_find_attack_index_in_period_(
loplayer_combat_t* combat, size_t* index, uint64_t st, uint64_t ed) {
assert(combat != NULL);
assert(st <= ed);
static size_t dummy_;
if (index == NULL) index = &dummy_;
for (size_t i = 0; i < combat->length; ++i) {
const uint64_t ist = combat->attacks[i].start;
const uint64_t ied = combat->attacks[i].duration + ist;
if (ist < ed && ied > st) {
*index = i;
return true;
}
}
return false;
}
static bool loplayer_combat_find_unused_attack_index_(
loplayer_combat_t* combat, size_t *index) {
assert(combat != NULL);
for (size_t i = 0; i < combat->length; ++i) {
const uint64_t ed = combat->attacks[i].start + combat->attacks[i].duration;
if (ed <= combat->ticker->time) {
*index = i;
return true;
}
}
return false;
}
static void loplayer_combat_execute_reflective_attack_(
loplayer_combat_t* combat,
loplayer_combat_attack_t* attack,
loentity_character_t* chara) {
static float loplayer_combat_calc_guard_ratio_(
const loplayer_combat_t* combat, const loplayer_combat_attack_t* attack) {
assert(combat != NULL);
assert(attack != NULL);
if (chara == NULL) return;
if (combat->last_guard_start >= combat->last_guard_end) {
return 0;
}
const uint64_t st = attack->start;
const uint64_t ed = st + attack->duration;
const loeffect_t dmg =
loeffect_immediate_damage(combat->status->recipient.status.attack);
loentity_character_apply_effect(chara, &dmg);
vec2_t knockback = attack->knockback;
vec2_muleq(&knockback, -1);
loentity_character_knockback(chara, &knockback);
loresource_sound_play(combat->sound, "reflection");
float r = 0;
r += MATH_DIFF(combat->last_guard_start, st)/1000.f;
r += MATH_DIFF(combat->last_guard_end, ed)/1000.f;
return 1.f-MATH_CLAMP(r, 0.f, 1.f);
}
static void loplayer_combat_execute_enemy_attack_(
loplayer_combat_t* combat,
loplayer_combat_attack_t* attack,
loentity_character_t* chara) {
static loplayer_combat_attack_t* loplayer_combat_find_first_attack_(
loplayer_combat_t* combat) {
assert(combat != NULL);
assert(attack != NULL);
/* chara can be NULL */
(void) chara;
loplayer_status_apply_effect(combat->status, &attack->effect);
loentity_character_knockback(&combat->entity->super, &attack->knockback);
}
static void loplayer_combat_handle_attack_(
loplayer_combat_t* combat,
loplayer_combat_attack_t* attack,
loentity_character_t* chara) {
assert(combat != NULL);
assert(attack != NULL);
if (combat->guard_start < combat->guard_end) {
const uint64_t atked = attack->start + attack->duration;
const uint64_t stdiff =
MATH_MAX(attack->start, combat->guard_start) -
MATH_MIN(attack->start, combat->guard_start);
const uint64_t eddiff =
MATH_MAX(atked, combat->guard_end) -
MATH_MIN(atked, combat->guard_end);
const float guard_error = (stdiff + eddiff)*1.0f / attack->duration;
const bool back_attack =
attack->knockback.x * combat->entity->direction > 0;
float guard_error_thresh = LOPLAYER_COMBAT_GUARD_ERROR_THRESHOLD;
if (back_attack) guard_error_thresh /= 10;
if (guard_error < guard_error_thresh) {
loplayer_combat_execute_reflective_attack_(combat, attack, chara);
return;
loplayer_combat_attack_t* first = NULL;
for (size_t i = 0; i < LOPLAYER_COMBAT_RESERVE; ++i) {
loplayer_combat_attack_t* a = &combat->attacks[i];
if (a->duration > 0) {
if (first == NULL || first->start > a->start) first = a;
}
}
loplayer_combat_execute_enemy_attack_(combat, attack, chara);
return first;
}
static void loplayer_combat_draw_ring_base_(
const loplayer_combat_t* combat) {
static loplayer_combat_attack_t* loplayer_combat_find_unused_attack_(
loplayer_combat_t* combat) {
assert(combat != NULL);
loshader_combat_ring_drawer_add_instance(
combat->drawer, &(loshader_combat_ring_drawer_instance_t) {
.range = -1, /* = draw ring base */
.color = vec4(0, 0, 0, .8f*combat->alpha),
});
}
static void loplayer_combat_draw_attacks_(const loplayer_combat_t* combat) {
assert(combat != NULL);
const uint64_t ring_st = combat->ring_start;
const uint64_t ring_ed = combat->ring_end;
assert(ring_st <= ring_ed);
const uint64_t ring_dur = ring_ed - ring_st;
for (size_t i = 0; i < combat->length; ++i) {
const uint64_t st = combat->attacks[i].start;
const uint64_t ed = combat->attacks[i].duration + st;
if (st < ring_ed && ring_st < ed) {
const uint64_t rst = st - MATH_MIN(ring_st, st);
const uint64_t red = ed - ring_st;
assert(rst <= red);
loshader_combat_ring_drawer_add_instance(
combat->drawer, &(loshader_combat_ring_drawer_instance_t) {
.range = .8f,
.start = rst*1.f/ring_dur,
.end = MATH_MIN(red, ring_dur)*1.f/ring_dur,
.color = vec4(.7f, .1f, .1f, combat->alpha),
});
}
for (size_t i = 0; i < LOPLAYER_COMBAT_RESERVE; ++i) {
if (combat->attacks[i].duration == 0) return &combat->attacks[i];
}
return NULL;
}
static void loplayer_combat_draw_guard_(const loplayer_combat_t* combat) {
assert(combat != NULL);
const bool now_guarding = (combat->guard_start > combat->guard_end);
if (!now_guarding && combat->guard_end <= combat->ring_start) {
return;
}
const float ring_dur = combat->ring_end - combat->ring_start;
assert(ring_dur > 0);
const uint64_t st = combat->guard_start -
MATH_MIN(combat->ring_start, combat->guard_start);
const uint64_t ed =
(now_guarding? combat->ticker->time: combat->guard_end) -
combat->ring_start;
loshader_combat_ring_drawer_add_instance(
combat->drawer, &(loshader_combat_ring_drawer_instance_t) {
.range = .7f,
.start = st/ring_dur,
.end = ed/ring_dur,
.color = vec4(.1f, .1f, .7f, combat->alpha),
});
}
static void loplayer_combat_draw_clockhand_(
const loplayer_combat_t* combat) {
assert(combat != NULL);
const uint64_t ring_dur = combat->ring_end - combat->ring_start;
assert(ring_dur > 0);
assert(combat->ticker->time >= combat->ring_start);
const uint64_t cur = combat->ticker->time - combat->ring_start;
const float curf = cur*1.f/ring_dur;
loshader_combat_ring_drawer_add_instance(
combat->drawer, &(loshader_combat_ring_drawer_instance_t) {
.range = 0, /* = draw clockhand */
.start = curf,
.color = vec4(1, 1, 1, combat->alpha),
});
}
loplayer_combat_t* loplayer_combat_new(
loresource_sound_t* sound,
loshader_combat_ring_drawer_t* drawer,
const locommon_ticker_t* ticker,
loentity_store_t* entities,
loplayer_status_t* status,
loplayer_entity_t* entity,
size_t length) {
assert(sound != NULL);
assert(drawer != NULL);
void loplayer_combat_initialize(
loplayer_combat_t* combat,
const locommon_ticker_t* ticker,
loentity_store_t* entities) {
assert(combat != NULL);
assert(ticker != NULL);
assert(entities != NULL);
assert(status != NULL);
assert(entity != NULL);
assert(length > 0);
loplayer_combat_t* combat = memory_new(
sizeof(*combat) + (length-1)*sizeof(combat->attacks[0]));
*combat = (typeof(*combat)) {
.sound = sound,
.drawer = drawer,
.ticker = ticker,
.entities = entities,
.status = status,
.entity = entity,
.length = length,
};
for (size_t i = 0; i < combat->length; ++i) {
combat->attacks[i] = (typeof(combat->attacks[0])) {0};
}
return combat;
}
void loplayer_combat_delete(loplayer_combat_t* combat) {
if (combat == NULL) return;
void loplayer_combat_deinitialize(loplayer_combat_t* combat) {
assert(combat != NULL);
memory_delete(combat);
loplayer_combat_drop_all_attack(combat, true /* = by system */);
}
bool loplayer_combat_add_attack(
@@ -291,213 +76,132 @@ bool loplayer_combat_add_attack(
assert(combat != NULL);
assert(attack != NULL);
if (loplayer_combat_find_attack_index_in_period_(
combat, NULL, attack->start, attack->start + attack->duration)) {
return false;
if (!combat->accepting) return false;
const uint64_t st = attack->start;
const uint64_t ed = st + attack->duration;
if (ed <= combat->ticker->time || attack->duration == 0) return false;
for (size_t i = 0; i < LOPLAYER_COMBAT_RESERVE; ++i) {
const loplayer_combat_attack_t* a = &combat->attacks[i];
if (a->duration == 0) continue;
const uint64_t ist = a->start;
const uint64_t ied = ist + a->duration;
if (st < ied && ist < ed) return false;
}
size_t index;
if (!loplayer_combat_find_unused_attack_index_(combat, &index)) {
return false;
}
loplayer_combat_attack_t* a = loplayer_combat_find_unused_attack_(combat);
if (a == NULL) return false;
combat->attacks[index] = *attack;
*a = *attack;
if (combat->first_attack == NULL ||
combat->first_attack->start > a->start) {
combat->first_attack = a;
}
if (combat->last_attack == NULL ||
combat->last_attack->start < a->start) {
combat->last_attack = a;
}
return true;
}
bool loplayer_combat_accept_all_attacks(loplayer_combat_t* combat) {
void loplayer_combat_drop_dead_attack(loplayer_combat_t* combat) {
assert(combat != NULL);
if (combat->accepted) return true;
for (size_t i = 0; i < LOPLAYER_COMBAT_RESERVE; ++i) {
loplayer_combat_attack_t* a = &combat->attacks[i];
if (a->duration == 0) continue;
if (!loplayer_combat_find_attack_index_in_period_(
combat, NULL, combat->ticker->time, SIZE_MAX)) {
return false;
}
combat->accepted = true;
return true;
}
void loplayer_combat_drop_all_attacks(loplayer_combat_t* combat) {
assert(combat != NULL);
for (size_t i = 0; i < combat->length; ++i) {
combat->attacks[i] = (typeof(combat->attacks[0])) {0};
}
combat->accepted = false;
combat->ring_start = 0;
combat->ring_end = 0;
combat->guard_start = 0;
combat->guard_end = 0;
combat->attack_end = 0;
}
void loplayer_combat_guard(loplayer_combat_t* combat) {
assert(combat != NULL);
if (combat->ring_end <= combat->ticker->time ||
combat->guard_start > combat->guard_end) {
return;
}
combat->guard_start = combat->ticker->time;
loresource_sound_play(combat->sound, "guard");
}
void loplayer_combat_unguard(loplayer_combat_t* combat) {
assert(combat != NULL);
if (combat->guard_start <= combat->guard_end) return;
combat->guard_end = combat->ticker->time;
}
void loplayer_combat_update(loplayer_combat_t* combat) {
assert(combat != NULL);
if (!combat->accepted) return;
const uint64_t cur = combat->ticker->time;
const uint64_t pre = cur - combat->ticker->delta;
combat->ring_end = 0;
for (size_t i = 0; i < combat->length; ++i) {
loentity_store_iterator_t itr = (typeof(itr)) {0};
if (!loentity_store_find_item_by_id(
combat->entities, &itr, combat->attacks[i].attacker)) {
combat->attacks[i].start = 0;
combat->attacks[i].duration = 0;
loentity_store_iterator_t itr = {0};
if (loentity_store_find_item_by_id(
combat->entities, &itr, a->attacker)) {
continue;
}
const uint64_t st = combat->attacks[i].start;
const uint64_t ed = combat->attacks[i].duration + st;
const bool pre_active = st <= pre && pre < ed;
const bool cur_active = st <= cur && cur < ed;
if (!pre_active && cur_active) {
combat->attack_end = ed;
} else if (pre_active && !cur_active) {
loplayer_combat_handle_attack_(
combat, &combat->attacks[i], itr.character);
}
combat->ring_end = MATH_MAX(combat->ring_end, ed);
}
if (combat->ring_end > cur) {
if (combat->ring_start == 0) {
combat->ring_start = cur;
}
locommon_easing_smooth_float(
&combat->alpha, 1, combat->ticker->delta_f*10);
} else {
combat->alpha = 0;
loplayer_combat_drop_all_attacks(combat);
if (combat->first_attack == a) combat->first_attack = NULL;
if (combat->last_attack == a) combat->last_attack = NULL;
*a = (typeof(*a)) {0};
}
}
void loplayer_combat_draw_ui(const loplayer_combat_t* combat) {
void loplayer_combat_drop_all_attack(
loplayer_combat_t* combat, loplayer_combat_attack_result_t reason) {
assert(combat != NULL);
if (!combat->accepted || combat->ring_end <= combat->ticker->time) {
return;
for (size_t i = 0; i < LOPLAYER_COMBAT_RESERVE; ++i) {
loplayer_combat_attack_t* a = &combat->attacks[i];
if (a->duration != 0) loplayer_combat_attack_handle(a, reason);
*a = (typeof(*a)) {0};
}
loplayer_combat_draw_ring_base_(combat);
loplayer_combat_draw_attacks_(combat);
loplayer_combat_draw_guard_(combat);
loplayer_combat_draw_clockhand_(combat);
combat->first_attack = NULL;
combat->last_attack = NULL;
}
void loplayer_combat_pack(
const loplayer_combat_t* combat, msgpack_packer* packer) {
assert(combat != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, 1);
mpkutil_pack_str(packer, "attacks");
size_t len = 0;
for (size_t i = 0; i < combat->length; ++i) {
const uint64_t st = combat->attacks[i].start;
const uint64_t ed = combat->attacks[i].duration + st;
const uint64_t cur = combat->ticker->time;
if (st <= cur && cur < ed) ++len;
}
msgpack_pack_array(packer, len);
for (size_t i = 0; i < combat->length; ++i) {
const uint64_t st = combat->attacks[i].start;
const uint64_t ed = combat->attacks[i].duration + st;
const uint64_t cur = combat->ticker->time;
if (st <= cur && cur < ed) {
loplayer_combat_attack_pack(&combat->attacks[i], packer);
}
}
}
bool loplayer_combat_unpack(
loplayer_combat_t* combat, const msgpack_object* obj) {
assert(combat != NULL);
loplayer_combat_drop_all_attacks(combat);
if (obj == NULL) return false;
const msgpack_object_map* root = mpkutil_get_map(obj);
const msgpack_object_array* array =
mpkutil_get_array(mpkutil_get_map_item_by_str(root, "attacks"));
size_t src = 0, dst = 0;
while (src < array->size && dst < combat->length) {
if (loplayer_combat_attack_unpack(
&combat->attacks[dst], &array->ptr[src++])) {
++dst;
}
}
return true;
}
void loplayer_combat_attack_pack(
const loplayer_combat_attack_t* attack,
msgpack_packer* packer) {
assert(attack != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, LOPLAYER_COMBAT_ATTACK_PARAM_TO_PACK_COUNT);
# define pack_(name, var) do { \
mpkutil_pack_str(packer, name); \
LOCOMMON_MSGPACK_PACK_ANY(packer, &attack->var); \
} while (0)
LOPLAYER_COMBAT_ATTACK_PARAM_TO_PACK_EACH_(pack_);
# undef pack_
}
bool loplayer_combat_attack_unpack(
bool loplayer_combat_pop_attack(
loplayer_combat_t* combat,
loplayer_combat_attack_t* attack,
const msgpack_object* obj) {
float* guard_ratio) {
assert(combat != NULL);
assert(attack != NULL);
assert(guard_ratio != NULL);
const uint64_t t = combat->ticker->time;
const uint64_t pt = combat->ticker->prev_time;
loplayer_combat_attack_t* first = combat->first_attack;
if (first == NULL) return false;
const uint64_t ed = first->start + first->duration;
if (pt >= ed || ed > t) return false;
*attack = *first;
*guard_ratio = loplayer_combat_calc_guard_ratio_(combat, first);
*first = (typeof(*first)) {0};
combat->first_attack = loplayer_combat_find_first_attack_(combat);
if (combat->first_attack == NULL) combat->last_attack = NULL;
return true;
}
void loplayer_combat_set_accepting(
loplayer_combat_t* combat,
bool accepting,
loplayer_combat_attack_result_t reason) {
assert(combat != NULL);
if (combat->accepting == accepting) return;
combat->accepting = accepting;
if (!accepting) loplayer_combat_drop_all_attack(combat, reason);
}
void loplayer_combat_set_guarding(loplayer_combat_t* combat, bool guarding) {
assert(combat != NULL);
if (combat->last_guard_start <= combat->last_guard_end) {
if (guarding) combat->last_guard_start = combat->ticker->time;
} else {
if (!guarding) combat->last_guard_end = combat->ticker->time;
}
}
bool loplayer_combat_is_attack_pending(const loplayer_combat_t* combat) {
assert(combat != NULL);
if (combat->last_attack == NULL) return false;
const uint64_t st = combat->last_attack->start;
const uint64_t ed = st + combat->last_attack->duration;
return ed > combat->ticker->time;
}
void loplayer_combat_attack_handle(
const loplayer_combat_attack_t* attack,
loplayer_combat_attack_result_t result) {
assert(attack != NULL);
const msgpack_object_map* root = mpkutil_get_map(obj);
# define item_(v) mpkutil_get_map_item_by_str(root, v)
# define unpack_(name, var) do { \
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(name), &attack->var)) { \
return false; \
} \
} while (0)
LOPLAYER_COMBAT_ATTACK_PARAM_TO_PACK_EACH_(unpack_);
return true;
# undef unpack_
# undef item_
assert(attack->handle != NULL);
attack->handle(attack, result);
}

View File

@@ -1,107 +1,109 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <msgpack.h>
#include "util/math/vector.h"
#include "core/locommon/ticker.h"
#include "core/loeffect/effect.h"
#include "core/loentity/entity.h"
#include "core/loentity/store.h"
#include "core/loresource/sound.h"
#include "core/loshader/combat_ring.h"
#include "./entity.h"
#include "./status.h"
typedef enum {
LOPLAYER_COMBAT_ATTACK_RESULT_EXECUTED,
LOPLAYER_COMBAT_ATTACK_RESULT_REFLECTED,
LOPLAYER_COMBAT_ATTACK_RESULT_DODGED,
LOPLAYER_COMBAT_ATTACK_RESULT_ABORTED,
} loplayer_combat_attack_result_t;
struct loplayer_combat_t;
typedef struct loplayer_combat_t loplayer_combat_t;
typedef struct {
typedef struct loplayer_combat_attack_t loplayer_combat_attack_t;
struct loplayer_combat_attack_t {
loentity_id_t attacker;
uint64_t start;
uint64_t duration;
vec2_t knockback;
void* data1;
void* data2;
loeffect_t effect;
} loplayer_combat_attack_t;
void
(*handle)(
const loplayer_combat_attack_t* attack,
loplayer_combat_attack_result_t result
);
};
loplayer_combat_t* /* OWNERSHIP */
loplayer_combat_new(
loresource_sound_t* sound,
loshader_combat_ring_drawer_t* drawer,
const locommon_ticker_t* ticker,
loentity_store_t* entities,
loplayer_status_t* status,
loplayer_entity_t* entity,
size_t length
typedef struct {
const locommon_ticker_t* ticker;
loentity_store_t* entities;
# define LOPLAYER_COMBAT_RESERVE 32
loplayer_combat_attack_t attacks[LOPLAYER_COMBAT_RESERVE];
loplayer_combat_attack_t* first_attack;
loplayer_combat_attack_t* last_attack;
bool accepting;
uint64_t last_guard_start;
uint64_t last_guard_end;
} loplayer_combat_t;
void
loplayer_combat_initialize(
loplayer_combat_t* combat,
const locommon_ticker_t* ticker,
loentity_store_t* entities
);
void
loplayer_combat_delete(
loplayer_combat_t* combat /* OWNERSHIP */
loplayer_combat_deinitialize(
loplayer_combat_t* combat
);
/* All added attacks must finish with being called handle function
while the attacker is alive. */
bool
loplayer_combat_add_attack(
loplayer_combat_t* combat,
const loplayer_combat_attack_t* attack
);
void
loplayer_combat_drop_dead_attack(
loplayer_combat_t* combat
);
void
loplayer_combat_drop_all_attack(
loplayer_combat_t* combat,
loplayer_combat_attack_result_t reason
);
bool
loplayer_combat_accept_all_attacks(
loplayer_combat_t* combat
loplayer_combat_pop_attack(
loplayer_combat_t* combat,
loplayer_combat_attack_t* attack,
float* guard_ratio
);
void
loplayer_combat_drop_all_attacks(
loplayer_combat_t* combat
loplayer_combat_set_accepting(
loplayer_combat_t* combat,
bool accepting,
loplayer_combat_attack_result_t reason
);
void
loplayer_combat_guard(
loplayer_combat_t* combat
loplayer_combat_set_guarding(
loplayer_combat_t* combat,
bool guarding
);
void
loplayer_combat_unguard(
loplayer_combat_t* combat
);
void
loplayer_combat_update(
loplayer_combat_t* combat
);
void
loplayer_combat_draw_ui(
bool
loplayer_combat_is_attack_pending(
const loplayer_combat_t* combat
);
void
loplayer_combat_pack(
const loplayer_combat_t* combat,
msgpack_packer* packer
);
bool
loplayer_combat_unpack(
loplayer_combat_t* combat,
const msgpack_object* obj /* NULLABLE */
);
void
loplayer_combat_attack_pack(
loplayer_combat_attack_handle(
const loplayer_combat_attack_t* attack,
msgpack_packer* packer
);
bool
loplayer_combat_attack_unpack(
loplayer_combat_attack_t* attack,
const msgpack_object* obj /* NULLABLE */
loplayer_combat_attack_result_t result
);

View File

@@ -1,16 +1,22 @@
#include "./controller.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include "core/locommon/input.h"
#include "core/locommon/position.h"
#include "core/locommon/ticker.h"
void loplayer_controller_initialize(loplayer_controller_t* controller) {
#define DODGE_PRESS_MAX_DURATION_ 300
void loplayer_controller_initialize(
loplayer_controller_t* controller, const locommon_ticker_t* ticker) {
assert(controller != NULL);
assert(ticker != NULL);
*controller = (typeof(*controller)) {0};
*controller = (typeof(*controller)) {
.ticker = ticker,
};
}
void loplayer_controller_deinitialize(loplayer_controller_t* controller) {
@@ -18,62 +24,68 @@ void loplayer_controller_deinitialize(loplayer_controller_t* controller) {
}
void loplayer_controller_update(
void loplayer_controller_handle_input(
loplayer_controller_t* controller,
const locommon_input_t* input,
const locommon_position_t* cursor) {
assert(controller != NULL);
assert(input != NULL);
assert(locommon_position_valid(cursor));
controller->looking = *cursor;
controller->cursor = input->cursor;
if (locommon_position_valid(cursor)) {
controller->cursor = *cursor;
}
controller->movement = LOPLAYER_CONTROLLER_MOVEMENT_NONE;
controller->action = LOPLAYER_CONTROLLER_ACTION_NONE;
controller->state = LOPLAYER_CONTROLLER_STATE_NONE;
if (input == NULL) return;
const bool prev_jump =
controller->prev.buttons & LOCOMMON_INPUT_BUTTON_JUMP;
const bool prev_guarding =
controller->prev.buttons & LOCOMMON_INPUT_BUTTON_GUARD;
const bool prev_dash =
controller->prev.buttons & LOCOMMON_INPUT_BUTTON_DASH;
const bool prev_menu =
controller->prev.buttons & LOCOMMON_INPUT_BUTTON_MENU;
if (input->buttons & LOCOMMON_INPUT_BUTTON_LEFT) {
controller->state = LOPLAYER_CONTROLLER_STATE_WALK_LEFT;
}
if (input->buttons & LOCOMMON_INPUT_BUTTON_RIGHT) {
controller->state = LOPLAYER_CONTROLLER_STATE_WALK_RIGHT;
}
if (input->buttons & LOCOMMON_INPUT_BUTTON_JUMP && !prev_jump) {
controller->movement = LOPLAYER_CONTROLLER_MOVEMENT_JUMP;
} else if (input->buttons & LOCOMMON_INPUT_BUTTON_LEFT) {
controller->movement = LOPLAYER_CONTROLLER_MOVEMENT_WALK_LEFT;
if (input->buttons & LOCOMMON_INPUT_BUTTON_DASH) {
controller->movement = LOPLAYER_CONTROLLER_MOVEMENT_DASH_LEFT;
if (input->buttons & LOCOMMON_INPUT_BUTTON_DODGE) {
if (!controller->sprint) {
controller->sprint = true;
controller->last_sprint_start = controller->ticker->time;
}
if (input->buttons & LOCOMMON_INPUT_BUTTON_LEFT) {
controller->state = LOPLAYER_CONTROLLER_STATE_SPRINT_LEFT;
} else if (input->buttons & LOCOMMON_INPUT_BUTTON_RIGHT) {
controller->state = LOPLAYER_CONTROLLER_STATE_SPRINT_RIGHT;
}
} else {
if (controller->sprint) {
controller->sprint = false;
} else if (input->buttons & LOCOMMON_INPUT_BUTTON_RIGHT) {
controller->movement = LOPLAYER_CONTROLLER_MOVEMENT_WALK_RIGHT;
if (input->buttons & LOCOMMON_INPUT_BUTTON_DASH) {
controller->movement = LOPLAYER_CONTROLLER_MOVEMENT_DASH_RIGHT;
assert(controller->ticker->time >= controller->last_sprint_start);
const size_t t =
controller->ticker->time - controller->last_sprint_start;
if (t < DODGE_PRESS_MAX_DURATION_) {
if (input->buttons & LOCOMMON_INPUT_BUTTON_LEFT) {
controller->state = LOPLAYER_CONTROLLER_STATE_DODGE_LEFT;
} else if (input->buttons & LOCOMMON_INPUT_BUTTON_RIGHT) {
controller->state = LOPLAYER_CONTROLLER_STATE_DODGE_RIGHT;
} else {
controller->state = LOPLAYER_CONTROLLER_STATE_DODGE_FORWARD;
}
}
}
}
if (input->buttons & LOCOMMON_INPUT_BUTTON_ATTACK) {
controller->action = LOPLAYER_CONTROLLER_ACTION_ATTACK;
if (input->buttons & LOCOMMON_INPUT_BUTTON_JUMP) {
if (!controller->jump) {
controller->jump = true;
controller->state = LOPLAYER_CONTROLLER_STATE_JUMP;
}
} else {
controller->jump = false;
}
if (input->buttons & LOCOMMON_INPUT_BUTTON_GUARD) {
if (!prev_guarding) controller->action = LOPLAYER_CONTROLLER_ACTION_GUARD;
} else {
if (prev_guarding) controller->action = LOPLAYER_CONTROLLER_ACTION_UNGUARD;
controller->state = LOPLAYER_CONTROLLER_STATE_GUARD;
}
if (input->buttons & LOCOMMON_INPUT_BUTTON_DASH && !prev_dash) {
controller->action = LOPLAYER_CONTROLLER_ACTION_DODGE;
if (input->buttons & LOCOMMON_INPUT_BUTTON_SHOOT) {
controller->state = LOPLAYER_CONTROLLER_STATE_SHOOT;
}
if (input->buttons & LOCOMMON_INPUT_BUTTON_MENU && !prev_menu) {
controller->action = LOPLAYER_CONTROLLER_ACTION_MENU;
}
controller->prev = *input;
}

View File

@@ -1,41 +1,38 @@
#pragma once
#include <stdbool.h>
#include "core/locommon/input.h"
#include "core/locommon/position.h"
#include "core/locommon/ticker.h"
typedef enum {
LOPLAYER_CONTROLLER_MOVEMENT_NONE,
LOPLAYER_CONTROLLER_MOVEMENT_JUMP,
LOPLAYER_CONTROLLER_MOVEMENT_WALK_LEFT,
LOPLAYER_CONTROLLER_MOVEMENT_WALK_RIGHT,
LOPLAYER_CONTROLLER_MOVEMENT_DASH_LEFT,
LOPLAYER_CONTROLLER_MOVEMENT_DASH_RIGHT,
} loplayer_controller_movement_t;
typedef enum {
LOPLAYER_CONTROLLER_ACTION_NONE,
LOPLAYER_CONTROLLER_ACTION_ATTACK,
LOPLAYER_CONTROLLER_ACTION_GUARD,
LOPLAYER_CONTROLLER_ACTION_UNGUARD,
LOPLAYER_CONTROLLER_ACTION_DODGE,
LOPLAYER_CONTROLLER_ACTION_MENU,
} loplayer_controller_action_t;
LOPLAYER_CONTROLLER_STATE_NONE,
LOPLAYER_CONTROLLER_STATE_WALK_LEFT,
LOPLAYER_CONTROLLER_STATE_WALK_RIGHT,
LOPLAYER_CONTROLLER_STATE_SPRINT_LEFT,
LOPLAYER_CONTROLLER_STATE_SPRINT_RIGHT,
LOPLAYER_CONTROLLER_STATE_DODGE_FORWARD,
LOPLAYER_CONTROLLER_STATE_DODGE_LEFT,
LOPLAYER_CONTROLLER_STATE_DODGE_RIGHT,
LOPLAYER_CONTROLLER_STATE_JUMP,
LOPLAYER_CONTROLLER_STATE_GUARD,
LOPLAYER_CONTROLLER_STATE_SHOOT,
} loplayer_controller_state_t;
typedef struct {
locommon_position_t looking;
vec2_t cursor; /* display coordinate (-1~1) */
const locommon_ticker_t* ticker;
loplayer_controller_movement_t movement;
loplayer_controller_action_t action;
loplayer_controller_state_t state;
locommon_position_t cursor;
locommon_input_t prev;
bool jump;
bool sprint;
uint64_t last_sprint_start;
} loplayer_controller_t;
void
loplayer_controller_initialize(
loplayer_controller_t* controller
loplayer_controller_t* controller,
const locommon_ticker_t* ticker
);
void
@@ -44,8 +41,8 @@ loplayer_controller_deinitialize(
);
void
loplayer_controller_update(
loplayer_controller_handle_input(
loplayer_controller_t* controller,
const locommon_input_t* input,
const locommon_position_t* cursor
const locommon_input_t* input, /* NULLABLE */
const locommon_position_t* cursor /* NULLABLE */
);

View File

@@ -1,311 +0,0 @@
#include "./entity.h"
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <msgpack.h>
#include "util/math/algorithm.h"
#include "util/math/vector.h"
#include "util/memory/memory.h"
#include "util/mpkutil/get.h"
#include "util/mpkutil/pack.h"
#include "core/locommon/easing.h"
#include "core/locommon/msgpack.h"
#include "core/locommon/physics.h"
#include "core/locommon/ticker.h"
#include "core/loentity/character.h"
#include "core/loentity/entity.h"
#include "core/loentity/store.h"
#include "core/loresource/sound.h"
#include "core/loshader/character.h"
#include "./event.h"
#include "./status.h"
#define LOPLAYER_ENTITY_WIDTH .02f
#define LOPLAYER_ENTITY_HEIGHT .05f
#define LOPLAYER_ENTITY_DRAW_SIZE LOPLAYER_ENTITY_HEIGHT
#define LOPLAYER_ENTITY_SHIFT_Y .03f
#define LOPLAYER_ENTITY_GRAVITY_ACCELARATION 2.2f
#define LOPLAYER_ENTITY_RECOVERY_ACCELARATION 1.f
#define LOPLAYER_ENTITY_MAX_GRAVITY 2.f
#define LOPLAYER_ENTITY_DIRECTION_EPSILON .05f
#define LOPLAYER_ENTITY_PARAM_TO_PACK_EACH_(PROC) do { \
PROC("pos", super.super.pos); \
PROC("movement", movement); \
PROC("knockback", knockback); \
PROC("gravity", gravity); \
} while (0)
#define LOPLAYER_ENTITY_PARAM_TO_PACK_COUNT 4
static void loplayer_entity_update_position_(
loplayer_entity_t* p, vec2_t* velocity) {
assert(p != NULL);
assert(vec2_valid(velocity));
vec2_t disp = *velocity;
vec2_muleq(&disp, p->ticker->delta_f);
vec2_addeq(&p->super.super.pos.fract, &disp);
p->super.super.pos.fract.y -= LOPLAYER_ENTITY_SHIFT_Y;
locommon_position_reduce(&p->super.super.pos);
locommon_physics_entity_t e = {
.size = vec2(LOPLAYER_ENTITY_WIDTH, LOPLAYER_ENTITY_HEIGHT),
.pos = p->super.super.pos,
.velocity = *velocity,
};
loentity_store_solve_collision_between_ground(
p->entities, &e, p->ticker->delta_f);
p->super.super.pos = e.pos;
p->super.super.pos.fract.y += LOPLAYER_ENTITY_SHIFT_Y;
locommon_position_reduce(&p->super.super.pos);
p->on_ground = false;
if (e.velocity.y == 0) {
if (velocity->y <= 0) {
p->on_ground = true;
}
if (p->gravity*velocity->y >= 0) p->gravity = 0;
if (p->knockback.y*velocity->y >= 0) p->knockback.y = 0;
}
if (e.velocity.x == 0 && velocity->x != 0) {
if (p->knockback.x*velocity->x >= 0) p->knockback.x = 0;
}
p->last_velocity = *velocity = e.velocity;
}
static void loplayer_entity_bind_in_event_area_(loplayer_entity_t* p) {
assert(p != NULL);
const loplayer_event_param_t* e = loplayer_event_get_param(p->event);
if (e == NULL || e->area_size.x <= 0 || e->area_size.y <= 0) return;
vec2_t v;
locommon_position_sub(&v, &p->super.super.pos, &e->area_pos);
if (MATH_ABS(v.x) > e->area_size.x) {
v.x = MATH_SIGN(v.x) * e->area_size.x;
}
if (MATH_ABS(v.y) > e->area_size.y) {
v.y = MATH_SIGN(v.y) * e->area_size.y;
}
p->super.super.pos = e->area_pos;
vec2_addeq(&p->super.super.pos.fract, &v);
locommon_position_reduce(&p->super.super.pos);
}
static void loplayer_entity_delete_(loentity_t* entity) {
assert(entity != NULL);
/* does not anything */
}
static bool loplayer_entity_update_(loentity_t* entity) {
assert(entity != NULL);
loplayer_entity_t* p = (typeof(p)) entity;
/* ---- position ---- */
vec2_t velocity = p->movement;
vec2_addeq(&velocity, &p->knockback);
velocity.y += p->gravity;
loplayer_entity_update_position_(p, &velocity);
loplayer_entity_bind_in_event_area_(p);
/* ---- gravity ---- */
const float dt = p->ticker->delta_f;
p->gravity -= LOPLAYER_ENTITY_GRAVITY_ACCELARATION*dt;
p->gravity = MATH_MAX(p->gravity, -LOPLAYER_ENTITY_MAX_GRAVITY);
/* ---- recovery from knockback ---- */
locommon_easing_linear_float(
&p->knockback.x, 0, LOPLAYER_ENTITY_RECOVERY_ACCELARATION*dt);
locommon_easing_linear_float(
&p->knockback.y, 0, LOPLAYER_ENTITY_RECOVERY_ACCELARATION*dt);
return true;
}
static void loplayer_entity_draw_(
loentity_t* entity, const locommon_position_t* basepos) {
assert(entity != NULL);
assert(basepos != NULL);
loplayer_entity_t* p = (typeof(p)) entity;
locommon_position_t center = p->super.super.pos;
center.fract.y -= LOPLAYER_ENTITY_SHIFT_Y;
locommon_position_reduce(&center);
loshader_character_drawer_instance_t instance = {
.character_id = LOSHADER_CHARACTER_ID_PLAYER,
.from_motion_id = p->motion.from,
.to_motion_id = p->motion.to,
.motion_time = p->motion.time,
.marker = p->status->bullet_immune_until < p->ticker->time,
.marker_offset = vec2(0, LOPLAYER_ENTITY_SHIFT_Y),
.size = vec2(
LOPLAYER_ENTITY_DRAW_SIZE*p->direction, LOPLAYER_ENTITY_DRAW_SIZE),
.color = vec4(0, 0, 0, 1),
};
locommon_position_sub(&instance.pos, &center, basepos);
loshader_character_drawer_add_instance(p->drawer, &instance);
}
static void loplayer_entity_apply_effect_(
loentity_character_t* chara, const loeffect_t* effect) {
assert(chara != NULL);
assert(effect != NULL);
loplayer_entity_t* p = (typeof(p)) chara;
loplayer_status_apply_effect(p->status, effect);
}
static void loplayer_entity_knockback_(
loentity_character_t* chara, const vec2_t* v) {
assert(chara != NULL);
assert(vec2_valid(v));
loplayer_entity_t* p = (typeof(p)) chara;
vec2_addeq(&p->knockback, v);
}
void loplayer_entity_initialize(
loplayer_entity_t* player,
loentity_id_t id,
loresource_sound_t* sound,
loshader_character_drawer_t* drawer,
const locommon_ticker_t* ticker,
loentity_store_t* entities,
const loplayer_event_t* event,
loplayer_status_t* status) {
assert(player != NULL);
assert(sound != NULL);
assert(drawer != NULL);
assert(ticker != NULL);
assert(entities != NULL);
assert(event != NULL);
assert(status != NULL);
*player = (typeof(*player)) {
.super = {
.super = {
.vtable = {
.delete = loplayer_entity_delete_,
.update = loplayer_entity_update_,
.draw = loplayer_entity_draw_,
},
.subclass = LOENTITY_SUBCLASS_CHARACTER,
.id = id,
.pos = locommon_position(0, 0, vec2(0.5, 0.5)),
.dont_save = true,
},
.vtable = {
.apply_effect = loplayer_entity_apply_effect_,
.knockback = loplayer_entity_knockback_,
},
},
.sound = sound,
.drawer = drawer,
.ticker = ticker,
.entities = entities,
.event = event,
.status = status,
.direction = 1,
};
}
void loplayer_entity_deinitialize(loplayer_entity_t* player) {
assert(player != NULL);
}
void loplayer_entity_move(
loplayer_entity_t* player, const locommon_position_t* pos) {
assert(player != NULL);
assert(locommon_position_valid(pos));
player->super.super.pos = *pos;
player->on_ground = false;
player->movement = vec2(0, 0);
player->knockback = vec2(0, 0);
player->gravity = 0;
}
void loplayer_entity_aim(
loplayer_entity_t* player, const locommon_position_t* pos) {
assert(player != NULL);
assert(locommon_position_valid(pos));
vec2_t dir;
locommon_position_sub(&dir, pos, &player->super.super.pos);
if (MATH_ABS(dir.x) > LOPLAYER_ENTITY_DIRECTION_EPSILON) {
player->direction = MATH_SIGN(dir.x);
}
}
bool loplayer_entity_affect_bullet(loplayer_entity_t* player) {
assert(player != NULL);
return loentity_store_affect_bullets_shot_by_others(
player->entities,
&player->super,
&player->last_velocity,
player->ticker->delta_f);
}
void loplayer_entity_pack(
const loplayer_entity_t* player, msgpack_packer* packer) {
assert(player != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, LOPLAYER_ENTITY_PARAM_TO_PACK_COUNT);
# define pack_(name, var) do { \
mpkutil_pack_str(packer, name); \
LOCOMMON_MSGPACK_PACK_ANY(packer, &player->var); \
} while (0)
LOPLAYER_ENTITY_PARAM_TO_PACK_EACH_(pack_);
# undef pack_
}
bool loplayer_entity_unpack(
loplayer_entity_t* player, const msgpack_object* obj) {
assert(player != NULL);
if (obj == NULL) return false;
const msgpack_object_map* root = mpkutil_get_map(obj);
# define item_(v) mpkutil_get_map_item_by_str(root, v)
# define unpack_(name, var) do { \
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(name), &player->var)) { \
return false; \
} \
} while (0)
LOPLAYER_ENTITY_PARAM_TO_PACK_EACH_(unpack_);
return true;
# undef unpack_
# undef item_
}

View File

@@ -1,93 +0,0 @@
#pragma once
#include <stdbool.h>
#include <msgpack.h>
#include "util/math/vector.h"
#include "core/locommon/ticker.h"
#include "core/loentity/character.h"
#include "core/loentity/store.h"
#include "core/loresource/sound.h"
#include "core/loshader/character.h"
#include "./event.h"
#include "./status.h"
typedef struct {
loentity_character_t super;
/* injected deps */
const locommon_ticker_t* ticker;
loresource_sound_t* sound;
loshader_character_drawer_t* drawer;
loentity_store_t* entities;
const loplayer_event_t* event;
loplayer_status_t* status;
/* read-only mutable params */
float direction;
/* public params */
vec2_t movement;
vec2_t knockback;
float gravity;
/* temporary cache for update */
bool on_ground;
vec2_t last_velocity;
/* temporary cache for draw */
struct {
loshader_character_motion_id_t from;
loshader_character_motion_id_t to;
float time;
} motion;
} loplayer_entity_t;
void
loplayer_entity_initialize(
loplayer_entity_t* player,
loentity_id_t id,
loresource_sound_t* sound,
loshader_character_drawer_t* drawer,
const locommon_ticker_t* ticker,
loentity_store_t* entities,
const loplayer_event_t* event,
loplayer_status_t* status
);
void
loplayer_entity_deinitialize(
loplayer_entity_t* player
);
void
loplayer_entity_move(
loplayer_entity_t* player,
const locommon_position_t* pos
);
void
loplayer_entity_aim(
loplayer_entity_t* player,
const locommon_position_t* pos
);
bool
loplayer_entity_affect_bullet(
loplayer_entity_t* player
);
void
loplayer_entity_pack(
const loplayer_entity_t* player,
msgpack_packer* packer
);
bool
loplayer_entity_unpack(
loplayer_entity_t* player,
const msgpack_object* obj
);

View File

@@ -3,144 +3,159 @@
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "util/glyphas/block.h"
#include "util/glyphas/cache.h"
#include <msgpack.h>
#include "util/math/algorithm.h"
#include "util/math/vector.h"
#include "util/memory/memory.h"
#include "util/mpkutil/get.h"
#include "util/mpkutil/pack.h"
#include "core/locommon/msgpack.h"
#include "core/locommon/position.h"
#include "core/loresource/set.h"
#include "core/loshader/set.h"
#include "core/locommon/ticker.h"
#include "core/loentity/entity.h"
struct loplayer_event_t {
loplayer_event_param_t param;
/* convertible between loplayer_event_t* and loplayer_event_param_t* */
/* generated serializer */
#include "core/loplayer/crial/event.h"
/* injected deps */
loresource_set_t* res;
loshader_set_t* shaders;
static bool loplayer_event_execute_command_(
loplayer_event_t* ev, const loplayer_event_command_t* command) {
assert(ev != NULL);
/* owned objects */
glyphas_cache_t* font;
glyphas_block_t* text;
if (ev->exectime > ev->ticker->time) return false;
/* immutable params */
struct {
vec2_t fontsz;
} geometry;
};
static void loplayer_event_calculate_geometry_(loplayer_event_t* event) {
assert(event != NULL);
typeof(event->geometry)* geo = &event->geometry;
geo->fontsz = event->shaders->dpi;
vec2_muleq(&geo->fontsz, .15f);
switch (command->type) {
case LOPLAYER_EVENT_COMMAND_TYPE_NONE:
return false;
case LOPLAYER_EVENT_COMMAND_TYPE_FINALIZE:
ev->basetime = 0;
ev->executor = 0;
ev->commands = NULL;
ev->ctx = (typeof(ev->ctx)) {
.line = { .last_update = ev->ticker->time, },
};
return true;
case LOPLAYER_EVENT_COMMAND_TYPE_PLAY_MUSIC:
ev->ctx.music = (typeof(ev->ctx.music)) {
.enable = true,
.id = command->music,
.since = ev->exectime,
};
return true;
case LOPLAYER_EVENT_COMMAND_TYPE_STOP_MUSIC:
ev->ctx.music = (typeof(ev->ctx.music)) {
.enable = false,
};
return true;
case LOPLAYER_EVENT_COMMAND_TYPE_SET_AREA:
ev->ctx.area.size = command->area;
return true;
case LOPLAYER_EVENT_COMMAND_TYPE_SET_CINESCOPE:
ev->ctx.cinescope = command->cinescope;
return true;
case LOPLAYER_EVENT_COMMAND_TYPE_SET_LINE:
if (ev->ctx.line.text_id != command->line) {
ev->ctx.line.text_id = command->line;
ev->ctx.line.last_update = ev->ticker->time;
}
return true;
case LOPLAYER_EVENT_COMMAND_TYPE_WAIT:
ev->exectime += command->wait;
return true;
}
__builtin_unreachable();
}
loplayer_event_t* loplayer_event_new(
loresource_set_t* res, loshader_set_t* shaders) {
assert(res != NULL);
assert(shaders != NULL);
void loplayer_event_initialize(
loplayer_event_t* ev, const locommon_ticker_t* ticker) {
assert(ev != NULL);
assert(ticker != NULL);
loplayer_event_t* event = memory_new(sizeof(*event));
*event = (typeof(*event)) {
.res = res,
.shaders = shaders,
*ev = (typeof(*ev)) {
.ticker = ticker,
};
loplayer_event_calculate_geometry_(event);
event->font = glyphas_cache_new(
shaders->tex.event_line,
&res->font.serif,
event->geometry.fontsz.x,
event->geometry.fontsz.y);
event->text = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-event->geometry.fontsz.y,
INT32_MAX,
256);
return event;
}
void loplayer_event_delete(loplayer_event_t* event) {
if (event == NULL) return;
void loplayer_event_deinitialize(loplayer_event_t* ev) {
assert(ev != NULL);
glyphas_cache_delete(event->font);
glyphas_block_delete(event->text);
memory_delete(event);
}
loplayer_event_param_t* loplayer_event_take_control(
loplayer_event_t* event, loentity_id_t id) {
assert(event != NULL);
bool loplayer_event_execute_commands(
loplayer_event_t* ev,
loentity_id_t executor,
const locommon_position_t* pos,
const loplayer_event_command_t* commands) {
assert(ev != NULL);
assert(commands != NULL);
assert(locommon_position_valid(pos));
if (event->param.controlled) return NULL;
const bool first = ev->basetime == 0;
event->param = (typeof(event->param)) {
.controlled = true,
.controlled_by = id,
};
return &event->param;
if (first) ev->executor = executor;
if (ev->executor != executor) return false;
if (ev->commands != commands) {
if (ev->commands != NULL || first) {
ev->basetime = ev->ticker->time;
}
ev->commands = commands;
ev->itr = commands;
ev->exectime = ev->basetime;
}
ev->ctx.area.pos = *pos;
while (loplayer_event_execute_command_(ev, ev->itr)) ++ev->itr;
return true;
}
void loplayer_event_abort(loplayer_event_t* event) {
assert(event != NULL);
void loplayer_event_pack(const loplayer_event_t* ev, msgpack_packer* packer) {
assert(ev != NULL);
assert(packer != NULL);
if (!event->param.controlled) return;
loplayer_event_param_release_control(&event->param);
msgpack_pack_map(packer, CRIAL_PROPERTY_COUNT_);
CRIAL_SERIALIZER_;
}
void loplayer_event_draw(const loplayer_event_t* event) {
assert(event != NULL);
bool loplayer_event_unpack(loplayer_event_t* ev, const msgpack_object* obj) {
assert(ev != NULL);
assert(obj != NULL);
if (!event->param.controlled) return;
loshader_event_line_drawer_add_block(
&event->shaders->drawer.event_line, event->text);
const msgpack_object_map* root = mpkutil_get_map(obj);
CRIAL_DESERIALIZER_;
return true;
}
const loplayer_event_param_t* loplayer_event_get_param(
const loplayer_event_t* event) {
assert(event != NULL);
void loplayer_event_bind_position_in_area(
const loplayer_event_t* ev, locommon_position_t* pos) {
assert(ev != NULL);
assert(locommon_position_valid(pos));
if (!event->param.controlled) return NULL;
return &event->param;
loplayer_event_bind_rect_in_area(ev, pos, &vec2(0, 0));
}
void loplayer_event_param_set_line(
loplayer_event_param_t* param, const char* str, size_t len) {
assert(param != NULL);
void loplayer_event_bind_rect_in_area(
const loplayer_event_t* ev, locommon_position_t* pos, const vec2_t* size) {
assert(ev != NULL);
assert(locommon_position_valid(pos));
assert(vec2_valid(size));
loplayer_event_t* event = (typeof(event)) param;
if (ev->ctx.area.size.x <= 0 || ev->ctx.area.size.y <= 0) return;
glyphas_block_clear(event->text);
vec2_t sz;
vec2_sub(&sz, &ev->ctx.area.size, size);
if (sz.x < 0) sz.x = 0;
if (sz.y < 0) sz.y = 0;
static const vec4_t white = vec4(1, 1, 1, 1);
glyphas_block_add_characters(event->text, event->font, &white, str, len);
vec2_t v;
locommon_position_sub(&v, pos, &ev->ctx.area.pos);
static const vec2_t center = vec2(.5f, -.5f);
glyphas_block_set_origin(event->text, &center);
if (fabs(v.x) > sz.x) v.x = MATH_SIGN(v.x)*sz.x;
if (fabs(v.y) > sz.y) v.y = MATH_SIGN(v.y)*sz.y;
const vec2_t scale = vec2(
2/event->shaders->resolution.x, 2/event->shaders->resolution.y);
glyphas_block_scale(event->text, &scale);
static const vec2_t trans = vec2(0, -.85f);
glyphas_block_translate(event->text, &trans);
}
void loplayer_event_param_release_control(loplayer_event_param_t* param) {
assert(param != NULL);
assert(param->controlled);
loplayer_event_param_set_line(param, "", 0);
param->controlled = false;
*pos = ev->ctx.area.pos;
vec2_addeq(&pos->fract, &v);
locommon_position_reduce(pos);
}

15
core/loplayer/event.crial Normal file
View File

@@ -0,0 +1,15 @@
/* CRIAL
SERIALIZER_BEGIN
mpkutil_pack_str(packer, "$name");
LOCOMMON_MSGPACK_PACK_ANY(packer, &ev->$code);
END
DESERIALIZER_BEGIN
if (!LOCOMMON_MSGPACK_UNPACK_ANY(
mpkutil_get_map_item_by_str(root, "$name"), &ev->$code)) {
return false;
}
END
PROPERTY executor = executor
PROPERTY basetime = basetime
*/

View File

@@ -1,74 +1,153 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <msgpack.h>
#include "util/math/vector.h"
#include "core/locommon/position.h"
#include "core/locommon/ticker.h"
#include "core/loentity/entity.h"
#include "core/loresource/music.h"
#include "core/loresource/set.h"
#include "core/loshader/set.h"
struct loplayer_event_t;
typedef struct loplayer_event_t loplayer_event_t;
typedef enum {
LOPLAYER_EVENT_COMMAND_TYPE_NONE,
LOPLAYER_EVENT_COMMAND_TYPE_FINALIZE,
LOPLAYER_EVENT_COMMAND_TYPE_PLAY_MUSIC,
LOPLAYER_EVENT_COMMAND_TYPE_STOP_MUSIC,
LOPLAYER_EVENT_COMMAND_TYPE_SET_AREA,
LOPLAYER_EVENT_COMMAND_TYPE_SET_CINESCOPE,
LOPLAYER_EVENT_COMMAND_TYPE_SET_LINE,
LOPLAYER_EVENT_COMMAND_TYPE_WAIT,
} loplayer_event_command_type_t;
typedef struct {
bool controlled;
/* You can return the control by assigning false. */
loplayer_event_command_type_t type;
union {
loresource_music_id_t music;
loentity_id_t controlled_by;
vec2_t area;
locommon_position_t area_pos;
vec2_t area_size;
float cinescope;
bool cinescope;
bool hide_hud;
const char* line;
loresource_music_player_t* music;
} loplayer_event_param_t;
uint64_t wait;
};
} loplayer_event_command_t;
loplayer_event_t* /* OWNERSHIP */
loplayer_event_new(
loresource_set_t* res,
loshader_set_t* shaders
#define loplayer_event_command_finalize() \
(loplayer_event_command_t) { \
.type = LOPLAYER_EVENT_COMMAND_TYPE_FINALIZE, \
}
#define loplayer_event_command_play_music(id) \
(loplayer_event_command_t) { \
.type = LOPLAYER_EVENT_COMMAND_TYPE_PLAY_MUSIC, \
.music = id, \
}
#define loplayer_event_command_stop_music() \
(loplayer_event_command_t) { \
.type = LOPLAYER_EVENT_COMMAND_TYPE_STOP_MUSIC, \
}
#define loplayer_event_command_set_area(w, h) \
(loplayer_event_command_t) { \
.type = LOPLAYER_EVENT_COMMAND_TYPE_SET_AREA, \
.area = {{w, h}}, \
}
#define loplayer_event_command_set_cinescope(v) \
(loplayer_event_command_t) { \
.type = LOPLAYER_EVENT_COMMAND_TYPE_SET_CINESCOPE, \
.cinescope = v, \
}
#define loplayer_event_command_set_line(text_id) \
(loplayer_event_command_t) { \
.type = LOPLAYER_EVENT_COMMAND_TYPE_SET_LINE, \
.line = text_id, \
}
#define loplayer_event_command_wait(dur) \
(loplayer_event_command_t) { \
.type = LOPLAYER_EVENT_COMMAND_TYPE_WAIT, \
.wait = dur, \
}
typedef struct {
struct {
locommon_position_t pos;
vec2_t size;
} area;
float cinescope;
struct {
const char* text_id;
uint64_t last_update;
} line;
struct {
bool enable;
loresource_music_id_t id;
uint64_t since;
} music;
} loplayer_event_context_t;
typedef struct {
const locommon_ticker_t* ticker;
loentity_id_t executor;
uint64_t basetime;
const loplayer_event_command_t* commands;
const loplayer_event_command_t* itr;
uint64_t exectime;
loplayer_event_context_t ctx;
} loplayer_event_t;
void
loplayer_event_initialize(
loplayer_event_t* ev,
const locommon_ticker_t* ticker
);
void
loplayer_event_delete(
loplayer_event_t* event /* OWNERSHIP*/
loplayer_event_deinitialize(
loplayer_event_t* ev
);
loplayer_event_param_t* /* NULLABLE */
loplayer_event_take_control(
loplayer_event_t* event,
loentity_id_t id
bool
loplayer_event_execute_commands(
loplayer_event_t* ev,
loentity_id_t executor,
const locommon_position_t* pos,
const loplayer_event_command_t* commands /* ends with NONE */
);
void
loplayer_event_abort(
loplayer_event_t* event
loplayer_event_pack(
const loplayer_event_t* ev,
msgpack_packer* packer
);
bool
loplayer_event_unpack(
loplayer_event_t* ev,
const msgpack_object* obj
);
void
loplayer_event_draw(
const loplayer_event_t* event
);
const loplayer_event_param_t* /* NULLABLE */
loplayer_event_get_param(
const loplayer_event_t* event
loplayer_event_bind_position_in_area(
const loplayer_event_t* ev,
locommon_position_t* pos
);
void
loplayer_event_param_set_line(
loplayer_event_param_t* param,
const char* str,
size_t len
);
void
loplayer_event_param_release_control(
loplayer_event_param_t* param
loplayer_event_bind_rect_in_area(
const loplayer_event_t* ev,
locommon_position_t* pos,
const vec2_t* size
);

View File

@@ -1,409 +0,0 @@
#include "./hud.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "util/conv/charcode.h"
#include "util/glyphas/block.h"
#include "util/glyphas/cache.h"
#include "util/math/algorithm.h"
#include "util/memory/memory.h"
#include "core/locommon/easing.h"
#include "core/locommon/position.h"
#include "core/locommon/ticker.h"
#include "core/loresource/set.h"
#include "core/loresource/text.h"
#include "core/loshader/hud_bar.h"
#include "core/loshader/set.h"
#include "./entity.h"
#include "./event.h"
#include "./status.h"
typedef struct {
float alpha;
float remain;
glyphas_block_t* text;
} loplayer_hud_effect_t;
struct loplayer_hud_t {
/* injected deps */
loshader_set_t* shaders;
const locommon_ticker_t* ticker;
const loplayer_event_t* event;
const loplayer_status_t* status;
const loplayer_entity_t* entity;
/* owned objects */
struct {
glyphas_cache_t* sans;
glyphas_cache_t* serif;
} font;
/* immutable params */
struct {
vec2_t fontsz_normal_px;
vec2_t fontsz_normal;
vec2_t fontsz_small_px;
vec2_t fontsz_small;
vec2_t padding;
vec2_t madness_bar_pos;
vec2_t madness_bar_size;
vec2_t faith_bar_pos;
vec2_t faith_bar_size;
vec2_t effect_bar_pos;
vec2_t effect_bar_size;
} geometry;
/* mutable params */
float alpha;
float prev_madness;
float prev_faith;
bool shown;
glyphas_block_t* biome_text;
union {
struct {
loplayer_hud_effect_t curse;
loplayer_hud_effect_t amnesia;
loplayer_hud_effect_t lost;
};
loplayer_hud_effect_t array[3];
} effects;
};
static void loplayer_hud_calculate_geometry_(loplayer_hud_t* hud) {
assert(hud != NULL);
const vec2_t* dpi = &hud->shaders->dpi;
const vec2_t* reso = &hud->shaders->resolution;
typeof(hud->geometry)* geo = &hud->geometry;
# define px_to_disp_(v) vec2((v).x/reso->x*2, (v).y/reso->y*2)
geo->fontsz_normal_px = *dpi;
vec2_muleq(&geo->fontsz_normal_px, .4f);
geo->fontsz_normal = px_to_disp_(geo->fontsz_normal_px);
geo->fontsz_small_px = *dpi;
vec2_muleq(&geo->fontsz_small_px, .3f);
geo->fontsz_small = px_to_disp_(geo->fontsz_small_px);
geo->padding = *dpi;
vec2_muleq(&geo->padding, .4f);
geo->padding = px_to_disp_(geo->padding);
geo->madness_bar_size = vec2(.5f, .1f*dpi->y);
geo->madness_bar_size.y /= reso->y/2;
geo->madness_bar_pos = vec2(
-1 + geo->padding.x + geo->madness_bar_size.x,
1 - geo->padding.y - geo->madness_bar_size.y);
geo->faith_bar_size = vec2(.4f, .05f*dpi->y);
geo->faith_bar_size.y /= reso->y/2;
geo->faith_bar_pos = vec2(
-1 + geo->padding.x + geo->faith_bar_size.x,
1 - geo->padding.y - geo->madness_bar_size.y*2 - geo->faith_bar_size.y);
geo->effect_bar_size = vec2(3*dpi->x/reso->x, 4/reso->y);
geo->effect_bar_pos = vec2(1-geo->padding.x, -1+geo->padding.y);
vec2_subeq(&geo->effect_bar_pos, &geo->effect_bar_size);
# undef px_to_disp_
}
static glyphas_block_t* loplayer_hud_create_effect_text_block_(
const loplayer_hud_t* hud, const char* text) {
assert(hud != NULL);
glyphas_block_t* block = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-hud->geometry.fontsz_normal_px.y,
INT32_MAX,
32);
static const vec4_t white = vec4(1, 1, 1, 1);
glyphas_block_add_characters(
block, hud->font.sans, &white, text, strlen(text));
const vec2_t scale = vec2(
2/hud->shaders->resolution.x, 2/hud->shaders->resolution.y);
glyphas_block_scale(block, &scale);
return block;
}
static void loplayer_hud_initialize_effect_text_(
loplayer_hud_t* hud, loresource_set_t* res) {
assert(hud != NULL);
# define init_effect_text_(name) \
hud->effects.name.text = loplayer_hud_create_effect_text_block_( \
hud, loresource_text_get(res->lang, "effect_"#name))
init_effect_text_(curse);
init_effect_text_(amnesia);
init_effect_text_(lost);
# undef init_effect_
}
static void loplayer_hud_update_bars_(loplayer_hud_t* hud) {
assert(hud != NULL);
const float d = hud->ticker->delta_f;
locommon_easing_smooth_float(
&hud->prev_madness, hud->status->recipient.madness, d*2);
locommon_easing_smooth_float(
&hud->prev_faith, hud->status->recipient.faith, d*2);
}
static void loplayer_hud_update_lasting_effect_(
loplayer_hud_t* hud,
loplayer_hud_effect_t* e,
const loeffect_generic_lasting_param_t* p) {
assert(hud != NULL);
assert(e != NULL);
assert(p != NULL);
const uint64_t end = p->begin + p->duration;
const uint64_t t = hud->ticker->time;
if (p->duration == 0 || end <= t || p->begin > t) {
e->remain = 0;
return;
}
e->remain = 1 - (t - p->begin)*1.f/p->duration;
}
static void loplayer_hud_update_effects_(loplayer_hud_t* hud) {
assert(hud != NULL);
const float d = hud->ticker->delta_f;
loplayer_hud_update_lasting_effect_(
hud, &hud->effects.curse, &hud->status->recipient.effects.curse);
loplayer_hud_update_lasting_effect_(
hud, &hud->effects.amnesia, &hud->status->recipient.effects.amnesia);
hud->effects.lost.remain =
hud->status->recipient.faith > 0? 0: (1-hud->status->recipient.madness);
static const size_t len =
sizeof(hud->effects.array)/sizeof(hud->effects.array[0]);
for (size_t i = 0; i < len; ++i) {
loplayer_hud_effect_t* e = &hud->effects.array[i];
locommon_easing_linear_float(&e->alpha, !!(e->remain > 0), d*2);
}
}
static void loplayer_hud_draw_bars_(const loplayer_hud_t* hud) {
assert(hud != NULL);
const typeof(hud->geometry)* geo = &hud->geometry;
const loshader_hud_bar_drawer_instance_t madness = {
.pos = geo->madness_bar_pos,
.size = geo->madness_bar_size,
.bgcolor = vec4(0, 0, 0, 1*hud->alpha),
.fgcolor = vec4(1, 1, 1, .9f*hud->alpha),
.value = MATH_CLAMP(hud->status->recipient.madness, 0, 1),
.prev_value = MATH_CLAMP(hud->prev_madness, 0, 1),
};
loshader_hud_bar_drawer_add_instance(hud->shaders->drawer.hud_bar, &madness);
const loshader_hud_bar_drawer_instance_t faith = {
.pos = geo->faith_bar_pos,
.size = geo->faith_bar_size,
.bgcolor = vec4(0, 0, 0, 1*hud->alpha),
.fgcolor = vec4(.9f, .9f, .9f, .9f*hud->alpha),
.value = MATH_CLAMP(hud->status->recipient.faith, 0, 1),
.prev_value = MATH_CLAMP(hud->prev_faith, 0, 1),
};
loshader_hud_bar_drawer_add_instance(hud->shaders->drawer.hud_bar, &faith);
}
static void loplayer_hud_draw_effects_(const loplayer_hud_t* hud) {
assert(hud != NULL);
const typeof(hud->geometry)* geo = &hud->geometry;
const float lineheight = hud->geometry.fontsz_normal.y;
float h = 0;
static const size_t len =
sizeof(hud->effects.array)/sizeof(hud->effects.array[0]);
for (size_t i = 0; i < len; ++i) {
const loplayer_hud_effect_t* e = &hud->effects.array[i];
if (e->alpha == 0) continue;
const float y = h*lineheight + geo->effect_bar_pos.y;
static const vec2_t origin = vec2(1, -1);
glyphas_block_set_origin(e->text, &origin);
const vec2_t trans = vec2(1-geo->padding.x, y);
glyphas_block_translate(e->text, &trans);
glyphas_block_set_alpha(e->text, e->alpha);
loshader_hud_text_drawer_add_block(
&hud->shaders->drawer.hud_text, e->text);
const loshader_hud_bar_drawer_instance_t instance = {
.pos = vec2(geo->effect_bar_pos.x, y),
.size = vec2(-geo->effect_bar_size.x, geo->effect_bar_size.y),
.bgcolor = vec4(1, 1, 1, .2f*hud->alpha),
.fgcolor = vec4(1, 1, 1, .8f*hud->alpha),
.value = MATH_CLAMP(e->remain, 0, 1),
.prev_value = MATH_CLAMP(e->remain, 0, 1),
};
loshader_hud_bar_drawer_add_instance(
hud->shaders->drawer.hud_bar, &instance);
h += e->alpha * e->alpha * (3-2*e->alpha);
}
}
loplayer_hud_t* loplayer_hud_new(
loresource_set_t* res,
loshader_set_t* shaders,
const locommon_ticker_t* ticker,
const loplayer_event_t* event,
const loplayer_status_t* status,
const loplayer_entity_t* entity) {
assert(res != NULL);
assert(shaders != NULL);
assert(ticker != NULL);
assert(event != NULL);
assert(status != NULL);
assert(entity != NULL);
loplayer_hud_t* hud = memory_new(sizeof(*hud));
*hud = (typeof(*hud)) {
.shaders = shaders,
.ticker = ticker,
.event = event,
.status = status,
.entity = entity,
};
loplayer_hud_calculate_geometry_(hud);
hud->font = (typeof(hud->font)) {
.sans = glyphas_cache_new(
shaders->tex.hud_text,
&res->font.sans,
hud->geometry.fontsz_normal_px.x,
hud->geometry.fontsz_normal_px.y),
.serif = glyphas_cache_new(
shaders->tex.hud_text,
&res->font.serif,
hud->geometry.fontsz_small_px.x,
hud->geometry.fontsz_small_px.y),
};
hud->biome_text = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-hud->geometry.fontsz_normal_px.y,
INT32_MAX,
32);
loplayer_hud_initialize_effect_text_(hud, res);
return hud;
}
void loplayer_hud_delete(loplayer_hud_t* hud) {
if (hud == NULL) return;
static const size_t len =
sizeof(hud->effects.array) / sizeof(hud->effects.array[0]);
for (size_t i = 0; i < len; ++i) {
glyphas_block_delete(hud->effects.array[i].text);
}
glyphas_block_delete(hud->biome_text);
glyphas_cache_delete(hud->font.sans);
glyphas_cache_delete(hud->font.serif);
memory_delete(hud);
}
void loplayer_hud_show(loplayer_hud_t* hud) {
assert(hud != NULL);
hud->shown = true;
}
void loplayer_hud_hide(loplayer_hud_t* hud) {
assert(hud != NULL);
hud->shown = false;
}
void loplayer_hud_set_biome_text(loplayer_hud_t* hud, const char* text) {
assert(hud != NULL);
glyphas_block_clear(hud->biome_text);
static const vec4_t white = vec4(1, 1, 1, 1);
glyphas_block_add_characters(
hud->biome_text, hud->font.serif, &white, text, strlen(text));
glyphas_block_set_origin(hud->biome_text, &vec2(0, -1));
const vec2_t scale = vec2(
2/hud->shaders->resolution.x, 2/hud->shaders->resolution.y);
glyphas_block_scale(hud->biome_text, &scale);
const typeof(hud->geometry)* geo = &hud->geometry;
glyphas_block_translate(
hud->biome_text, &vec2(-1+geo->padding.x, -1+geo->padding.y));
glyphas_block_make_glitched(hud->biome_text, hud->ticker->time);
}
void loplayer_hud_update(loplayer_hud_t* hud) {
assert(hud != NULL);
bool shown = hud->shown;
const loplayer_event_param_t* e = loplayer_event_get_param(hud->event);
if (e != NULL) {
shown = shown && !e->hide_hud;
}
const float dt = hud->ticker->delta_f;
locommon_easing_smooth_float(&hud->alpha, !!shown, dt*2);
loplayer_hud_update_bars_(hud);
loplayer_hud_update_effects_(hud);
}
void loplayer_hud_draw_ui(const loplayer_hud_t* hud) {
assert(hud != NULL);
hud->shaders->drawer.hud_text.alpha = hud->alpha;
loplayer_hud_draw_bars_(hud);
loplayer_hud_draw_effects_(hud);
loshader_hud_text_drawer_add_block(
&hud->shaders->drawer.hud_text, hud->biome_text);
}

View File

@@ -1,53 +0,0 @@
#pragma once
#include "core/locommon/ticker.h"
#include "core/loresource/set.h"
#include "core/loshader/set.h"
#include "./entity.h"
#include "./event.h"
#include "./status.h"
struct loplayer_hud_t;
typedef struct loplayer_hud_t loplayer_hud_t;
loplayer_hud_t*
loplayer_hud_new(
loresource_set_t* resource,
loshader_set_t* shaders,
const locommon_ticker_t* ticker,
const loplayer_event_t* event,
const loplayer_status_t* status,
const loplayer_entity_t* entity
);
void
loplayer_hud_delete(
loplayer_hud_t* hud
);
void
loplayer_hud_show(
loplayer_hud_t* hud
);
void
loplayer_hud_hide(
loplayer_hud_t* hud
);
void
loplayer_hud_set_biome_text(
loplayer_hud_t* hud,
const char* text
);
void
loplayer_hud_update(
loplayer_hud_t* hud
);
void
loplayer_hud_draw_ui(
const loplayer_hud_t* hud
);

View File

@@ -1,565 +0,0 @@
#include "./menu.h"
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "util/glyphas/block.h"
#include "util/glyphas/cache.h"
#include "util/math/algorithm.h"
#include "util/math/constant.h"
#include "util/memory/memory.h"
#include "util/mpkutil/get.h"
#include "util/mpkutil/pack.h"
#include "core/locommon/easing.h"
#include "core/locommon/ticker.h"
#include "core/loeffect/stance.h"
#include "core/loresource/set.h"
#include "core/loresource/text.h"
#include "core/loshader/set.h"
#include "./controller.h"
#include "./entity.h"
#include "./status.h"
typedef struct {
glyphas_block_t* name;
glyphas_block_t* desc;
glyphas_block_t* note;
} loplayer_menu_stance_text_set_t;
typedef enum {
LOPLAYER_MENU_STATE_HIDDEN,
LOPLAYER_MENU_STATE_STATUS,
LOPLAYER_MENU_STATE_POPUP,
} loplayer_menu_state_t;
struct loplayer_menu_t {
/* injected deps */
loresource_set_t* res;
loshader_set_t* shaders;
const locommon_ticker_t* ticker;
const loplayer_status_t* status;
const loplayer_controller_t* controller;
/* owned objects */
struct {
glyphas_cache_t* large;
glyphas_cache_t* normal;
glyphas_cache_t* small;
} font;
struct {
loplayer_menu_stance_text_set_t stance[LOEFFECT_STANCE_ID_LENGTH_+1];
/* [0] is for unknown stance */
glyphas_block_t* popup_title;
glyphas_block_t* popup_body;
glyphas_block_t* exit;
} text;
/* immutable params */
struct {
vec2_t fontsz_large;
vec2_t fontsz_normal;
vec2_t fontsz_small;
vec2_t fontsz_large_px;
vec2_t fontsz_normal_px;
vec2_t fontsz_small_px;
vec2_t range;
vec2_t icon;
vec2_t padding;
} geometry;
/* mutable params */
loplayer_menu_state_t state;
float alpha;
bool stance_hovering;
size_t display_stance_text_index;
float stance_highlight;
size_t highlighted_stance;
bool request_exit;
};
static void loplayer_menu_calculate_geometry_(loplayer_menu_t* menu) {
assert(menu != NULL);
const vec2_t* dpi = &menu->shaders->dpi;
const vec2_t* reso = &menu->shaders->resolution;
typeof(menu->geometry)* geo = &menu->geometry;
geo->fontsz_large_px = *dpi;
vec2_muleq(&geo->fontsz_large_px, .2f);
geo->fontsz_normal_px = *dpi;
vec2_muleq(&geo->fontsz_normal_px, .15f);
geo->fontsz_small_px = *dpi;
vec2_muleq(&geo->fontsz_small_px, .12f);
# define px_to_disp_(v) vec2((v).x/reso->x*2, (v).y/reso->y*2)
geo->fontsz_large = px_to_disp_(geo->fontsz_large_px);
geo->fontsz_normal = px_to_disp_(geo->fontsz_normal_px);
geo->fontsz_small = px_to_disp_(geo->fontsz_small_px);
geo->range = *dpi;
vec2_muleq(&geo->range, 2);
geo->range = px_to_disp_(geo->range);
vec2_diveq(&geo->range, MATH_MAX(geo->range.x, 1));
vec2_diveq(&geo->range, MATH_MAX(geo->range.y, 1));
geo->icon = *dpi;
vec2_muleq(&geo->icon, .4f);
geo->icon = px_to_disp_(geo->icon);
geo->padding = *dpi;
vec2_muleq(&geo->padding, .2f);
geo->padding = px_to_disp_(geo->padding);
# undef px_to_disp_
}
static void loplayer_menu_initialize_stance_text_set_(
const loplayer_menu_t* menu,
loplayer_menu_stance_text_set_t* set,
const char* name,
const char* desc,
const char* note) {
assert(menu != NULL);
assert(set != NULL);
const typeof(menu->geometry)* geo = &menu->geometry;
*set = (typeof(*set)) {
.name = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-geo->fontsz_large_px.y,
INT32_MAX,
32),
.desc = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-geo->fontsz_normal_px.y*1.3f,
geo->fontsz_normal_px.x*18,
512),
.note = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-geo->fontsz_small_px.y*1.1f,
geo->fontsz_normal_px.x*18,
512),
};
static const vec4_t white = vec4(1, 1, 1, 1);
static const vec4_t yellow = vec4(1, .7f, 0, 1);
glyphas_block_add_characters(
set->name, menu->font.large, &white, name, strlen(name));
glyphas_block_add_characters(
set->desc, menu->font.normal, &white, desc, strlen(desc));
glyphas_block_add_characters(
set->note, menu->font.small, &yellow, note, strlen(note));
glyphas_block_set_origin(set->name, &vec2(.5f, -1));
glyphas_block_set_origin(set->desc, &vec2(.5f, -.5f));
glyphas_block_set_origin(set->note, &vec2(.5f, 0));
const vec2_t scale =
vec2(2/menu->shaders->resolution.x, 2/menu->shaders->resolution.y);
glyphas_block_scale(set->name, &scale);
glyphas_block_scale(set->desc, &scale);
glyphas_block_scale(set->note, &scale);
vec2_t offset, size;
glyphas_block_calculate_geometry(set->desc, &offset, &size);
glyphas_block_translate(set->name, &vec2(0, size.y/2 * 1.1f));
glyphas_block_translate(set->note, &vec2(0, -size.y/2 * 1.2f));
}
static void loplayer_menu_deinitialize_stance_text_set_(
loplayer_menu_stance_text_set_t* set) {
assert(set != NULL);
glyphas_block_delete(set->name);
glyphas_block_delete(set->desc);
glyphas_block_delete(set->note);
}
static void loplayer_menu_create_stance_text_(loplayer_menu_t* menu) {
assert(menu != NULL);
# define text_(name) loresource_text_get(menu->res->lang, name)
loplayer_menu_initialize_stance_text_set_(
menu, &menu->text.stance[0], "???", "???", "");
# define init_stance_set_(NAME, name) \
loplayer_menu_initialize_stance_text_set_( \
menu, \
&menu->text.stance[LOEFFECT_STANCE_ID_##NAME+1], \
text_("stance_"#name"_name"), \
text_("stance_"#name"_desc"), \
text_("stance_"#name"_note"))
LOEFFECT_STANCE_EACH(init_stance_set_);
# undef init_stance_set_
# undef text_
}
static void loplayer_menu_create_exit_text_(loplayer_menu_t* menu) {
assert(menu != NULL);
menu->text.exit = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-menu->geometry.fontsz_normal_px.y,
INT32_MAX,
32);
const char* text = loresource_text_get(menu->res->lang, "menu_exit");
static const vec4_t white = vec4(1, 1, 1, 1);
glyphas_block_add_characters(
menu->text.exit, menu->font.normal, &white, text, strlen(text));
glyphas_block_set_origin(menu->text.exit, &vec2(.5f, -1));
const vec2_t scale =
vec2(2/menu->shaders->resolution.x, 2/menu->shaders->resolution.y);
glyphas_block_scale(menu->text.exit, &scale);
glyphas_block_translate(menu->text.exit, &vec2(0, -1+menu->geometry.padding.y));
}
static void loplayer_menu_build_popup_text_(
loplayer_menu_t* menu, const char* title, const char* text) {
assert(menu != NULL);
glyphas_block_clear(menu->text.popup_title);
glyphas_block_clear(menu->text.popup_body);
static const vec4_t white = vec4(1, 1, 1, 1);
glyphas_block_add_characters(
menu->text.popup_title, menu->font.large, &white, title, strlen(title));
glyphas_block_add_characters(
menu->text.popup_body, menu->font.normal, &white, text, strlen(text));
static const vec2_t bottom = vec2(.5f, -1);
static const vec2_t center = vec2(.5f, -.5f);
glyphas_block_set_origin(menu->text.popup_title, &bottom);
glyphas_block_set_origin(menu->text.popup_body, &center);
const vec2_t scale =
vec2(2/menu->shaders->resolution.x, 2/menu->shaders->resolution.y);
glyphas_block_scale(menu->text.popup_title, &scale);
glyphas_block_scale(menu->text.popup_body, &scale);
vec2_t offset, size;
glyphas_block_calculate_geometry(menu->text.popup_body, &offset, &size);
const vec2_t upper = vec2(0, size.y/2*1.1f);
glyphas_block_translate(menu->text.popup_title, &upper);
}
static bool loplayer_menu_find_stance_icon_at_(
const loplayer_menu_t* menu, size_t* index, const vec2_t* pos) {
assert(menu != NULL);
assert(index != NULL);
assert(vec2_valid(pos));
const typeof(menu->geometry)* geo = &menu->geometry;
for (size_t i = 0; i < 5; ++i) {
const float theta = MATH_PI*2/5 * i + MATH_PI/2;
const vec2_t p =
vec2(geo->range.x*cos(theta), geo->range.y*sin(theta));
vec2_t diff = *pos;
vec2_subeq(&diff, &p);
diff.x /= geo->icon.x;
diff.y /= geo->icon.y;
const float manhattan = MATH_ABS(diff.x) + MATH_ABS(diff.y);
if (manhattan < 1) {
*index = i;
return true;
}
}
return false;
}
static void loplayer_menu_update_status_(loplayer_menu_t* menu) {
assert(menu != NULL);
if (menu->alpha != 1) return;
locommon_easing_linear_float(
&menu->stance_highlight, 0, menu->ticker->delta_f);
size_t hovered;
menu->stance_hovering = loplayer_menu_find_stance_icon_at_(
menu, &hovered, &menu->controller->cursor);
if (menu->stance_hovering) {
const bool taken =
loeffect_stance_set_has(&menu->status->stances, hovered);
menu->display_stance_text_index = taken? hovered+1: 0;
}
const typeof(menu->geometry)* geo = &menu->geometry;
if (menu->controller->cursor.y < -1+geo->fontsz_normal.y+geo->padding.y) {
if (menu->controller->prev.buttons & LOCOMMON_INPUT_BUTTON_ATTACK) {
menu->request_exit = true;
}
}
}
static void loplayer_menu_update_popup_(loplayer_menu_t* menu) {
assert(menu != NULL);
}
static void loplayer_menu_draw_stance_icons_(const loplayer_menu_t* menu) {
assert(menu != NULL);
const typeof(menu->geometry)* geo = &menu->geometry;
for (size_t i = 0; i < 5; ++i) {
const bool taken =
loeffect_stance_set_has(&menu->status->stances, i);
const float theta = MATH_PI*2/5 * i + MATH_PI/2;
const loshader_menu_stance_drawer_instance_t instance = {
.id = taken?
loeffect_stance_get_id_for_menu_shader(i):
LOSHADER_MENU_STANCE_ID_EMPTY,
.pos = vec2(geo->range.x*cos(theta), geo->range.y*sin(theta)),
.size = geo->icon,
.alpha = menu->alpha,
.highlight = menu->stance_highlight * !!(menu->highlighted_stance == i),
};
loshader_menu_stance_drawer_add_instance(
menu->shaders->drawer.menu_stance, &instance);
}
}
static void loplayer_menu_draw_status_(const loplayer_menu_t* menu) {
assert(menu != NULL);
loplayer_menu_draw_stance_icons_(menu);
if (menu->stance_hovering) {
const loplayer_menu_stance_text_set_t* set =
&menu->text.stance[menu->display_stance_text_index];
loshader_menu_text_drawer_add_block(
&menu->shaders->drawer.menu_text, set->name);
loshader_menu_text_drawer_add_block(
&menu->shaders->drawer.menu_text, set->desc);
loshader_menu_text_drawer_add_block(
&menu->shaders->drawer.menu_text, set->note);
}
loshader_menu_text_drawer_add_block(
&menu->shaders->drawer.menu_text, menu->text.exit);
}
static void loplayer_menu_draw_popup_(const loplayer_menu_t* menu) {
assert(menu != NULL);
loshader_menu_text_drawer_add_block(
&menu->shaders->drawer.menu_text, menu->text.popup_title);
loshader_menu_text_drawer_add_block(
&menu->shaders->drawer.menu_text, menu->text.popup_body);
}
loplayer_menu_t* loplayer_menu_new(
loresource_set_t* res,
loshader_set_t* shaders,
const locommon_ticker_t* ticker,
const loplayer_status_t* status,
const loplayer_controller_t* controller) {
assert(res != NULL);
assert(shaders != NULL);
assert(ticker != NULL);
assert(status != NULL);
assert(controller != NULL);
loplayer_menu_t* menu = memory_new(sizeof(*menu));
*menu = (typeof(*menu)) {
.res = res,
.shaders = shaders,
.ticker = ticker,
.status = status,
.controller = controller,
};
loplayer_menu_calculate_geometry_(menu);
menu->font = (typeof(menu->font)) {
.large = glyphas_cache_new(
shaders->tex.menu_text,
&res->font.sans,
menu->geometry.fontsz_large_px.x,
menu->geometry.fontsz_large_px.y),
.normal = glyphas_cache_new(
shaders->tex.menu_text,
&res->font.sans,
menu->geometry.fontsz_normal_px.x,
menu->geometry.fontsz_normal_px.y),
.small = glyphas_cache_new(
shaders->tex.menu_text,
&res->font.serif,
menu->geometry.fontsz_small_px.x,
menu->geometry.fontsz_small_px.y),
};
menu->text = (typeof(menu->text)) {
.popup_title = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-menu->geometry.fontsz_large_px.y,
INT32_MAX,
32),
.popup_body = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
-menu->geometry.fontsz_normal_px.y*1.3f,
menu->geometry.fontsz_normal_px.x*18,
512),
};
loplayer_menu_create_stance_text_(menu);
loplayer_menu_create_exit_text_(menu);
return menu;
}
void loplayer_menu_delete(loplayer_menu_t* menu) {
if (menu == NULL) return;
static const size_t len =
sizeof(menu->text.stance)/sizeof(menu->text.stance[0]);
for (size_t i = 0; i < len; ++i) {
loplayer_menu_deinitialize_stance_text_set_(&menu->text.stance[i]);
}
glyphas_block_delete(menu->text.popup_title);
glyphas_block_delete(menu->text.popup_body);
glyphas_block_delete(menu->text.exit);
glyphas_cache_delete(menu->font.large);
glyphas_cache_delete(menu->font.normal);
glyphas_cache_delete(menu->font.small);
memory_delete(menu);
}
void loplayer_menu_show_status(loplayer_menu_t* menu) {
assert(menu != NULL);
menu->state = LOPLAYER_MENU_STATE_STATUS;
menu->stance_hovering = false;
menu->stance_highlight = 0;
}
void loplayer_menu_show_status_with_stance_highlighted(
loplayer_menu_t* menu, loeffect_stance_id_t id) {
assert(menu != NULL);
loplayer_menu_show_status(menu);
menu->stance_highlight = 1;
menu->highlighted_stance = id;
}
void loplayer_menu_popup(
loplayer_menu_t* menu, const char* title, const char* text) {
assert(menu != NULL);
menu->state = LOPLAYER_MENU_STATE_POPUP;
loplayer_menu_build_popup_text_(menu, title, text);
}
void loplayer_menu_hide(loplayer_menu_t* menu) {
assert(menu != NULL);
menu->state = LOPLAYER_MENU_STATE_HIDDEN;
}
void loplayer_menu_update(loplayer_menu_t* menu) {
assert(menu != NULL);
if (menu->state == LOPLAYER_MENU_STATE_HIDDEN) {
locommon_easing_linear_float(&menu->alpha, 0, menu->ticker->delta_f*2);
return;
}
locommon_easing_linear_float(&menu->alpha, 1, menu->ticker->delta_f*2);
switch (menu->state) {
case LOPLAYER_MENU_STATE_STATUS:
loplayer_menu_update_status_(menu);
return;
case LOPLAYER_MENU_STATE_POPUP:
loplayer_menu_update_popup_(menu);
return;
default:
assert(false);
}
}
void loplayer_menu_draw_ui(const loplayer_menu_t* menu) {
assert(menu != NULL);
loshader_menu_background_drawer_set_alpha(
menu->shaders->drawer.menu_background, menu->alpha);
menu->shaders->drawer.menu_text.alpha = menu->alpha;
switch (menu->state) {
case LOPLAYER_MENU_STATE_HIDDEN:
return;
case LOPLAYER_MENU_STATE_STATUS:
loplayer_menu_draw_status_(menu);
return;
case LOPLAYER_MENU_STATE_POPUP:
loplayer_menu_draw_popup_(menu);
return;
default:
assert(false);
}
}
bool loplayer_menu_is_shown(const loplayer_menu_t* menu) {
assert(menu != NULL);
return menu->state != LOPLAYER_MENU_STATE_HIDDEN;
}
bool loplayer_menu_is_exit_requested(const loplayer_menu_t* menu) {
assert(menu != NULL);
return menu->request_exit;
}
void loplayer_menu_pack(const loplayer_menu_t* menu, msgpack_packer* packer) {
assert(menu != NULL);
assert(packer != NULL);
mpkutil_pack_bool(packer, menu->state != LOPLAYER_MENU_STATE_HIDDEN);
}
bool loplayer_menu_unpack(loplayer_menu_t* menu, const msgpack_object* obj) {
assert(menu != NULL);
bool shown = false;
if (!mpkutil_get_bool(obj, &shown)) return false;
if (shown) loplayer_menu_show_status(menu);
return true;
}

View File

@@ -1,86 +0,0 @@
#pragma once
#include <stdbool.h>
#include <msgpack.h>
#include "core/locommon/ticker.h"
#include "core/loeffect/stance.h"
#include "core/loresource/set.h"
#include "core/loshader/set.h"
#include "./controller.h"
#include "./entity.h"
#include "./status.h"
struct loplayer_menu_t;
typedef struct loplayer_menu_t loplayer_menu_t;
loplayer_menu_t* /* OWNERSHIP */
loplayer_menu_new(
loresource_set_t* res,
loshader_set_t* shaders,
const locommon_ticker_t* ticker,
const loplayer_status_t* status,
const loplayer_controller_t* controller
);
void
loplayer_menu_delete(
loplayer_menu_t* menu /* OWNERSHIP */
);
void
loplayer_menu_show_status(
loplayer_menu_t* menu
);
void
loplayer_menu_show_status_with_stance_highlighted(
loplayer_menu_t* menu,
loeffect_stance_id_t id
);
void
loplayer_menu_popup(
loplayer_menu_t* menu,
const char* title,
const char* text
);
void
loplayer_menu_hide(
loplayer_menu_t* menu
);
void
loplayer_menu_update(
loplayer_menu_t* menu
);
void
loplayer_menu_draw_ui(
const loplayer_menu_t* menu
);
bool
loplayer_menu_is_shown(
const loplayer_menu_t* menu
);
bool
loplayer_menu_is_exit_requested(
const loplayer_menu_t* menu
);
void
loplayer_menu_pack(
const loplayer_menu_t* menu,
msgpack_packer* packer
);
bool
loplayer_menu_unpack(
loplayer_menu_t* menu,
const msgpack_object* obj /* NULLABLE */
);

View File

@@ -1,183 +1,60 @@
#include "./player.h"
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <msgpack.h>
#include <msgpack/sbuffer.h>
#include "util/math/matrix.h"
#include "util/mpkutil/get.h"
#include "util/mpkutil/pack.h"
#include "core/lobullet/pool.h"
#include "core/locommon/input.h"
#include "core/locommon/position.h"
#include "core/locommon/screen.h"
#include "core/locommon/ticker.h"
#include "core/loentity/entity.h"
#include "core/loentity/store.h"
#include "core/loshader/hud_bar.h"
#include "core/loshader/posteffect.h"
#include "core/loshader/set.h"
#include "./action.h"
#include "./camera.h"
#include "./combat.h"
#include "./controller.h"
#include "./entity.h"
#include "./event.h"
#include "./hud.h"
#include "./menu.h"
#include "./status.h"
#define LOPLAYER_COMBAT_ATTACK_RESERVE 32
/* generated serializer */
#include "core/loplayer/crial/player.h"
void loplayer_initialize(
loplayer_t* player,
loentity_id_t id,
loresource_set_t* res,
loshader_set_t* shaders,
const locommon_screen_t* screen,
const locommon_ticker_t* ticker,
lobullet_pool_t* bullets,
loentity_store_t* entities,
const mat4_t* proj) {
loentity_store_t* entities) {
assert(player != NULL);
assert(res != NULL);
assert(shaders != NULL);
assert(ticker != NULL);
assert(bullets != NULL);
assert(entities != NULL);
assert(mat4_valid(proj));
*player = (typeof(*player)) {
.shaders = shaders,
};
*player = (typeof(*player)) {0};
player->event = loplayer_event_new(
res,
shaders);
loplayer_stance_set_initialize(&player->stances);
loplayer_stance_set_add(&player->stances, LOPLAYER_STANCE_MISSIONARY);
loplayer_status_initialize(&player->status, res, ticker);
loplayer_entity_initialize(
&player->entity,
id,
res->sound,
shaders->drawer.character,
ticker,
entities,
player->event,
&player->status);
loentity_store_add(entities, &player->entity.super.super);
player->combat = loplayer_combat_new(
res->sound,
shaders->drawer.combat_ring,
ticker,
entities,
&player->status,
&player->entity,
LOPLAYER_COMBAT_ATTACK_RESERVE);
loplayer_controller_initialize(&player->controller);
loplayer_camera_initialize(
&player->camera,
shaders,
ticker,
player->event,
&player->status,
&player->entity,
proj);
player->hud = loplayer_hud_new(
res,
shaders,
ticker,
player->event,
&player->status,
&player->entity);
player->menu = loplayer_menu_new(
res,
shaders,
ticker,
&player->status,
&player->controller);
player->action = loplayer_action_new(
res,
ticker,
bullets,
entities,
player->event,
&player->status,
&player->entity,
player->combat,
&player->controller,
&player->camera,
player->hud,
player->menu);
loplayer_combat_initialize(&player->combat, ticker, entities);
loplayer_controller_initialize(&player->controller, ticker);
loplayer_camera_initialize(&player->camera, screen, ticker);
loplayer_event_initialize(&player->event, ticker);
loplayer_popup_initialize(&player->popup);
}
void loplayer_deinitialize(loplayer_t* player) {
assert(player != NULL);
loplayer_action_delete(player->action);
loplayer_menu_delete(player->menu);
loplayer_hud_delete(player->hud);
loplayer_popup_deinitialize(&player->popup);
loplayer_event_deinitialize(&player->event);
loplayer_camera_deinitialize(&player->camera);
loplayer_controller_deinitialize(&player->controller);
loplayer_combat_deinitialize(&player->combat);
loplayer_combat_delete(player->combat);
loplayer_entity_deinitialize(&player->entity);
loplayer_status_deinitialize(&player->status);
loplayer_event_delete(player->event);
}
void loplayer_popup(loplayer_t* player, const char* title, const char* text) {
assert(player != NULL);
loplayer_action_start_menu_popup_state(player->action);
loplayer_menu_popup(player->menu, title, text);
}
bool loplayer_attack(
loplayer_t* player, const loplayer_combat_attack_t* attack) {
assert(player != NULL);
assert(attack != NULL);
return loplayer_combat_add_attack(player->combat, attack);
}
void loplayer_touch_encephalon(loplayer_t* player) {
assert(player != NULL);
loplayer_status_set_respawn_position(
&player->status, &player->entity.super.super.pos);
loplayer_status_reset(&player->status);
}
void loplayer_gain_stance(loplayer_t* player, loeffect_stance_id_t id) {
assert(player != NULL);
if (!loplayer_status_add_stance(&player->status, id)) return;
loplayer_action_start_menu_popup_state(player->action);
loplayer_menu_show_status_with_stance_highlighted(player->menu, id);
}
void loplayer_gain_faith(loplayer_t* player, float amount) {
assert(player != NULL);
player->status.recipient.faith += amount;
loplayer_stance_set_deinitialize(&player->stances);
}
void loplayer_update(
@@ -185,104 +62,29 @@ void loplayer_update(
const locommon_input_t* input,
const locommon_position_t* cursor) {
assert(player != NULL);
assert(input != NULL);
assert(locommon_position_valid(cursor));
loplayer_status_update(&player->status);
/* entity is updated through entity store. */
loplayer_controller_handle_input(&player->controller, input, cursor);
loplayer_combat_update(player->combat);
loplayer_camera_update(&player->camera);
loplayer_hud_update(player->hud);
loplayer_menu_update(player->menu);
loplayer_controller_update(&player->controller, input, cursor);
loplayer_action_execute(player->action);
}
void loplayer_draw(const loplayer_t* player) {
assert(player != NULL);
loplayer_camera_draw(&player->camera);
loplayer_event_draw(player->event);
loplayer_combat_draw_ui(player->combat);
loplayer_hud_draw_ui(player->hud);
loplayer_menu_draw_ui(player->menu);
loplayer_combat_drop_dead_attack(&player->combat);
}
void loplayer_pack(const loplayer_t* player, msgpack_packer* packer) {
assert(player != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, 6);
mpkutil_pack_str(packer, "status");
loplayer_status_pack(&player->status, packer);
msgpack_pack_map(packer, CRIAL_PROPERTY_COUNT_+1);
CRIAL_SERIALIZER_;
mpkutil_pack_str(packer, "entity");
loplayer_entity_pack(&player->entity, packer);
mpkutil_pack_str(packer, "combat");
loplayer_combat_pack(player->combat, packer);
mpkutil_pack_str(packer, "camera");
loplayer_camera_pack(&player->camera, packer);
mpkutil_pack_str(packer, "menu");
loplayer_menu_pack(player->menu, packer);
mpkutil_pack_str(packer, "action");
loplayer_action_pack(player->action, packer);
loentity_pack((const loentity_t*) player->entity, packer);
}
bool loplayer_unpack(loplayer_t* player, const msgpack_object* obj) {
assert(player != NULL);
if (obj == NULL) return false;
const msgpack_object_map* root = mpkutil_get_map(obj);
if (root == NULL) return false;
# define item_(v) mpkutil_get_map_item_by_str(root, v)
if (!loplayer_status_unpack(&player->status, item_("status")) ||
!loplayer_entity_unpack(&player->entity, item_("entity")) ||
!loplayer_combat_unpack( player->combat, item_("combat")) ||
!loplayer_camera_unpack(&player->camera, item_("camera")) ||
!loplayer_menu_unpack ( player->menu, item_("menu" )) ||
!loplayer_action_unpack( player->action, item_("action"))) {
return false;
}
# undef item_
CRIAL_DESERIALIZER_;
return true;
}
void loplayer_test_packing(const loplayer_t* player) {
assert(player != NULL);
msgpack_sbuffer buf;
msgpack_sbuffer_init(&buf);
msgpack_packer pk;
msgpack_packer_init(&pk, &buf, msgpack_sbuffer_write);
loplayer_pack(player, &pk);
msgpack_unpacked upk;
msgpack_unpacked_init(&upk);
size_t off = 0;
const msgpack_unpack_return r =
msgpack_unpack_next(&upk, buf.data, buf.size, &off);
if (r != MSGPACK_UNPACK_SUCCESS) {
fprintf(stderr, "player: invalid msgpack format\n");
abort();
}
if (!loplayer_unpack((loplayer_t*) player, &upk.data)) {
fprintf(stderr, "player: failed to unpack\n");
abort();
}
printf("player: packing test passed\n");
msgpack_unpacked_destroy(&upk);
msgpack_sbuffer_destroy(&buf);
}

View File

@@ -0,0 +1,14 @@
/* CRIAL
SERIALIZER_BEGIN
mpkutil_pack_str(packer, "$name");
$code_pack(&player->$name, packer);
END
DESERIALIZER_BEGIN
if (!$code_unpack(
&player->$name, mpkutil_get_map_item_by_str(root, "$name"))) {
return false;
}
END
PROPERTY stances = loplayer_stance_set
PROPERTY event = loplayer_event
*/

View File

@@ -4,57 +4,39 @@
#include <msgpack.h>
#include "util/math/matrix.h"
#include "core/lobullet/pool.h"
#include "core/locommon/input.h"
#include "core/locommon/position.h"
#include "core/locommon/screen.h"
#include "core/locommon/ticker.h"
#include "core/loeffect/stance.h"
#include "core/loentity/entity.h"
#include "core/loentity/store.h"
#include "core/loresource/set.h"
#include "core/loshader/set.h"
#include "./action.h"
#include "./camera.h"
#include "./combat.h"
#include "./controller.h"
#include "./entity.h"
#include "./event.h"
#include "./hud.h"
#include "./menu.h"
#include "./status.h"
#include "./popup.h"
#include "./stance.h"
typedef struct lochara_base_t lochara_base_t;
typedef struct {
loshader_set_t* shaders;
loplayer_event_t* event;
loplayer_status_t status;
loplayer_entity_t entity;
loplayer_combat_t* combat;
loplayer_stance_set_t stances;
loplayer_combat_t combat;
loplayer_controller_t controller;
loplayer_camera_t camera;
loplayer_event_t event;
loplayer_popup_t popup;
loplayer_camera_t camera;
loplayer_hud_t* hud;
loplayer_menu_t* menu;
loplayer_action_t* action;
lochara_base_t* entity;
} loplayer_t;
void
loplayer_initialize(
loplayer_t* player,
loentity_id_t id,
loresource_set_t* res,
loshader_set_t* shaders,
const locommon_screen_t* screen,
const locommon_ticker_t* ticker,
lobullet_pool_t* bullets,
loentity_store_t* entities,
const mat4_t* proj
loentity_store_t* entities
);
void
@@ -62,46 +44,11 @@ loplayer_deinitialize(
loplayer_t* player
);
void
loplayer_popup(
loplayer_t* player,
const char* title,
const char* text
);
bool
loplayer_attack(
loplayer_t* player,
const loplayer_combat_attack_t* attack
);
void
loplayer_touch_encephalon(
loplayer_t* player
);
void
loplayer_gain_stance(
loplayer_t* player,
loeffect_stance_id_t id
);
void
loplayer_gain_faith(
loplayer_t* player,
float amount
);
void
loplayer_update(
loplayer_t* player,
const locommon_input_t* input,
const locommon_position_t* cursor
);
void
loplayer_draw(
const loplayer_t* player
const locommon_input_t* input, /* NULLABLE */
const locommon_position_t* cursor /* NULLABLE */
);
void
@@ -110,13 +57,9 @@ loplayer_pack(
msgpack_packer* packer
);
/* WARNING: the entity must be unpacked manually */
bool
loplayer_unpack(
loplayer_t* player,
const msgpack_object* obj /* NULLABLE */
);
void
loplayer_test_packing(
const loplayer_t* player
);

53
core/loplayer/popup.c Normal file
View File

@@ -0,0 +1,53 @@
#include "./popup.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "./stance.h"
static loplayer_popup_item_t* loplayer_popup_queue_empty_(
loplayer_popup_t* popup) {
assert(popup != NULL);
const size_t next_tail = (popup->queue_tail+1)%LOPLAYER_POPUP_QUEUE_LENGTH;
if (next_tail == popup->queue_head) return NULL;
const size_t i = popup->queue_tail;
popup->queue_tail = next_tail;
return &popup->queue[i];
}
void loplayer_popup_initialize(loplayer_popup_t* popup) {
assert(popup != NULL);
*popup = (typeof(*popup)) {0};
}
void loplayer_popup_deinitialize(loplayer_popup_t* popup) {
assert(popup != NULL);
}
void loplayer_popup_queue_new_stance(
loplayer_popup_t* popup, loplayer_stance_t stance) {
assert(popup != NULL);
loplayer_popup_item_t* item = loplayer_popup_queue_empty_(popup);
if (item == NULL) return;
*item = (typeof(*item)) {
.type = LOPLAYER_POPUP_ITEM_TYPE_NEW_STANCE,
.stance = stance,
};
}
const loplayer_popup_item_t* loplayer_popup_enqueue(loplayer_popup_t* popup) {
assert(popup != NULL);
if (popup->queue_tail == popup->queue_head) return NULL;
const size_t i = popup->queue_head;
popup->queue_head = (popup->queue_head+1)%LOPLAYER_POPUP_QUEUE_LENGTH;
return &popup->queue[i];
}

48
core/loplayer/popup.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "./stance.h"
typedef enum {
LOPLAYER_POPUP_ITEM_TYPE_NEW_STANCE,
} loplayer_popup_item_type_t;
typedef struct {
loplayer_popup_item_type_t type;
union {
loplayer_stance_t stance;
};
} loplayer_popup_item_t;
typedef struct {
# define LOPLAYER_POPUP_QUEUE_LENGTH 32
loplayer_popup_item_t queue[LOPLAYER_POPUP_QUEUE_LENGTH];
size_t queue_tail;
size_t queue_head;
uint64_t last_enqueue;
} loplayer_popup_t;
void
loplayer_popup_initialize(
loplayer_popup_t* popup
);
void
loplayer_popup_deinitialize(
loplayer_popup_t* popup
);
void
loplayer_popup_queue_new_stance(
loplayer_popup_t* popup,
loplayer_stance_t stance
);
const loplayer_popup_item_t*
loplayer_popup_enqueue(
loplayer_popup_t* popup
);

103
core/loplayer/stance.c Normal file
View File

@@ -0,0 +1,103 @@
#include "./stance.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "util/mpkutil/get.h"
#include "util/mpkutil/pack.h"
#include "core/loshader/menu_stance.h"
loshader_menu_stance_id_t loplayer_stance_get_menu_shader_id(
loplayer_stance_t stance) {
# define each_(NAME, name) do {\
if (stance == LOPLAYER_STANCE_##NAME) { \
return LOSHADER_MENU_STANCE_ID_##NAME; \
} \
} while (0)
LOPLAYER_STANCE_EACH(each_);
assert(false);
return LOSHADER_MENU_STANCE_ID_EMPTY;
# undef each_
}
void loplayer_stance_set_initialize(loplayer_stance_set_t* set) {
assert(set != NULL);
*set = 0;
}
void loplayer_stance_set_deinitialize(loplayer_stance_set_t* set) {
assert(set != NULL);
}
void loplayer_stance_set_add(
loplayer_stance_set_t* set, loplayer_stance_t stance) {
assert(set != NULL);
*set |= 1 << stance;
}
void loplayer_stance_set_remove(
loplayer_stance_set_t* set, loplayer_stance_t stance) {
assert(set != NULL);
*set &= ~(1 << stance);
}
bool loplayer_stance_set_has(
const loplayer_stance_set_t* set, loplayer_stance_t stance) {
assert(set != NULL);
return *set & (1 << stance);
}
void loplayer_stance_set_pack(
const loplayer_stance_set_t* set, msgpack_packer* packer) {
assert(set != NULL);
assert(packer != NULL);
loplayer_stance_t mask = 1;
size_t len = 0;
while (mask <= *set) {
len += !!(*set & mask);
mask <<= 1;
}
msgpack_pack_array(packer, len);
mask = 1;
size_t i = 0;
while (*set >= mask) {
if (*set & mask) {
mpkutil_pack_str(packer, loplayer_stance_stringify(i));
}
++i;
mask <<= 1;
}
}
bool loplayer_stance_set_unpack(
loplayer_stance_set_t* set, const msgpack_object* obj) {
assert(set != NULL);
const msgpack_object_array* array = mpkutil_get_array(obj);
if (array == NULL) return false;
for (size_t i = 0; i < array->size; ++i) {
size_t len;
const char* name;
if (!mpkutil_get_str(&array->ptr[i], &name, &len)) continue;
loplayer_stance_t stance;
if (!loplayer_stance_unstringify(&stance, name, len)) continue;
*set |= 1 << stance;
}
return true;
}

81
core/loplayer/stance.h Normal file
View File

@@ -0,0 +1,81 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <msgpack.h>
#include "core/loshader/menu_stance.h"
typedef enum {
/* BENUM BEGIN loplayer_stance */
LOPLAYER_STANCE_MISSIONARY,
LOPLAYER_STANCE_REVOLUTIONER,
LOPLAYER_STANCE_UNFINISHER,
LOPLAYER_STANCE_PHILOSOPHER,
LOPLAYER_STANCE_BETRAYER,
/* BENUM END */
} loplayer_stance_t;
#include "core/loplayer/benum/stance.h"
_Static_assert(LOPLAYER_STANCE_COUNT < 16, "too many stances");
typedef uint16_t loplayer_stance_set_t;
const char*
loplayer_stance_stringify(
loplayer_stance_t stance
);
bool
loplayer_stance_unstringify(
loplayer_stance_t* stance,
const char* str,
size_t len
);
loshader_menu_stance_id_t
loplayer_stance_get_menu_shader_id(
loplayer_stance_t stance
);
void
loplayer_stance_set_initialize(
loplayer_stance_set_t* set
);
void
loplayer_stance_set_deinitialize(
loplayer_stance_set_t* set
);
void
loplayer_stance_set_add(
loplayer_stance_set_t* set,
loplayer_stance_t stance
);
void
loplayer_stance_set_remove(
loplayer_stance_set_t* set,
loplayer_stance_t stance
);
bool
loplayer_stance_set_has(
const loplayer_stance_set_t* set,
loplayer_stance_t stance
);
void
loplayer_stance_set_pack(
const loplayer_stance_set_t* set,
msgpack_packer* packer
);
bool
loplayer_stance_set_unpack(
loplayer_stance_set_t* set,
const msgpack_object* obj
);

View File

@@ -1,193 +0,0 @@
#include "./status.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <msgpack.h>
#include "util/math/algorithm.h"
#include "util/math/vector.h"
#include "util/mpkutil/get.h"
#include "util/mpkutil/pack.h"
#include "core/locommon/easing.h"
#include "core/locommon/msgpack.h"
#include "core/locommon/position.h"
#include "core/locommon/ticker.h"
#include "core/loeffect/recipient.h"
#include "core/loeffect/stance.h"
#include "core/loresource/set.h"
#include "core/loresource/sound.h"
static const loeffect_recipient_status_t loplayer_status_base_ = {
.attack = .3f,
.defence = .2f,
.speed = .3f,
.jump = 1.f,
};
#define LOPLAYER_STATUS_PARAM_TO_PACK_EACH_(PROC) do { \
PROC("stances", stances); \
PROC("madness", recipient.madness); \
PROC("faith", recipient.faith); \
PROC("effects", recipient.effects); \
PROC("respawn-pos", respawn_pos); \
PROC("last-damage-time", last_damage_time); \
PROC("bullet-immune-until", bullet_immune_until); \
} while (0)
#define LOPLAYER_STATUS_PARAM_TO_PACK_COUNT 7
#define LOPLAYER_STATUS_MADNESS_DYING_THRESHOLD .3f
void loplayer_status_initialize(
loplayer_status_t* status,
loresource_set_t* res,
const locommon_ticker_t* ticker) {
assert(status != NULL);
assert(ticker != NULL);
*status = (typeof(*status)) {
.res = res,
.ticker = ticker,
.respawn_pos = locommon_position(0, 0, vec2(.5f, .5f)),
};
loeffect_stance_set_initialize(&status->stances);
loeffect_recipient_initialize(&status->recipient, ticker);
}
void loplayer_status_deinitialize(loplayer_status_t* status) {
assert(status != NULL);
loeffect_stance_set_deinitialize(&status->stances);
loeffect_recipient_deinitialize(&status->recipient);
}
void loplayer_status_reset(loplayer_status_t* status) {
assert(status != NULL);
loeffect_recipient_reset(&status->recipient);
}
bool loplayer_status_add_stance(
loplayer_status_t* status, loeffect_stance_id_t id) {
assert(status != NULL);
if (loeffect_stance_set_has(&status->stances, id)) return false;
loeffect_stance_set_add(&status->stances, id);
return true;
}
void loplayer_status_remove_stance(
loplayer_status_t* status, loeffect_stance_id_t id) {
assert(status != NULL);
loeffect_stance_set_remove(&status->stances, id);
}
void loplayer_status_apply_effect(
loplayer_status_t* status, const loeffect_t* effect) {
assert(status != NULL);
assert(effect != NULL);
if (effect->id == LOEFFECT_ID_IMMEDIATE_DAMAGE) {
status->last_damage_time = status->ticker->time;
loresource_sound_play(status->res->sound, "damage");
}
loeffect_recipient_apply_effect(&status->recipient, effect);
}
void loplayer_status_update(loplayer_status_t* status) {
assert(status != NULL);
static const uint64_t dying_sound_period = 4000;
loeffect_recipient_status_t base = loplayer_status_base_;
if (loeffect_stance_set_has(&status->stances, LOEFFECT_STANCE_ID_PHILOSOPHER)) {
base.defence += .3f;
}
loeffect_recipient_update(&status->recipient, &base);
/* Makes decreasing madness faster if the player has a stance, UNFINISHER. */
if (loeffect_stance_set_has(
&status->stances, LOEFFECT_STANCE_ID_UNFINISHER)) {
if (status->recipient.madness > 0) {
if (status->recipient.faith <= 0) {
status->recipient.madness -= status->ticker->delta_f/30;
status->recipient.last_damage = LOEFFECT_ID_LOST;
} else if (status->recipient.faith >= .5f) {
status->recipient.madness += status->ticker->delta_f/120;
}
}
}
if (status->recipient.madness > 0 &&
status->recipient.madness <= LOPLAYER_STATUS_MADNESS_DYING_THRESHOLD) {
uint64_t t = status->ticker->time;
uint64_t pt = t - status->ticker->delta;
status->dying_effect = t%dying_sound_period*1.f / dying_sound_period;
t /= dying_sound_period;
pt /= dying_sound_period;
if (t != pt) loresource_sound_play(status->res->sound, "dying");
} else {
locommon_easing_linear_float(
&status->dying_effect, 0, status->ticker->delta_f*5);
}
}
void loplayer_status_set_respawn_position(
loplayer_status_t* status, const locommon_position_t* pos) {
assert(status != NULL);
assert(locommon_position_valid(pos));
status->respawn_pos = *pos;
}
void loplayer_status_pack(
const loplayer_status_t* status, msgpack_packer* packer) {
assert(status != NULL);
assert(packer != NULL);
msgpack_pack_map(packer, LOPLAYER_STATUS_PARAM_TO_PACK_COUNT);
# define pack_(name, var) do { \
mpkutil_pack_str(packer, name); \
LOCOMMON_MSGPACK_PACK_ANY(packer, &status->var); \
} while (0)
LOPLAYER_STATUS_PARAM_TO_PACK_EACH_(pack_);
# undef pack_
}
bool loplayer_status_unpack(
loplayer_status_t* status, const msgpack_object* obj) {
assert(status != NULL);
if (obj == NULL) return false;
const msgpack_object_map* root = mpkutil_get_map(obj);
# define item_(v) mpkutil_get_map_item_by_str(root, v)
# define unpack_(name, var) do { \
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(name), &status->var)) { \
return false; \
} \
} while (0)
LOPLAYER_STATUS_PARAM_TO_PACK_EACH_(unpack_);
return true;
# undef unpack_
# undef item_
}

View File

@@ -1,87 +0,0 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <msgpack.h>
#include "util/math/vector.h"
#include "core/locommon/position.h"
#include "core/locommon/ticker.h"
#include "core/loeffect/recipient.h"
#include "core/loeffect/stance.h"
#include "core/loresource/set.h"
typedef struct {
loresource_set_t* res;
const locommon_ticker_t* ticker;
loeffect_stance_set_t stances;
loeffect_recipient_t recipient;
locommon_position_t respawn_pos;
uint64_t last_damage_time;
uint64_t bullet_immune_until;
float dying_effect; /* 0~1 */
} loplayer_status_t;
void
loplayer_status_initialize(
loplayer_status_t* status,
loresource_set_t* res,
const locommon_ticker_t* ticker
);
void
loplayer_status_deinitialize(
loplayer_status_t* status
);
void
loplayer_status_reset(
loplayer_status_t* status
);
bool
loplayer_status_add_stance(
loplayer_status_t* status,
loeffect_stance_id_t id
);
void
loplayer_status_remove_stance(
loplayer_status_t* status,
loeffect_stance_id_t id
);
void
loplayer_status_apply_effect(
loplayer_status_t* status,
const loeffect_t* effect
);
void
loplayer_status_update(
loplayer_status_t* status
);
void
loplayer_status_set_respawn_position(
loplayer_status_t* status,
const locommon_position_t* pos
);
void
loplayer_status_pack(
const loplayer_status_t* status,
msgpack_packer* packer
);
bool
loplayer_status_unpack(
loplayer_status_t* status,
const msgpack_object* obj
);