This repository has been archived on 2022-05-21. You can view files and clone it, but cannot push or open issues or pull requests.
LEFTONE/core/loplayer/action.c

881 lines
24 KiB
C
Raw Normal View History

#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;
}