[RELEASE] u22-v03
This version is submitted to U22 breau.
This commit is contained in:
28
core/loplayer/CMakeLists.txt
Normal file
28
core/loplayer/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
add_library(loplayer
|
||||
action.c
|
||||
camera.c
|
||||
combat.c
|
||||
controller.c
|
||||
entity.c
|
||||
event.c
|
||||
hud.c
|
||||
menu.c
|
||||
player.c
|
||||
status.c
|
||||
)
|
||||
target_link_libraries(loplayer
|
||||
msgpackc
|
||||
|
||||
conv
|
||||
glyphas
|
||||
math
|
||||
memory
|
||||
mpkutil
|
||||
|
||||
lobullet
|
||||
locommon
|
||||
loeffect
|
||||
loentity
|
||||
loresource
|
||||
loshader
|
||||
)
|
880
core/loplayer/action.c
Normal file
880
core/loplayer/action.c
Normal file
@@ -0,0 +1,880 @@
|
||||
#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;
|
||||
}
|
64
core/loplayer/action.h
Normal file
64
core/loplayer/action.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#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 */
|
||||
);
|
273
core/loplayer/camera.c
Normal file
273
core/loplayer/camera.c
Normal file
@@ -0,0 +1,273 @@
|
||||
#include "./camera.h"
|
||||
|
||||
#include <assert.h>
|
||||
#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/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_
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
mat4_t inv_proj;
|
||||
mat4_inv(&inv_proj, proj);
|
||||
|
||||
static const vec4_t chunk = vec4(1, 1, 0, 0);
|
||||
vec4_t chunksz;
|
||||
mat4_mul_vec4(&chunksz, &inv_proj, &chunk);
|
||||
|
||||
*camera = (typeof(*camera)) {
|
||||
.shaders = shaders,
|
||||
.ticker = ticker,
|
||||
.event = event,
|
||||
.status = status,
|
||||
.entity = entity,
|
||||
|
||||
.display_chunksz = chunksz.xy,
|
||||
|
||||
.matrix = mat4_scale(1, 1, 1),
|
||||
.scale = 1.0f,
|
||||
|
||||
.cinesco = {
|
||||
.color = vec4(0, 0, 0, 1),
|
||||
},
|
||||
|
||||
.state = LOPLAYER_CAMERA_STATE_DEFAULT,
|
||||
.brightness = 1,
|
||||
};
|
||||
}
|
||||
|
||||
void loplayer_camera_deinitialize(loplayer_camera_t* camera) {
|
||||
assert(camera != NULL);
|
||||
|
||||
}
|
||||
|
||||
void loplayer_camera_update(loplayer_camera_t* camera) {
|
||||
assert(camera != 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;
|
||||
}
|
101
core/loplayer/camera.h
Normal file
101
core/loplayer/camera.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/matrix.h"
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loshader/cinescope.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;
|
||||
|
||||
/* read-only mutable params */
|
||||
locommon_position_t pos;
|
||||
mat4_t matrix;
|
||||
|
||||
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;
|
||||
|
||||
} 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
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_camera_deinitialize(
|
||||
loplayer_camera_t* camera
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_camera_update(
|
||||
loplayer_camera_t* camera
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_camera_draw(
|
||||
const loplayer_camera_t* camera
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_camera_pack(
|
||||
const loplayer_camera_t* camera,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
loplayer_camera_unpack(
|
||||
loplayer_camera_t* camera,
|
||||
const msgpack_object* packer
|
||||
);
|
503
core/loplayer/combat.c
Normal file
503
core/loplayer/combat.c
Normal file
@@ -0,0 +1,503 @@
|
||||
#include "./combat.h"
|
||||
|
||||
#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/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) {
|
||||
assert(combat != NULL);
|
||||
assert(attack != NULL);
|
||||
|
||||
if (chara == NULL) return;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
static void loplayer_combat_execute_enemy_attack_(
|
||||
loplayer_combat_t* combat,
|
||||
loplayer_combat_attack_t* attack,
|
||||
loentity_character_t* chara) {
|
||||
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_execute_enemy_attack_(combat, attack, chara);
|
||||
}
|
||||
|
||||
static void loplayer_combat_draw_ring_base_(
|
||||
const 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),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
memory_delete(combat);
|
||||
}
|
||||
|
||||
bool loplayer_combat_add_attack(
|
||||
loplayer_combat_t* combat, const loplayer_combat_attack_t* 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;
|
||||
}
|
||||
|
||||
size_t index;
|
||||
if (!loplayer_combat_find_unused_attack_index_(combat, &index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
combat->attacks[index] = *attack;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loplayer_combat_accept_all_attacks(loplayer_combat_t* combat) {
|
||||
assert(combat != NULL);
|
||||
|
||||
if (combat->accepted) return true;
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void loplayer_combat_draw_ui(const loplayer_combat_t* combat) {
|
||||
assert(combat != NULL);
|
||||
|
||||
if (!combat->accepted || combat->ring_end <= combat->ticker->time) {
|
||||
return;
|
||||
}
|
||||
loplayer_combat_draw_ring_base_(combat);
|
||||
loplayer_combat_draw_attacks_(combat);
|
||||
loplayer_combat_draw_guard_(combat);
|
||||
loplayer_combat_draw_clockhand_(combat);
|
||||
}
|
||||
|
||||
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(
|
||||
loplayer_combat_attack_t* attack,
|
||||
const msgpack_object* obj) {
|
||||
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_
|
||||
}
|
107
core/loplayer/combat.h
Normal file
107
core/loplayer/combat.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#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/store.h"
|
||||
#include "core/loresource/sound.h"
|
||||
#include "core/loshader/combat_ring.h"
|
||||
|
||||
#include "./entity.h"
|
||||
#include "./status.h"
|
||||
|
||||
struct loplayer_combat_t;
|
||||
typedef struct loplayer_combat_t loplayer_combat_t;
|
||||
|
||||
typedef struct {
|
||||
loentity_id_t attacker;
|
||||
|
||||
uint64_t start;
|
||||
uint64_t duration;
|
||||
|
||||
vec2_t knockback;
|
||||
|
||||
loeffect_t effect;
|
||||
} loplayer_combat_attack_t;
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_combat_delete(
|
||||
loplayer_combat_t* combat /* OWNERSHIP */
|
||||
);
|
||||
|
||||
bool
|
||||
loplayer_combat_add_attack(
|
||||
loplayer_combat_t* combat,
|
||||
const loplayer_combat_attack_t* attack
|
||||
);
|
||||
|
||||
bool
|
||||
loplayer_combat_accept_all_attacks(
|
||||
loplayer_combat_t* combat
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_combat_drop_all_attacks(
|
||||
loplayer_combat_t* combat
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_combat_guard(
|
||||
loplayer_combat_t* combat
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_combat_unguard(
|
||||
loplayer_combat_t* combat
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_combat_update(
|
||||
loplayer_combat_t* combat
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_combat_draw_ui(
|
||||
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(
|
||||
const loplayer_combat_attack_t* attack,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
loplayer_combat_attack_unpack(
|
||||
loplayer_combat_attack_t* attack,
|
||||
const msgpack_object* obj /* NULLABLE */
|
||||
);
|
79
core/loplayer/controller.c
Normal file
79
core/loplayer/controller.c
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "./controller.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "core/locommon/input.h"
|
||||
#include "core/locommon/position.h"
|
||||
|
||||
void loplayer_controller_initialize(loplayer_controller_t* controller) {
|
||||
assert(controller != NULL);
|
||||
|
||||
*controller = (typeof(*controller)) {0};
|
||||
}
|
||||
|
||||
void loplayer_controller_deinitialize(loplayer_controller_t* controller) {
|
||||
assert(controller != NULL);
|
||||
|
||||
}
|
||||
|
||||
void loplayer_controller_update(
|
||||
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;
|
||||
|
||||
controller->movement = LOPLAYER_CONTROLLER_MOVEMENT_NONE;
|
||||
controller->action = LOPLAYER_CONTROLLER_ACTION_NONE;
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (input->buttons & LOCOMMON_INPUT_BUTTON_ATTACK) {
|
||||
controller->action = LOPLAYER_CONTROLLER_ACTION_ATTACK;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (input->buttons & LOCOMMON_INPUT_BUTTON_DASH && !prev_dash) {
|
||||
controller->action = LOPLAYER_CONTROLLER_ACTION_DODGE;
|
||||
}
|
||||
|
||||
if (input->buttons & LOCOMMON_INPUT_BUTTON_MENU && !prev_menu) {
|
||||
controller->action = LOPLAYER_CONTROLLER_ACTION_MENU;
|
||||
}
|
||||
|
||||
controller->prev = *input;
|
||||
}
|
51
core/loplayer/controller.h
Normal file
51
core/loplayer/controller.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "core/locommon/input.h"
|
||||
#include "core/locommon/position.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;
|
||||
|
||||
typedef struct {
|
||||
locommon_position_t looking;
|
||||
vec2_t cursor; /* display coordinate (-1~1) */
|
||||
|
||||
loplayer_controller_movement_t movement;
|
||||
loplayer_controller_action_t action;
|
||||
|
||||
locommon_input_t prev;
|
||||
} loplayer_controller_t;
|
||||
|
||||
void
|
||||
loplayer_controller_initialize(
|
||||
loplayer_controller_t* controller
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_controller_deinitialize(
|
||||
loplayer_controller_t* controller
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_controller_update(
|
||||
loplayer_controller_t* controller,
|
||||
const locommon_input_t* input,
|
||||
const locommon_position_t* cursor
|
||||
);
|
311
core/loplayer/entity.c
Normal file
311
core/loplayer/entity.c
Normal file
@@ -0,0 +1,311 @@
|
||||
#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(¢er);
|
||||
|
||||
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, ¢er, 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_
|
||||
}
|
93
core/loplayer/entity.h
Normal file
93
core/loplayer/entity.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#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
|
||||
);
|
146
core/loplayer/event.c
Normal file
146
core/loplayer/event.c
Normal file
@@ -0,0 +1,146 @@
|
||||
#include "./event.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/glyphas/block.h"
|
||||
#include "util/glyphas/cache.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/memory/memory.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loshader/set.h"
|
||||
|
||||
struct loplayer_event_t {
|
||||
loplayer_event_param_t param;
|
||||
/* convertible between loplayer_event_t* and loplayer_event_param_t* */
|
||||
|
||||
/* injected deps */
|
||||
loresource_set_t* res;
|
||||
loshader_set_t* shaders;
|
||||
|
||||
/* owned objects */
|
||||
glyphas_cache_t* font;
|
||||
glyphas_block_t* text;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
loplayer_event_t* loplayer_event_new(
|
||||
loresource_set_t* res, loshader_set_t* shaders) {
|
||||
assert(res != NULL);
|
||||
assert(shaders != NULL);
|
||||
|
||||
loplayer_event_t* event = memory_new(sizeof(*event));
|
||||
*event = (typeof(*event)) {
|
||||
.res = res,
|
||||
.shaders = shaders,
|
||||
};
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
if (event->param.controlled) return NULL;
|
||||
|
||||
event->param = (typeof(event->param)) {
|
||||
.controlled = true,
|
||||
.controlled_by = id,
|
||||
};
|
||||
return &event->param;
|
||||
}
|
||||
|
||||
void loplayer_event_abort(loplayer_event_t* event) {
|
||||
assert(event != NULL);
|
||||
|
||||
if (!event->param.controlled) return;
|
||||
|
||||
loplayer_event_param_release_control(&event->param);
|
||||
}
|
||||
|
||||
void loplayer_event_draw(const loplayer_event_t* event) {
|
||||
assert(event != NULL);
|
||||
|
||||
if (!event->param.controlled) return;
|
||||
|
||||
loshader_event_line_drawer_add_block(
|
||||
&event->shaders->drawer.event_line, event->text);
|
||||
}
|
||||
|
||||
const loplayer_event_param_t* loplayer_event_get_param(
|
||||
const loplayer_event_t* event) {
|
||||
assert(event != NULL);
|
||||
|
||||
if (!event->param.controlled) return NULL;
|
||||
|
||||
return &event->param;
|
||||
}
|
||||
|
||||
void loplayer_event_param_set_line(
|
||||
loplayer_event_param_t* param, const char* str, size_t len) {
|
||||
assert(param != NULL);
|
||||
|
||||
loplayer_event_t* event = (typeof(event)) param;
|
||||
|
||||
glyphas_block_clear(event->text);
|
||||
|
||||
static const vec4_t white = vec4(1, 1, 1, 1);
|
||||
glyphas_block_add_characters(event->text, event->font, &white, str, len);
|
||||
|
||||
static const vec2_t center = vec2(.5f, -.5f);
|
||||
glyphas_block_set_origin(event->text, ¢er);
|
||||
|
||||
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;
|
||||
}
|
74
core/loplayer/event.h
Normal file
74
core/loplayer/event.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/position.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 struct {
|
||||
bool controlled;
|
||||
/* You can return the control by assigning false. */
|
||||
|
||||
loentity_id_t controlled_by;
|
||||
|
||||
locommon_position_t area_pos;
|
||||
vec2_t area_size;
|
||||
|
||||
bool cinescope;
|
||||
bool hide_hud;
|
||||
|
||||
loresource_music_player_t* music;
|
||||
} loplayer_event_param_t;
|
||||
|
||||
loplayer_event_t* /* OWNERSHIP */
|
||||
loplayer_event_new(
|
||||
loresource_set_t* res,
|
||||
loshader_set_t* shaders
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_event_delete(
|
||||
loplayer_event_t* event /* OWNERSHIP*/
|
||||
);
|
||||
|
||||
loplayer_event_param_t* /* NULLABLE */
|
||||
loplayer_event_take_control(
|
||||
loplayer_event_t* event,
|
||||
loentity_id_t id
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_event_abort(
|
||||
loplayer_event_t* event
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_event_draw(
|
||||
const loplayer_event_t* event
|
||||
);
|
||||
|
||||
const loplayer_event_param_t* /* NULLABLE */
|
||||
loplayer_event_get_param(
|
||||
const loplayer_event_t* event
|
||||
);
|
||||
|
||||
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
|
||||
);
|
409
core/loplayer/hud.c
Normal file
409
core/loplayer/hud.c
Normal file
@@ -0,0 +1,409 @@
|
||||
#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);
|
||||
}
|
53
core/loplayer/hud.h
Normal file
53
core/loplayer/hud.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#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
|
||||
);
|
565
core/loplayer/menu.c
Normal file
565
core/loplayer/menu.c
Normal file
@@ -0,0 +1,565 @@
|
||||
#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, ¢er);
|
||||
|
||||
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;
|
||||
}
|
86
core/loplayer/menu.h
Normal file
86
core/loplayer/menu.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#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 */
|
||||
);
|
288
core/loplayer/player.c
Normal file
288
core/loplayer/player.c
Normal file
@@ -0,0 +1,288 @@
|
||||
#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/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
|
||||
|
||||
void loplayer_initialize(
|
||||
loplayer_t* player,
|
||||
loentity_id_t id,
|
||||
loresource_set_t* res,
|
||||
loshader_set_t* shaders,
|
||||
const locommon_ticker_t* ticker,
|
||||
lobullet_pool_t* bullets,
|
||||
loentity_store_t* entities,
|
||||
const mat4_t* proj) {
|
||||
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->event = loplayer_event_new(
|
||||
res,
|
||||
shaders);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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_camera_deinitialize(&player->camera);
|
||||
|
||||
loplayer_controller_deinitialize(&player->controller);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void loplayer_update(
|
||||
loplayer_t* player,
|
||||
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_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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
# 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_
|
||||
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);
|
||||
}
|
122
core/loplayer/player.h
Normal file
122
core/loplayer/player.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#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/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"
|
||||
|
||||
typedef struct {
|
||||
loshader_set_t* shaders;
|
||||
|
||||
loplayer_event_t* event;
|
||||
|
||||
loplayer_status_t status;
|
||||
loplayer_entity_t entity;
|
||||
|
||||
loplayer_combat_t* combat;
|
||||
|
||||
loplayer_controller_t controller;
|
||||
|
||||
loplayer_camera_t camera;
|
||||
loplayer_hud_t* hud;
|
||||
loplayer_menu_t* menu;
|
||||
|
||||
loplayer_action_t* action;
|
||||
} loplayer_t;
|
||||
|
||||
void
|
||||
loplayer_initialize(
|
||||
loplayer_t* player,
|
||||
loentity_id_t id,
|
||||
loresource_set_t* res,
|
||||
loshader_set_t* shaders,
|
||||
const locommon_ticker_t* ticker,
|
||||
lobullet_pool_t* bullets,
|
||||
loentity_store_t* entities,
|
||||
const mat4_t* proj
|
||||
);
|
||||
|
||||
void
|
||||
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
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_pack(
|
||||
const loplayer_t* player,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
loplayer_unpack(
|
||||
loplayer_t* player,
|
||||
const msgpack_object* obj /* NULLABLE */
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_test_packing(
|
||||
const loplayer_t* player
|
||||
);
|
193
core/loplayer/status.c
Normal file
193
core/loplayer/status.c
Normal file
@@ -0,0 +1,193 @@
|
||||
#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_
|
||||
}
|
87
core/loplayer/status.h
Normal file
87
core/loplayer/status.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#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
|
||||
);
|
Reference in New Issue
Block a user