334 lines
8.6 KiB
C
334 lines
8.6 KiB
C
#include "./base.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 "util/statman/statman.h"
|
|
|
|
#include "core/lobullet/pool.h"
|
|
#include "core/locommon/easing.h"
|
|
#include "core/locommon/msgpack.h"
|
|
#include "core/locommon/physics.h"
|
|
#include "core/locommon/ticker.h"
|
|
#include "core/loeffect/recipient.h"
|
|
#include "core/loentity/character.h"
|
|
#include "core/loentity/ground.h"
|
|
#include "core/loentity/store.h"
|
|
#include "core/loplayer/player.h"
|
|
#include "core/loresource/sound.h"
|
|
#include "core/loshader/character.h"
|
|
|
|
#include "./big_warder.h"
|
|
#include "./cavia.h"
|
|
#include "./encephalon.h"
|
|
#include "./player.h"
|
|
#include "./state.h"
|
|
#include "./theists_child.h"
|
|
#include "./type.h"
|
|
#include "./warder.h"
|
|
|
|
/* generated serializer */
|
|
#include "core/lochara/crial/base.h"
|
|
|
|
#define GRAVITY_ACCELERATION_ 2.2f
|
|
#define KNOCKBACK_RECOVERY_ACCELERATION_ 4.4f
|
|
|
|
#define BULLET_INVINCIBLE_DURATION_ 500
|
|
|
|
static bool
|
|
(*const update_function_vtable_[LOCHARA_TYPE_COUNT])(lochara_base_t* base) = {
|
|
[LOCHARA_TYPE_PLAYER] = lochara_player_update,
|
|
|
|
[LOCHARA_TYPE_ENCEPHALON] = lochara_encephalon_update,
|
|
|
|
[LOCHARA_TYPE_CAVIA] = lochara_cavia_update,
|
|
[LOCHARA_TYPE_WARDER] = lochara_warder_update,
|
|
|
|
[LOCHARA_TYPE_BIG_WARDER] = lochara_big_warder_update,
|
|
[LOCHARA_TYPE_THEISTS_CHILD] = lochara_theists_child_update,
|
|
};
|
|
|
|
static loentity_ground_t* lochara_base_find_ground_(
|
|
lochara_base_t* base, loentity_id_t id, vec2_t* pos) {
|
|
assert(base != NULL);
|
|
assert(pos != NULL);
|
|
|
|
loentity_store_iterator_t itr;
|
|
if (!loentity_store_find_item_by_id(base->entities, &itr, id) ||
|
|
itr.ground == NULL) {
|
|
return NULL;
|
|
}
|
|
locommon_position_sub(pos, &base->super.super.pos, &itr.ground->super.pos);
|
|
pos->x /= itr.ground->size.x;
|
|
pos->y -= itr.ground->size.y;
|
|
return itr.ground;
|
|
}
|
|
|
|
static void lochara_base_delete_(loentity_t* entity) {
|
|
assert(entity != NULL);
|
|
|
|
lochara_base_t* base = (typeof(base)) entity;
|
|
|
|
loeffect_recipient_deinitialize(&base->param.recipient);
|
|
|
|
base->used = false;
|
|
}
|
|
|
|
static void lochara_base_die_(loentity_t* entity) {
|
|
assert(entity != NULL);
|
|
|
|
}
|
|
|
|
static bool lochara_base_update_(loentity_t* entity) {
|
|
assert(entity != NULL);
|
|
|
|
lochara_base_t* base = (typeof(base)) entity;
|
|
|
|
base->cache = (typeof(base->cache)) {0};
|
|
base->super.velocity = vec2(0, 0);
|
|
|
|
base->cache.ground = lochara_base_find_ground_(
|
|
base, base->param.ground, &base->cache.ground_pos);
|
|
|
|
assert(update_function_vtable_[base->param.type] != NULL);
|
|
return update_function_vtable_[base->param.type](base);
|
|
}
|
|
|
|
static void lochara_base_draw_(
|
|
loentity_t* entity, const locommon_position_t* basepos) {
|
|
assert(entity != NULL);
|
|
assert(locommon_position_valid(basepos));
|
|
|
|
lochara_base_t* base = (typeof(base)) entity;
|
|
|
|
vec2_t pos;
|
|
locommon_position_sub(&pos, &base->super.super.pos, basepos);
|
|
vec2_addeq(&base->cache.instance.pos, &pos);
|
|
|
|
loshader_character_drawer_add_instance(
|
|
&base->shaders->drawer.character, &base->cache.instance);
|
|
}
|
|
|
|
static void lochara_base_pack_(
|
|
const loentity_t* entity, msgpack_packer* packer) {
|
|
assert(entity != NULL);
|
|
assert(packer != NULL);
|
|
|
|
lochara_base_t* base = (typeof(base)) entity;
|
|
|
|
msgpack_pack_map(packer, CRIAL_PROPERTY_COUNT_);
|
|
CRIAL_SERIALIZER_;
|
|
}
|
|
|
|
static void lochara_base_apply_effect_(
|
|
loentity_character_t* chara, const loeffect_t* effect) {
|
|
assert(chara != NULL);
|
|
assert(effect != NULL);
|
|
|
|
lochara_base_t* base = (typeof(base)) chara;
|
|
|
|
const bool player = base->param.type == LOCHARA_TYPE_PLAYER;
|
|
|
|
loeffect_recipient_apply_effect(&base->param.recipient, effect);
|
|
|
|
switch (effect->id) {
|
|
case LOEFFECT_ID_DAMAGE:
|
|
if (player) {
|
|
loresource_sound_set_play(&base->res->sound, LORESOURCE_SOUND_ID_DAMAGE);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lochara_base_knockback_(
|
|
loentity_character_t* chara, const vec2_t* v) {
|
|
assert(chara != NULL);
|
|
assert(vec2_valid(v));
|
|
|
|
lochara_base_t* base = (typeof(base)) chara;
|
|
|
|
base->param.gravity += v->y;
|
|
base->param.knockback += v->x;
|
|
|
|
if (vec2_pow_length(v) > 0) {
|
|
base->param.last_knockback = base->ticker->time;
|
|
}
|
|
}
|
|
|
|
void lochara_base_initialize(
|
|
lochara_base_t* base,
|
|
loresource_set_t* res,
|
|
loshader_set_t* shaders,
|
|
const locommon_ticker_t* ticker,
|
|
loentity_store_t* entities,
|
|
loplayer_t* player,
|
|
lobullet_pool_t* bullet) {
|
|
assert(base != NULL);
|
|
assert(res != NULL);
|
|
assert(shaders != NULL);
|
|
assert(ticker != NULL);
|
|
assert(entities != NULL);
|
|
assert(player != NULL);
|
|
assert(bullet != NULL);
|
|
|
|
*base = (typeof(*base)) {
|
|
.res = res,
|
|
.shaders = shaders,
|
|
.ticker = ticker,
|
|
.entities = entities,
|
|
.player = player,
|
|
.bullet = bullet,
|
|
};
|
|
}
|
|
|
|
void lochara_base_reinitialize(lochara_base_t* base, loentity_id_t id) {
|
|
assert(base != NULL);
|
|
assert(!base->used);
|
|
|
|
base->super = (typeof(base->super)) {
|
|
.super = {
|
|
.vtable = {
|
|
.delete = lochara_base_delete_,
|
|
.die = lochara_base_die_,
|
|
.update = lochara_base_update_,
|
|
.draw = lochara_base_draw_,
|
|
.pack = lochara_base_pack_,
|
|
},
|
|
.id = id,
|
|
.subclass = LOENTITY_SUBCLASS_CHARACTER,
|
|
},
|
|
.vtable = {
|
|
.apply_effect = lochara_base_apply_effect_,
|
|
.knockback = lochara_base_knockback_,
|
|
},
|
|
};
|
|
|
|
base->param = (typeof(base->param)) {0};
|
|
}
|
|
|
|
void lochara_base_deinitialize(lochara_base_t* base) {
|
|
assert(base != NULL);
|
|
assert(!base->used);
|
|
|
|
}
|
|
|
|
void lochara_base_calculate_physics(
|
|
lochara_base_t* base, const vec2_t* size, const vec2_t* offset) {
|
|
assert(base != NULL);
|
|
assert(vec2_valid(size));
|
|
assert(vec2_valid(offset));
|
|
|
|
const float dt = base->ticker->delta_f;
|
|
|
|
vec2_t velocity = base->param.movement;
|
|
velocity.y += base->param.gravity;
|
|
velocity.x += base->param.knockback;
|
|
|
|
base->param.gravity -= GRAVITY_ACCELERATION_*dt;
|
|
locommon_easing_linear_float(
|
|
&base->param.knockback, 0, KNOCKBACK_RECOVERY_ACCELERATION_*dt);
|
|
|
|
vec2_t disp;
|
|
vec2_mul(&disp, &velocity, base->ticker->delta_f);
|
|
|
|
vec2_addeq(&base->super.super.pos.fract, &disp);
|
|
vec2_subeq(&base->super.super.pos.fract, offset);
|
|
locommon_position_reduce(&base->super.super.pos);
|
|
|
|
locommon_physics_entity_t e = {
|
|
.size = *size,
|
|
.pos = base->super.super.pos,
|
|
.velocity = velocity,
|
|
};
|
|
|
|
loentity_store_solve_collision_between_ground(
|
|
base->entities, &e, base->ticker->delta_f);
|
|
|
|
base->super.super.pos = e.pos;
|
|
vec2_addeq(&base->super.super.pos.fract, offset);
|
|
locommon_position_reduce(&base->super.super.pos);
|
|
|
|
base->param.on_ground = false;
|
|
if (e.velocity.y == 0) {
|
|
if (velocity.y <= 0) {
|
|
base->param.on_ground = true;
|
|
}
|
|
if (base->param.gravity*velocity.y > 0) {
|
|
base->param.gravity = 0;
|
|
}
|
|
}
|
|
if (e.velocity.x == 0 && velocity.x != 0) {
|
|
if (base->param.knockback*velocity.x >= 0) {
|
|
base->param.knockback = 0;
|
|
}
|
|
}
|
|
base->super.velocity = velocity = e.velocity;
|
|
}
|
|
|
|
void lochara_base_bind_on_ground(lochara_base_t* base, const vec2_t* offset) {
|
|
assert(base != NULL);
|
|
assert(vec2_valid(offset));
|
|
|
|
if (base->cache.ground == NULL) return;
|
|
|
|
vec2_t p;
|
|
locommon_position_sub(
|
|
&p, &base->super.super.pos, &base->cache.ground->super.pos);
|
|
|
|
const vec2_t sz = base->cache.ground->size;
|
|
p.x = MATH_CLAMP(p.x, -sz.x+offset->x, sz.x-offset->x);
|
|
p.y = MATH_CLAMP(p.y, sz.y+offset->y, 1);
|
|
|
|
base->super.super.pos = base->cache.ground->super.pos;
|
|
vec2_addeq(&base->super.super.pos.fract, &p);
|
|
locommon_position_reduce(&base->super.super.pos);
|
|
}
|
|
|
|
bool lochara_base_affect_bullets(lochara_base_t* base) {
|
|
assert(base != NULL);
|
|
|
|
const uint64_t t = base->ticker->time;
|
|
if (base->param.last_bullet_hit + BULLET_INVINCIBLE_DURATION_ > t) {
|
|
return false;
|
|
}
|
|
|
|
const bool hit = loentity_store_affect_bullets_shot_by_others(
|
|
base->entities,
|
|
&base->super,
|
|
&base->super.velocity,
|
|
base->ticker->delta_f);
|
|
if (hit) {
|
|
base->param.last_bullet_hit = base->ticker->time;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool lochara_base_unpack(lochara_base_t* base, const msgpack_object* obj) {
|
|
assert(base != NULL);
|
|
assert(obj != NULL);
|
|
assert(!base->used);
|
|
|
|
lochara_base_reinitialize(base, 0); /* id will be overwritten */
|
|
|
|
loeffect_recipient_initialize(&base->param.recipient, base->ticker, NULL);
|
|
|
|
const msgpack_object_map* root = mpkutil_get_map(obj);
|
|
if (root == NULL) goto FAIL;
|
|
CRIAL_DESERIALIZER_;
|
|
return true;
|
|
|
|
FAIL:
|
|
lochara_base_delete_(&base->super.super);
|
|
return false;
|
|
}
|