#include "./entity.h" #include #include #include #include #include #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_ }