[RELEASE] u22-v04
This version is submitted for U22 final presentation. (squashed 158 commits)
This commit is contained in:
37
core/lochara/CMakeLists.txt
Normal file
37
core/lochara/CMakeLists.txt
Normal file
@@ -0,0 +1,37 @@
|
||||
add_library(lochara
|
||||
base.c
|
||||
big_warder.c
|
||||
cavia.c
|
||||
combat.c
|
||||
encephalon.c
|
||||
player.c
|
||||
pool.c
|
||||
state.c
|
||||
strategy.c
|
||||
theists_child.c
|
||||
warder.c
|
||||
)
|
||||
target_benum_sources(lochara
|
||||
state.h
|
||||
strategy.h
|
||||
type.h
|
||||
)
|
||||
target_crial_sources(lochara
|
||||
base.crial
|
||||
)
|
||||
|
||||
target_link_libraries(lochara
|
||||
msgpackc
|
||||
|
||||
chaos
|
||||
math
|
||||
statman
|
||||
|
||||
lobullet
|
||||
locommon
|
||||
loeffect
|
||||
loentity
|
||||
loplayer
|
||||
loresource
|
||||
loshader
|
||||
)
|
333
core/lochara/base.c
Normal file
333
core/lochara/base.c
Normal file
@@ -0,0 +1,333 @@
|
||||
#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;
|
||||
}
|
60
core/lochara/base.crial
Normal file
60
core/lochara/base.crial
Normal file
@@ -0,0 +1,60 @@
|
||||
/* CRIAL
|
||||
SERIALIZER_BEGIN
|
||||
mpkutil_pack_str(packer, "$name");
|
||||
mpkutil_pack_str(packer, $code);
|
||||
END
|
||||
DESERIALIZER_BEGIN
|
||||
const char* v;
|
||||
size_t vlen;
|
||||
if (!mpkutil_get_str(
|
||||
mpkutil_get_map_item_by_str(root, "$name"), &v, &vlen) ||
|
||||
strncmp(v, $code, vlen) != 0 || $code[vlen] != 0) {
|
||||
goto FAIL;
|
||||
}
|
||||
END
|
||||
PROPERTY subclass = "character"
|
||||
|
||||
SERIALIZER_BEGIN
|
||||
mpkutil_pack_str(packer, "$name");
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, &base->$code);
|
||||
END
|
||||
DESERIALIZER_BEGIN
|
||||
if (!LOCOMMON_MSGPACK_UNPACK_ANY(
|
||||
mpkutil_get_map_item_by_str(root, "$name"), &base->$code)) {
|
||||
goto FAIL;
|
||||
}
|
||||
END
|
||||
PROPERTY id = super.super.id
|
||||
PROPERTY pos = super.super.pos
|
||||
PROPERTY velocity = super.velocity
|
||||
PROPERTY type = param.type
|
||||
PROPERTY recipient = param.recipient
|
||||
PROPERTY last_state_changed = param.last_state_changed
|
||||
PROPERTY last_strategy_changed = param.last_strategy_changed
|
||||
PROPERTY ground = param.ground
|
||||
PROPERTY on_ground = param.on_ground
|
||||
PROPERTY direction = param.direction
|
||||
PROPERTY movement = param.movement
|
||||
PROPERTY gravity = param.gravity
|
||||
PROPERTY knockback = param.knockback
|
||||
PROPERTY last_knockback = param.last_knockback
|
||||
PROPERTY last_bullet_hit = param.last_bullet_hit
|
||||
PROPERTY anchor_pos = param.anchor.pos
|
||||
PROPERTY anchor_vec = param.anchor.vec
|
||||
|
||||
SERIALIZER_BEGIN
|
||||
const $code v = base->param.$name;
|
||||
mpkutil_pack_str(packer, "$name");
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, &v);
|
||||
END
|
||||
DESERIALIZER_BEGIN
|
||||
$code v;
|
||||
if (!LOCOMMON_MSGPACK_UNPACK_ANY(
|
||||
mpkutil_get_map_item_by_str(root, "$name"), &v)) {
|
||||
goto FAIL;
|
||||
}
|
||||
base->param.$name = v;
|
||||
END
|
||||
PROPERTY state = lochara_state_t
|
||||
PROPERTY strategy = lochara_strategy_t
|
||||
*/
|
116
core/lochara/base.h
Normal file
116
core/lochara/base.h
Normal file
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
#include "util/statman/statman.h"
|
||||
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loeffect/recipient.h"
|
||||
#include "core/loentity/character.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loentity/ground.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loshader/set.h"
|
||||
|
||||
#include "./state.h"
|
||||
#include "./strategy.h"
|
||||
#include "./type.h"
|
||||
|
||||
typedef struct lochara_base_t {
|
||||
loentity_character_t super;
|
||||
bool used;
|
||||
|
||||
loresource_set_t* res;
|
||||
loshader_set_t* shaders;
|
||||
const locommon_ticker_t* ticker;
|
||||
loentity_store_t* entities;
|
||||
loplayer_t* player;
|
||||
lobullet_pool_t* bullet;
|
||||
|
||||
struct {
|
||||
lochara_type_t type;
|
||||
|
||||
loeffect_recipient_t recipient;
|
||||
|
||||
statman_state_t state; /* actual type is lochara_state_t */
|
||||
uint64_t last_state_changed;
|
||||
|
||||
statman_state_t strategy; /* actual type is lochara_strategy_t */
|
||||
uint64_t last_strategy_changed;
|
||||
|
||||
loentity_id_t ground;
|
||||
bool on_ground;
|
||||
|
||||
vec2_t direction;
|
||||
vec2_t movement;
|
||||
float gravity;
|
||||
float knockback;
|
||||
uint64_t last_knockback;
|
||||
uint64_t last_bullet_hit;
|
||||
|
||||
/* some character uses these params for some strategy */
|
||||
struct {
|
||||
locommon_position_t pos;
|
||||
vec2_t vec;
|
||||
} anchor;
|
||||
} param;
|
||||
|
||||
struct {
|
||||
loentity_ground_t* ground;
|
||||
vec2_t ground_pos;
|
||||
loshader_character_drawer_instance_t instance;
|
||||
} cache;
|
||||
} lochara_base_t;
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
void
|
||||
lochara_base_reinitialize(
|
||||
lochara_base_t* base,
|
||||
loentity_id_t id
|
||||
);
|
||||
|
||||
void
|
||||
lochara_base_deinitialize(
|
||||
lochara_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
lochara_base_calculate_physics(
|
||||
lochara_base_t* base,
|
||||
const vec2_t* size,
|
||||
const vec2_t* offset
|
||||
);
|
||||
|
||||
void
|
||||
lochara_base_bind_on_ground(
|
||||
lochara_base_t* base,
|
||||
const vec2_t* offset
|
||||
);
|
||||
|
||||
bool
|
||||
lochara_base_affect_bullets(
|
||||
lochara_base_t* base
|
||||
);
|
||||
|
||||
bool
|
||||
lochara_base_unpack(
|
||||
lochara_base_t* base,
|
||||
const msgpack_object* obj
|
||||
);
|
728
core/lochara/big_warder.c
Normal file
728
core/lochara/big_warder.c
Normal file
@@ -0,0 +1,728 @@
|
||||
#include "./big_warder.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/chaos/xorshift.h"
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/statman/statman.h"
|
||||
|
||||
#include "core/lobullet/linear.h"
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/loentity/ground.h"
|
||||
#include "core/loplayer/event.h"
|
||||
#include "core/loplayer/stance.h"
|
||||
#include "core/loresource/music.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./state.h"
|
||||
#include "./strategy.h"
|
||||
#include "./type.h"
|
||||
|
||||
#define WIDTH_ .025f
|
||||
#define HEIGHT_ .06f
|
||||
#define MARKER_ .03f
|
||||
#define COLOR_ vec4(0, 0, 0, 1)
|
||||
|
||||
#define BPM_ 80
|
||||
#define BEAT_ (60.f/BPM_)
|
||||
#define BEAT_MS_ (BEAT_*1000)
|
||||
#define MUSIC_DURATION_ (BEAT_MS_*144)
|
||||
|
||||
#define WAKE_UP_RANGE_ (WIDTH_*8)
|
||||
|
||||
#define REWARD_STANCE_ LOPLAYER_STANCE_UNFINISHER
|
||||
|
||||
static const loeffect_recipient_status_t base_status_ = {
|
||||
.attack = .2f,
|
||||
.defence = .92f,
|
||||
.speed = .31f,
|
||||
.jump = 1.1f,
|
||||
};
|
||||
|
||||
static void lochara_big_warder_initialize_shoot_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
lochara_state_initialize_any_(meta, instance, state);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
vec2_t dir;
|
||||
locommon_position_sub(
|
||||
&dir, &base->player->entity->super.super.pos, &base->super.super.pos);
|
||||
|
||||
if (dir.x == 0 && dir.y == 0) dir = vec2(1, 0);
|
||||
vec2_diveq(&dir, vec2_length(&dir));
|
||||
|
||||
const vec2_t invdir = vec2(dir.y, -dir.x);
|
||||
|
||||
base->param.direction = vec2(MATH_SIGN_NOZERO(dir.x), 0);
|
||||
for (int32_t i = -4; i <= 4; ++i) {
|
||||
vec2_t a;
|
||||
vec2_mul(&a, &dir, 1.5f-MATH_ABS(i)/4.f*.5f);
|
||||
|
||||
vec2_t v;
|
||||
vec2_mul(&v, &dir, .5f);
|
||||
|
||||
vec2_t p;
|
||||
vec2_mul(&p, &invdir, i*.02f);
|
||||
|
||||
locommon_position_t pos = base->super.super.pos;
|
||||
vec2_addeq(&pos.fract, &p);
|
||||
locommon_position_reduce(&pos);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(base->bullet);
|
||||
lobullet_linear_circle_build(b,
|
||||
.owner = base->super.super.id,
|
||||
.basepos = pos,
|
||||
.size = vec2(.02f, .02f),
|
||||
.color = vec4(1, 1, 1, 1),
|
||||
.acceleration = a,
|
||||
.velocity = v,
|
||||
.knockback = 1,
|
||||
.effect = loeffect_damage(
|
||||
base->param.recipient.status.attack*.6f),
|
||||
.duration = 2000,
|
||||
);
|
||||
loentity_store_add(base->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
|
||||
static void lochara_big_warder_update_thrust_in_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
static const uint64_t dur = BEAT_MS_/4;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
if (t > dur) t = dur;
|
||||
|
||||
const float tf = t*1.f / dur;
|
||||
base->cache.instance.motion.time = powf(tf, 2);
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
}
|
||||
|
||||
static void lochara_big_warder_update_thrust_out_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
static const uint64_t dur = BEAT_MS_/4;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
if (t > dur) t = dur;
|
||||
|
||||
const float tf = t*1.f / dur;
|
||||
base->cache.instance.motion.time = 1-powf(1-tf, 2);
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
}
|
||||
|
||||
static void lochara_big_warder_update_down_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
const bool fast =
|
||||
meta->state == LOCHARA_STATE_DOWN ||
|
||||
meta->state == LOCHARA_STATE_REVIVE;
|
||||
const bool reverse =
|
||||
meta->state == LOCHARA_STATE_REVIVE ||
|
||||
meta->state == LOCHARA_STATE_RESUSCITATE;
|
||||
|
||||
const uint64_t dur = fast? reverse? BEAT_MS_*3: BEAT_MS_: BEAT_MS_*4;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
base->param.movement = vec2(0, 0);
|
||||
|
||||
uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
if (t > dur) t = dur;
|
||||
if (reverse) t = dur - t;
|
||||
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
base->cache.instance.motion.time = powf(t*1.f/dur, 6);
|
||||
}
|
||||
|
||||
static const statman_meta_t state_table_[] = {
|
||||
lochara_state_stand(
|
||||
.period = BEAT_MS_*2,
|
||||
.acceleration = {{5, 5}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_ATTACK1,
|
||||
),
|
||||
lochara_state_walk(
|
||||
.period = BEAT_MS_,
|
||||
.acceleration = {{5, 5}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_WALK,
|
||||
),
|
||||
lochara_state_dodge(
|
||||
.duration = BEAT_MS_/2,
|
||||
.speed = 1,
|
||||
.acceleration = {{3, 3}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_WALK,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
),
|
||||
|
||||
lochara_state_teleport(
|
||||
.duration = BEAT_MS_,
|
||||
.offset = {{WIDTH_*2, 0}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_ATTACK2,
|
||||
),
|
||||
|
||||
{
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.name = "THRUST_IN",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_big_warder_update_thrust_in_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
.name = "THRUST_OUT",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_big_warder_update_thrust_out_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_SHOOT,
|
||||
.name = "SHOOT",
|
||||
.initialize = lochara_big_warder_initialize_shoot_state_,
|
||||
.update = lochara_big_warder_update_thrust_in_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_DOWN,
|
||||
.name = "DOWN",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_big_warder_update_down_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_REVIVE,
|
||||
.name = "REVIVE",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_big_warder_update_down_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
.name = "DEAD",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_big_warder_update_down_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_RESUSCITATE,
|
||||
.name = "RESUSCITATE",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_big_warder_update_down_state_,
|
||||
},
|
||||
{0},
|
||||
};
|
||||
|
||||
static void lochara_big_warder_update_wait_strategy_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
static const float range2 = WAKE_UP_RANGE_*WAKE_UP_RANGE_;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
vec2_t disp;
|
||||
locommon_position_sub(
|
||||
&disp, &base->player->entity->super.super.pos, &base->super.super.pos);
|
||||
|
||||
if (fabsf(disp.y) < HEIGHT_/2 && vec2_pow_length(&disp) < range2) {
|
||||
loeffect_recipient_apply_effect(
|
||||
&base->param.recipient, &loeffect_resuscitate());
|
||||
if (loplayer_stance_set_has(&base->player->stances, REWARD_STANCE_)) {
|
||||
*next = LOCHARA_STRATEGY_WAKE_UP;
|
||||
} else {
|
||||
*next = LOCHARA_STRATEGY_WAKE_UP_EVENT;
|
||||
}
|
||||
return;
|
||||
}
|
||||
statman_transition_to(
|
||||
state_table_, instance, &base->param.state, LOCHARA_STATE_DEAD);
|
||||
}
|
||||
|
||||
static void lochara_big_warder_initialize_wake_up_strategy_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
lochara_strategy_initialize_any_(meta, instance, next);
|
||||
if (meta->state != LOCHARA_STRATEGY_WAKE_UP_EVENT) return;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
loentity_character_apply_effect(
|
||||
&base->super, &loeffect_fanatic(MUSIC_DURATION_));
|
||||
loentity_character_apply_effect(
|
||||
&base->player->entity->super, &loeffect_curse(MUSIC_DURATION_));
|
||||
}
|
||||
|
||||
static void lochara_big_warder_update_wake_up_strategy_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
const bool ev = meta->state == LOCHARA_STRATEGY_WAKE_UP_EVENT;
|
||||
const uint64_t dur = (ev? BEAT_MS_*16: BEAT_MS_*4);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
const uint64_t t = base->ticker->time - base->param.last_strategy_changed;
|
||||
if (t >= dur) {
|
||||
*next = LOCHARA_STRATEGY_APPROACH;
|
||||
return;
|
||||
}
|
||||
statman_transition_to(
|
||||
state_table_, instance, &base->param.state, LOCHARA_STATE_RESUSCITATE);
|
||||
}
|
||||
|
||||
static void lochara_big_warder_update_approach_strategy_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
if (!loeffect_recipient_is_alive(&base->param.recipient)) {
|
||||
*next = LOCHARA_STRATEGY_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t since = base->param.last_strategy_changed;
|
||||
|
||||
uint64_t until = since + BEAT_MS_;
|
||||
if (base->player->event.executor == base->super.super.id) {
|
||||
const uint64_t msince = base->player->event.ctx.music.since;
|
||||
if (msince < since) {
|
||||
const uint64_t beats = (since - msince)/BEAT_MS_ + 1;
|
||||
until = msince + beats*BEAT_MS_;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- strategy transition ---- */
|
||||
const locommon_position_t* player = &base->player->entity->super.super.pos;
|
||||
|
||||
vec2_t disp;
|
||||
locommon_position_sub(&disp, player, &base->super.super.pos);
|
||||
|
||||
if (player->chunk.x != base->super.super.pos.chunk.x ||
|
||||
player->chunk.y != base->super.super.pos.chunk.y ||
|
||||
disp.y < -HEIGHT_) {
|
||||
*next = LOCHARA_STRATEGY_WAIT;
|
||||
return;
|
||||
}
|
||||
|
||||
const float dist = MATH_ABS(disp.x);
|
||||
if (base->ticker->time >= until) {
|
||||
if (MATH_ABS(disp.y) > HEIGHT_) {
|
||||
*next = LOCHARA_STRATEGY_SHOOT1;
|
||||
} else if (dist < WIDTH_*5) {
|
||||
*next = LOCHARA_STRATEGY_COMBO1;
|
||||
if (chaos_xorshift(base->ticker->time)%3 == 0) {
|
||||
*next = LOCHARA_STRATEGY_COMBO2;
|
||||
}
|
||||
} else if (dist < WIDTH_*10) {
|
||||
*next = LOCHARA_STRATEGY_COMBO2;
|
||||
} else {
|
||||
*next = LOCHARA_STRATEGY_SHOOT1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (dist > WIDTH_*6) {
|
||||
base->param.direction = vec2(MATH_SIGN_NOZERO(disp.x), 0);
|
||||
}
|
||||
}
|
||||
|
||||
static const lochara_combat_action_t combo1_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1.2f,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_DODGE_LEFT,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t combo2_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_,
|
||||
.state = LOCHARA_STATE_TELEPORT_FRONT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_DODGE_RIGHT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_,
|
||||
.state = LOCHARA_STATE_TELEPORT_BEHIND,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t combo3_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_DODGE_RIGHT,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t shoot1_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_,
|
||||
.state = LOCHARA_STATE_SHOOT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_DODGE_RIGHT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_,
|
||||
.state = LOCHARA_STATE_SHOOT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_DODGE_LEFT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/2*3,
|
||||
.state = LOCHARA_STATE_STAND,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t down_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_,
|
||||
.state = LOCHARA_STATE_DOWN,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_*3,
|
||||
.state = LOCHARA_STATE_REVIVE,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t kill_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_*4,
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t dead_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = 30000,
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const statman_meta_t strategy_table_[] = {
|
||||
{
|
||||
.state = LOCHARA_STRATEGY_WAIT,
|
||||
.name = "WAIT",
|
||||
.initialize = lochara_strategy_initialize_any_,
|
||||
.update = lochara_big_warder_update_wait_strategy_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STRATEGY_WAKE_UP,
|
||||
.name = "WAKE_UP",
|
||||
.initialize = lochara_strategy_initialize_any_,
|
||||
.update = lochara_big_warder_update_wake_up_strategy_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STRATEGY_WAKE_UP_EVENT,
|
||||
.name = "WAKE_UP_EVENT",
|
||||
.initialize = lochara_big_warder_initialize_wake_up_strategy_,
|
||||
.update = lochara_big_warder_update_wake_up_strategy_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STRATEGY_APPROACH,
|
||||
.name = "APPROACH",
|
||||
.initialize = lochara_strategy_initialize_any_,
|
||||
.update = lochara_big_warder_update_approach_strategy_,
|
||||
},
|
||||
|
||||
lochara_strategy_combat(COMBO1,
|
||||
.state_table = state_table_,
|
||||
.actions = combo1_,
|
||||
.parry_window = 100,
|
||||
.parried_next = LOCHARA_STRATEGY_COMBO3,
|
||||
.next = LOCHARA_STRATEGY_APPROACH,
|
||||
),
|
||||
lochara_strategy_combat(COMBO2,
|
||||
.state_table = state_table_,
|
||||
.actions = combo2_,
|
||||
.parry_window = 100,
|
||||
.parried_next = LOCHARA_STRATEGY_DOWN,
|
||||
.next = LOCHARA_STRATEGY_COMBO3,
|
||||
),
|
||||
lochara_strategy_combat(COMBO3,
|
||||
.state_table = state_table_,
|
||||
.actions = combo3_,
|
||||
.parried_next = LOCHARA_STRATEGY_DOWN,
|
||||
.next = LOCHARA_STRATEGY_APPROACH,
|
||||
),
|
||||
lochara_strategy_combat(SHOOT1,
|
||||
.state_table = state_table_,
|
||||
.actions = shoot1_,
|
||||
.parry_window = 200,
|
||||
.parried_next = LOCHARA_STRATEGY_DOWN,
|
||||
.gravity = true,
|
||||
.next = LOCHARA_STRATEGY_APPROACH,
|
||||
),
|
||||
|
||||
lochara_strategy_combat(DOWN,
|
||||
.state_table = state_table_,
|
||||
.actions = down_,
|
||||
.gravity = true,
|
||||
.next = LOCHARA_STRATEGY_APPROACH,
|
||||
),
|
||||
lochara_strategy_combat(KILL,
|
||||
.state_table = state_table_,
|
||||
.actions = kill_,
|
||||
.gravity = true,
|
||||
.next = LOCHARA_STRATEGY_WAIT,
|
||||
),
|
||||
lochara_strategy_combat(DEAD,
|
||||
.state_table = state_table_,
|
||||
.actions = dead_,
|
||||
.gravity = true,
|
||||
.next = LOCHARA_STRATEGY_WAIT,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static void lochara_big_warder_update_event_(lochara_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const loplayer_event_command_t wake_up[] = {
|
||||
loplayer_event_command_play_music(LORESOURCE_MUSIC_ID_BOSS_BIG_WARDER),
|
||||
loplayer_event_command_set_area(.49f, .45f),
|
||||
loplayer_event_command_set_cinescope(1),
|
||||
loplayer_event_command_wait(BEAT_MS_*4),
|
||||
|
||||
loplayer_event_command_set_line("boss_big_warder_line0"),
|
||||
loplayer_event_command_wait(BEAT_MS_*8),
|
||||
loplayer_event_command_set_line("boss_big_warder_line1"),
|
||||
loplayer_event_command_wait(BEAT_MS_*4),
|
||||
|
||||
loplayer_event_command_set_line(NULL),
|
||||
loplayer_event_command_set_cinescope(0),
|
||||
{0},
|
||||
};
|
||||
static const loplayer_event_command_t kill[] = {
|
||||
loplayer_event_command_set_area(0, 0),
|
||||
loplayer_event_command_set_cinescope(1),
|
||||
loplayer_event_command_wait(BEAT_MS_),
|
||||
loplayer_event_command_stop_music(),
|
||||
|
||||
loplayer_event_command_set_line("boss_big_warder_kill_line"),
|
||||
loplayer_event_command_wait(BEAT_MS_*4),
|
||||
|
||||
loplayer_event_command_finalize(),
|
||||
{0},
|
||||
};
|
||||
static const loplayer_event_command_t dead[] = {
|
||||
loplayer_event_command_set_area(0, 0),
|
||||
loplayer_event_command_wait(BEAT_MS_),
|
||||
loplayer_event_command_stop_music(),
|
||||
loplayer_event_command_finalize(),
|
||||
{0},
|
||||
};
|
||||
|
||||
const uint64_t t = base->ticker->time;
|
||||
|
||||
loplayer_event_t* event = &base->player->event;
|
||||
const loentity_id_t id = base->super.super.id;
|
||||
|
||||
locommon_position_t basepos = base->super.super.pos;
|
||||
basepos.fract = vec2(.5f, .5f);
|
||||
|
||||
if (base->param.strategy == LOCHARA_STRATEGY_WAKE_UP_EVENT) {
|
||||
loplayer_event_execute_commands(event, id, &basepos, wake_up);
|
||||
return;
|
||||
}
|
||||
if (event->executor != id) return;
|
||||
|
||||
if (base->player->entity->param.state == LOCHARA_STATE_DEAD) {
|
||||
statman_transition_to(
|
||||
strategy_table_, base, &base->param.strategy, LOCHARA_STRATEGY_KILL);
|
||||
loplayer_event_execute_commands(event, id, &basepos, kill);
|
||||
return;
|
||||
}
|
||||
if (base->param.strategy == LOCHARA_STRATEGY_DEAD) {
|
||||
loplayer_event_execute_commands(event, id, &basepos, dead);
|
||||
if (!loplayer_stance_set_has(&base->player->stances, REWARD_STANCE_)) {
|
||||
loplayer_stance_set_add(&base->player->stances, REWARD_STANCE_);
|
||||
loplayer_popup_queue_new_stance(&base->player->popup, REWARD_STANCE_);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (base->player->event.basetime+MUSIC_DURATION_ <= t &&
|
||||
loeffect_recipient_is_alive(&base->param.recipient)) {
|
||||
loentity_character_apply_effect(
|
||||
&base->player->entity->super, &loeffect_curse_trigger());
|
||||
return;
|
||||
}
|
||||
loplayer_event_execute_commands(event, id, &basepos, wake_up);
|
||||
}
|
||||
|
||||
bool lochara_big_warder_update(lochara_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
loeffect_recipient_update(&base->param.recipient, &base_status_);
|
||||
|
||||
statman_update(strategy_table_, base, &base->param.strategy);
|
||||
lochara_big_warder_update_event_(base);
|
||||
|
||||
const float dir = MATH_SIGN_NOZERO(base->param.direction.x);
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_WARDER,
|
||||
.pos = vec2(0, -MARKER_),
|
||||
.size = vec2(dir*HEIGHT_, HEIGHT_),
|
||||
.color = COLOR_,
|
||||
.marker_offset = vec2(0, MARKER_),
|
||||
};
|
||||
statman_update(state_table_, base, &base->param.state);
|
||||
|
||||
if (base->param.strategy != LOCHARA_STRATEGY_WAIT &&
|
||||
base->param.strategy != LOCHARA_STRATEGY_WAKE_UP &&
|
||||
base->param.strategy != LOCHARA_STRATEGY_WAKE_UP_EVENT &&
|
||||
base->param.strategy != LOCHARA_STRATEGY_DEAD) {
|
||||
base->cache.instance.marker = lochara_base_affect_bullets(base);
|
||||
}
|
||||
|
||||
lochara_base_calculate_physics(
|
||||
base, &vec2(WIDTH_, HEIGHT_), &vec2(0, MARKER_));
|
||||
lochara_base_bind_on_ground(base, &vec2(WIDTH_, HEIGHT_+MARKER_));
|
||||
return true;
|
||||
}
|
||||
|
||||
void lochara_big_warder_build(
|
||||
lochara_base_t* base, loentity_ground_t* gnd) {
|
||||
assert(base != NULL);
|
||||
assert(gnd != NULL);
|
||||
|
||||
base->super.super.pos = gnd->super.pos;
|
||||
vec2_addeq(&base->super.super.pos.fract, &vec2(0, gnd->size.y));
|
||||
locommon_position_reduce(&base->super.super.pos);
|
||||
|
||||
base->param = (typeof(base->param)) {
|
||||
.type = LOCHARA_TYPE_BIG_WARDER,
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
.last_state_changed = base->ticker->time,
|
||||
.strategy = LOCHARA_STRATEGY_WAIT,
|
||||
.last_strategy_changed = base->ticker->time,
|
||||
|
||||
.ground = gnd->super.id,
|
||||
};
|
||||
loeffect_recipient_initialize(
|
||||
&base->param.recipient, base->ticker, &base_status_);
|
||||
}
|
18
core/lochara/big_warder.h
Normal file
18
core/lochara/big_warder.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "core/loentity/ground.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
bool
|
||||
lochara_big_warder_update(
|
||||
lochara_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
lochara_big_warder_build(
|
||||
lochara_base_t* base,
|
||||
loentity_ground_t* gnd
|
||||
);
|
305
core/lochara/cavia.c
Normal file
305
core/lochara/cavia.c
Normal file
@@ -0,0 +1,305 @@
|
||||
#include "./cavia.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/statman/statman.h"
|
||||
|
||||
#include "core/lobullet/base.h"
|
||||
#include "core/lobullet/linear.h"
|
||||
#include "core/loeffect/effect.h"
|
||||
#include "core/loeffect/recipient.h"
|
||||
#include "core/loentity/ground.h"
|
||||
#include "core/loresource/sound.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./combat.h"
|
||||
|
||||
#define WIDTH_ .02f
|
||||
#define HEIGHT_ .05f
|
||||
#define MARKER_ .02f
|
||||
#define COLOR_ vec4(0, 0, 0, 1)
|
||||
|
||||
static const loeffect_recipient_status_t base_status_ = {
|
||||
.attack = .1f,
|
||||
.defence = .3f,
|
||||
.speed = .05f,
|
||||
.jump = 1.f,
|
||||
};
|
||||
|
||||
static void lochara_cavia_update_thrust_in_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
static const uint64_t dur = 100;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
if (t > dur) t = dur;
|
||||
|
||||
const float tf = t*1.f / dur;
|
||||
base->cache.instance.motion.time = powf(tf, 2);
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
}
|
||||
|
||||
static void lochara_cavia_update_thrust_out_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
static const uint64_t dur = 100;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
if (t > dur) t = dur;
|
||||
|
||||
const float tf = t*1.f / dur;
|
||||
base->cache.instance.motion.time = powf(tf, 2);
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
}
|
||||
|
||||
static void lochara_cavia_initialize_dead_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
lochara_state_initialize_any_(meta, instance, state);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
for (size_t i = 0; i < 12; ++i) {
|
||||
const float t = MATH_PI/6*i;
|
||||
|
||||
vec2_t v, a, p;
|
||||
p = vec2(cos(t), sin(t));
|
||||
vec2_mul(&v, &p, .7f);
|
||||
vec2_mul(&a, &p, -1.4f);
|
||||
vec2_muleq(&p, .1f);
|
||||
|
||||
locommon_position_t pos = base->super.super.pos;
|
||||
vec2_addeq(&pos.fract, &p);
|
||||
locommon_position_reduce(&pos);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(base->bullet);
|
||||
lobullet_linear_triangle_build(b,
|
||||
.owner = b->super.super.id,
|
||||
.basepos = pos,
|
||||
.velocity = v,
|
||||
.acceleration = a,
|
||||
.size = vec2(.01f, .045f),
|
||||
.color = vec4(0, 0, 0, .8f),
|
||||
.angle = t-MATH_PI,
|
||||
.knockback = 1,
|
||||
.effect = loeffect_damage(.1f),
|
||||
.duration = 1200,
|
||||
);
|
||||
loentity_store_add(base->entities, &b->super.super);
|
||||
}
|
||||
loresource_sound_set_play(&base->res->sound, LORESOURCE_SOUND_ID_ENEMY_TRIGGER);
|
||||
}
|
||||
|
||||
static void lochara_cavia_update_dead_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
base->param.movement = vec2(0, 0);
|
||||
|
||||
uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
if (t > 2000) {
|
||||
t = 2000;
|
||||
}
|
||||
if (meta->state == LOCHARA_STATE_RESUSCITATE) {
|
||||
if (t == 0) {
|
||||
loeffect_recipient_apply_effect(
|
||||
&base->param.recipient, &loeffect_resuscitate());
|
||||
}
|
||||
t = 2000 - t;
|
||||
}
|
||||
|
||||
if (t < 1000) {
|
||||
const float tf = t/1000.f;
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
base->cache.instance.motion.time = powf(tf, 6);
|
||||
} else {
|
||||
const float tf = (t-1000)/1000.f;
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
base->cache.instance.motion.time = powf(tf, 4);
|
||||
}
|
||||
}
|
||||
|
||||
static const statman_meta_t state_table_[] = {
|
||||
lochara_state_stand(
|
||||
.period = 4000,
|
||||
.acceleration = {{5, 5}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND2,
|
||||
),
|
||||
lochara_state_walk(
|
||||
.period = 1000,
|
||||
.acceleration = {{5, 5}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_WALK,
|
||||
),
|
||||
{
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.name = "THRUST_IN",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_cavia_update_thrust_in_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
.name = "THRUST_OUT",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_cavia_update_thrust_out_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
.name = "DEAD",
|
||||
.initialize = lochara_cavia_initialize_dead_,
|
||||
.update = lochara_cavia_update_dead_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_RESUSCITATE,
|
||||
.name = "RESUSCITATE",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_cavia_update_dead_,
|
||||
},
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t combo1_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = 300,
|
||||
.state = LOCHARA_STATE_STAND,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = 300,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1.2f,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = 100,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = 150,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = .8f,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = 150,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = 150,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = 1000,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t dead_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = 30000,
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = 2000,
|
||||
.state = LOCHARA_STATE_RESUSCITATE,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const statman_meta_t strategy_table_[] = {
|
||||
lochara_strategy_scouting(
|
||||
.state_table = state_table_,
|
||||
.period = 2000,
|
||||
.stagger = .5f,
|
||||
.range_close = WIDTH_*2,
|
||||
.found_close = LOCHARA_STRATEGY_COMBO1,
|
||||
.range_back = 1,
|
||||
),
|
||||
lochara_strategy_combat(COMBO1,
|
||||
.state_table = state_table_,
|
||||
.actions = combo1_,
|
||||
.next = LOCHARA_STRATEGY_SCOUTING,
|
||||
),
|
||||
lochara_strategy_combat(DEAD,
|
||||
.state_table = state_table_,
|
||||
.actions = dead_,
|
||||
.next = LOCHARA_STRATEGY_SCOUTING,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
bool lochara_cavia_update(lochara_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
loeffect_recipient_update(&base->param.recipient, &base_status_);
|
||||
|
||||
statman_update(strategy_table_, base, &base->param.strategy);
|
||||
|
||||
const float dir = MATH_SIGN_NOZERO(base->param.direction.x);
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_CAVIA,
|
||||
.pos = vec2(0, -MARKER_),
|
||||
.size = vec2(dir*HEIGHT_, HEIGHT_),
|
||||
.color = COLOR_,
|
||||
.marker_offset = vec2(0, MARKER_),
|
||||
};
|
||||
statman_update(state_table_, base, &base->param.state);
|
||||
|
||||
if (base->param.state != LOCHARA_STATE_DEAD) {
|
||||
base->cache.instance.marker = lochara_base_affect_bullets(base);
|
||||
}
|
||||
|
||||
lochara_base_calculate_physics(
|
||||
base, &vec2(WIDTH_, HEIGHT_), &vec2(0, MARKER_));
|
||||
lochara_base_bind_on_ground(base, &vec2(WIDTH_, HEIGHT_+MARKER_));
|
||||
return true;
|
||||
}
|
||||
|
||||
void lochara_cavia_build(
|
||||
lochara_base_t* base, const loentity_ground_t* gnd, float pos) {
|
||||
assert(base != NULL);
|
||||
assert(gnd != NULL);
|
||||
assert(MATH_FLOAT_VALID(pos));
|
||||
|
||||
base->super.super.pos = gnd->super.pos;
|
||||
vec2_addeq(
|
||||
&base->super.super.pos.fract,
|
||||
&vec2(gnd->size.x*pos, gnd->size.y));
|
||||
locommon_position_reduce(&base->super.super.pos);
|
||||
|
||||
base->param = (typeof(base->param)) {
|
||||
.type = LOCHARA_TYPE_CAVIA,
|
||||
.state = LOCHARA_STATE_STAND,
|
||||
.last_state_changed = base->ticker->time,
|
||||
.strategy = LOCHARA_STRATEGY_SCOUTING,
|
||||
.last_strategy_changed = base->ticker->time,
|
||||
|
||||
.ground = gnd->super.id,
|
||||
};
|
||||
loeffect_recipient_initialize(
|
||||
&base->param.recipient, base->ticker, &base_status_);
|
||||
}
|
19
core/lochara/cavia.h
Normal file
19
core/lochara/cavia.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "core/loentity/ground.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
bool
|
||||
lochara_cavia_update(
|
||||
lochara_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
lochara_cavia_build(
|
||||
lochara_base_t* base,
|
||||
const loentity_ground_t* gnd,
|
||||
float pos
|
||||
);
|
79
core/lochara/combat.c
Normal file
79
core/lochara/combat.c
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "./combat.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
|
||||
#include "core/loeffect/effect.h"
|
||||
#include "core/loentity/character.h"
|
||||
#include "core/loplayer/combat.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./state.h"
|
||||
|
||||
static void lochara_combat_action_handle_attack_(
|
||||
const loplayer_combat_attack_t* attack,
|
||||
loplayer_combat_attack_result_t result) {
|
||||
assert(attack != NULL);
|
||||
|
||||
lochara_base_t* attacker = attack->data1;
|
||||
/* TODO(catfoot): store the result in the attacker */
|
||||
if (result != LOPLAYER_COMBAT_ATTACK_RESULT_EXECUTED) return;
|
||||
|
||||
const float v = attacker->param.recipient.status.attack*
|
||||
MATH_SIGN_NOZERO(attacker->param.direction.x);
|
||||
loentity_character_knockback(&attacker->player->entity->super, &vec2(v, 0));
|
||||
|
||||
const lochara_combat_action_t* action = attack->data2;
|
||||
|
||||
switch (action->type) {
|
||||
case LOCHARA_COMBAT_ACTION_TYPE_REST:
|
||||
break;
|
||||
case LOCHARA_COMBAT_ACTION_TYPE_ATTACK:
|
||||
loentity_character_apply_effect(
|
||||
&attacker->player->entity->super,
|
||||
&loeffect_damage(
|
||||
action->damage*attacker->param.recipient.status.attack));
|
||||
break;
|
||||
case LOCHARA_COMBAT_ACTION_TYPE_ATTACK_EFFECT:
|
||||
loentity_character_apply_effect(
|
||||
&attacker->player->entity->super, &action->effect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void lochara_combat_action_execute_all_attacks(
|
||||
const lochara_combat_action_t* actions, lochara_base_t* attacker) {
|
||||
assert(actions != NULL);
|
||||
assert(attacker != NULL);
|
||||
|
||||
loplayer_combat_t* combat = &attacker->player->combat;
|
||||
|
||||
uint64_t t = attacker->ticker->time;
|
||||
for (; actions->duration != 0; t += actions->duration, ++actions) {
|
||||
if (actions->type == LOCHARA_COMBAT_ACTION_TYPE_REST) continue;
|
||||
loplayer_combat_add_attack(
|
||||
combat,
|
||||
&(loplayer_combat_attack_t) {
|
||||
.attacker = attacker->super.super.id,
|
||||
.start = t,
|
||||
.duration = actions->duration,
|
||||
.data1 = attacker,
|
||||
.data2 = (void*) actions,
|
||||
.handle = lochara_combat_action_handle_attack_,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const lochara_combat_action_t* lochara_combat_action_find_by_time(
|
||||
const lochara_combat_action_t* actions, uint64_t time) {
|
||||
assert(actions != NULL);
|
||||
|
||||
while (actions->duration != 0 && actions->duration <= time) {
|
||||
time -= actions->duration;
|
||||
++actions;
|
||||
}
|
||||
return actions->duration > 0? actions: NULL;
|
||||
}
|
51
core/lochara/combat.h
Normal file
51
core/lochara/combat.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "core/loeffect/effect.h"
|
||||
|
||||
#include "./state.h"
|
||||
|
||||
typedef struct lochara_base_t lochara_base_t;
|
||||
|
||||
typedef enum {
|
||||
LOCHARA_COMBAT_ACTION_TYPE_REST,
|
||||
LOCHARA_COMBAT_ACTION_TYPE_ATTACK,
|
||||
LOCHARA_COMBAT_ACTION_TYPE_ATTACK_EFFECT,
|
||||
} lochara_combat_action_type_t;
|
||||
|
||||
typedef struct {
|
||||
lochara_combat_action_type_t type;
|
||||
lochara_state_t state;
|
||||
uint64_t duration;
|
||||
|
||||
union {
|
||||
float damage;
|
||||
loeffect_t effect;
|
||||
};
|
||||
} lochara_combat_action_t;
|
||||
|
||||
#define lochara_combat_action_rest(...) { \
|
||||
.type = LOCHARA_COMBAT_ACTION_TYPE_REST, \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
#define lochara_combat_action_attack(...) { \
|
||||
.type = LOCHARA_COMBAT_ACTION_TYPE_ATTACK, \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
#define lochara_combat_action_attack_effect(...) { \
|
||||
.type = LOCHARA_COMBAT_ACTION_TYPE_ATTACK_EFFECT, \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
|
||||
void
|
||||
lochara_combat_action_execute_all_attacks(
|
||||
const lochara_combat_action_t* actions,
|
||||
lochara_base_t* attacker
|
||||
);
|
||||
|
||||
const lochara_combat_action_t* /* NULLABLE */
|
||||
lochara_combat_action_find_by_time(
|
||||
const lochara_combat_action_t* actions,
|
||||
uint64_t t
|
||||
);
|
85
core/lochara/encephalon.c
Normal file
85
core/lochara/encephalon.c
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "./encephalon.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/chaos/xorshift.h"
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/loeffect/effect.h"
|
||||
#include "core/loeffect/recipient.h"
|
||||
#include "core/loentity/ground.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
#define WIDTH_ .1f
|
||||
#define HEIGHT_ .1f
|
||||
#define RANGE_ .15f
|
||||
#define COLOR_ vec4(0, 0, 0, .8f)
|
||||
|
||||
bool lochara_encephalon_update(lochara_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_ENCEPHALON,
|
||||
.size = vec2(WIDTH_, HEIGHT_),
|
||||
.color = COLOR_,
|
||||
};
|
||||
|
||||
vec2_t disp;
|
||||
locommon_position_sub(
|
||||
&disp, &base->player->entity->super.super.pos, &base->super.super.pos);
|
||||
|
||||
const bool active = fabsf(disp.x) < RANGE_ && fabsf(disp.y) < HEIGHT_;
|
||||
if (active) {
|
||||
if (base->param.state != LOCHARA_STATE_SPELL) {
|
||||
loresource_sound_set_play(
|
||||
&base->res->sound, LORESOURCE_SOUND_ID_TOUCH_GATE);
|
||||
base->param.last_state_changed = base->ticker->time;
|
||||
}
|
||||
base->param.state = LOCHARA_STATE_SPELL;
|
||||
|
||||
loentity_character_apply_effect(
|
||||
&base->player->entity->super, &loeffect_resuscitate());
|
||||
base->player->entity->param.anchor.pos =
|
||||
base->player->entity->super.super.pos;
|
||||
|
||||
if (base->param.last_state_changed+500 > base->ticker->time) {
|
||||
base->cache.instance.color.w = chaos_xorshift(base->ticker->time)%3 >= 1;
|
||||
}
|
||||
} else {
|
||||
base->param.state = LOCHARA_STATE_STAND;
|
||||
}
|
||||
|
||||
if (base->cache.ground == NULL) return false;
|
||||
base->super.super.pos = base->cache.ground->super.pos;
|
||||
base->super.super.pos.fract.y += base->cache.ground->size.y + HEIGHT_;
|
||||
locommon_position_reduce(&base->super.super.pos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void lochara_encephalon_build(
|
||||
lochara_base_t* base, const loentity_ground_t* gnd) {
|
||||
assert(base != NULL);
|
||||
assert(gnd != NULL);
|
||||
|
||||
base->super.super.pos = gnd->super.pos;
|
||||
base->super.super.pos.fract.y += gnd->size.y + HEIGHT_;
|
||||
locommon_position_reduce(&base->super.super.pos);
|
||||
|
||||
base->param = (typeof(base->param)) {
|
||||
.type = LOCHARA_TYPE_ENCEPHALON,
|
||||
.state = LOCHARA_STATE_STAND,
|
||||
.last_state_changed = base->ticker->time,
|
||||
.ground = gnd->super.id,
|
||||
};
|
||||
loeffect_recipient_initialize(
|
||||
&base->param.recipient,
|
||||
base->ticker,
|
||||
&(loeffect_recipient_status_t) {0});
|
||||
}
|
18
core/lochara/encephalon.h
Normal file
18
core/lochara/encephalon.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "core/loentity/ground.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
bool
|
||||
lochara_encephalon_update(
|
||||
lochara_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
lochara_encephalon_build(
|
||||
lochara_base_t* base,
|
||||
const loentity_ground_t* gnd
|
||||
);
|
492
core/lochara/player.c
Normal file
492
core/lochara/player.c
Normal file
@@ -0,0 +1,492 @@
|
||||
#include "./player.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/statman/statman.h"
|
||||
|
||||
#include "core/lobullet/linear.h"
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/easing.h"
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/loeffect/effect.h"
|
||||
#include "core/loeffect/recipient.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loplayer/stance.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./state.h"
|
||||
#include "./type.h"
|
||||
|
||||
#define INITIAL_POS_ locommon_position(0, 0, vec2(.5f, .8f))
|
||||
|
||||
#define WIDTH_ .02f
|
||||
#define HEIGHT_ .05f
|
||||
#define MARKER_ .03f
|
||||
#define COLOR_ vec4(0, 0, 0, 1)
|
||||
|
||||
#define RESPAWN_DURATION_ 4000
|
||||
|
||||
#define BULLET_SIZE_ vec2(.02f, .02f)
|
||||
#define BULLET_COLOR_ vec4(1, .9f, .9f, 1)
|
||||
#define BULLET_KNOCKBACK_ 4
|
||||
#define BULLET_DAMAGE_ .2f
|
||||
#define BULLET_DURATION_ 1000
|
||||
#define BULLET_COST_ .06f
|
||||
|
||||
#define CAMERA_SPEED_ 10
|
||||
#define CAMERA_SHIFT_Y_ .1f
|
||||
|
||||
#define CAMERA_FADE_RADIAL_ .6f
|
||||
|
||||
#define CAMERA_DEAD_FADE_ 2
|
||||
#define CAMERA_DEAD_SPEED_ .3f
|
||||
#define CAMERA_DEAD_RECOVERY_SPEED_ 10
|
||||
|
||||
#define CAMERA_COMBAT_SPEED_ 10
|
||||
#define CAMERA_COMBAT_RECOVERY_SPEED_ 5
|
||||
|
||||
#define CAMERA_ENEMY_ATTACK_INTENSITY_ .2f
|
||||
#define CAMERA_ENEMY_ATTACK_SPEED_ 4
|
||||
|
||||
#define CAMERA_DAMAGE_INTENSITY_ .5f
|
||||
#define CAMERA_DAMAGE_DURATION_ 1000
|
||||
|
||||
#define CAMERA_ABERRATION_ 1
|
||||
|
||||
#define CAMERA_CORRUPTION_THRESH_ .2f
|
||||
#define CAMERA_CORRUPTION_PERIOD_ 4000
|
||||
|
||||
#define DIRECTION_X_EPSILON_ .005f
|
||||
|
||||
#define GUARD_THRESHOLD_ .8f
|
||||
#define BACKGUARD_THRESHOLD_ .95f
|
||||
|
||||
static const loeffect_recipient_status_t base_status_ = {
|
||||
.attack = .2f,
|
||||
.defence = .2f,
|
||||
.speed = .3f,
|
||||
.jump = 1.f,
|
||||
};
|
||||
|
||||
static void lochara_player_update_dead_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
base->param.movement = vec2(0, 0);
|
||||
|
||||
const uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
|
||||
if (t >= RESPAWN_DURATION_ && base->player->event.executor == 0) {
|
||||
loeffect_recipient_apply_effect(
|
||||
&base->param.recipient, &loeffect_resuscitate());
|
||||
base->super.super.pos = base->param.anchor.pos;
|
||||
|
||||
*next = LOCHARA_STATE_STAND;
|
||||
return;
|
||||
}
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
base->cache.instance.motion.time = 0;
|
||||
}
|
||||
|
||||
static void lochara_player_update_shoot_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
static const uint64_t dur = 200;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
const uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
const float dt = base->ticker->delta_f;
|
||||
|
||||
locommon_easing_linear_float(&base->param.movement.x, 0, dt);
|
||||
locommon_easing_linear_float(&base->param.movement.y, 0, dt);
|
||||
|
||||
const bool has_stance = loplayer_stance_set_has(
|
||||
&base->player->stances, LOPLAYER_STANCE_REVOLUTIONER);
|
||||
|
||||
if (has_stance && t >= dur && base->param.recipient.faith > 0) {
|
||||
locommon_position_t pos = base->super.super.pos;
|
||||
pos.fract.x += WIDTH_*MATH_SIGN_NOZERO(base->param.direction.x);
|
||||
locommon_position_reduce(&pos);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(base->bullet);
|
||||
lobullet_linear_circle_build(b,
|
||||
.owner = base->super.super.id,
|
||||
.basepos = pos,
|
||||
.size = BULLET_SIZE_,
|
||||
.color = BULLET_COLOR_,
|
||||
.velocity = base->param.direction,
|
||||
.knockback = BULLET_KNOCKBACK_,
|
||||
.effect = loeffect_damage(
|
||||
base->param.recipient.status.attack*BULLET_DAMAGE_),
|
||||
.duration = BULLET_DURATION_,);
|
||||
loentity_store_add(base->entities, &b->super.super);
|
||||
|
||||
loentity_character_apply_effect(
|
||||
&base->super, &loeffect_lost(BULLET_COST_));
|
||||
base->param.last_state_changed = base->ticker->time;
|
||||
}
|
||||
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_STAND2;
|
||||
base->cache.instance.motion.time = t > dur? 1: t*1.f / dur;
|
||||
}
|
||||
|
||||
static const statman_meta_t state_table_[] = {
|
||||
lochara_state_stand(
|
||||
.period = 2000,
|
||||
.acceleration = {{5, 5}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND2,
|
||||
),
|
||||
lochara_state_walk(
|
||||
.period = 350,
|
||||
.acceleration = {{5, 5}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_WALK,
|
||||
),
|
||||
lochara_state_sprint(
|
||||
.period = 300,
|
||||
.acceleration = {{5, 5}},
|
||||
.speed = 1.4f,
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_WALK,
|
||||
),
|
||||
lochara_state_dodge(
|
||||
.duration = 100,
|
||||
.acceleration = {{3, 3}},
|
||||
.speed = 1,
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_WALK,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
),
|
||||
lochara_state_jump(),
|
||||
|
||||
{
|
||||
.state = LOCHARA_STATE_GUARD,
|
||||
.name = "GUARD",
|
||||
.data = &(lochara_state_move_param_t) {
|
||||
.speed = 0,
|
||||
.period = 1000,
|
||||
.acceleration = {{10, 10}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
},
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_state_update_move_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_SHOOT,
|
||||
.name = "SHOOT",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_player_update_shoot_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
.name = "DEAD",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_player_update_dead_state_,
|
||||
.finalize = lochara_state_cancel_transition_,
|
||||
},
|
||||
{0},
|
||||
};
|
||||
|
||||
static void lochara_player_handle_controller_(lochara_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
lochara_state_t next = base->param.state;
|
||||
switch (base->player->controller.state) {
|
||||
case LOPLAYER_CONTROLLER_STATE_NONE:
|
||||
next = LOCHARA_STATE_STAND;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_STATE_WALK_LEFT:
|
||||
next = LOCHARA_STATE_WALK_LEFT;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_STATE_WALK_RIGHT:
|
||||
next = LOCHARA_STATE_WALK_RIGHT;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_STATE_SPRINT_LEFT:
|
||||
next = LOCHARA_STATE_SPRINT_LEFT;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_STATE_SPRINT_RIGHT:
|
||||
next = LOCHARA_STATE_SPRINT_RIGHT;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_STATE_DODGE_FORWARD:
|
||||
next =
|
||||
base->param.direction.x < 0?
|
||||
LOCHARA_STATE_DODGE_LEFT:
|
||||
LOCHARA_STATE_DODGE_RIGHT;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_STATE_DODGE_LEFT:
|
||||
next = LOCHARA_STATE_DODGE_LEFT;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_STATE_DODGE_RIGHT:
|
||||
next = LOCHARA_STATE_DODGE_RIGHT;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_STATE_JUMP:
|
||||
if (base->param.on_ground) next = LOCHARA_STATE_JUMP;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_STATE_GUARD:
|
||||
next = LOCHARA_STATE_GUARD;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_STATE_SHOOT:
|
||||
next = LOCHARA_STATE_SHOOT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (loplayer_combat_is_attack_pending(&base->player->combat)) {
|
||||
if (next != LOCHARA_STATE_STAND &&
|
||||
next != LOCHARA_STATE_DODGE_LEFT &&
|
||||
next != LOCHARA_STATE_DODGE_RIGHT &&
|
||||
next != LOCHARA_STATE_GUARD) {
|
||||
next = LOCHARA_STATE_STAND;
|
||||
}
|
||||
} else {
|
||||
vec2_t dir;
|
||||
locommon_position_sub(
|
||||
&dir, &base->player->controller.cursor, &base->super.super.pos);
|
||||
if (fabsf(dir.x) > DIRECTION_X_EPSILON_) {
|
||||
vec2_div(&base->param.direction, &dir, vec2_length(&dir));
|
||||
}
|
||||
}
|
||||
statman_transition_to(state_table_, base, &base->param.state, next);
|
||||
}
|
||||
|
||||
static void lochara_player_update_combat_(lochara_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
const lochara_state_t state = base->param.state;
|
||||
|
||||
if (state == LOCHARA_STATE_DEAD) {
|
||||
loplayer_combat_set_accepting(
|
||||
&base->player->combat,
|
||||
false,
|
||||
LOPLAYER_COMBAT_ATTACK_RESULT_ABORTED);
|
||||
} else {
|
||||
loplayer_combat_set_accepting(
|
||||
&base->player->combat,
|
||||
state != LOCHARA_STATE_DODGE_LEFT &&
|
||||
state != LOCHARA_STATE_DODGE_RIGHT,
|
||||
LOPLAYER_COMBAT_ATTACK_RESULT_DODGED);
|
||||
}
|
||||
|
||||
loplayer_combat_set_guarding(
|
||||
&base->player->combat,
|
||||
state == LOCHARA_STATE_GUARD);
|
||||
|
||||
if (loplayer_combat_is_attack_pending(&base->player->combat)) {
|
||||
base->param.movement = vec2(0, 0);
|
||||
base->param.gravity = 0;
|
||||
}
|
||||
|
||||
float r;
|
||||
loplayer_combat_attack_t a;
|
||||
if (!loplayer_combat_pop_attack(&base->player->combat, &a, &r)) return;
|
||||
|
||||
loentity_store_iterator_t itr;
|
||||
if (!loentity_store_find_item_by_id(base->entities, &itr, a.attacker) ||
|
||||
itr.character == NULL) {
|
||||
return;
|
||||
}
|
||||
vec2_t disp;
|
||||
locommon_position_sub(
|
||||
&disp, &itr.character->super.pos, &base->super.super.pos);
|
||||
|
||||
const float dir = disp.x * base->param.direction.x;
|
||||
const bool reflected =
|
||||
r > (dir < 0? BACKGUARD_THRESHOLD_: GUARD_THRESHOLD_);
|
||||
|
||||
loplayer_combat_attack_handle(
|
||||
&a,
|
||||
reflected?
|
||||
LOPLAYER_COMBAT_ATTACK_RESULT_REFLECTED:
|
||||
LOPLAYER_COMBAT_ATTACK_RESULT_EXECUTED);
|
||||
if (!reflected) return;
|
||||
|
||||
loresource_sound_set_play(&base->res->sound, LORESOURCE_SOUND_ID_REFLECTION);
|
||||
|
||||
loentity_character_apply_effect(
|
||||
itr.character,
|
||||
&loeffect_damage(base->param.recipient.status.attack));
|
||||
|
||||
loentity_character_knockback(
|
||||
itr.character,
|
||||
&vec2(MATH_SIGN(disp.x)*base->param.recipient.status.attack, 0));
|
||||
}
|
||||
|
||||
static void lochara_player_update_camera_(lochara_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
const loeffect_recipient_t* r = &base->param.recipient;
|
||||
const uint64_t t = base->ticker->time;
|
||||
const uint64_t pt = base->ticker->prev_time;
|
||||
const float dt = base->ticker->delta_f;
|
||||
|
||||
const bool combat = loplayer_combat_is_attack_pending(&base->player->combat);
|
||||
|
||||
loplayer_camera_t* camera = &base->player->camera;
|
||||
|
||||
vec2_t sz = base->player->camera.chunk_winsz;
|
||||
vec2_diveq(&sz,
|
||||
base->player->camera.scale*
|
||||
(camera->posteffect.distortion_radial+1));
|
||||
|
||||
locommon_position_t pos = base->super.super.pos;
|
||||
if (!combat) {
|
||||
pos.fract.y += CAMERA_SHIFT_Y_;
|
||||
locommon_position_reduce(&pos);
|
||||
}
|
||||
|
||||
loplayer_event_bind_rect_in_area(&base->player->event, &pos, &sz);
|
||||
|
||||
vec2_t disp;
|
||||
locommon_position_sub(&disp, &pos, &camera->pos);
|
||||
if (vec2_pow_length(&disp) < 1) {
|
||||
locommon_easing_smooth_position(&camera->pos, &pos, dt*CAMERA_SPEED_);
|
||||
} else {
|
||||
camera->pos = pos;
|
||||
}
|
||||
locommon_easing_smooth_float(&camera->scale, 1, dt);
|
||||
|
||||
/* ---- dead ---- */
|
||||
if (base->param.state == LOCHARA_STATE_DEAD) {
|
||||
locommon_easing_smooth_float(
|
||||
&camera->posteffect.fade_radial,
|
||||
CAMERA_DEAD_FADE_,
|
||||
dt*CAMERA_DEAD_SPEED_);
|
||||
} else {
|
||||
locommon_easing_smooth_float(
|
||||
&camera->posteffect.fade_radial,
|
||||
CAMERA_FADE_RADIAL_,
|
||||
dt*CAMERA_DEAD_RECOVERY_SPEED_);
|
||||
}
|
||||
|
||||
/* ---- combat ---- */
|
||||
if (combat) {
|
||||
locommon_easing_smooth_float(
|
||||
&camera->posteffect.distortion_radial,
|
||||
1,
|
||||
dt*CAMERA_COMBAT_SPEED_);
|
||||
} else {
|
||||
locommon_easing_smooth_float(
|
||||
&camera->posteffect.distortion_radial,
|
||||
0,
|
||||
dt*CAMERA_COMBAT_RECOVERY_SPEED_);
|
||||
}
|
||||
|
||||
/* ---- enemy attack ---- */
|
||||
const loplayer_combat_attack_t* attack = base->player->combat.first_attack;
|
||||
if (attack != NULL && pt < attack->start && attack->start <= t) {
|
||||
camera->posteffect.distortion_urgent = CAMERA_ENEMY_ATTACK_INTENSITY_;
|
||||
} else {
|
||||
locommon_easing_smooth_float(
|
||||
&camera->posteffect.distortion_urgent,
|
||||
0,
|
||||
dt*CAMERA_ENEMY_ATTACK_SPEED_);
|
||||
}
|
||||
|
||||
/* ---- damage ----- */
|
||||
if (0 < r->last_damage &&
|
||||
r->last_damage < t && t < r->last_damage+CAMERA_DAMAGE_DURATION_) {
|
||||
camera->posteffect.raster_whole =
|
||||
(1 - (t - r->last_damage)*1.f/CAMERA_DAMAGE_DURATION_)*
|
||||
CAMERA_DAMAGE_INTENSITY_;
|
||||
} else {
|
||||
camera->posteffect.raster_whole = 0;
|
||||
}
|
||||
|
||||
/* ---- amnesia ---- */
|
||||
locommon_easing_smooth_float(
|
||||
&camera->posteffect.distortion_amnesia,
|
||||
!!(r->effects.amnesia.start < t &&
|
||||
t < r->effects.amnesia.start+r->effects.amnesia.duration),
|
||||
dt);
|
||||
|
||||
/* ---- corruption ---- */
|
||||
if (r->madness <= CAMERA_CORRUPTION_THRESH_) {
|
||||
if (camera->corruption_since == 0) camera->corruption_since = t;
|
||||
const uint64_t p =
|
||||
(t - camera->corruption_since)%CAMERA_CORRUPTION_PERIOD_;
|
||||
camera->pixsort = 1-powf(p*1.f/CAMERA_CORRUPTION_PERIOD_, 2);
|
||||
} else {
|
||||
camera->corruption_since = 0;
|
||||
locommon_easing_smooth_float(&camera->pixsort, 0, dt);
|
||||
}
|
||||
|
||||
/* ---- passive ---- */
|
||||
locommon_easing_smooth_float(
|
||||
&camera->posteffect.aberration_radial, CAMERA_ABERRATION_, dt);
|
||||
}
|
||||
|
||||
bool lochara_player_update(lochara_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
loeffect_recipient_update(&base->param.recipient, &base_status_);
|
||||
if (!loeffect_recipient_is_alive(&base->param.recipient)) {
|
||||
statman_transition_to(
|
||||
state_table_, base, &base->param.state, LOCHARA_STATE_DEAD);
|
||||
}
|
||||
if (loplayer_stance_set_has(
|
||||
&base->player->stances, LOPLAYER_STANCE_UNFINISHER)) {
|
||||
if (base->param.recipient.faith > .5f) {
|
||||
loeffect_recipient_apply_effect(
|
||||
&base->param.recipient, &loeffect_heal(base->ticker->delta_f*.01f));
|
||||
}
|
||||
}
|
||||
|
||||
lochara_player_handle_controller_(base);
|
||||
|
||||
const float dir = MATH_SIGN_NOZERO(base->param.direction.x);
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_PLAYER,
|
||||
.pos = vec2(0, -MARKER_),
|
||||
.size = vec2(dir*HEIGHT_, HEIGHT_),
|
||||
.color = COLOR_,
|
||||
.marker_offset = vec2(0, MARKER_),
|
||||
};
|
||||
|
||||
statman_update(state_table_, base, &base->param.state);
|
||||
|
||||
lochara_player_update_combat_(base);
|
||||
|
||||
if (base->param.state != LOCHARA_STATE_DODGE_LEFT &&
|
||||
base->param.state != LOCHARA_STATE_DODGE_RIGHT &&
|
||||
base->param.state != LOCHARA_STATE_DEAD) {
|
||||
base->cache.instance.marker = lochara_base_affect_bullets(base);
|
||||
}
|
||||
|
||||
lochara_base_calculate_physics(
|
||||
base, &vec2(WIDTH_, HEIGHT_), &vec2(0, MARKER_));
|
||||
loplayer_event_bind_rect_in_area(
|
||||
&base->player->event,
|
||||
&base->super.super.pos,
|
||||
&vec2(WIDTH_*.8f, HEIGHT_));
|
||||
|
||||
lochara_player_update_camera_(base);
|
||||
return true;
|
||||
}
|
||||
|
||||
void lochara_player_build(lochara_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->super.super.pos = base->param.anchor.pos = INITIAL_POS_;
|
||||
|
||||
base->param = (typeof(base->param)) {
|
||||
.type = LOCHARA_TYPE_PLAYER,
|
||||
.state = LOCHARA_STATE_STAND,
|
||||
.last_state_changed = base->ticker->time,
|
||||
};
|
||||
loeffect_recipient_initialize(
|
||||
&base->param.recipient, base->ticker, &base_status_);
|
||||
}
|
15
core/lochara/player.h
Normal file
15
core/lochara/player.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
bool
|
||||
lochara_player_update(
|
||||
lochara_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
lochara_player_build(
|
||||
lochara_base_t* base
|
||||
);
|
58
core/lochara/pool.c
Normal file
58
core/lochara/pool.c
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "./pool.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/memory/memory.h"
|
||||
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/counter.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loentity/pool.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loshader/set.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
LOENTITY_POOL_SOURCE_TEMPLATE(lochara)
|
||||
|
||||
lochara_pool_t* lochara_pool_new(
|
||||
loresource_set_t* res,
|
||||
loshader_set_t* shaders,
|
||||
locommon_counter_t* idgen,
|
||||
const locommon_ticker_t* ticker,
|
||||
loentity_store_t* entities,
|
||||
loplayer_t* player,
|
||||
lobullet_pool_t* bullet,
|
||||
size_t length) {
|
||||
assert(res != NULL);
|
||||
assert(shaders != NULL);
|
||||
assert(idgen != NULL);
|
||||
assert(ticker != NULL);
|
||||
assert(entities != NULL);
|
||||
assert(player != NULL);
|
||||
assert(bullet != NULL);
|
||||
assert(length > 0);
|
||||
|
||||
lochara_pool_t* pool =
|
||||
memory_new(sizeof(*pool) + (length-1)*sizeof(pool->items[0]));
|
||||
*pool = (typeof(*pool)) {
|
||||
.idgen = idgen,
|
||||
.length = length,
|
||||
};
|
||||
for (size_t i = 0; i < pool->length; ++i) {
|
||||
lochara_base_initialize(
|
||||
&pool->items[i],
|
||||
res,
|
||||
shaders,
|
||||
ticker,
|
||||
entities,
|
||||
player,
|
||||
bullet);
|
||||
}
|
||||
return pool;
|
||||
}
|
46
core/lochara/pool.h
Normal file
46
core/lochara/pool.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/counter.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loshader/set.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
struct lochara_pool_t;
|
||||
typedef struct lochara_pool_t lochara_pool_t;
|
||||
|
||||
lochara_pool_t* /* OWNERSHIP */
|
||||
lochara_pool_new(
|
||||
loresource_set_t* res,
|
||||
loshader_set_t* shaders,
|
||||
locommon_counter_t* idgen,
|
||||
const locommon_ticker_t* ticker,
|
||||
loentity_store_t* entities,
|
||||
loplayer_t* player,
|
||||
lobullet_pool_t* bullet,
|
||||
size_t length
|
||||
);
|
||||
|
||||
void
|
||||
lochara_pool_delete(
|
||||
lochara_pool_t* pool /* OWNERSHIP */
|
||||
);
|
||||
|
||||
lochara_base_t*
|
||||
lochara_pool_create(
|
||||
lochara_pool_t* pool
|
||||
);
|
||||
|
||||
lochara_base_t*
|
||||
lochara_pool_unpack_item(
|
||||
lochara_pool_t* pool,
|
||||
const msgpack_object* obj
|
||||
);
|
179
core/lochara/state.c
Normal file
179
core/lochara/state.c
Normal file
@@ -0,0 +1,179 @@
|
||||
#include "./state.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/statman/statman.h"
|
||||
|
||||
#include "core/locommon/easing.h"
|
||||
#include "core/loresource/sound.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
#define MIDAIR_SPEED_ .8f
|
||||
#define BACKWALK_SPEED_ .6f
|
||||
|
||||
void lochara_state_initialize_any_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
base->param.last_state_changed = base->ticker->time;
|
||||
}
|
||||
void lochara_state_cancel_transition_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
*state = meta->state;
|
||||
}
|
||||
|
||||
void lochara_state_update_move_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
const lochara_state_move_param_t* p = meta->data;
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
const uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
const float dt = base->ticker->delta_f;
|
||||
|
||||
assert(vec2_valid(&p->acceleration));
|
||||
assert(MATH_FLOAT_VALID(p->speed));
|
||||
assert(vec2_valid(&p->velocity));
|
||||
assert(p->period > 0);
|
||||
|
||||
/* ---- acceleration ---- */
|
||||
float speed = p->speed*base->param.recipient.status.speed;
|
||||
if (!base->param.on_ground) {
|
||||
speed *= MIDAIR_SPEED_;
|
||||
}
|
||||
if (vec2_dot(&p->velocity, &base->param.direction) < 0) {
|
||||
speed *= BACKWALK_SPEED_;
|
||||
}
|
||||
|
||||
vec2_t velocity = p->velocity;
|
||||
vec2_muleq(&velocity, speed);
|
||||
|
||||
locommon_easing_linear_float(
|
||||
&base->param.movement.x, velocity.x, p->acceleration.x*dt);
|
||||
locommon_easing_linear_float(
|
||||
&base->param.movement.y, velocity.y, p->acceleration.y*dt);
|
||||
|
||||
/* ---- periodic motion ---- */
|
||||
base->cache.instance.motion.time =
|
||||
(!!base->param.on_ground)*(1-fabs(t%p->period*1.f/p->period*2 - 1));
|
||||
base->cache.instance.motion.from = p->motion1;
|
||||
base->cache.instance.motion.to = p->motion2;
|
||||
}
|
||||
|
||||
void lochara_state_initialize_dodge_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
lochara_state_initialize_any_(meta, instance, state);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
loresource_sound_set_play(&base->res->sound, LORESOURCE_SOUND_ID_DODGE);
|
||||
}
|
||||
|
||||
void lochara_state_update_dodge_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
const lochara_state_dodge_param_t* p = meta->data;
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
const uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
|
||||
assert(p->duration > 0);
|
||||
assert(vec2_valid(&p->acceleration));
|
||||
assert(vec2_valid(&p->velocity));
|
||||
|
||||
if (t > p->duration) {
|
||||
*state = LOCHARA_STATE_STAND;
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---- acceleration ---- */
|
||||
base->param.movement = p->acceleration;
|
||||
|
||||
vec2_muleq(&base->param.movement, t/1000.f);
|
||||
base->param.movement.x *= -MATH_SIGN(p->velocity.x);
|
||||
base->param.movement.y *= -MATH_SIGN(p->velocity.y);
|
||||
|
||||
vec2_addeq(&base->param.movement, &p->velocity);
|
||||
|
||||
/* ---- motion ---- */
|
||||
base->cache.instance.motion.time = powf(t*1.f/p->duration, 1.2f);
|
||||
base->cache.instance.motion.from = p->motion1;
|
||||
base->cache.instance.motion.to = p->motion2;
|
||||
|
||||
const float a = powf(t*2.f/p->duration-1, 2);
|
||||
base->cache.instance.size.x *= (1-a)*.8f+1;
|
||||
base->cache.instance.color.w *= MATH_MIN(a+.2f, 1);
|
||||
}
|
||||
|
||||
void lochara_state_initialize_jump_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
lochara_state_initialize_any_(meta, instance, next);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
base->param.gravity += base->param.recipient.status.jump;
|
||||
*next = LOCHARA_STATE_STAND;
|
||||
}
|
||||
|
||||
void lochara_state_update_teleport_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
const lochara_state_teleport_param_t* p = meta->data;
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
base->param.movement = vec2(0, 0);
|
||||
|
||||
const uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
const uint64_t pt =
|
||||
t == 0? 0: base->ticker->prev_time - base->param.last_state_changed;
|
||||
|
||||
if (pt < p->duration/2 && p->duration/2 <= t) {
|
||||
const lochara_base_t* player = base->player->entity;
|
||||
|
||||
const float pdir =
|
||||
MATH_SIGN_NOZERO(player->param.direction.x)*p->direction;
|
||||
base->param.direction = vec2(-pdir, 0);
|
||||
|
||||
vec2_t offset = p->offset;
|
||||
offset.x *= pdir;
|
||||
|
||||
base->super.super.pos = player->super.super.pos;
|
||||
vec2_addeq(&base->super.super.pos.fract, &offset);
|
||||
locommon_position_reduce(&base->super.super.pos);
|
||||
}
|
||||
|
||||
const float tf = fabsf(t*1.f/p->duration*2 - 1);
|
||||
base->cache.instance.motion.from = p->motion2;
|
||||
base->cache.instance.motion.to = p->motion1;
|
||||
base->cache.instance.motion.time = tf;
|
||||
base->cache.instance.color.w = tf;
|
||||
}
|
233
core/lochara/state.h
Normal file
233
core/lochara/state.h
Normal file
@@ -0,0 +1,233 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
#include "util/statman/statman.h"
|
||||
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
typedef enum {
|
||||
/* BENUM BEGIN lochara_state */
|
||||
LOCHARA_STATE_STAND,
|
||||
|
||||
LOCHARA_STATE_WALK_LEFT,
|
||||
LOCHARA_STATE_WALK_RIGHT,
|
||||
|
||||
LOCHARA_STATE_SPRINT_LEFT,
|
||||
LOCHARA_STATE_SPRINT_RIGHT,
|
||||
|
||||
LOCHARA_STATE_DODGE_LEFT,
|
||||
LOCHARA_STATE_DODGE_RIGHT,
|
||||
|
||||
LOCHARA_STATE_JUMP,
|
||||
|
||||
LOCHARA_STATE_TELEPORT_BEHIND,
|
||||
LOCHARA_STATE_TELEPORT_FRONT,
|
||||
|
||||
LOCHARA_STATE_THRUST_IN,
|
||||
LOCHARA_STATE_THRUST_OUT,
|
||||
LOCHARA_STATE_SLASH,
|
||||
LOCHARA_STATE_SPELL,
|
||||
LOCHARA_STATE_SHOOT,
|
||||
|
||||
LOCHARA_STATE_DOWN,
|
||||
LOCHARA_STATE_DEAD,
|
||||
LOCHARA_STATE_REVIVE,
|
||||
LOCHARA_STATE_RESUSCITATE,
|
||||
|
||||
LOCHARA_STATE_GUARD,
|
||||
/* BENUM END */
|
||||
} lochara_state_t;
|
||||
|
||||
/* generated benum utils */
|
||||
#include "core/lochara/benum/state.h"
|
||||
|
||||
|
||||
/* ---- default state handlers ---- */
|
||||
|
||||
void
|
||||
lochara_state_initialize_any_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
void
|
||||
lochara_state_cancel_transition_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
|
||||
void
|
||||
lochara_state_update_move_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
|
||||
void
|
||||
lochara_state_initialize_dodge_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
|
||||
void
|
||||
lochara_state_update_dodge_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
|
||||
void
|
||||
lochara_state_initialize_jump_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
|
||||
void
|
||||
lochara_state_update_teleport_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
|
||||
|
||||
/* ---- state meta constructor ---- */
|
||||
|
||||
typedef struct {
|
||||
vec2_t acceleration;
|
||||
float speed;
|
||||
vec2_t velocity;
|
||||
|
||||
uint64_t period;
|
||||
loshader_character_motion_id_t motion1;
|
||||
loshader_character_motion_id_t motion2;
|
||||
} lochara_state_move_param_t;
|
||||
#define lochara_state_stand(...) \
|
||||
(statman_meta_t) { \
|
||||
.state = LOCHARA_STATE_STAND, \
|
||||
.name = "STAND", \
|
||||
.data = &(lochara_state_move_param_t) { \
|
||||
.speed = 0, \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
.initialize = lochara_state_initialize_any_, \
|
||||
.update = lochara_state_update_move_, \
|
||||
}
|
||||
#define lochara_state_walk(...) \
|
||||
(statman_meta_t) { \
|
||||
.state = LOCHARA_STATE_WALK_LEFT, \
|
||||
.name = "WALK_LEFT", \
|
||||
.data = &(lochara_state_move_param_t) { \
|
||||
.speed = 1, \
|
||||
.velocity = {{-1, 0}}, \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
.initialize = lochara_state_initialize_any_, \
|
||||
.update = lochara_state_update_move_, \
|
||||
}, \
|
||||
(statman_meta_t) { \
|
||||
.state = LOCHARA_STATE_WALK_RIGHT, \
|
||||
.name = "WALK_RIGHT", \
|
||||
.data = &(lochara_state_move_param_t) { \
|
||||
.speed = 1, \
|
||||
.velocity = {{1, 0}}, \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
.initialize = lochara_state_initialize_any_, \
|
||||
.update = lochara_state_update_move_, \
|
||||
}
|
||||
#define lochara_state_sprint(...) \
|
||||
(statman_meta_t) { \
|
||||
.state = LOCHARA_STATE_SPRINT_LEFT, \
|
||||
.name = "SPRINT_LEFT", \
|
||||
.data = &(lochara_state_move_param_t) { \
|
||||
.velocity = {{-1, 0}}, \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
.initialize = lochara_state_initialize_any_, \
|
||||
.update = lochara_state_update_move_, \
|
||||
}, \
|
||||
(statman_meta_t) { \
|
||||
.state = LOCHARA_STATE_SPRINT_RIGHT, \
|
||||
.name = "SPRINT_RIGHT", \
|
||||
.data = &(lochara_state_move_param_t) { \
|
||||
.velocity = {{1, 0}}, \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
.initialize = lochara_state_initialize_any_, \
|
||||
.update = lochara_state_update_move_, \
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint64_t duration;
|
||||
float speed;
|
||||
vec2_t acceleration;
|
||||
vec2_t velocity;
|
||||
|
||||
loshader_character_motion_id_t motion1;
|
||||
loshader_character_motion_id_t motion2;
|
||||
} lochara_state_dodge_param_t;
|
||||
#define lochara_state_dodge(...) \
|
||||
(statman_meta_t) { \
|
||||
.state = LOCHARA_STATE_DODGE_LEFT, \
|
||||
.name = "DODGE_LEFT", \
|
||||
.data = &(lochara_state_dodge_param_t) { \
|
||||
.velocity = {{-1, 0}}, \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
.initialize = lochara_state_initialize_dodge_, \
|
||||
.update = lochara_state_update_dodge_, \
|
||||
.finalize = lochara_state_cancel_transition_, \
|
||||
}, \
|
||||
(statman_meta_t) { \
|
||||
.state = LOCHARA_STATE_DODGE_RIGHT, \
|
||||
.name = "DODGE_RIGHT", \
|
||||
.data = &(lochara_state_dodge_param_t) { \
|
||||
.velocity = {{1, 0}}, \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
.initialize = lochara_state_initialize_dodge_, \
|
||||
.update = lochara_state_update_dodge_, \
|
||||
.finalize = lochara_state_cancel_transition_, \
|
||||
}
|
||||
|
||||
#define lochara_state_jump() \
|
||||
(statman_meta_t) { \
|
||||
.state = LOCHARA_STATE_JUMP, \
|
||||
.name = "JUMP", \
|
||||
.initialize = lochara_state_initialize_jump_, \
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint64_t duration;
|
||||
float direction;
|
||||
vec2_t offset;
|
||||
|
||||
loshader_character_motion_id_t motion1;
|
||||
loshader_character_motion_id_t motion2;
|
||||
} lochara_state_teleport_param_t;
|
||||
#define lochara_state_teleport(...) \
|
||||
(statman_meta_t) { \
|
||||
.state = LOCHARA_STATE_TELEPORT_FRONT, \
|
||||
.name = "TELEPORT_FRONT", \
|
||||
.data = &(lochara_state_teleport_param_t) { \
|
||||
.direction = 1, \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
.initialize = lochara_state_initialize_any_, \
|
||||
.update = lochara_state_update_teleport_, \
|
||||
}, \
|
||||
(statman_meta_t) { \
|
||||
.state = LOCHARA_STATE_TELEPORT_BEHIND, \
|
||||
.name = "TELEPORT_BEHIND", \
|
||||
.data = &(lochara_state_teleport_param_t) { \
|
||||
.direction = -1, \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
.initialize = lochara_state_initialize_any_, \
|
||||
.update = lochara_state_update_teleport_, \
|
||||
}
|
184
core/lochara/strategy.c
Normal file
184
core/lochara/strategy.c
Normal file
@@ -0,0 +1,184 @@
|
||||
#include "./strategy.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/chaos/xorshift.h"
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/statman/statman.h"
|
||||
|
||||
#include "core/loeffect/effect.h"
|
||||
#include "core/loeffect/recipient.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./combat.h"
|
||||
#include "./state.h"
|
||||
|
||||
#define SEED_ 3467
|
||||
|
||||
#define PARRY_DAMAGE_ 1
|
||||
|
||||
void lochara_strategy_initialize_any_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
base->param.last_strategy_changed = base->ticker->time;
|
||||
}
|
||||
|
||||
void lochara_strategy_cancel_transition_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
*next = meta->state;
|
||||
}
|
||||
|
||||
void lochara_strategy_initialize_scouting_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
lochara_strategy_initialize_any_(meta, instance, next);
|
||||
|
||||
const lochara_strategy_scouting_param_t* p = meta->data;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
statman_transition_to(
|
||||
p->state_table, base, &base->param.state, LOCHARA_STATE_STAND);
|
||||
}
|
||||
|
||||
void lochara_strategy_update_scouting_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
const lochara_strategy_scouting_param_t* p = meta->data;
|
||||
assert(p->state_table != NULL);
|
||||
assert(p->period > 0);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
const uint64_t t = base->ticker->time;
|
||||
|
||||
const bool knockback = base->param.last_knockback+500 > t;
|
||||
|
||||
const bool event = base->player->event.executor != 0;
|
||||
|
||||
/* ---- die ---- */
|
||||
if (!loeffect_recipient_is_alive(&base->param.recipient)) {
|
||||
*next = LOCHARA_STRATEGY_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---- strategy transition ---- */
|
||||
vec2_t disp;
|
||||
locommon_position_sub(
|
||||
&disp, &base->player->entity->super.super.pos, &base->super.super.pos);
|
||||
|
||||
if (!knockback && MATH_ABS(disp.y) < .1f && !event) {
|
||||
disp.x *= MATH_SIGN_NOZERO(base->param.direction.x);
|
||||
|
||||
const float b = p->range_back;
|
||||
if (-p->range_close*b < disp.x && disp.x < p->range_close) {
|
||||
*next = p->found_close;
|
||||
} else if (-p->range_mid*b < disp.x && disp.x < p->range_mid) {
|
||||
*next = p->found_mid;
|
||||
} else if (-p->range_long*b < disp.x && disp.x < p->range_long) {
|
||||
*next = p->found_long;
|
||||
}
|
||||
if (*next != LOCHARA_STRATEGY_SCOUTING) return;
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
const uint64_t since = base->param.last_state_changed;
|
||||
|
||||
uint64_t seed = 1+since*SEED_;
|
||||
# define rand_() (seed = chaos_xorshift(seed))
|
||||
|
||||
const uint64_t dur = rand_()%p->period + p->period/2;
|
||||
|
||||
lochara_state_t state = base->param.state;
|
||||
if (knockback) {
|
||||
state = LOCHARA_STATE_STAND;
|
||||
|
||||
} else if (since+dur < t) {
|
||||
seed = 1+t*SEED_;
|
||||
if (state == LOCHARA_STATE_STAND && rand_()%100/100.f < p->stagger) {
|
||||
const float r = .5f + base->cache.ground_pos.x*.5f;
|
||||
if (rand_()%100/100.f < r) {
|
||||
base->param.direction = vec2(-1, 0);
|
||||
state = LOCHARA_STATE_WALK_LEFT;
|
||||
} else {
|
||||
base->param.direction = vec2(1, 0);
|
||||
state = LOCHARA_STATE_WALK_RIGHT;
|
||||
}
|
||||
} else {
|
||||
state = LOCHARA_STATE_STAND;
|
||||
}
|
||||
}
|
||||
statman_transition_to(p->state_table, base, &base->param.state, state);
|
||||
|
||||
# undef rand_
|
||||
}
|
||||
|
||||
void lochara_strategy_initialize_combat_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
const lochara_strategy_combat_param_t* p = meta->data;
|
||||
assert(p->actions != NULL);
|
||||
|
||||
if (base->param.last_knockback+p->parry_window > base->ticker->time) {
|
||||
loentity_character_apply_effect(
|
||||
&base->super, &loeffect_damage(PARRY_DAMAGE_));
|
||||
*next = p->parried_next;
|
||||
return;
|
||||
}
|
||||
|
||||
lochara_strategy_initialize_any_(meta, instance, next);
|
||||
|
||||
lochara_combat_action_execute_all_attacks(p->actions, base);
|
||||
|
||||
vec2_t disp;
|
||||
locommon_position_sub(
|
||||
&disp, &base->player->entity->super.super.pos, &base->super.super.pos);
|
||||
base->param.direction = vec2(MATH_SIGN_NOZERO(disp.x), 0);
|
||||
}
|
||||
|
||||
void lochara_strategy_update_combat_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
const lochara_strategy_combat_param_t* p = meta->data;
|
||||
assert(p->actions != NULL);
|
||||
assert(p->next != meta->state);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
if (!p->gravity) base->param.gravity = 0;
|
||||
|
||||
const uint64_t t = base->ticker->time - base->param.last_strategy_changed;
|
||||
|
||||
const lochara_combat_action_t* action =
|
||||
lochara_combat_action_find_by_time(p->actions, t);
|
||||
if (action == NULL) {
|
||||
*next = p->next;
|
||||
return;
|
||||
}
|
||||
statman_transition_to(
|
||||
p->state_table, base, &base->param.state, action->state);
|
||||
}
|
131
core/lochara/strategy.h
Normal file
131
core/lochara/strategy.h
Normal file
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/statman/statman.h"
|
||||
|
||||
#include "./combat.h"
|
||||
|
||||
typedef enum {
|
||||
/* BENUM BEGIN lochara_strategy */
|
||||
LOCHARA_STRATEGY_WAIT,
|
||||
LOCHARA_STRATEGY_WAKE_UP,
|
||||
LOCHARA_STRATEGY_WAKE_UP_EVENT,
|
||||
|
||||
LOCHARA_STRATEGY_SCOUTING,
|
||||
LOCHARA_STRATEGY_APPROACH,
|
||||
|
||||
LOCHARA_STRATEGY_COMBO1,
|
||||
LOCHARA_STRATEGY_COMBO2,
|
||||
LOCHARA_STRATEGY_COMBO3,
|
||||
|
||||
LOCHARA_STRATEGY_SHOOT1,
|
||||
LOCHARA_STRATEGY_SHOOT2,
|
||||
LOCHARA_STRATEGY_SHOOT3,
|
||||
|
||||
LOCHARA_STRATEGY_SPELL1,
|
||||
LOCHARA_STRATEGY_SPELL2,
|
||||
LOCHARA_STRATEGY_SPELL3,
|
||||
|
||||
LOCHARA_STRATEGY_DOWN,
|
||||
LOCHARA_STRATEGY_DEAD,
|
||||
LOCHARA_STRATEGY_KILL,
|
||||
/* BENUM END */
|
||||
} lochara_strategy_t;
|
||||
|
||||
/* generated benum header */
|
||||
#include "core/lochara/benum/strategy.h"
|
||||
|
||||
|
||||
/* ---- default strategy handlers ---- */
|
||||
|
||||
void
|
||||
lochara_strategy_initialize_any_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
|
||||
void
|
||||
lochara_strategy_cancel_transition_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
|
||||
void
|
||||
lochara_strategy_initialize_scouting_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
|
||||
void
|
||||
lochara_strategy_update_scouting_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
|
||||
void
|
||||
lochara_strategy_initialize_combat_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
|
||||
void
|
||||
lochara_strategy_update_combat_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
|
||||
|
||||
/* ---- default strategy constructors ---- */
|
||||
|
||||
typedef struct {
|
||||
const statman_meta_t* state_table;
|
||||
|
||||
uint64_t period;
|
||||
float stagger; /* 0~1 */
|
||||
float range_back;
|
||||
float range_close;
|
||||
float range_mid;
|
||||
float range_long;
|
||||
|
||||
lochara_strategy_t found_close;
|
||||
lochara_strategy_t found_mid;
|
||||
lochara_strategy_t found_long;
|
||||
} lochara_strategy_scouting_param_t;
|
||||
#define lochara_strategy_scouting(...) \
|
||||
(statman_meta_t) { \
|
||||
.state = LOCHARA_STRATEGY_SCOUTING, \
|
||||
.name = "SCOUTING", \
|
||||
.data = &(lochara_strategy_scouting_param_t) { \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
.initialize = lochara_strategy_initialize_scouting_, \
|
||||
.update = lochara_strategy_update_scouting_, \
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const statman_meta_t* state_table;
|
||||
const lochara_combat_action_t* actions;
|
||||
|
||||
uint64_t parry_window;
|
||||
lochara_strategy_t parried_next;
|
||||
|
||||
bool gravity;
|
||||
|
||||
lochara_strategy_t next;
|
||||
} lochara_strategy_combat_param_t;
|
||||
#define lochara_strategy_combat(NAME, ...) \
|
||||
(statman_meta_t) { \
|
||||
.state = LOCHARA_STRATEGY_##NAME, \
|
||||
.name = #NAME, \
|
||||
.data = &(lochara_strategy_combat_param_t) { \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
.initialize = lochara_strategy_initialize_combat_, \
|
||||
.update = lochara_strategy_update_combat_, \
|
||||
.finalize = lochara_strategy_cancel_transition_, \
|
||||
}
|
718
core/lochara/theists_child.c
Normal file
718
core/lochara/theists_child.c
Normal file
@@ -0,0 +1,718 @@
|
||||
#include "./theists_child.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/statman/statman.h"
|
||||
|
||||
#include "core/lobullet/linear.h"
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/loentity/ground.h"
|
||||
#include "core/loplayer/event.h"
|
||||
#include "core/loplayer/stance.h"
|
||||
#include "core/loresource/music.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./state.h"
|
||||
#include "./strategy.h"
|
||||
#include "./type.h"
|
||||
|
||||
#define WIDTH_ .025f
|
||||
#define HEIGHT_ .06f
|
||||
#define MARKER_ .03f
|
||||
#define COLOR_ vec4(0, 0, 0, 1)
|
||||
|
||||
#define BPM_ 140
|
||||
#define BEAT_ (60.f/BPM_)
|
||||
#define BEAT_MS_ (BEAT_*1000)
|
||||
#define MUSIC_DURATION_ (BEAT_MS_*232)
|
||||
|
||||
#define WAKE_UP_RANGE_ (WIDTH_*6)
|
||||
|
||||
#define REWARD_STANCE_ LOPLAYER_STANCE_REVOLUTIONER
|
||||
|
||||
static const loeffect_recipient_status_t base_status_ = {
|
||||
.attack = .3f,
|
||||
.defence = .92f,
|
||||
.speed = .31f,
|
||||
.jump = 1.1f,
|
||||
};
|
||||
|
||||
static void lochara_theists_child_initialize_shoot_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
lochara_state_initialize_any_(meta, instance, state);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
vec2_t dir;
|
||||
locommon_position_sub(
|
||||
&dir, &base->player->entity->super.super.pos, &base->super.super.pos);
|
||||
|
||||
if (dir.x == 0 && dir.y == 0) dir = vec2(1, 0);
|
||||
vec2_diveq(&dir, vec2_length(&dir));
|
||||
|
||||
const vec2_t invdir = vec2(dir.y, -dir.x);
|
||||
|
||||
base->param.direction = vec2(MATH_SIGN_NOZERO(dir.x), 0);
|
||||
for (int32_t i = -4; i <= 4; ++i) {
|
||||
vec2_t v;
|
||||
vec2_mul(&v, &dir, 1);
|
||||
|
||||
vec2_t p;
|
||||
vec2_mul(&p, &invdir, i*.02f);
|
||||
|
||||
locommon_position_t pos = base->super.super.pos;
|
||||
vec2_addeq(&pos.fract, &p);
|
||||
locommon_position_reduce(&pos);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(base->bullet);
|
||||
lobullet_linear_circle_build(b,
|
||||
.owner = base->super.super.id,
|
||||
.basepos = pos,
|
||||
.size = vec2(.02f, .02f),
|
||||
.color = vec4(1, 1, 1, 1),
|
||||
.velocity = v,
|
||||
.knockback = 1,
|
||||
.effect = loeffect_damage(
|
||||
base->param.recipient.status.attack*.6f),
|
||||
.duration = 1000,
|
||||
);
|
||||
loentity_store_add(base->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
|
||||
static void lochara_theists_child_update_thrust_in_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
static const uint64_t dur = BEAT_MS_/4;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
if (t > dur) t = dur;
|
||||
|
||||
const float tf = t*1.f / dur;
|
||||
base->cache.instance.motion.time = powf(tf, 2);
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
}
|
||||
|
||||
static void lochara_theists_child_update_thrust_out_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
static const uint64_t dur = BEAT_MS_/4;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
if (t > dur) t = dur;
|
||||
|
||||
const float tf = t*1.f / dur;
|
||||
base->cache.instance.motion.time = powf(tf, 2);
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
}
|
||||
|
||||
static void lochara_theists_child_update_down_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
const bool fast =
|
||||
meta->state == LOCHARA_STATE_DOWN ||
|
||||
meta->state == LOCHARA_STATE_REVIVE;
|
||||
const bool reverse =
|
||||
meta->state == LOCHARA_STATE_REVIVE ||
|
||||
meta->state == LOCHARA_STATE_RESUSCITATE;
|
||||
|
||||
const uint64_t dur = fast? reverse? BEAT_MS_*3: BEAT_MS_: BEAT_MS_*16;
|
||||
const uint64_t key = fast? dur: dur/2;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
base->param.movement = vec2(0, 0);
|
||||
|
||||
uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
if (t > dur) t = dur;
|
||||
if (reverse) t = dur - t;
|
||||
|
||||
if (t < key) {
|
||||
const float tf = t*1.f/key;
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
base->cache.instance.motion.time = powf(tf, 6);
|
||||
} else {
|
||||
const float tf = (t-key)*1.f/(dur-key);
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
base->cache.instance.motion.time = powf(tf, 4);
|
||||
}
|
||||
}
|
||||
|
||||
static const statman_meta_t state_table_[] = {
|
||||
lochara_state_stand(
|
||||
.period = BEAT_MS_*2,
|
||||
.acceleration = {{5, 5}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_ATTACK1,
|
||||
),
|
||||
lochara_state_walk(
|
||||
.period = BEAT_MS_,
|
||||
.acceleration = {{5, 5}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_WALK,
|
||||
),
|
||||
lochara_state_dodge(
|
||||
.duration = BEAT_MS_/2,
|
||||
.speed = 1,
|
||||
.acceleration = {{3, 3}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_WALK,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
),
|
||||
lochara_state_jump(),
|
||||
|
||||
lochara_state_teleport(
|
||||
.duration = BEAT_MS_*2,
|
||||
.offset = {{WIDTH_*2, 0}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_ATTACK2,
|
||||
),
|
||||
{
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.name = "THRUST_IN",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_theists_child_update_thrust_in_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
.name = "THRUST_OUT",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_theists_child_update_thrust_out_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_SHOOT,
|
||||
.name = "SHOOT",
|
||||
.initialize = lochara_theists_child_initialize_shoot_state_,
|
||||
.update = lochara_theists_child_update_thrust_in_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_DOWN,
|
||||
.name = "DOWN",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_theists_child_update_down_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_REVIVE,
|
||||
.name = "REVIVE",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_theists_child_update_down_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
.name = "DEAD",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_theists_child_update_down_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_RESUSCITATE,
|
||||
.name = "RESUSCITATE",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_theists_child_update_down_state_,
|
||||
},
|
||||
{0},
|
||||
};
|
||||
|
||||
static void lochara_theists_child_update_wait_strategy_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
static const float range2 = WAKE_UP_RANGE_*WAKE_UP_RANGE_;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
vec2_t disp;
|
||||
locommon_position_sub(
|
||||
&disp, &base->player->entity->super.super.pos, &base->super.super.pos);
|
||||
|
||||
if (fabsf(disp.y) < HEIGHT_/2 && vec2_pow_length(&disp) < range2) {
|
||||
loeffect_recipient_apply_effect(
|
||||
&base->param.recipient, &loeffect_resuscitate());
|
||||
if (loplayer_stance_set_has(&base->player->stances, REWARD_STANCE_)) {
|
||||
*next = LOCHARA_STRATEGY_WAKE_UP;
|
||||
} else {
|
||||
*next = LOCHARA_STRATEGY_WAKE_UP_EVENT;
|
||||
}
|
||||
return;
|
||||
}
|
||||
statman_transition_to(
|
||||
state_table_, instance, &base->param.state, LOCHARA_STATE_DEAD);
|
||||
}
|
||||
|
||||
static void lochara_theists_child_initialize_wake_up_strategy_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
lochara_strategy_initialize_any_(meta, instance, next);
|
||||
if (meta->state != LOCHARA_STRATEGY_WAKE_UP_EVENT) return;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
loentity_character_apply_effect(
|
||||
&base->super, &loeffect_fanatic(MUSIC_DURATION_));
|
||||
loentity_character_apply_effect(
|
||||
&base->player->entity->super, &loeffect_curse(MUSIC_DURATION_));
|
||||
}
|
||||
|
||||
static void lochara_theists_child_update_wake_up_strategy_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
const bool ev = meta->state == LOCHARA_STRATEGY_WAKE_UP_EVENT;
|
||||
const uint64_t dur = (ev? BEAT_MS_*64: BEAT_MS_*16);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
const uint64_t t = base->ticker->time - base->param.last_strategy_changed;
|
||||
if (t >= dur) {
|
||||
*next = LOCHARA_STRATEGY_APPROACH;
|
||||
return;
|
||||
}
|
||||
statman_transition_to(
|
||||
state_table_, instance, &base->param.state, LOCHARA_STATE_RESUSCITATE);
|
||||
}
|
||||
|
||||
static void lochara_theists_child_update_approach_strategy_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
if (!loeffect_recipient_is_alive(&base->param.recipient)) {
|
||||
*next = LOCHARA_STRATEGY_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t since = base->param.last_strategy_changed;
|
||||
|
||||
uint64_t until = since + BEAT_MS_;
|
||||
if (base->player->event.executor == base->super.super.id) {
|
||||
const uint64_t msince = base->player->event.ctx.music.since;
|
||||
if (msince < since) {
|
||||
const uint64_t beats = (since - msince)/BEAT_MS_ + 1;
|
||||
until = msince + beats*BEAT_MS_;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- strategy transition ---- */
|
||||
const locommon_position_t* player = &base->player->entity->super.super.pos;
|
||||
|
||||
vec2_t disp;
|
||||
locommon_position_sub(&disp, player, &base->super.super.pos);
|
||||
|
||||
if (player->chunk.x != base->super.super.pos.chunk.x ||
|
||||
player->chunk.y != base->super.super.pos.chunk.y ||
|
||||
disp.y < -HEIGHT_) {
|
||||
*next = LOCHARA_STRATEGY_WAIT;
|
||||
return;
|
||||
}
|
||||
|
||||
const float dist = MATH_ABS(disp.x);
|
||||
if (base->ticker->time >= until) {
|
||||
if (MATH_ABS(disp.y) > HEIGHT_) {
|
||||
*next = LOCHARA_STRATEGY_SHOOT1;
|
||||
} else if (dist < WIDTH_*4) {
|
||||
*next = LOCHARA_STRATEGY_COMBO1;
|
||||
} else if (dist < WIDTH_*8) {
|
||||
*next = LOCHARA_STRATEGY_SHOOT1;
|
||||
} else {
|
||||
*next = LOCHARA_STRATEGY_COMBO2;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---- approaching ---- */
|
||||
if (dist > WIDTH_*6) {
|
||||
base->param.direction = vec2(MATH_SIGN_NOZERO(disp.x), 0);
|
||||
|
||||
const lochara_state_t state =
|
||||
disp.x < 0? LOCHARA_STATE_WALK_LEFT: LOCHARA_STATE_WALK_RIGHT;
|
||||
statman_transition_to(state_table_, base, &base->param.state, state);
|
||||
}
|
||||
}
|
||||
|
||||
static const lochara_combat_action_t combo1_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4*3,
|
||||
.state = LOCHARA_STATE_STAND,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1.2f,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = .8f,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/4*3,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_DODGE_LEFT,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t combo2_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_*2,
|
||||
.state = LOCHARA_STATE_TELEPORT_FRONT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1.2f,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = .8f,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = BEAT_MS_/4,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = .6f,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_/2,
|
||||
.state = LOCHARA_STATE_DODGE_RIGHT,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t shoot1_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = 1,
|
||||
.state = LOCHARA_STATE_JUMP,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_-1,
|
||||
.state = LOCHARA_STATE_STAND,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_,
|
||||
.state = LOCHARA_STATE_SHOOT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_*2,
|
||||
.state = LOCHARA_STATE_TELEPORT_BEHIND,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t down_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_,
|
||||
.state = LOCHARA_STATE_DOWN,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_*3,
|
||||
.state = LOCHARA_STATE_REVIVE,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t kill_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = BEAT_MS_*12,
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t dead_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = 30000,
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const statman_meta_t strategy_table_[] = {
|
||||
{
|
||||
.state = LOCHARA_STRATEGY_WAIT,
|
||||
.name = "WAIT",
|
||||
.initialize = lochara_strategy_initialize_any_,
|
||||
.update = lochara_theists_child_update_wait_strategy_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STRATEGY_WAKE_UP,
|
||||
.name = "WAKE_UP",
|
||||
.initialize = lochara_strategy_initialize_any_,
|
||||
.update = lochara_theists_child_update_wake_up_strategy_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STRATEGY_WAKE_UP_EVENT,
|
||||
.name = "WAKE_UP_EVENT",
|
||||
.initialize = lochara_theists_child_initialize_wake_up_strategy_,
|
||||
.update = lochara_theists_child_update_wake_up_strategy_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STRATEGY_APPROACH,
|
||||
.name = "APPROACH",
|
||||
.initialize = lochara_strategy_initialize_any_,
|
||||
.update = lochara_theists_child_update_approach_strategy_,
|
||||
},
|
||||
|
||||
lochara_strategy_combat(COMBO1,
|
||||
.state_table = state_table_,
|
||||
.actions = combo1_,
|
||||
.parry_window = 100,
|
||||
.parried_next = LOCHARA_STRATEGY_DOWN,
|
||||
.next = LOCHARA_STRATEGY_APPROACH,
|
||||
),
|
||||
lochara_strategy_combat(COMBO2,
|
||||
.state_table = state_table_,
|
||||
.actions = combo2_,
|
||||
.parry_window = 100,
|
||||
.parried_next = LOCHARA_STRATEGY_DOWN,
|
||||
.next = LOCHARA_STRATEGY_APPROACH,
|
||||
),
|
||||
lochara_strategy_combat(SHOOT1,
|
||||
.state_table = state_table_,
|
||||
.actions = shoot1_,
|
||||
.parry_window = 200,
|
||||
.parried_next = LOCHARA_STRATEGY_DOWN,
|
||||
.gravity = true,
|
||||
.next = LOCHARA_STRATEGY_APPROACH,
|
||||
),
|
||||
|
||||
lochara_strategy_combat(DOWN,
|
||||
.state_table = state_table_,
|
||||
.actions = down_,
|
||||
.gravity = true,
|
||||
.next = LOCHARA_STRATEGY_APPROACH,
|
||||
),
|
||||
lochara_strategy_combat(KILL,
|
||||
.state_table = state_table_,
|
||||
.actions = kill_,
|
||||
.gravity = true,
|
||||
.next = LOCHARA_STRATEGY_WAIT,
|
||||
),
|
||||
lochara_strategy_combat(DEAD,
|
||||
.state_table = state_table_,
|
||||
.actions = dead_,
|
||||
.gravity = true,
|
||||
.next = LOCHARA_STRATEGY_WAIT,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static void lochara_theists_child_update_event_(lochara_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const loplayer_event_command_t wake_up[] = {
|
||||
loplayer_event_command_play_music(LORESOURCE_MUSIC_ID_BOSS_THEISTS_CHILD),
|
||||
loplayer_event_command_set_area(.49f, .45f),
|
||||
loplayer_event_command_set_cinescope(1),
|
||||
loplayer_event_command_wait(BEAT_MS_*16),
|
||||
|
||||
loplayer_event_command_set_line("boss_theists_child_line0"),
|
||||
loplayer_event_command_wait(BEAT_MS_*16),
|
||||
loplayer_event_command_set_line("boss_theists_child_line1"),
|
||||
loplayer_event_command_wait(BEAT_MS_*16),
|
||||
loplayer_event_command_set_line("boss_theists_child_line2"),
|
||||
loplayer_event_command_wait(BEAT_MS_*15),
|
||||
|
||||
loplayer_event_command_set_line(NULL),
|
||||
loplayer_event_command_set_cinescope(0),
|
||||
{0},
|
||||
};
|
||||
static const loplayer_event_command_t kill[] = {
|
||||
loplayer_event_command_set_area(0, 0),
|
||||
loplayer_event_command_set_cinescope(1),
|
||||
loplayer_event_command_wait(BEAT_MS_),
|
||||
loplayer_event_command_stop_music(),
|
||||
|
||||
loplayer_event_command_set_line("boss_theists_child_kill_line0"),
|
||||
loplayer_event_command_wait(BEAT_MS_*4),
|
||||
loplayer_event_command_set_line("boss_theists_child_kill_line1"),
|
||||
loplayer_event_command_wait(BEAT_MS_*4),
|
||||
|
||||
loplayer_event_command_finalize(),
|
||||
{0},
|
||||
};
|
||||
static const loplayer_event_command_t dead[] = {
|
||||
loplayer_event_command_set_area(0, 0),
|
||||
loplayer_event_command_wait(BEAT_MS_),
|
||||
loplayer_event_command_stop_music(),
|
||||
|
||||
loplayer_event_command_set_line("boss_theists_child_dead_line"),
|
||||
loplayer_event_command_wait(BEAT_MS_*8),
|
||||
|
||||
loplayer_event_command_finalize(),
|
||||
{0},
|
||||
};
|
||||
|
||||
const uint64_t t = base->ticker->time;
|
||||
|
||||
loplayer_event_t* event = &base->player->event;
|
||||
const loentity_id_t id = base->super.super.id;
|
||||
|
||||
locommon_position_t basepos = base->super.super.pos;
|
||||
basepos.fract = vec2(.5f, .5f);
|
||||
|
||||
if (base->param.strategy == LOCHARA_STRATEGY_WAKE_UP_EVENT) {
|
||||
loplayer_event_execute_commands(event, id, &basepos, wake_up);
|
||||
return;
|
||||
}
|
||||
if (event->executor != id) return;
|
||||
|
||||
if (base->player->entity->param.state == LOCHARA_STATE_DEAD) {
|
||||
statman_transition_to(
|
||||
strategy_table_, base, &base->param.strategy, LOCHARA_STRATEGY_KILL);
|
||||
loplayer_event_execute_commands(event, id, &basepos, kill);
|
||||
return;
|
||||
}
|
||||
if (base->param.strategy == LOCHARA_STRATEGY_DEAD) {
|
||||
loplayer_event_execute_commands(event, id, &basepos, dead);
|
||||
if (!loplayer_stance_set_has(&base->player->stances, REWARD_STANCE_)) {
|
||||
loplayer_stance_set_add(&base->player->stances, REWARD_STANCE_);
|
||||
loplayer_popup_queue_new_stance(&base->player->popup, REWARD_STANCE_);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (base->player->event.basetime+MUSIC_DURATION_ <= t &&
|
||||
loeffect_recipient_is_alive(&base->param.recipient)) {
|
||||
loentity_character_apply_effect(
|
||||
&base->player->entity->super, &loeffect_curse_trigger());
|
||||
return;
|
||||
}
|
||||
loplayer_event_execute_commands(event, id, &basepos, wake_up);
|
||||
}
|
||||
|
||||
bool lochara_theists_child_update(lochara_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
loeffect_recipient_update(&base->param.recipient, &base_status_);
|
||||
|
||||
statman_update(strategy_table_, base, &base->param.strategy);
|
||||
lochara_theists_child_update_event_(base);
|
||||
|
||||
const float dir = MATH_SIGN_NOZERO(base->param.direction.x);
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_CAVIA,
|
||||
.pos = vec2(0, -MARKER_),
|
||||
.size = vec2(dir*HEIGHT_, HEIGHT_),
|
||||
.color = COLOR_,
|
||||
.marker_offset = vec2(0, MARKER_),
|
||||
};
|
||||
statman_update(state_table_, base, &base->param.state);
|
||||
|
||||
if (base->param.strategy != LOCHARA_STRATEGY_WAIT &&
|
||||
base->param.strategy != LOCHARA_STRATEGY_WAKE_UP &&
|
||||
base->param.strategy != LOCHARA_STRATEGY_WAKE_UP_EVENT &&
|
||||
base->param.strategy != LOCHARA_STRATEGY_DEAD) {
|
||||
base->cache.instance.marker = lochara_base_affect_bullets(base);
|
||||
}
|
||||
|
||||
lochara_base_calculate_physics(
|
||||
base, &vec2(WIDTH_, HEIGHT_), &vec2(0, MARKER_));
|
||||
lochara_base_bind_on_ground(base, &vec2(WIDTH_, HEIGHT_+MARKER_));
|
||||
return true;
|
||||
}
|
||||
|
||||
void lochara_theists_child_build(
|
||||
lochara_base_t* base, loentity_ground_t* gnd) {
|
||||
assert(base != NULL);
|
||||
assert(gnd != NULL);
|
||||
|
||||
base->super.super.pos = gnd->super.pos;
|
||||
vec2_addeq(&base->super.super.pos.fract, &vec2(0, gnd->size.y));
|
||||
locommon_position_reduce(&base->super.super.pos);
|
||||
|
||||
base->param = (typeof(base->param)) {
|
||||
.type = LOCHARA_TYPE_THEISTS_CHILD,
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
.last_state_changed = base->ticker->time,
|
||||
.strategy = LOCHARA_STRATEGY_WAIT,
|
||||
.last_strategy_changed = base->ticker->time,
|
||||
|
||||
.ground = gnd->super.id,
|
||||
};
|
||||
loeffect_recipient_initialize(
|
||||
&base->param.recipient, base->ticker, &base_status_);
|
||||
}
|
18
core/lochara/theists_child.h
Normal file
18
core/lochara/theists_child.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "core/loentity/ground.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
bool
|
||||
lochara_theists_child_update(
|
||||
lochara_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
lochara_theists_child_build(
|
||||
lochara_base_t* base,
|
||||
loentity_ground_t* gnd
|
||||
);
|
18
core/lochara/type.h
Normal file
18
core/lochara/type.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
/* BENUM BEGIN lochara_type */
|
||||
LOCHARA_TYPE_PLAYER,
|
||||
|
||||
LOCHARA_TYPE_ENCEPHALON,
|
||||
|
||||
LOCHARA_TYPE_CAVIA,
|
||||
LOCHARA_TYPE_WARDER,
|
||||
|
||||
LOCHARA_TYPE_BIG_WARDER,
|
||||
LOCHARA_TYPE_THEISTS_CHILD,
|
||||
/* BENUM END*/
|
||||
} lochara_type_t;
|
||||
|
||||
/* generated benum utils */
|
||||
#include "core/lochara/benum/type.h"
|
370
core/lochara/warder.c
Normal file
370
core/lochara/warder.c
Normal file
@@ -0,0 +1,370 @@
|
||||
#include "./warder.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/statman/statman.h"
|
||||
|
||||
#include "core/lobullet/base.h"
|
||||
#include "core/lobullet/linear.h"
|
||||
#include "core/loeffect/effect.h"
|
||||
#include "core/loeffect/recipient.h"
|
||||
#include "core/loentity/ground.h"
|
||||
#include "core/loresource/sound.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./combat.h"
|
||||
|
||||
#define WIDTH_ .02f
|
||||
#define HEIGHT_ .055f
|
||||
#define MARKER_ .022f
|
||||
#define COLOR_ vec4(0, 0, 0, 1)
|
||||
|
||||
#define BULLET_DAMAGE_ .9f
|
||||
#define BULLET_KNOCKBACK_ 6
|
||||
#define BULLET_DURATION_ 2000
|
||||
#define BULLET_SPEED_ .6f
|
||||
#define BULLET_COLOR_ vec4(0, 0, 0, 1)
|
||||
#define BULLET_SIZE_ vec2(.03f, .03f)
|
||||
|
||||
#define SHOOT_DURATION_ 2000
|
||||
|
||||
static const loeffect_recipient_status_t base_status_ = {
|
||||
.attack = .2f,
|
||||
.defence = .05f,
|
||||
.speed = .05f,
|
||||
.jump = 1.f,
|
||||
};
|
||||
|
||||
static void lochara_warder_update_thrust_in_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
static const uint64_t dur = 100;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
if (t > dur) t = dur;
|
||||
|
||||
const float tf = t*1.f / dur;
|
||||
base->cache.instance.motion.time = powf(tf, 2);
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
}
|
||||
|
||||
static void lochara_warder_update_thrust_out_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
static const uint64_t dur = 100;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
|
||||
uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
if (t > dur) t = dur;
|
||||
|
||||
const float tf = t*1.f / dur;
|
||||
base->cache.instance.motion.time = powf(tf, 2);
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
}
|
||||
|
||||
static void lochara_warder_update_shoot_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
const uint64_t since = base->param.last_state_changed;
|
||||
|
||||
uint64_t t = base->ticker->time - since;
|
||||
if (t > SHOOT_DURATION_) t = SHOOT_DURATION_;
|
||||
|
||||
const uint64_t pt = t == 0? 0: base->ticker->prev_time - since;
|
||||
if (pt < SHOOT_DURATION_/2 && SHOOT_DURATION_/2 <= t) {
|
||||
vec2_t v = base->param.direction;
|
||||
vec2_muleq(&v, BULLET_SPEED_);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(base->bullet);
|
||||
lobullet_linear_circle_build(b,
|
||||
.owner = base->super.super.id,
|
||||
.basepos = base->super.super.pos,
|
||||
.size = BULLET_SIZE_,
|
||||
.color = BULLET_COLOR_,
|
||||
.velocity = v,
|
||||
.knockback = BULLET_KNOCKBACK_,
|
||||
.effect = loeffect_damage(
|
||||
base->param.recipient.status.attack*BULLET_DAMAGE_),
|
||||
.duration = BULLET_DURATION_,);
|
||||
loentity_store_add(base->entities, &b->super.super);
|
||||
loresource_sound_set_play(
|
||||
&base->res->sound, LORESOURCE_SOUND_ID_ENEMY_SHOOT);
|
||||
}
|
||||
|
||||
float tf = t*1.f/SHOOT_DURATION_*2;
|
||||
if (tf < 1) {
|
||||
tf *= 2;
|
||||
if (tf < 1) {
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
} else {
|
||||
tf -= 1;
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
}
|
||||
base->cache.instance.motion.time = powf(tf, 4);
|
||||
} else {
|
||||
tf -= 1;
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
base->cache.instance.motion.time = tf;
|
||||
}
|
||||
}
|
||||
|
||||
static void lochara_warder_initialize_resuscitate_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
lochara_state_initialize_any_(meta, instance, state);
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
loeffect_recipient_apply_effect(
|
||||
&base->param.recipient, &loeffect_resuscitate());
|
||||
}
|
||||
|
||||
static void lochara_warder_update_dead_state_(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(instance != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
static uint64_t dur = 1000;
|
||||
|
||||
lochara_base_t* base = instance;
|
||||
base->param.movement = vec2(0, 0);
|
||||
|
||||
uint64_t t = base->ticker->time - base->param.last_state_changed;
|
||||
if (t > dur) t = dur;
|
||||
|
||||
if (base->param.state == LOCHARA_STATE_RESUSCITATE) t = dur - t;
|
||||
|
||||
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
base->cache.instance.motion.time = powf(t*1.f/dur, 6);
|
||||
}
|
||||
|
||||
static const statman_meta_t state_table_[] = {
|
||||
lochara_state_stand(
|
||||
.period = 4000,
|
||||
.acceleration = {{5, 5}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
),
|
||||
lochara_state_walk(
|
||||
.period = 1000,
|
||||
.acceleration = {{5, 5}},
|
||||
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.motion2 = LOSHADER_CHARACTER_MOTION_ID_WALK,
|
||||
),
|
||||
{
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.name = "THRUST_IN",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_warder_update_thrust_in_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
.name = "THRUST_OUT",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_warder_update_thrust_out_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_SHOOT,
|
||||
.name = "SHOOT",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_warder_update_shoot_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
.name = "DEAD",
|
||||
.initialize = lochara_state_initialize_any_,
|
||||
.update = lochara_warder_update_dead_state_,
|
||||
},
|
||||
{
|
||||
.state = LOCHARA_STATE_RESUSCITATE,
|
||||
.name = "RESUSCITATE",
|
||||
.initialize = lochara_warder_initialize_resuscitate_state_,
|
||||
.update = lochara_warder_update_dead_state_,
|
||||
},
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t combo1_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = 400,
|
||||
.state = LOCHARA_STATE_STAND,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = 200,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = 200,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = 300,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = 200,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = 300,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = 1.2f,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = 200,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_attack(
|
||||
.duration = 300,
|
||||
.state = LOCHARA_STATE_THRUST_IN,
|
||||
.damage = .8f,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = 400,
|
||||
.state = LOCHARA_STATE_THRUST_OUT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = 600,
|
||||
.state = LOCHARA_STATE_STAND,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t shoot1_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = 100,
|
||||
.state = LOCHARA_STATE_STAND,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = SHOOT_DURATION_,
|
||||
.state = LOCHARA_STATE_SHOOT,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = 500,
|
||||
.state = LOCHARA_STATE_STAND,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const lochara_combat_action_t dead_[] = {
|
||||
lochara_combat_action_rest(
|
||||
.duration = 30000,
|
||||
.state = LOCHARA_STATE_DEAD,
|
||||
),
|
||||
lochara_combat_action_rest(
|
||||
.duration = 2000,
|
||||
.state = LOCHARA_STATE_RESUSCITATE,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
static const statman_meta_t strategy_table_[] = {
|
||||
lochara_strategy_scouting(
|
||||
.state_table = state_table_,
|
||||
.period = 2000,
|
||||
.stagger = .8f,
|
||||
.range_back = .5f,
|
||||
.range_close = WIDTH_*4,
|
||||
.found_close = LOCHARA_STRATEGY_COMBO1,
|
||||
.range_mid = .4f,
|
||||
.found_mid = LOCHARA_STRATEGY_SHOOT1,
|
||||
),
|
||||
lochara_strategy_combat(COMBO1,
|
||||
.state_table = state_table_,
|
||||
.actions = combo1_,
|
||||
.next = LOCHARA_STRATEGY_SCOUTING,
|
||||
),
|
||||
lochara_strategy_combat(SHOOT1,
|
||||
.state_table = state_table_,
|
||||
.actions = shoot1_,
|
||||
.next = LOCHARA_STRATEGY_SCOUTING,
|
||||
),
|
||||
lochara_strategy_combat(DEAD,
|
||||
.state_table = state_table_,
|
||||
.actions = dead_,
|
||||
.next = LOCHARA_STRATEGY_SCOUTING,
|
||||
),
|
||||
{0},
|
||||
};
|
||||
|
||||
bool lochara_warder_update(lochara_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
loeffect_recipient_update(&base->param.recipient, &base_status_);
|
||||
|
||||
statman_update(strategy_table_, base, &base->param.strategy);
|
||||
|
||||
const float dir = MATH_SIGN_NOZERO(base->param.direction.x);
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_WARDER,
|
||||
.pos = vec2(0, -MARKER_),
|
||||
.size = vec2(dir*HEIGHT_, HEIGHT_),
|
||||
.color = COLOR_,
|
||||
.marker_offset = vec2(0, MARKER_),
|
||||
};
|
||||
statman_update(state_table_, base, &base->param.state);
|
||||
|
||||
if (base->param.state != LOCHARA_STATE_DEAD) {
|
||||
base->cache.instance.marker = lochara_base_affect_bullets(base);
|
||||
}
|
||||
|
||||
lochara_base_calculate_physics(
|
||||
base, &vec2(WIDTH_, HEIGHT_), &vec2(0, MARKER_));
|
||||
lochara_base_bind_on_ground(base, &vec2(WIDTH_, HEIGHT_+MARKER_));
|
||||
return true;
|
||||
}
|
||||
|
||||
void lochara_warder_build(
|
||||
lochara_base_t* base, const loentity_ground_t* gnd, float pos) {
|
||||
assert(base != NULL);
|
||||
assert(gnd != NULL);
|
||||
assert(MATH_FLOAT_VALID(pos));
|
||||
|
||||
base->super.super.pos = gnd->super.pos;
|
||||
vec2_addeq(
|
||||
&base->super.super.pos.fract,
|
||||
&vec2(gnd->size.x*pos, gnd->size.y));
|
||||
locommon_position_reduce(&base->super.super.pos);
|
||||
|
||||
base->param = (typeof(base->param)) {
|
||||
.type = LOCHARA_TYPE_WARDER,
|
||||
.state = LOCHARA_STATE_STAND,
|
||||
.last_state_changed = base->ticker->time,
|
||||
.strategy = LOCHARA_STRATEGY_SCOUTING,
|
||||
.last_strategy_changed = base->ticker->time,
|
||||
|
||||
.ground = gnd->super.id,
|
||||
};
|
||||
loeffect_recipient_initialize(
|
||||
&base->param.recipient, base->ticker, &base_status_);
|
||||
}
|
19
core/lochara/warder.h
Normal file
19
core/lochara/warder.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "core/loentity/ground.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
bool
|
||||
lochara_warder_update(
|
||||
lochara_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
lochara_warder_build(
|
||||
lochara_base_t* base,
|
||||
const loentity_ground_t* gnd,
|
||||
float pos
|
||||
);
|
Reference in New Issue
Block a user