[RELEASE] u22-v03
This version is submitted to U22 breau.
This commit is contained in:
parent
360595de37
commit
84c3a02b9a
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/.build
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "thirdparty/miniaudio"]
|
||||
path = thirdparty/miniaudio/repo
|
||||
url = https://github.com/dr-soft/miniaudio
|
40
CMakeLists.txt
Normal file
40
CMakeLists.txt
Normal file
@ -0,0 +1,40 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(leftone C)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
enable_testing()
|
||||
endif()
|
||||
|
||||
set(LEFTONE_TOOL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tool")
|
||||
|
||||
set(CMAKE_C_FLAGS
|
||||
"${CMAKE_C_FLAGS} -Wall -Wextra -Wno-missing-field-initializers")
|
||||
set(CMAKE_C_FLAGS_RELEASE
|
||||
"${CMAKE_C_FLAGS_RELEASE} -Wno-unused-parameter")
|
||||
|
||||
if (WIN32)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mconsole")
|
||||
endif()
|
||||
|
||||
find_package(Freetype REQUIRED)
|
||||
find_package(GLEW REQUIRED)
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(msgpack REQUIRED)
|
||||
|
||||
include_directories(SYSTEM
|
||||
${FREETYPE_INCLUDE_DIRS}
|
||||
${GLEW_INCLUDE_DIRS}
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
)
|
||||
include_directories(.)
|
||||
|
||||
include(cmake/anysrc.cmake)
|
||||
include(cmake/sos.cmake)
|
||||
|
||||
add_subdirectory(app)
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(thirdparty)
|
||||
add_subdirectory(tool)
|
||||
add_subdirectory(util)
|
1
app/CMakeLists.txt
Normal file
1
app/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
add_subdirectory(sdl)
|
14
app/sdl/CMakeLists.txt
Normal file
14
app/sdl/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
add_executable(app-sdl
|
||||
args.c
|
||||
event.c
|
||||
main.c
|
||||
)
|
||||
target_link_libraries(app-sdl
|
||||
GLEW::GLEW
|
||||
SDL2::SDL2
|
||||
|
||||
conv
|
||||
parsarg
|
||||
|
||||
loscene
|
||||
)
|
104
app/sdl/args.c
Normal file
104
app/sdl/args.c
Normal file
@ -0,0 +1,104 @@
|
||||
#include "./args.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/parsarg/parsarg.h"
|
||||
|
||||
#include "core/loscene/param.h"
|
||||
|
||||
void app_args_parse(app_args_t* args, int argc, char** argv) {
|
||||
assert(args != NULL);
|
||||
assert(argc > 0);
|
||||
|
||||
parsarg_t pa;
|
||||
parsarg_initialize(&pa, argc-1, argv+1);
|
||||
|
||||
while (!parsarg_finished(&pa)) {
|
||||
size_t nlen;
|
||||
char* n = parsarg_pop_name(&pa, &nlen);
|
||||
|
||||
char* v;
|
||||
parsarg_pop_value(&pa, &v);
|
||||
|
||||
if (n == NULL && v == NULL) continue;
|
||||
|
||||
bool ok = false;
|
||||
|
||||
# define bool_(name, var) do { \
|
||||
if (strncmp(name, n, nlen) == 0 && name[nlen] == 0) { \
|
||||
if (v == NULL) { \
|
||||
var = true; \
|
||||
ok = true; \
|
||||
} else { \
|
||||
fprintf(stderr, "option '"name"' cannot take any values"); \
|
||||
abort(); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
# define int_(name, var, min, max) do { \
|
||||
if (strncmp(name, n, nlen) == 0 && name[nlen] == 0) { \
|
||||
char* end; \
|
||||
const intmax_t i = strtoimax(v, &end, 0); \
|
||||
if (*end == 0 && min <= i && i < max) { \
|
||||
var = i; \
|
||||
ok = true; \
|
||||
} else { \
|
||||
fprintf(stderr, \
|
||||
"option '"name"' requires " \
|
||||
"an integer value (%"PRIdMAX"~%"PRIdMAX")\n", \
|
||||
(intmax_t) min, (intmax_t) max); \
|
||||
abort(); \
|
||||
} \
|
||||
continue; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* ---- scene parameters ---- */
|
||||
int_("width", args->scene.width, 640, INT32_MAX);
|
||||
int_("height", args->scene.height, 360, INT32_MAX);
|
||||
int_("dpi-x", args->scene.dpi.x, 1, INT32_MAX);
|
||||
int_("dpi-y", args->scene.dpi.y, 1, INT32_MAX);
|
||||
|
||||
int_("max-msaa", args->scene.max_msaa, 1, INT32_MAX);
|
||||
int_("brightness", args->scene.brightness, 0, 2000);
|
||||
|
||||
bool_("disable-heavy-backwall",
|
||||
args->scene.environment.disable_heavy_backwall);
|
||||
bool_("disable-heavy-fog",
|
||||
args->scene.environment.disable_heavy_fog);
|
||||
|
||||
bool_("skip-title", args->scene.skip_title);
|
||||
|
||||
bool_("test-poolset-packing", args->scene.test.loworld_poolset_packing);
|
||||
bool_("test-player-packing", args->scene.test.loplayer_packing);
|
||||
|
||||
/* ---- app parameters ---- */
|
||||
int_("max-fps", args->max_fps, 1, INT32_MAX);
|
||||
|
||||
bool_("override-dpi", args->override_dpi);
|
||||
|
||||
bool_("force-window", args->force_window);
|
||||
bool_("force-desktop-fullscreen", args->force_desktop_fullscreen);
|
||||
bool_("force-fullscreen", args->force_fullscreen);
|
||||
|
||||
# undef int_
|
||||
# undef bool_
|
||||
|
||||
if (!ok) {
|
||||
if (n == NULL) {
|
||||
fprintf(stderr, "missing option name for the value '%s'\n", v);
|
||||
} else {
|
||||
fprintf(stderr, "unknown option '%.*s'\n", (int) nlen, n);
|
||||
}
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
parsarg_deinitialize(&pa);
|
||||
}
|
24
app/sdl/args.h
Normal file
24
app/sdl/args.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "core/loscene/param.h"
|
||||
|
||||
typedef struct {
|
||||
loscene_param_t scene;
|
||||
|
||||
int32_t max_fps;
|
||||
|
||||
bool override_dpi;
|
||||
|
||||
bool force_window;
|
||||
bool force_desktop_fullscreen;
|
||||
bool force_fullscreen;
|
||||
} app_args_t;
|
||||
|
||||
void
|
||||
app_args_parse(
|
||||
app_args_t* args,
|
||||
int argc,
|
||||
char** argv
|
||||
);
|
52
app/sdl/event.c
Normal file
52
app/sdl/event.c
Normal file
@ -0,0 +1,52 @@
|
||||
#include "./event.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "core/locommon/input.h"
|
||||
|
||||
#define APP_EVENT_GET_BUTTON_BIT_FROM_KEY(k) ( \
|
||||
(k) == SDLK_a? LOCOMMON_INPUT_BUTTON_LEFT: \
|
||||
(k) == SDLK_d? LOCOMMON_INPUT_BUTTON_RIGHT: \
|
||||
(k) == SDLK_w? LOCOMMON_INPUT_BUTTON_UP: \
|
||||
(k) == SDLK_s? LOCOMMON_INPUT_BUTTON_DOWN: \
|
||||
(k) == SDLK_SPACE? LOCOMMON_INPUT_BUTTON_JUMP: \
|
||||
(k) == SDLK_LSHIFT? LOCOMMON_INPUT_BUTTON_DASH: \
|
||||
(k) == SDLK_ESCAPE? LOCOMMON_INPUT_BUTTON_MENU: \
|
||||
0)
|
||||
|
||||
#define APP_EVENT_GET_BUTTON_BIT_FROM_MOUSE(m) ( \
|
||||
(m) == SDL_BUTTON_LEFT? LOCOMMON_INPUT_BUTTON_ATTACK: \
|
||||
(m) == SDL_BUTTON_RIGHT? LOCOMMON_INPUT_BUTTON_GUARD: \
|
||||
0)
|
||||
|
||||
bool app_event_handle(locommon_input_t* input, const SDL_Event* e) {
|
||||
assert(input != NULL);
|
||||
assert(e != NULL);
|
||||
|
||||
switch (e->type) {
|
||||
case SDL_MOUSEMOTION:
|
||||
input->cursor = vec2(
|
||||
e->motion.x/input->resolution.x, e->motion.y/input->resolution.y);
|
||||
input->cursor.x = input->cursor.x*2 - 1;
|
||||
input->cursor.y = 1 - input->cursor.y*2;
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
input->buttons |= APP_EVENT_GET_BUTTON_BIT_FROM_MOUSE(e->button.button);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
input->buttons &= ~APP_EVENT_GET_BUTTON_BIT_FROM_MOUSE(e->button.button);
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
input->buttons |= APP_EVENT_GET_BUTTON_BIT_FROM_KEY(e->key.keysym.sym);
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
input->buttons &= ~APP_EVENT_GET_BUTTON_BIT_FROM_KEY(e->key.keysym.sym);
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
13
app/sdl/event.h
Normal file
13
app/sdl/event.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "core/locommon/input.h"
|
||||
|
||||
bool
|
||||
app_event_handle(
|
||||
locommon_input_t* input,
|
||||
const SDL_Event* e
|
||||
);
|
172
app/sdl/main.c
Normal file
172
app/sdl/main.c
Normal file
@ -0,0 +1,172 @@
|
||||
#define SDL_MAIN_HANDLED
|
||||
#define NO_STDIO_REDIRECT
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "core/loscene/context.h"
|
||||
|
||||
#include "./args.h"
|
||||
#include "./event.h"
|
||||
|
||||
static const app_args_t app_default_args_ = {
|
||||
.scene = {
|
||||
.width = 960,
|
||||
.height = 540,
|
||||
.dpi = vec2(96, 96),
|
||||
|
||||
.max_msaa = 8,
|
||||
.brightness = 1000,
|
||||
},
|
||||
.max_fps = 60,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
SDL_Window* win;
|
||||
SDL_GLContext gl;
|
||||
} libs_t;
|
||||
|
||||
static void app_initialize_libraries_(libs_t* libs, app_args_t* args) {
|
||||
assert(libs != NULL);
|
||||
assert(args != NULL);
|
||||
|
||||
SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "0");
|
||||
if (SDL_Init(SDL_INIT_VIDEO)) {
|
||||
fprintf(stderr, "failed to initialize SDL: %s\n", SDL_GetError());
|
||||
abort();
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(
|
||||
SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
|
||||
SDL_GL_SetAttribute(
|
||||
SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
uint32_t win_flags =
|
||||
SDL_WINDOW_ALLOW_HIGHDPI |
|
||||
SDL_WINDOW_OPENGL |
|
||||
SDL_WINDOW_SHOWN;
|
||||
if (args->force_window) {
|
||||
} else if (args->force_desktop_fullscreen) {
|
||||
win_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
} else if (args->force_fullscreen) {
|
||||
win_flags |= SDL_WINDOW_FULLSCREEN;
|
||||
} else {
|
||||
# ifdef NDEBUG
|
||||
win_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
# endif
|
||||
}
|
||||
|
||||
libs->win = SDL_CreateWindow(
|
||||
"LEFTONE", /* title */
|
||||
SDL_WINDOWPOS_CENTERED, /* X position */
|
||||
SDL_WINDOWPOS_CENTERED, /* Y position */
|
||||
args->scene.width,
|
||||
args->scene.height,
|
||||
win_flags);
|
||||
if (libs->win == NULL) {
|
||||
fprintf(stderr, "failed to create window: %s\n", SDL_GetError());
|
||||
abort();
|
||||
}
|
||||
|
||||
int w, h;
|
||||
SDL_GetWindowSize(libs->win, &w, &h);
|
||||
args->scene.width = w;
|
||||
args->scene.height = h;
|
||||
|
||||
libs->gl = SDL_GL_CreateContext(libs->win);
|
||||
|
||||
glewExperimental = 1;
|
||||
if (glewInit() != GLEW_OK) {
|
||||
fprintf(stderr, "failed to init GLEW\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
glViewport(0, 0, args->scene.width, args->scene.height);
|
||||
}
|
||||
static void app_deinitialize_libraries_(libs_t* libs) {
|
||||
assert(libs != NULL);
|
||||
|
||||
SDL_GL_DeleteContext(libs->gl);
|
||||
SDL_DestroyWindow(libs->win);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
static void app_get_dpi_(libs_t* libs, app_args_t* args) {
|
||||
assert(libs != NULL);
|
||||
assert(args != NULL);
|
||||
|
||||
if (args->override_dpi) return;
|
||||
|
||||
const int32_t disp = SDL_GetWindowDisplayIndex(libs->win);
|
||||
|
||||
float x, y;
|
||||
if (SDL_GetDisplayDPI(disp, NULL, &x, &y) == 0) {
|
||||
args->scene.dpi = vec2(x, y);
|
||||
} else {
|
||||
fprintf(stderr, "failed to get display DPI: %s\n", SDL_GetError());
|
||||
fprintf(stderr, "Anti-aliasing may not work properly.\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
(void) argc, (void) argv;
|
||||
|
||||
app_args_t args = app_default_args_;
|
||||
app_args_parse(&args, argc, argv);
|
||||
|
||||
libs_t libs;
|
||||
app_initialize_libraries_(&libs, &args);
|
||||
|
||||
app_get_dpi_(&libs, &args);
|
||||
|
||||
loscene_context_t* ctx = loscene_context_new(&args.scene);
|
||||
|
||||
locommon_input_t input = {
|
||||
.resolution = vec2(args.scene.width, args.scene.height),
|
||||
.dpi = args.scene.dpi,
|
||||
.cursor = vec2(0, 0),
|
||||
};
|
||||
|
||||
glClearColor(0, 0, 0, 0);
|
||||
|
||||
const uint64_t min_frame_time = 1000 / args.max_fps;
|
||||
for (;;) {
|
||||
const uint64_t base_time = SDL_GetTicks();
|
||||
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
if (!app_event_handle(&input, &e)) goto EXIT;
|
||||
}
|
||||
|
||||
if (!loscene_context_update(ctx, &input, base_time)) goto EXIT;
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
loscene_context_draw(ctx);
|
||||
SDL_GL_SwapWindow(libs.win);
|
||||
|
||||
# ifndef NDEBUG
|
||||
/* for debugging in MSYS */
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
# endif
|
||||
|
||||
const uint64_t elapsed = SDL_GetTicks() - base_time;
|
||||
if (elapsed < min_frame_time) {
|
||||
SDL_Delay(min_frame_time - elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
EXIT:
|
||||
loscene_context_delete(ctx);
|
||||
app_deinitialize_libraries_(&libs);
|
||||
return 0;
|
||||
}
|
20
cmake/anysrc.cmake
Normal file
20
cmake/anysrc.cmake
Normal file
@ -0,0 +1,20 @@
|
||||
function(target_any_sources target)
|
||||
set(bin2c ${LEFTONE_TOOL_DIR}/bin2c.sh)
|
||||
|
||||
target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
foreach (path ${ARGN})
|
||||
get_filename_component(dirname ${path} DIRECTORY)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/anysrc/${dirname})
|
||||
|
||||
set(name ${target}_${path}_)
|
||||
set(in ${CMAKE_CURRENT_SOURCE_DIR}/${path})
|
||||
set(out ${CMAKE_CURRENT_BINARY_DIR}/anysrc/${path})
|
||||
add_custom_command(
|
||||
OUTPUT ${out}.c ${out}.h
|
||||
COMMAND cat ${in} | ${bin2c} ${name} ${out}
|
||||
DEPENDS ${path} ${bin2c}
|
||||
COMMENT "converting ${path} to C header")
|
||||
target_sources(${target} PRIVATE ${out}.c ${out}.h)
|
||||
endforeach()
|
||||
endfunction()
|
20
cmake/sos.cmake
Normal file
20
cmake/sos.cmake
Normal file
@ -0,0 +1,20 @@
|
||||
function(target_source_of_source target)
|
||||
target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/sos)
|
||||
foreach (file ${ARGN})
|
||||
get_filename_component(name ${file} NAME_WE)
|
||||
|
||||
set(sos_target sos-${target}-${name})
|
||||
set(in ${CMAKE_CURRENT_SOURCE_DIR}/${file})
|
||||
set(out ${CMAKE_CURRENT_BINARY_DIR}/sos/${file})
|
||||
|
||||
add_executable(${sos_target} ${in})
|
||||
add_custom_command(
|
||||
OUTPUT ${out}
|
||||
COMMAND ${sos_target} > ${out}
|
||||
DEPENDS ${sos_target}
|
||||
COMMENT "generating ${file}")
|
||||
target_sources(${target} PRIVATE ${out})
|
||||
endforeach()
|
||||
endfunction()
|
13
core/CMakeLists.txt
Normal file
13
core/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
include_directories(.)
|
||||
|
||||
add_subdirectory(lobullet)
|
||||
add_subdirectory(locharacter)
|
||||
add_subdirectory(locommon)
|
||||
add_subdirectory(loeffect)
|
||||
add_subdirectory(loentity)
|
||||
add_subdirectory(loground)
|
||||
add_subdirectory(loplayer)
|
||||
add_subdirectory(loresource)
|
||||
add_subdirectory(loscene)
|
||||
add_subdirectory(loshader)
|
||||
add_subdirectory(loworld)
|
20
core/lobullet/CMakeLists.txt
Normal file
20
core/lobullet/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
add_library(lobullet
|
||||
base.c
|
||||
bomb.c
|
||||
linear.c
|
||||
misc.c
|
||||
pool.c
|
||||
)
|
||||
target_link_libraries(lobullet
|
||||
msgpackc
|
||||
|
||||
math
|
||||
memory
|
||||
mpkutil
|
||||
coly2d
|
||||
|
||||
locommon
|
||||
loeffect
|
||||
loentity
|
||||
loshader
|
||||
)
|
235
core/lobullet/base.c
Normal file
235
core/lobullet/base.c
Normal file
@ -0,0 +1,235 @@
|
||||
#include "./base.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loentity/bullet.h"
|
||||
#include "core/loentity/character.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loshader/bullet.h"
|
||||
|
||||
#include "./bomb.h"
|
||||
#include "./linear.h"
|
||||
#include "./misc.h"
|
||||
|
||||
static void lobullet_base_delete_(loentity_t* entity) {
|
||||
assert(entity != NULL);
|
||||
|
||||
lobullet_base_t* base = (typeof(base)) entity;
|
||||
if (!base->used) return;
|
||||
|
||||
base->used = false;
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (base->type == LOBULLET_TYPE_##NAME) { \
|
||||
lobullet_##name##_tear_down(base); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOBULLET_TYPE_EACH_(each_);
|
||||
assert(false);
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
static void lobullet_base_die_(loentity_t* entity) {
|
||||
assert(entity != NULL);
|
||||
|
||||
}
|
||||
|
||||
static bool lobullet_base_update_(loentity_t* entity) {
|
||||
assert(entity != NULL);
|
||||
|
||||
lobullet_base_t* base = (typeof(base)) entity;
|
||||
base->cache = (typeof(base->cache)) {0};
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (base->type == LOBULLET_TYPE_##NAME) { \
|
||||
return lobullet_##name##_update(base); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOBULLET_TYPE_EACH_(each_);
|
||||
return false;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
static void lobullet_base_draw_(
|
||||
loentity_t* entity, const locommon_position_t* basepos) {
|
||||
assert(entity != NULL);
|
||||
assert(locommon_position_valid(basepos));
|
||||
|
||||
lobullet_base_t* base = (typeof(base)) entity;
|
||||
|
||||
vec2_t p;
|
||||
locommon_position_sub(&p, &base->super.super.pos, basepos);
|
||||
vec2_addeq(&base->cache.instance.pos, &p);
|
||||
|
||||
loshader_bullet_drawer_add_instance(base->drawer, &base->cache.instance);
|
||||
}
|
||||
|
||||
static void lobullet_base_pack_(
|
||||
const loentity_t* entity, msgpack_packer* packer) {
|
||||
assert(entity != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
const lobullet_base_t* base = (typeof(base)) entity;
|
||||
|
||||
msgpack_pack_map(packer, 4);
|
||||
|
||||
mpkutil_pack_str(packer, "subclass");
|
||||
mpkutil_pack_str(packer, "bullet");
|
||||
|
||||
mpkutil_pack_str(packer, "type");
|
||||
mpkutil_pack_str(packer, lobullet_type_stringify(base->type));
|
||||
|
||||
mpkutil_pack_str(packer, "id");
|
||||
msgpack_pack_uint64(packer, base->super.super.id);
|
||||
|
||||
mpkutil_pack_str(packer, "data");
|
||||
# define each_(NAME, name) do { \
|
||||
if (base->type == LOBULLET_TYPE_##NAME) { \
|
||||
lobullet_##name##_pack_data(base, packer); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOBULLET_TYPE_EACH_(each_);
|
||||
assert(false);
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
static bool lobullet_base_affect_(
|
||||
loentity_bullet_t* bullet, loentity_character_t* chara) {
|
||||
assert(bullet != NULL);
|
||||
|
||||
lobullet_base_t* base = (typeof(base)) bullet;
|
||||
|
||||
vec2_t v = vec2(0, 0);
|
||||
switch (base->cache.knockback.algorithm) {
|
||||
case LOBULLET_BASE_KNOCKBACK_ALGORITHM_VELOCITY:
|
||||
v = base->super.velocity;
|
||||
break;
|
||||
case LOBULLET_BASE_KNOCKBACK_ALGORITHM_POSITION:
|
||||
locommon_position_sub(&v, &chara->super.pos, &base->super.super.pos);
|
||||
break;
|
||||
}
|
||||
const float plen = vec2_pow_length(&v);
|
||||
if (plen != 0) {
|
||||
vec2_diveq(&v, sqrtf(plen));
|
||||
vec2_muleq(&v, base->cache.knockback.acceleration);
|
||||
loentity_character_knockback(chara, &v);
|
||||
}
|
||||
|
||||
if (base->cache.toxic) {
|
||||
loentity_character_apply_effect(chara, &base->cache.effect);
|
||||
}
|
||||
return base->cache.toxic;
|
||||
}
|
||||
|
||||
void lobullet_base_initialize(
|
||||
lobullet_base_t* base,
|
||||
loresource_set_t* res,
|
||||
loshader_bullet_drawer_t* drawer,
|
||||
const locommon_ticker_t* ticker,
|
||||
loentity_store_t* entities) {
|
||||
assert(base != NULL);
|
||||
assert(res != NULL);
|
||||
assert(drawer != NULL);
|
||||
assert(ticker != NULL);
|
||||
assert(entities != NULL);
|
||||
|
||||
*base = (typeof(*base)) {
|
||||
.super = {
|
||||
.super = {
|
||||
.vtable = {
|
||||
.delete = lobullet_base_delete_,
|
||||
.die = lobullet_base_die_,
|
||||
.update = lobullet_base_update_,
|
||||
.draw = lobullet_base_draw_,
|
||||
.pack = lobullet_base_pack_,
|
||||
},
|
||||
.subclass = LOENTITY_SUBCLASS_BULLET,
|
||||
},
|
||||
.vtable = {
|
||||
.affect = lobullet_base_affect_,
|
||||
},
|
||||
},
|
||||
.res = res,
|
||||
.drawer = drawer,
|
||||
.ticker = ticker,
|
||||
.entities = entities,
|
||||
};
|
||||
}
|
||||
|
||||
void lobullet_base_reinitialize(lobullet_base_t* base, loentity_id_t id) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->super.super.id = id;
|
||||
}
|
||||
|
||||
void lobullet_base_deinitialize(lobullet_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
lobullet_base_delete_(&base->super.super);
|
||||
}
|
||||
|
||||
bool lobullet_base_unpack(lobullet_base_t* base, const msgpack_object* obj) {
|
||||
assert(base != NULL);
|
||||
|
||||
lobullet_base_reinitialize(base, 0);
|
||||
|
||||
const char* v;
|
||||
size_t vlen;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
# define streq_(v1, len, v2) \
|
||||
(strncmp(v1, v2, len) == 0 && v2[len] == 0)
|
||||
if (!mpkutil_get_str(item_("subclass"), &v, &vlen) ||
|
||||
!streq_(v, vlen, "bullet")) {
|
||||
return false;
|
||||
}
|
||||
# undef streq_
|
||||
|
||||
if (!mpkutil_get_str(item_("type"), &v, &vlen) ||
|
||||
!lobullet_type_unstringify(&base->type, v, vlen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mpkutil_get_uint64(item_("id"), &base->super.super.id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const msgpack_object* data = item_("data");
|
||||
# define each_(NAME, name) do { \
|
||||
if (base->type == LOBULLET_TYPE_##NAME) { \
|
||||
if (!lobullet_##name##_unpack_data(base, data)) return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOBULLET_TYPE_EACH_(each_);
|
||||
|
||||
# undef each_
|
||||
|
||||
# undef item_
|
||||
return true;
|
||||
}
|
81
core/lobullet/base.h
Normal file
81
core/lobullet/base.h
Normal file
@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loentity/bullet.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loshader/bullet.h"
|
||||
|
||||
#include "./misc.h"
|
||||
|
||||
typedef enum {
|
||||
LOBULLET_BASE_KNOCKBACK_ALGORITHM_VELOCITY,
|
||||
LOBULLET_BASE_KNOCKBACK_ALGORITHM_POSITION,
|
||||
} lobullet_base_knockback_algorithm_t;
|
||||
|
||||
typedef struct {
|
||||
loentity_bullet_t super;
|
||||
bool used;
|
||||
|
||||
/* injected deps */
|
||||
loresource_set_t* res;
|
||||
loshader_bullet_drawer_t* drawer;
|
||||
const locommon_ticker_t* ticker;
|
||||
loentity_store_t* entities;
|
||||
|
||||
/* params not to be packed */
|
||||
struct {
|
||||
bool toxic;
|
||||
loeffect_t effect;
|
||||
/* When toxic is true, apply this effect to characters hit. */
|
||||
|
||||
struct {
|
||||
float acceleration;
|
||||
lobullet_base_knockback_algorithm_t algorithm;
|
||||
} knockback;
|
||||
|
||||
loshader_bullet_drawer_instance_t instance;
|
||||
/* instance pos is added to draw pos */
|
||||
} cache;
|
||||
|
||||
/* params to be packed (includes id) */
|
||||
lobullet_type_t type;
|
||||
|
||||
# define LOBULLET_BASE_DATA_MAX_SIZE 256
|
||||
uint8_t data[LOBULLET_BASE_DATA_MAX_SIZE];
|
||||
/* pack function for the type is used */
|
||||
} lobullet_base_t;
|
||||
|
||||
void
|
||||
lobullet_base_initialize(
|
||||
lobullet_base_t* base,
|
||||
loresource_set_t* res,
|
||||
loshader_bullet_drawer_t* drawer,
|
||||
const locommon_ticker_t* ticker,
|
||||
loentity_store_t* entities
|
||||
);
|
||||
|
||||
void
|
||||
lobullet_base_reinitialize(
|
||||
lobullet_base_t* base,
|
||||
loentity_id_t id
|
||||
);
|
||||
|
||||
void
|
||||
lobullet_base_deinitialize(
|
||||
lobullet_base_t* base
|
||||
);
|
||||
|
||||
bool
|
||||
lobullet_base_unpack(
|
||||
lobullet_base_t* base,
|
||||
const msgpack_object* obj
|
||||
);
|
205
core/lobullet/bomb.c
Normal file
205
core/lobullet/bomb.c
Normal file
@ -0,0 +1,205 @@
|
||||
#include "./bomb.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/coly2d/shape.h"
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/constant.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/locommon/msgpack.h"
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loresource/sound.h"
|
||||
#include "core/loshader/bullet.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./misc.h"
|
||||
|
||||
#define LOBULLET_BOMB_PARAM_TO_PACK_EACH_(PROC) do { \
|
||||
PROC("owner", owner); \
|
||||
PROC("pos", pos); \
|
||||
PROC("size", size); \
|
||||
PROC("angle", angle); \
|
||||
PROC("color", color); \
|
||||
PROC("silent", silent); \
|
||||
PROC("beat", beat); \
|
||||
PROC("step", step); \
|
||||
PROC("knockback", knockback); \
|
||||
PROC("effect", effect); \
|
||||
PROC("since", since); \
|
||||
} while (0)
|
||||
#define LOBULLET_BOMB_PARAM_TO_PACK_COUNT 11
|
||||
|
||||
_Static_assert(sizeof(lobullet_bomb_param_t) <= LOBULLET_BASE_DATA_MAX_SIZE);
|
||||
|
||||
static bool lobullet_bomb_update_(lobullet_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
const lobullet_bomb_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
base->super.super.pos = p->pos;
|
||||
base->super.owner = p->owner;
|
||||
base->super.velocity = vec2(0, 0);
|
||||
base->super.shape.size = p->size;
|
||||
base->super.shape.angle = p->angle;
|
||||
|
||||
const uint64_t st = (p->step-1) * p->beat;
|
||||
const uint64_t ed = st + 100;
|
||||
|
||||
const uint64_t t = base->ticker->time - p->since;
|
||||
const uint64_t pt =
|
||||
(int64_t) t >= base->ticker->delta? t - base->ticker->delta: 0;
|
||||
|
||||
if (!p->silent && pt < st && t >= st) {
|
||||
loresource_sound_play(base->res->sound, "bomb");
|
||||
}
|
||||
|
||||
base->cache.toxic = st <= t && t < ed;
|
||||
base->cache.effect = p->effect;
|
||||
return t < p->step*p->beat;
|
||||
}
|
||||
|
||||
bool lobullet_bomb_param_valid(const lobullet_bomb_param_t* param) {
|
||||
return
|
||||
param != NULL &&
|
||||
locommon_position_valid(¶m->pos) &&
|
||||
vec2_valid(¶m->size) &&
|
||||
MATH_FLOAT_VALID(param->angle) &&
|
||||
vec4_valid(¶m->color) &&
|
||||
MATH_FLOAT_VALID(param->beat) &&
|
||||
param->step > 0 &&
|
||||
MATH_FLOAT_VALID(param->knockback);
|
||||
}
|
||||
void lobullet_bomb_param_pack(
|
||||
const lobullet_bomb_param_t* p, msgpack_packer* packer) {
|
||||
assert(lobullet_bomb_param_valid(p));
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, LOBULLET_BOMB_PARAM_TO_PACK_COUNT);
|
||||
|
||||
# define pack_(name, var) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, &p->var); \
|
||||
} while (0)
|
||||
|
||||
LOBULLET_BOMB_PARAM_TO_PACK_EACH_(pack_);
|
||||
|
||||
# undef pack_
|
||||
}
|
||||
bool lobullet_bomb_param_unpack(
|
||||
lobullet_bomb_param_t* p, const msgpack_object* obj) {
|
||||
assert(p != NULL);
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
# define unpack_(name, var) do { \
|
||||
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(name), &p->var)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOBULLET_BOMB_PARAM_TO_PACK_EACH_(unpack_);
|
||||
return lobullet_bomb_param_valid(p);
|
||||
|
||||
# undef unpack_
|
||||
|
||||
# undef item_
|
||||
}
|
||||
|
||||
void lobullet_bomb_build(
|
||||
lobullet_base_t* base,
|
||||
lobullet_type_t type,
|
||||
const lobullet_bomb_param_t* param) {
|
||||
assert(base != NULL);
|
||||
assert(lobullet_bomb_param_valid(param));
|
||||
|
||||
base->type = type;
|
||||
|
||||
lobullet_bomb_param_t* p = (typeof(p)) base->data;
|
||||
*p = *param;
|
||||
p->since = base->ticker->time;
|
||||
}
|
||||
|
||||
bool lobullet_bomb_square_update(lobullet_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
if (!lobullet_bomb_update_(base)) return false;
|
||||
|
||||
const lobullet_bomb_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
/* ---- calculate motion ---- */
|
||||
const float beats = (base->ticker->time - p->since) / p->beat;
|
||||
|
||||
float time = 0;
|
||||
float angle = p->angle;
|
||||
float alpha = 1;
|
||||
if (beats < p->step-1) {
|
||||
time = beats - (int64_t) beats;
|
||||
alpha = 1-time;
|
||||
time = time*time;
|
||||
time = (1-time)*.05f;
|
||||
} else {
|
||||
time = 1 - powf(1-(beats - (int64_t) beats), 2);
|
||||
angle += time * MATH_PI/4;
|
||||
time = 1-time;
|
||||
}
|
||||
|
||||
/* ---- apply motion ---- */
|
||||
base->super.shape.type = COLY2D_SHAPE_TYPE_RECT;
|
||||
base->super.shape.angle = angle;
|
||||
|
||||
base->cache.instance = (loshader_bullet_drawer_instance_t) {
|
||||
.bullet_id = LOSHADER_BULLET_ID_SQUARE,
|
||||
.size = p->size,
|
||||
.theta = angle,
|
||||
.color = p->color,
|
||||
.time = time,
|
||||
};
|
||||
base->cache.instance.color.w *= alpha;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lobullet_bomb_triangle_update(lobullet_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
if (!lobullet_bomb_update_(base)) return false;
|
||||
|
||||
const lobullet_bomb_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
/* ---- calculate motion ---- */
|
||||
const float beats = (base->ticker->time - p->since) / p->beat;
|
||||
|
||||
float time = 0;
|
||||
float alpha = 1;
|
||||
if (beats < p->step-1) {
|
||||
time = beats - (int64_t) beats;
|
||||
alpha = 1-time;
|
||||
time = time*time;
|
||||
time = (1-time)*.05f;
|
||||
} else {
|
||||
time = 1 - powf(1-(beats - (int64_t) beats), 2);
|
||||
time = 1-time;
|
||||
}
|
||||
|
||||
/* ---- apply motion ---- */
|
||||
base->super.shape.type = COLY2D_SHAPE_TYPE_TRIANGLE;
|
||||
|
||||
base->cache.instance = (loshader_bullet_drawer_instance_t) {
|
||||
.bullet_id = LOSHADER_BULLET_ID_TRIANGLE,
|
||||
.size = p->size,
|
||||
.theta = base->super.shape.angle,
|
||||
.color = p->color,
|
||||
.time = time,
|
||||
};
|
||||
base->cache.instance.color.w *= alpha;
|
||||
return true;
|
||||
}
|
82
core/lobullet/bomb.h
Normal file
82
core/lobullet/bomb.h
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/loeffect/effect.h"
|
||||
#include "core/loentity/entity.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./misc.h"
|
||||
|
||||
typedef struct {
|
||||
loentity_id_t owner;
|
||||
locommon_position_t pos;
|
||||
vec2_t size;
|
||||
float angle;
|
||||
vec4_t color;
|
||||
bool silent;
|
||||
|
||||
float beat;
|
||||
int32_t step;
|
||||
float knockback;
|
||||
|
||||
loeffect_t effect;
|
||||
|
||||
uint64_t since; /* set by build function */
|
||||
} lobullet_bomb_param_t;
|
||||
|
||||
bool
|
||||
lobullet_bomb_param_valid(
|
||||
const lobullet_bomb_param_t* param /* NULLABLE */
|
||||
);
|
||||
void
|
||||
lobullet_bomb_param_pack(
|
||||
const lobullet_bomb_param_t* param,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
bool
|
||||
lobullet_bomb_param_unpack(
|
||||
lobullet_bomb_param_t* param,
|
||||
const msgpack_object* obj /* NULLABLE */
|
||||
);
|
||||
|
||||
void
|
||||
lobullet_bomb_build(
|
||||
lobullet_base_t* base,
|
||||
lobullet_type_t type,
|
||||
const lobullet_bomb_param_t* param
|
||||
);
|
||||
|
||||
bool
|
||||
lobullet_bomb_square_update(
|
||||
lobullet_base_t* base
|
||||
);
|
||||
#define lobullet_bomb_square_build(base, param) \
|
||||
lobullet_bomb_build(base, LOBULLET_TYPE_BOMB_SQUARE, param)
|
||||
#define lobullet_bomb_square_tear_down(base)
|
||||
#define lobullet_bomb_square_pack_data(base, packer) \
|
||||
lobullet_bomb_param_pack( \
|
||||
(const lobullet_bomb_param_t*) base->data, packer)
|
||||
#define lobullet_bomb_square_unpack_data(base, obj) \
|
||||
lobullet_bomb_param_unpack( \
|
||||
(lobullet_bomb_param_t*) base->data, obj)
|
||||
|
||||
bool
|
||||
lobullet_bomb_triangle_update(
|
||||
lobullet_base_t* base
|
||||
);
|
||||
#define lobullet_bomb_triangle_build(base, param) \
|
||||
lobullet_bomb_build(base, LOBULLET_TYPE_BOMB_TRIANGLE, param)
|
||||
#define lobullet_bomb_triangle_tear_down(base)
|
||||
#define lobullet_bomb_triangle_pack_data(base, packer) \
|
||||
lobullet_bomb_param_pack( \
|
||||
(const lobullet_bomb_param_t*) base->data, packer)
|
||||
#define lobullet_bomb_triangle_unpack_data(base, obj) \
|
||||
lobullet_bomb_param_unpack( \
|
||||
(lobullet_bomb_param_t*) base->data, obj)
|
197
core/lobullet/linear.c
Normal file
197
core/lobullet/linear.c
Normal file
@ -0,0 +1,197 @@
|
||||
#include "./linear.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/coly2d/shape.h"
|
||||
#include "util/math/constant.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/locommon/msgpack.h"
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/loeffect/effect.h"
|
||||
#include "core/loentity/entity.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./misc.h"
|
||||
|
||||
#define LOBULLET_LINEAR_PARAM_TO_PACK_EACH_(PROC) do { \
|
||||
PROC("owner", owner); \
|
||||
PROC("pos", pos); \
|
||||
PROC("size", size); \
|
||||
PROC("velocity", velocity); \
|
||||
PROC("acceleration", acceleration); \
|
||||
PROC("color", color); \
|
||||
PROC("duration", duration); \
|
||||
PROC("knockback", knockback); \
|
||||
PROC("effect", effect); \
|
||||
PROC("since", since); \
|
||||
} while (0)
|
||||
#define LOBULLET_LINEAR_PARAM_TO_PACK_COUNT 10
|
||||
|
||||
_Static_assert(sizeof(lobullet_linear_param_t) <= LOBULLET_BASE_DATA_MAX_SIZE);
|
||||
|
||||
static bool lobullet_linear_update_(lobullet_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
const lobullet_linear_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
const float t = (base->ticker->time - p->since)/1000.f;
|
||||
|
||||
base->super.owner = p->owner;
|
||||
|
||||
/* ---- movement ---- */
|
||||
vec2_t v1;
|
||||
vec2_mul(&v1, &p->velocity, t);
|
||||
|
||||
vec2_t v2;
|
||||
vec2_mul(&v2, &p->acceleration, t*t/2);
|
||||
|
||||
base->super.super.pos = p->pos;
|
||||
vec2_addeq(&base->super.super.pos.fract, &v1);
|
||||
vec2_addeq(&base->super.super.pos.fract, &v2);
|
||||
locommon_position_reduce(&base->super.super.pos);
|
||||
|
||||
/* ---- velocity ---- */
|
||||
vec2_mul(&base->super.velocity, &p->acceleration, t);
|
||||
vec2_addeq(&base->super.velocity, &p->velocity);
|
||||
|
||||
/* ---- angle ---- */
|
||||
const float theta = vec2_pow_length(&base->super.velocity) != 0?
|
||||
atan2f(base->super.velocity.y, base->super.velocity.x): 0;
|
||||
base->super.shape.size = p->size;
|
||||
base->super.shape.angle = theta;
|
||||
|
||||
/* ---- parameter update ---- */
|
||||
base->cache.toxic = true;
|
||||
base->cache.effect = p->effect;
|
||||
|
||||
base->cache.knockback = (typeof(base->cache.knockback)) {
|
||||
.acceleration = p->knockback,
|
||||
.algorithm = LOBULLET_BASE_KNOCKBACK_ALGORITHM_VELOCITY,
|
||||
};
|
||||
|
||||
return p->since + p->duration > base->ticker->time;
|
||||
}
|
||||
|
||||
bool lobullet_linear_param_valid(const lobullet_linear_param_t* param) {
|
||||
return
|
||||
param != NULL &&
|
||||
locommon_position_valid(¶m->pos) &&
|
||||
vec2_valid(¶m->size) &&
|
||||
vec2_valid(¶m->velocity) &&
|
||||
vec2_valid(¶m->acceleration) &&
|
||||
param->duration > 0;
|
||||
}
|
||||
void lobullet_linear_param_pack(
|
||||
const lobullet_linear_param_t* p, msgpack_packer* packer) {
|
||||
assert(lobullet_linear_param_valid(p));
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, LOBULLET_LINEAR_PARAM_TO_PACK_COUNT);
|
||||
|
||||
# define pack_(name, var) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, &p->var); \
|
||||
} while (0)
|
||||
|
||||
LOBULLET_LINEAR_PARAM_TO_PACK_EACH_(pack_);
|
||||
|
||||
# undef pack_
|
||||
}
|
||||
bool lobullet_linear_param_unpack(
|
||||
lobullet_linear_param_t* p, const msgpack_object* obj) {
|
||||
assert(p != NULL);
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
# define unpack_(name, var) do { \
|
||||
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(name), &p->var)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOBULLET_LINEAR_PARAM_TO_PACK_EACH_(unpack_);
|
||||
return lobullet_linear_param_valid(p);
|
||||
|
||||
# undef unpack_
|
||||
|
||||
# undef item_
|
||||
}
|
||||
|
||||
void lobullet_linear_build(
|
||||
lobullet_base_t* base,
|
||||
lobullet_type_t type,
|
||||
const lobullet_linear_param_t* param) {
|
||||
assert(base != NULL);
|
||||
assert(lobullet_linear_param_valid(param));
|
||||
|
||||
base->type = type;
|
||||
|
||||
lobullet_linear_param_t* p = (typeof(p)) base->data;
|
||||
*p = *param;
|
||||
p->since = base->ticker->time;
|
||||
}
|
||||
|
||||
bool lobullet_linear_light_update(lobullet_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const uint64_t fadedur = 500;
|
||||
|
||||
if (!lobullet_linear_update_(base)) return false;
|
||||
|
||||
const lobullet_linear_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
/* ---- calculation ---- */
|
||||
vec2_t size = p->size;
|
||||
vec2_muleq(&size, 1.2f);
|
||||
|
||||
float alpha = 1;
|
||||
const uint64_t remain = p->duration - (base->ticker->time - p->since);
|
||||
if (remain <= fadedur) alpha = remain*1.f / fadedur;
|
||||
|
||||
/* ---- apply result ---- */
|
||||
base->super.shape.type = COLY2D_SHAPE_TYPE_ELLIPSE;
|
||||
|
||||
base->cache.instance = (loshader_bullet_drawer_instance_t) {
|
||||
.bullet_id = LOSHADER_BULLET_ID_LIGHT,
|
||||
.size = size,
|
||||
.theta = base->super.shape.angle,
|
||||
.color = p->color,
|
||||
};
|
||||
base->cache.instance.color.w = alpha;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lobullet_linear_triangle_update(lobullet_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
if (!lobullet_linear_update_(base)) return false;
|
||||
|
||||
const lobullet_linear_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
/* ---- calculation ---- */
|
||||
vec2_t size = p->size;
|
||||
size.x *= 1-(1-cos(MATH_PI/3))/2;
|
||||
size.y *= 1-(1-sin(MATH_PI/3))/2;
|
||||
|
||||
/* ---- apply result ---- */
|
||||
base->super.shape.type = COLY2D_SHAPE_TYPE_TRIANGLE;
|
||||
|
||||
base->cache.instance = (loshader_bullet_drawer_instance_t) {
|
||||
.bullet_id = LOSHADER_BULLET_ID_TRIANGLE,
|
||||
.size = size,
|
||||
.theta = base->super.shape.angle,
|
||||
.color = p->color,
|
||||
.time = 1,
|
||||
};
|
||||
return true;
|
||||
}
|
81
core/lobullet/linear.h
Normal file
81
core/lobullet/linear.h
Normal file
@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/loeffect/effect.h"
|
||||
#include "core/loentity/entity.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
typedef struct {
|
||||
loentity_id_t owner;
|
||||
|
||||
locommon_position_t pos;
|
||||
vec2_t size;
|
||||
vec2_t velocity;
|
||||
vec2_t acceleration;
|
||||
vec4_t color;
|
||||
|
||||
uint64_t duration;
|
||||
float knockback;
|
||||
|
||||
loeffect_t effect;
|
||||
|
||||
uint64_t since; /* set by build function */
|
||||
} lobullet_linear_param_t;
|
||||
|
||||
bool
|
||||
lobullet_linear_param_valid(
|
||||
const lobullet_linear_param_t* param
|
||||
);
|
||||
void
|
||||
lobullet_linear_param_pack(
|
||||
const lobullet_linear_param_t* param,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
bool
|
||||
lobullet_linear_param_unpack(
|
||||
lobullet_linear_param_t* param,
|
||||
const msgpack_object* obj /* NULLABLE */
|
||||
);
|
||||
|
||||
void
|
||||
lobullet_linear_build(
|
||||
lobullet_base_t* base,
|
||||
lobullet_type_t type,
|
||||
const lobullet_linear_param_t* param
|
||||
);
|
||||
|
||||
bool
|
||||
lobullet_linear_light_update(
|
||||
lobullet_base_t* base
|
||||
);
|
||||
#define lobullet_linear_light_build(base, param) \
|
||||
lobullet_linear_build(base, LOBULLET_TYPE_LINEAR_LIGHT, param)
|
||||
#define lobullet_linear_light_tear_down(base)
|
||||
#define lobullet_linear_light_pack_data(base, packer) \
|
||||
lobullet_linear_param_pack( \
|
||||
(const lobullet_linear_param_t*) base->data, packer)
|
||||
#define lobullet_linear_light_unpack_data(base, obj) \
|
||||
lobullet_linear_param_unpack( \
|
||||
(lobullet_linear_param_t*) base->data, obj)
|
||||
|
||||
bool
|
||||
lobullet_linear_triangle_update(
|
||||
lobullet_base_t* base
|
||||
);
|
||||
#define lobullet_linear_triangle_build(base, param) \
|
||||
lobullet_linear_build(base, LOBULLET_TYPE_LINEAR_TRIANGLE, param)
|
||||
#define lobullet_linear_triangle_tear_down(base)
|
||||
#define lobullet_linear_triangle_pack_data(base, packer) \
|
||||
lobullet_linear_param_pack( \
|
||||
(const lobullet_linear_param_t*) base->data, packer)
|
||||
#define lobullet_linear_triangle_unpack_data(base, obj) \
|
||||
lobullet_linear_param_unpack( \
|
||||
(lobullet_linear_param_t*) base->data, obj)
|
37
core/lobullet/misc.c
Normal file
37
core/lobullet/misc.c
Normal file
@ -0,0 +1,37 @@
|
||||
#include "./misc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
const char* lobullet_type_stringify(lobullet_type_t type) {
|
||||
# define each_(NAME, name) do { \
|
||||
if (type == LOBULLET_TYPE_##NAME) return #name; \
|
||||
} while (0)
|
||||
|
||||
LOBULLET_TYPE_EACH_(each_);
|
||||
|
||||
assert(false);
|
||||
return NULL;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
bool lobullet_type_unstringify(
|
||||
lobullet_type_t* type, const char* v, size_t len) {
|
||||
assert(type != NULL);
|
||||
assert(v != NULL || len == 0);
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (strncmp(v, #name, len) == 0 && #name[len] == 0) { \
|
||||
*type = LOBULLET_TYPE_##NAME; \
|
||||
return true; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOBULLET_TYPE_EACH_(each_);
|
||||
return false;
|
||||
|
||||
# undef each_
|
||||
}
|
31
core/lobullet/misc.h
Normal file
31
core/lobullet/misc.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* dont forget to update EACH macro */
|
||||
typedef enum {
|
||||
LOBULLET_TYPE_LINEAR_LIGHT,
|
||||
LOBULLET_TYPE_LINEAR_TRIANGLE,
|
||||
LOBULLET_TYPE_BOMB_SQUARE,
|
||||
LOBULLET_TYPE_BOMB_TRIANGLE,
|
||||
} lobullet_type_t;
|
||||
|
||||
#define LOBULLET_TYPE_EACH_(PROC) do { \
|
||||
PROC(LINEAR_LIGHT, linear_light); \
|
||||
PROC(LINEAR_TRIANGLE, linear_triangle); \
|
||||
PROC(BOMB_SQUARE, bomb_square); \
|
||||
PROC(BOMB_TRIANGLE, bomb_triangle); \
|
||||
} while (0)
|
||||
|
||||
const char*
|
||||
lobullet_type_stringify(
|
||||
lobullet_type_t type
|
||||
);
|
||||
|
||||
bool
|
||||
lobullet_type_unstringify(
|
||||
lobullet_type_t* type,
|
||||
const char* v,
|
||||
size_t len
|
||||
);
|
104
core/lobullet/pool.c
Normal file
104
core/lobullet/pool.c
Normal file
@ -0,0 +1,104 @@
|
||||
#include "./pool.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/memory/memory.h"
|
||||
|
||||
#include "core/locommon/counter.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loshader/bullet.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
struct lobullet_pool_t {
|
||||
loresource_set_t* res;
|
||||
loshader_bullet_drawer_t* drawer;
|
||||
locommon_counter_t* idgen;
|
||||
const locommon_ticker_t* ticker;
|
||||
loentity_store_t* entities;
|
||||
|
||||
size_t length;
|
||||
lobullet_base_t items[1];
|
||||
};
|
||||
|
||||
static size_t lobullet_pool_find_unused_item_index_(
|
||||
const lobullet_pool_t* pool) {
|
||||
assert(pool != NULL);
|
||||
|
||||
for (size_t i = 0; i < pool->length; ++i) {
|
||||
if (!pool->items[i].used) return i;
|
||||
}
|
||||
fprintf(stderr, "bullet pool overflow\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
lobullet_pool_t* lobullet_pool_new(
|
||||
loresource_set_t* res,
|
||||
loshader_bullet_drawer_t* drawer,
|
||||
locommon_counter_t* idgen,
|
||||
const locommon_ticker_t* ticker,
|
||||
loentity_store_t* entities,
|
||||
size_t length) {
|
||||
assert(res != NULL);
|
||||
assert(drawer != NULL);
|
||||
assert(idgen != NULL);
|
||||
assert(ticker != NULL);
|
||||
assert(entities != NULL);
|
||||
assert(length > 0);
|
||||
|
||||
lobullet_pool_t* pool =
|
||||
memory_new(sizeof(*pool) + (length-1)*sizeof(pool->items[0]));
|
||||
*pool = (typeof(*pool)) {
|
||||
.res = res,
|
||||
.drawer = drawer,
|
||||
.idgen = idgen,
|
||||
.ticker = ticker,
|
||||
.entities = entities,
|
||||
.length = length,
|
||||
};
|
||||
for (size_t i = 0; i < pool->length; ++i) {
|
||||
lobullet_base_initialize(
|
||||
&pool->items[i],
|
||||
res,
|
||||
drawer,
|
||||
ticker,
|
||||
entities);
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
void lobullet_pool_delete(lobullet_pool_t* pool) {
|
||||
if (pool == NULL) return;
|
||||
|
||||
for (size_t i = 0; i < pool->length; ++i) {
|
||||
lobullet_base_deinitialize(&pool->items[i]);
|
||||
}
|
||||
memory_delete(pool);
|
||||
}
|
||||
|
||||
lobullet_base_t* lobullet_pool_create(lobullet_pool_t* pool) {
|
||||
assert(pool != NULL);
|
||||
|
||||
const size_t i = lobullet_pool_find_unused_item_index_(pool);
|
||||
|
||||
pool->items[i].used = true;
|
||||
lobullet_base_reinitialize(
|
||||
&pool->items[i], locommon_counter_count(pool->idgen));
|
||||
return &pool->items[i];
|
||||
}
|
||||
|
||||
lobullet_base_t* lobullet_pool_unpack_item(
|
||||
lobullet_pool_t* pool, const msgpack_object* obj) {
|
||||
assert(pool != NULL);
|
||||
|
||||
const size_t i = lobullet_pool_find_unused_item_index_(pool);
|
||||
|
||||
if (!lobullet_base_unpack(&pool->items[i], obj)) return NULL;
|
||||
|
||||
pool->items[i].used = true;
|
||||
return &pool->items[i];
|
||||
}
|
42
core/lobullet/pool.h
Normal file
42
core/lobullet/pool.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/locommon/counter.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loshader/bullet.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
struct lobullet_pool_t;
|
||||
typedef struct lobullet_pool_t lobullet_pool_t;
|
||||
|
||||
lobullet_pool_t* /* OWNERSHIP */
|
||||
lobullet_pool_new(
|
||||
loresource_set_t* res,
|
||||
loshader_bullet_drawer_t* drawer,
|
||||
locommon_counter_t* idgen,
|
||||
const locommon_ticker_t* ticker,
|
||||
loentity_store_t* entities,
|
||||
size_t length
|
||||
);
|
||||
|
||||
void
|
||||
lobullet_pool_delete(
|
||||
lobullet_pool_t* pool /* OWNERSHIP */
|
||||
);
|
||||
|
||||
lobullet_base_t*
|
||||
lobullet_pool_create(
|
||||
lobullet_pool_t* pool
|
||||
);
|
||||
|
||||
lobullet_base_t*
|
||||
lobullet_pool_unpack_item(
|
||||
lobullet_pool_t* pool,
|
||||
const msgpack_object* obj
|
||||
);
|
27
core/locharacter/CMakeLists.txt
Normal file
27
core/locharacter/CMakeLists.txt
Normal file
@ -0,0 +1,27 @@
|
||||
add_library(locharacter
|
||||
base.c
|
||||
big_warder.c
|
||||
cavia.c
|
||||
encephalon.c
|
||||
greedy_scientist.c
|
||||
misc.c
|
||||
pool.c
|
||||
scientist.c
|
||||
theists_child.c
|
||||
util.c
|
||||
warder.c
|
||||
)
|
||||
target_link_libraries(locharacter
|
||||
msgpackc
|
||||
|
||||
math
|
||||
memory
|
||||
mpkutil
|
||||
|
||||
lobullet
|
||||
locommon
|
||||
loeffect
|
||||
loentity
|
||||
loplayer
|
||||
loshader
|
||||
)
|
434
core/locharacter/base.c
Normal file
434
core/locharacter/base.c
Normal file
@ -0,0 +1,434 @@
|
||||
#include "./base.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/easing.h"
|
||||
#include "core/locommon/msgpack.h"
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loeffect/recipient.h"
|
||||
#include "core/loentity/character.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./big_warder.h"
|
||||
#include "./cavia.h"
|
||||
#include "./encephalon.h"
|
||||
#include "./greedy_scientist.h"
|
||||
#include "./scientist.h"
|
||||
#include "./theists_child.h"
|
||||
#include "./misc.h"
|
||||
#include "./warder.h"
|
||||
|
||||
#define LOCHARACTER_BASE_PARAM_TO_PACK_EACH_( \
|
||||
PROC, PROC_type, PROC_state, PROC_str) do { \
|
||||
PROC_str ("subclass", "character"); \
|
||||
PROC_type ("type", type); \
|
||||
PROC ("id", super.super.id); \
|
||||
PROC ("ground", ground); \
|
||||
PROC ("pos", pos); \
|
||||
PROC ("direction", direction); \
|
||||
PROC ("knockback", knockback); \
|
||||
PROC ("gravity", gravity); \
|
||||
PROC ("madness", recipient.madness); \
|
||||
PROC ("effects", recipient.effects); \
|
||||
PROC_state("state", state); \
|
||||
PROC ("since", since); \
|
||||
PROC ("last-update-time", last_update_time); \
|
||||
PROC ("last-knockback-time", last_knockback_time); \
|
||||
PROC ("last-hit-time", last_hit_time); \
|
||||
} while (0)
|
||||
#define LOCHARACTER_BASE_PARAM_TO_PACK_COUNT 15
|
||||
|
||||
static void locharacter_base_convert_to_world_pos_(
|
||||
const loentity_ground_t* g, locommon_position_t* wpos, const vec2_t* pos) {
|
||||
assert(g != NULL);
|
||||
assert(wpos != NULL);
|
||||
assert(vec2_valid(pos));
|
||||
|
||||
vec2_t p = *pos;
|
||||
p.x *= g->size.x;
|
||||
p.y += g->size.y;
|
||||
|
||||
*wpos = g->super.pos;
|
||||
vec2_addeq(&wpos->fract, &p);
|
||||
locommon_position_reduce(wpos);
|
||||
}
|
||||
|
||||
static void locharacter_base_convert_from_world_pos_(
|
||||
const loentity_ground_t* g, vec2_t* pos, const locommon_position_t* wpos) {
|
||||
assert(g != NULL);
|
||||
assert(pos != NULL);
|
||||
assert(locommon_position_valid(wpos));
|
||||
|
||||
locommon_position_sub(pos, wpos, &g->super.pos);
|
||||
pos->x /= g->size.x;
|
||||
pos->y -= g->size.y;
|
||||
}
|
||||
|
||||
static loentity_ground_t* locharacter_base_get_ground_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
loentity_store_iterator_t itr;
|
||||
if (loentity_store_find_item_by_id(base->entities, &itr, base->ground)) {
|
||||
return itr.ground;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void locharacter_base_handle_knockback_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
vec2_t v = base->knockback;
|
||||
v.x /= base->cache.ground->size.x;
|
||||
vec2_muleq(&v, base->ticker->delta_f);
|
||||
vec2_addeq(&base->pos, &v);
|
||||
|
||||
locommon_easing_linear_float(&base->knockback.x, 0, base->ticker->delta_f/2);
|
||||
locommon_easing_linear_float(&base->knockback.y, 0, base->ticker->delta_f/2);
|
||||
}
|
||||
|
||||
static void locharacter_base_calculate_world_position_(
|
||||
locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->pos.x = MATH_CLAMP(base->pos.x, -1, 1);
|
||||
base->pos.y = MATH_CLAMP(base->pos.y, 0, 1);
|
||||
|
||||
if (base->pos.y < base->cache.height) {
|
||||
if (base->cache.gravity) base->gravity = 0;
|
||||
base->pos.y = base->cache.height;
|
||||
}
|
||||
locharacter_base_convert_to_world_pos_(
|
||||
base->cache.ground, &base->super.super.pos, &base->pos);
|
||||
}
|
||||
|
||||
static void locharacter_base_calculate_velocity_(
|
||||
locharacter_base_t* base, vec2_t* v, const locommon_position_t* oldpos) {
|
||||
assert(base != NULL);
|
||||
assert(v != NULL);
|
||||
assert(locommon_position_valid(oldpos));
|
||||
|
||||
locommon_position_sub(v, &base->super.super.pos, oldpos);
|
||||
vec2_diveq(v, base->ticker->delta_f);
|
||||
}
|
||||
|
||||
static void locharacter_base_execute_bullet_hittest_(
|
||||
locharacter_base_t* base, const vec2_t* velocity) {
|
||||
assert(base != NULL);
|
||||
assert(vec2_valid(velocity));
|
||||
|
||||
if (base->last_hit_time + 200 > base->ticker->time) return;
|
||||
|
||||
if (loentity_store_affect_bullets_shot_by_one(
|
||||
base->entities,
|
||||
&base->super,
|
||||
base->player->entity.super.super.id,
|
||||
velocity,
|
||||
base->ticker->delta_f)) {
|
||||
base->last_hit_time = base->ticker->time;
|
||||
}
|
||||
}
|
||||
|
||||
static void locharacter_base_delete_(loentity_t* entity) {
|
||||
assert(entity != NULL);
|
||||
|
||||
locharacter_base_t* base = (typeof(base)) entity;
|
||||
if (!base->used) return;
|
||||
|
||||
base->used = false;
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (base->type == LOCHARACTER_TYPE_##NAME) { \
|
||||
locharacter_##name##_tear_down(base); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_TYPE_EACH_(each_);
|
||||
assert(false);
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
static void locharacter_base_die_(loentity_t* entity) {
|
||||
assert(entity != NULL);
|
||||
|
||||
}
|
||||
|
||||
static bool locharacter_base_update_(loentity_t* entity) {
|
||||
assert(entity != NULL);
|
||||
|
||||
static const float gravity_acceleration = 2.f;
|
||||
|
||||
locharacter_base_t* base = (typeof(base)) entity;
|
||||
|
||||
base->cache = (typeof(base->cache)) {
|
||||
.time = base->ticker->time,
|
||||
};
|
||||
|
||||
base->cache.ground = locharacter_base_get_ground_(base);
|
||||
if (base->cache.ground == NULL) return false;
|
||||
|
||||
locharacter_base_convert_from_world_pos_(
|
||||
base->cache.ground,
|
||||
&base->cache.player_pos,
|
||||
&base->player->entity.super.super.pos);
|
||||
|
||||
locharacter_base_handle_knockback_(base);
|
||||
|
||||
locommon_position_t oldpos = base->super.super.pos;
|
||||
|
||||
base->pos.y += base->gravity * base->ticker->delta_f;
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (base->type == LOCHARACTER_TYPE_##NAME) { \
|
||||
if (!locharacter_##name##_update(base)) return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_TYPE_EACH_(each_);
|
||||
|
||||
# undef each_
|
||||
|
||||
locharacter_base_calculate_world_position_(base);
|
||||
|
||||
if (base->cache.gravity) {
|
||||
base->gravity -= base->ticker->delta_f * gravity_acceleration;
|
||||
} else {
|
||||
base->gravity = 0;
|
||||
}
|
||||
if (base->cache.bullet_hittest) {
|
||||
vec2_t velocity;
|
||||
locharacter_base_calculate_velocity_(base, &velocity, &oldpos);
|
||||
locharacter_base_execute_bullet_hittest_(base, &velocity);
|
||||
}
|
||||
|
||||
base->cache.ground = NULL;
|
||||
base->last_update_time = base->cache.time;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void locharacter_base_draw_(
|
||||
loentity_t* entity, const locommon_position_t* basepos) {
|
||||
assert(entity != NULL);
|
||||
assert(locommon_position_valid(basepos));
|
||||
|
||||
locharacter_base_t* base = (typeof(base)) entity;
|
||||
|
||||
vec2_t v;
|
||||
locommon_position_sub(&v, &base->super.super.pos, basepos);
|
||||
vec2_addeq(&base->cache.instance.pos, &v);
|
||||
|
||||
loshader_character_drawer_add_instance(base->drawer, &base->cache.instance);
|
||||
}
|
||||
|
||||
static void locharacter_base_apply_effect_(
|
||||
loentity_character_t* entity, const loeffect_t* effect) {
|
||||
assert(entity != NULL);
|
||||
assert(effect != NULL);
|
||||
|
||||
locharacter_base_t* base = (typeof(base)) entity;
|
||||
loeffect_recipient_apply_effect(&base->recipient, effect);
|
||||
}
|
||||
|
||||
static void locharacter_base_knockback_(
|
||||
loentity_character_t* chara, const vec2_t* knockback) {
|
||||
assert(chara != NULL);
|
||||
assert(vec2_valid(knockback));
|
||||
|
||||
locharacter_base_t* base = (typeof(base)) chara;
|
||||
|
||||
static const float r = .05f;
|
||||
if (vec2_pow_length(knockback) > r*r) {
|
||||
base->last_knockback_time = base->ticker->time;
|
||||
}
|
||||
vec2_addeq(&base->knockback, knockback);
|
||||
}
|
||||
|
||||
static void locharacter_base_pack_(
|
||||
const loentity_t* chara, msgpack_packer* packer) {
|
||||
assert(chara != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
const locharacter_base_t* base = (typeof(base)) chara;
|
||||
|
||||
msgpack_pack_map(packer, LOCHARACTER_BASE_PARAM_TO_PACK_COUNT+1);
|
||||
|
||||
# define pack_(name, var) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, &base->var); \
|
||||
} while (0)
|
||||
# define pack_type_(name, var) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
mpkutil_pack_str(packer, locharacter_type_stringify(base->var)); \
|
||||
} while (0)
|
||||
# define pack_state_(name, var) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
mpkutil_pack_str(packer, locharacter_state_stringify(base->var)); \
|
||||
} while (0)
|
||||
# define pack_str_(name, str) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
mpkutil_pack_str(packer, str); \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_BASE_PARAM_TO_PACK_EACH_(pack_, pack_type_, pack_state_, pack_str_);
|
||||
|
||||
# undef pack_str_
|
||||
# undef pack_state_
|
||||
# undef pack_type_
|
||||
# undef pack_
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (base->type == LOCHARACTER_TYPE_##NAME) { \
|
||||
locharacter_##name##_pack_data(base, packer); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
mpkutil_pack_str(packer, "data");
|
||||
LOCHARACTER_TYPE_EACH_(each_);
|
||||
assert(false);
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
void locharacter_base_initialize(
|
||||
locharacter_base_t* base,
|
||||
loresource_set_t* res,
|
||||
loshader_character_drawer_t* drawer,
|
||||
const locommon_ticker_t* ticker,
|
||||
lobullet_pool_t* bullets,
|
||||
loentity_store_t* entities,
|
||||
loplayer_t* player) {
|
||||
assert(base != NULL);
|
||||
assert(res != NULL);
|
||||
assert(drawer != NULL);
|
||||
assert(ticker != NULL);
|
||||
assert(bullets != NULL);
|
||||
assert(entities != NULL);
|
||||
assert(player != NULL);
|
||||
|
||||
*base = (typeof(*base)) {
|
||||
.super = {
|
||||
.super = {
|
||||
.vtable = {
|
||||
.delete = locharacter_base_delete_,
|
||||
.die = locharacter_base_die_,
|
||||
.update = locharacter_base_update_,
|
||||
.draw = locharacter_base_draw_,
|
||||
.pack = locharacter_base_pack_,
|
||||
},
|
||||
.subclass = LOENTITY_SUBCLASS_CHARACTER,
|
||||
},
|
||||
.vtable = {
|
||||
.apply_effect = locharacter_base_apply_effect_,
|
||||
.knockback = locharacter_base_knockback_,
|
||||
},
|
||||
},
|
||||
.res = res,
|
||||
.drawer = drawer,
|
||||
.ticker = ticker,
|
||||
.bullets = bullets,
|
||||
.entities = entities,
|
||||
.player = player,
|
||||
};
|
||||
loeffect_recipient_initialize(&base->recipient, ticker);
|
||||
}
|
||||
|
||||
void locharacter_base_reinitialize(locharacter_base_t* base, loentity_id_t id) {
|
||||
assert(base != NULL);
|
||||
|
||||
# define reset_(name, var) do { \
|
||||
base->var = (typeof(base->var)) {0}; \
|
||||
} while (0)
|
||||
# define reset_str_(name, str)
|
||||
|
||||
LOCHARACTER_BASE_PARAM_TO_PACK_EACH_(
|
||||
reset_, reset_, reset_, reset_str_);
|
||||
|
||||
# undef reset_str_
|
||||
# undef reset_
|
||||
|
||||
loeffect_recipient_reset(&base->recipient);
|
||||
base->super.super.id = id;
|
||||
}
|
||||
|
||||
void locharacter_base_deinitialize(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
if (base->used) locharacter_base_delete_(&base->super.super);
|
||||
loeffect_recipient_deinitialize(&base->recipient);
|
||||
}
|
||||
|
||||
bool locharacter_base_unpack(
|
||||
locharacter_base_t* base, const msgpack_object* obj) {
|
||||
assert(base != NULL);
|
||||
assert(obj != NULL);
|
||||
|
||||
locharacter_base_reinitialize(base, 0);
|
||||
/* id will be overwritten below */
|
||||
|
||||
const char* v;
|
||||
size_t vlen;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
# define unpack_(name, var) do { \
|
||||
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(name), &base->var)) { \
|
||||
return NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
# define unpack_type_(name, var) do { \
|
||||
if (!mpkutil_get_str(item_(name), &v, &vlen) || \
|
||||
!locharacter_type_unstringify(&base->var, v, vlen)) { \
|
||||
return NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
# define unpack_state_(name, var) do { \
|
||||
if (!mpkutil_get_str(item_(name), &v, &vlen) || \
|
||||
!locharacter_state_unstringify(&base->var, v, vlen)) { \
|
||||
return NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
# define unpack_str_(name, str) do { \
|
||||
if (!mpkutil_get_str(item_(name), &v, &vlen) || \
|
||||
!(strncmp(v, str, vlen) == 0 && str[vlen] == 0)) { \
|
||||
return NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_BASE_PARAM_TO_PACK_EACH_(
|
||||
unpack_, unpack_type_, unpack_state_, unpack_str_);
|
||||
|
||||
# undef unpack_str_
|
||||
# undef unpack_state_
|
||||
# undef unpack_type_
|
||||
# undef unpack_
|
||||
|
||||
const msgpack_object* data = item_("data");
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (base->type == LOCHARACTER_TYPE_##NAME) { \
|
||||
return locharacter_##name##_unpack_data(base, data); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_TYPE_EACH_(each_);
|
||||
return false;
|
||||
|
||||
# undef each_
|
||||
|
||||
# undef item_
|
||||
}
|
100
core/locharacter/base.h
Normal file
100
core/locharacter/base.h
Normal file
@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loeffect/recipient.h"
|
||||
#include "core/loentity/character.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./misc.h"
|
||||
|
||||
typedef struct {
|
||||
loentity_character_t super;
|
||||
bool used;
|
||||
|
||||
/* injected deps */
|
||||
loresource_set_t* res;
|
||||
loshader_character_drawer_t* drawer;
|
||||
const locommon_ticker_t* ticker;
|
||||
lobullet_pool_t* bullets;
|
||||
loentity_store_t* entities;
|
||||
loplayer_t* player;
|
||||
|
||||
/* temporary params for update */
|
||||
struct {
|
||||
/* set before calling update function */
|
||||
loentity_ground_t* ground;
|
||||
vec2_t player_pos;
|
||||
|
||||
uint64_t time;
|
||||
/* Defaultly equals to ticker->time.
|
||||
But characters who have an event with music
|
||||
overwrites this value for synchronization */
|
||||
|
||||
/* set by update function */
|
||||
float height;
|
||||
bool bullet_hittest;
|
||||
bool gravity;
|
||||
|
||||
loshader_character_drawer_instance_t instance;
|
||||
} cache;
|
||||
|
||||
/* params to be packed (includes id) */
|
||||
locharacter_type_t type;
|
||||
|
||||
loentity_id_t ground;
|
||||
vec2_t pos;
|
||||
float direction;
|
||||
vec2_t knockback;
|
||||
float gravity;
|
||||
|
||||
loeffect_recipient_t recipient;
|
||||
|
||||
locharacter_state_t state;
|
||||
uint64_t since;
|
||||
|
||||
uint64_t last_update_time;
|
||||
uint64_t last_knockback_time;
|
||||
uint64_t last_hit_time;
|
||||
|
||||
# define LOCHARACTER_BASE_DATA_MAX_SIZE 256
|
||||
uint8_t data[LOCHARACTER_BASE_DATA_MAX_SIZE];
|
||||
} locharacter_base_t;
|
||||
|
||||
void
|
||||
locharacter_base_initialize(
|
||||
locharacter_base_t* base,
|
||||
loresource_set_t* res,
|
||||
loshader_character_drawer_t* drawer,
|
||||
const locommon_ticker_t* ticker,
|
||||
lobullet_pool_t* bullets,
|
||||
loentity_store_t* entities,
|
||||
loplayer_t* player
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_base_reinitialize(
|
||||
locharacter_base_t* base,
|
||||
loentity_id_t id
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_base_deinitialize(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
bool
|
||||
locharacter_base_unpack(
|
||||
locharacter_base_t* base,
|
||||
const msgpack_object* obj
|
||||
);
|
787
core/locharacter/big_warder.c
Normal file
787
core/locharacter/big_warder.c
Normal file
@ -0,0 +1,787 @@
|
||||
#include "./big_warder.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/lobullet/base.h"
|
||||
#include "core/lobullet/bomb.h"
|
||||
#include "core/lobullet/linear.h"
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/easing.h"
|
||||
#include "core/locommon/msgpack.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loplayer/combat.h"
|
||||
#include "core/loplayer/event.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/music.h"
|
||||
#include "core/loresource/text.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./misc.h"
|
||||
#include "./util.h"
|
||||
|
||||
typedef struct {
|
||||
locharacter_event_holder_t event;
|
||||
|
||||
int32_t phase;
|
||||
vec2_t from;
|
||||
vec2_t to;
|
||||
} locharacter_big_warder_param_t;
|
||||
|
||||
_Static_assert(
|
||||
sizeof(locharacter_big_warder_param_t) <= LOCHARACTER_BASE_DATA_MAX_SIZE);
|
||||
|
||||
#define LOCHARACTER_BIG_WARDER_PARAM_TO_PACK_EACH_(PROC) do { \
|
||||
PROC("event-start-time", event.start_time); \
|
||||
PROC("phase", phase); \
|
||||
PROC("from", from); \
|
||||
PROC("to", to); \
|
||||
} while (0)
|
||||
#define LOCHARACTER_BIG_WARDER_PARAM_TO_PACK_COUNT 4
|
||||
|
||||
static const vec2_t locharacter_big_warder_size_ = vec2(.04f, .07f);
|
||||
|
||||
static const loeffect_recipient_status_t
|
||||
locharacter_big_warder_base_status_ = {
|
||||
.attack = .1f,
|
||||
.defence = .85f,
|
||||
.speed = .1f,
|
||||
.jump = .1f,
|
||||
};
|
||||
|
||||
#define LOCHARACTER_BIG_WARDER_BEAT (60000/80.f) /* 80 BPM */
|
||||
#define LOCHARACTER_BIG_WARDER_MUSIC_DURATION \
|
||||
((uint64_t) LOCHARACTER_BIG_WARDER_BEAT*144)
|
||||
|
||||
#define LOCHARACTER_BIG_WARDER_MELODY_B_BEAT 80
|
||||
|
||||
#include "./big_warder.private.h"
|
||||
|
||||
static void
|
||||
locharacter_big_warder_start_wait_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_big_warder_start_walk_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_big_warder_start_shoot_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_big_warder_start_combo_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_big_warder_start_thrust_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_big_warder_start_cooldown_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_big_warder_start_stunned_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_big_warder_start_dead_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
|
||||
static void locharacter_big_warder_finalize_event_(locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
/* This function must start next state. */
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
assert(p != NULL);
|
||||
|
||||
locharacter_event_holder_release_control(&p->event);
|
||||
|
||||
if (c->recipient.madness > 0) {
|
||||
loentity_character_apply_effect(
|
||||
&c->player->entity.super, &loeffect_curse_trigger());
|
||||
locharacter_big_warder_start_wait_state_(c);
|
||||
} else {
|
||||
loplayer_gain_stance(c->player, LOEFFECT_STANCE_ID_UNFINISHER);
|
||||
locharacter_big_warder_start_dead_state_(c);
|
||||
}
|
||||
}
|
||||
|
||||
static bool locharacter_big_warder_reset_if_player_left_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
if (MATH_ABS(c->cache.player_pos.x) < 1 &&
|
||||
0 < c->cache.player_pos.y && c->cache.player_pos.y < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
locharacter_event_holder_release_control(&p->event);
|
||||
|
||||
locharacter_big_warder_start_wait_state_(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void locharacter_big_warder_update_wait_state_(locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const uint64_t period = 1000;
|
||||
|
||||
c->cache.gravity = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = (c->cache.time - c->since)%period*1.f/period;
|
||||
t = (t*2) - 1;
|
||||
t = MATH_ABS(t);
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = t*t*(3-2*t);
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (MATH_ABS(c->cache.player_pos.x) < 1 &&
|
||||
0 < c->cache.player_pos.y && c->cache.player_pos.y < 1) {
|
||||
vec2_t diff;
|
||||
vec2_sub(&diff, &c->cache.player_pos, &c->pos);
|
||||
if (vec2_pow_length(&diff) < .5f*.5f) {
|
||||
locharacter_big_warder_start_walk_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void locharacter_big_warder_start_wait_state_(locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_WAIT;
|
||||
}
|
||||
|
||||
static void locharacter_big_warder_update_walk_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_BIG_WARDER_BEAT;
|
||||
static const float linedur = beat*4;
|
||||
static const uint64_t period = 800;
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
const bool event = locharacter_event_holder_has_control(&p->event);
|
||||
|
||||
const uint64_t min_duration = event? LOCHARACTER_BIG_WARDER_BEAT*16: 0;
|
||||
|
||||
/* ---- motion ---- */
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
if (c->pos.x != 0) {
|
||||
float t = (c->cache.time - c->since)%period*1.f/period;
|
||||
t = (t*2) - 1;
|
||||
t = MATH_ABS(t);
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_WALK;
|
||||
instance->motion_time = t;
|
||||
} else {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
}
|
||||
|
||||
/* ---- position ---- */
|
||||
if (c->pos.x != 0) c->direction = -MATH_SIGN(c->pos.x);
|
||||
|
||||
c->pos.y = 0;
|
||||
locommon_easing_linear_float(&c->pos.x, 0, c->ticker->delta_f/5);
|
||||
|
||||
/* ---- event ---- */
|
||||
if (event) {
|
||||
p->event.param->cinescope = true;
|
||||
p->event.param->hide_hud = true;
|
||||
|
||||
if (c->since+(p->phase+1)*linedur < c->cache.time) {
|
||||
static const char* text[] = {
|
||||
"boss_big_warder_line0",
|
||||
"boss_big_warder_line1",
|
||||
};
|
||||
if (p->phase < (int32_t) (sizeof(text)/sizeof(text[0]))) {
|
||||
const char* v = loresource_text_get(
|
||||
c->res->lang, text[(size_t) p->phase]);
|
||||
loplayer_event_param_set_line(p->event.param, v, strlen(v));
|
||||
} else {
|
||||
loplayer_event_param_set_line(p->event.param, "", 0);
|
||||
}
|
||||
++p->phase;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (locharacter_big_warder_reset_if_player_left_(c)) return;
|
||||
|
||||
if (c->pos.x == 0 && c->since + min_duration <= c->cache.time) {
|
||||
if (event) {
|
||||
p->event.param->hide_hud = false;
|
||||
p->event.param->cinescope = false;
|
||||
loplayer_event_param_set_line(p->event.param, "", 0);
|
||||
}
|
||||
locharacter_big_warder_start_shoot_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_big_warder_start_walk_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_WALK;
|
||||
|
||||
p->phase = 0;
|
||||
|
||||
loeffect_recipient_reset(&c->recipient);
|
||||
|
||||
if (!loeffect_stance_set_has(
|
||||
&c->player->status.stances, LOEFFECT_STANCE_ID_UNFINISHER)) {
|
||||
locharacter_event_holder_take_control(&p->event);
|
||||
}
|
||||
}
|
||||
|
||||
static void locharacter_big_warder_update_shoot_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_BIG_WARDER_BEAT;
|
||||
static const uint64_t duration = beat*3;
|
||||
|
||||
const uint64_t t = c->cache.time - c->since;
|
||||
|
||||
c->cache.bullet_hittest = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = t;
|
||||
|
||||
/* ---- shooting ---- */
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
if (p->phase < 4 && p->phase*beat/2 <= c->cache.time - c->since) {
|
||||
++p->phase;
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_linear_light_build(b, (&(lobullet_linear_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = c->super.super.pos,
|
||||
.size = vec2(.04f, .04f),
|
||||
.velocity = vec2(c->direction*.5f, 0),
|
||||
.color = vec4(.6f, .6f, .6f, .8f),
|
||||
.acceleration = vec2(0, 0),
|
||||
.duration = 2000,
|
||||
.knockback = .4f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/2),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (locharacter_big_warder_reset_if_player_left_(c)) return;
|
||||
|
||||
if (c->since + duration <= c->cache.time) {
|
||||
locharacter_big_warder_start_combo_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_big_warder_start_shoot_state_(locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_SHOOT;
|
||||
|
||||
c->direction = c->cache.player_pos.x - c->pos.x;
|
||||
c->direction = c->direction > 0? 1: -1;
|
||||
|
||||
p->phase = 0;
|
||||
}
|
||||
|
||||
static void locharacter_big_warder_update_combo_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_BIG_WARDER_BEAT;
|
||||
static const uint64_t step_dur = beat;
|
||||
static const uint64_t attack_dur = beat*2;
|
||||
|
||||
const locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
c->cache.bullet_hittest = true;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
if (c->since + step_dur > c->cache.time) {
|
||||
const float t = (c->cache.time - c->since)*1.f/step_dur;
|
||||
|
||||
/* ---- position ---- */
|
||||
vec2_t dist;
|
||||
vec2_sub(&dist, &p->to, &p->from);
|
||||
vec2_muleq(&dist, t*t*(3-2*t));
|
||||
c->pos = p->from;
|
||||
vec2_addeq(&c->pos, &dist);
|
||||
|
||||
/* ---- motion ---- */
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_WALK;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->motion_time = 1-powf(2*t-1, 4);
|
||||
} else {
|
||||
float t = (c->cache.time - c->since - step_dur)*1.f/attack_dur;
|
||||
t *= 4;
|
||||
t -= (uint64_t) t;
|
||||
|
||||
/* ---- motion ---- */
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
instance->motion_time = t*t;
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (locharacter_big_warder_reset_if_player_left_(c)) return;
|
||||
|
||||
if (c->since + step_dur + attack_dur <= c->cache.time) {
|
||||
locharacter_big_warder_start_thrust_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_big_warder_start_combo_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_BIG_WARDER_BEAT;
|
||||
static const uint64_t parry = 400;
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
if (c->last_knockback_time+parry > c->cache.time) {
|
||||
locharacter_big_warder_start_stunned_state_(c);
|
||||
return;
|
||||
}
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_COMBO;
|
||||
|
||||
c->direction = c->cache.player_pos.x - c->pos.x;
|
||||
c->direction = c->direction > 0? 1: -1;
|
||||
|
||||
c->gravity = 0;
|
||||
|
||||
p->from = c->pos;
|
||||
p->to = c->cache.player_pos;
|
||||
p->to.x -= c->direction * locharacter_big_warder_size_.x;
|
||||
p->to.y -= locharacter_big_warder_size_.y*.2f;
|
||||
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
loplayer_attack(c->player, &(loplayer_combat_attack_t) {
|
||||
.attacker = c->super.super.id,
|
||||
.start = c->ticker->time + (uint64_t) (beat*(i/2.f+1)),
|
||||
.duration = beat/4,
|
||||
.knockback = vec2(c->direction*.1f, 0),
|
||||
.effect = loeffect_immediate_damage(c->recipient.status.attack),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void locharacter_big_warder_update_thrust_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_BIG_WARDER_BEAT;
|
||||
static const uint64_t duration = beat;
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
c->cache.bullet_hittest = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
|
||||
const uint64_t ti = c->cache.time - c->since;
|
||||
if (p->phase <= 0) {
|
||||
/* ---- disappear ---- */
|
||||
instance->color.w *= 1 - ti/beat/4;
|
||||
if (ti > beat/4) {
|
||||
c->pos = p->to;
|
||||
if (p->phase < 0) { /* backattack */
|
||||
c->direction *= -1;
|
||||
p->phase = 0;
|
||||
}
|
||||
++p->phase;
|
||||
}
|
||||
|
||||
} else if (p->phase == 1) {
|
||||
/* ---- appear ---- */
|
||||
float t = (ti/beat/2 - .5f)*2;
|
||||
if (t > 1) t = 1;
|
||||
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->motion_time = t;
|
||||
instance->color.w *= t;
|
||||
|
||||
if (ti > beat/2) ++p->phase;
|
||||
|
||||
} else {
|
||||
/* ---- attack ---- */
|
||||
float t = (ti/beat - .5f)*2;
|
||||
if (t > 1) t = 1;
|
||||
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
instance->motion_time = t;
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (locharacter_big_warder_reset_if_player_left_(c)) return;
|
||||
|
||||
if (c->since + duration < c->cache.time) {
|
||||
locharacter_big_warder_start_cooldown_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_big_warder_start_thrust_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_BIG_WARDER_BEAT;
|
||||
static const float bmelo = LOCHARACTER_BIG_WARDER_MELODY_B_BEAT;
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_THRUST;
|
||||
|
||||
c->direction = c->cache.player_pos.x - c->pos.x;
|
||||
c->direction = c->direction > 0? 1: -1;
|
||||
|
||||
c->gravity = 0;
|
||||
|
||||
const bool backattack =
|
||||
locharacter_event_holder_has_control(&p->event) &&
|
||||
(c->cache.time - p->event.start_time >= beat*bmelo);
|
||||
const float backattack_f = backattack? 1: -1;
|
||||
|
||||
p->to = c->cache.player_pos;
|
||||
p->to.x += c->direction*locharacter_big_warder_size_.x*backattack_f;
|
||||
p->to.y -= locharacter_big_warder_size_.y*.2f;
|
||||
|
||||
p->phase = backattack? -1: 0;
|
||||
|
||||
loplayer_attack(c->player, &(loplayer_combat_attack_t) {
|
||||
.attacker = c->super.super.id,
|
||||
.start = c->ticker->time + (uint64_t) (beat/2),
|
||||
.duration = beat/2,
|
||||
.knockback = vec2(-backattack_f*c->direction*.1f, 0),
|
||||
.effect = loeffect_immediate_damage(c->recipient.status.attack),
|
||||
});
|
||||
}
|
||||
|
||||
static void locharacter_big_warder_update_cooldown_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_BIG_WARDER_BEAT;
|
||||
static const uint64_t duration = beat*4;
|
||||
|
||||
const uint64_t ti = c->cache.time - c->since;
|
||||
|
||||
const locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
c->cache.bullet_hittest = true;
|
||||
c->cache.gravity = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = ti*1.f/duration;
|
||||
if (t > 1) t = 1;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = t;
|
||||
|
||||
/* ---- position ---- */
|
||||
if (ti < beat*2) {
|
||||
t = ti/beat/2;
|
||||
vec2_sub(&c->pos, &p->to, &p->from);
|
||||
vec2_muleq(&c->pos, t*t*(3-2*t));
|
||||
vec2_addeq(&c->pos, &p->from);
|
||||
|
||||
t = t*2-1;
|
||||
t = 1-powf(MATH_ABS(t), 2);
|
||||
c->pos.y += t*c->recipient.status.jump/2;
|
||||
} else {
|
||||
c->pos = vec2(0, 0);
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (locharacter_big_warder_reset_if_player_left_(c)) return;
|
||||
|
||||
if (c->since + duration <= c->cache.time) {
|
||||
locharacter_big_warder_start_shoot_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_big_warder_start_cooldown_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
if (locharacter_event_holder_has_control(&p->event)) {
|
||||
static const uint64_t dur = LOCHARACTER_BIG_WARDER_MUSIC_DURATION;
|
||||
if (p->event.start_time+dur < c->cache.time) {
|
||||
locharacter_big_warder_finalize_event_(c);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (c->recipient.madness <= 0) {
|
||||
locharacter_big_warder_start_dead_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_COOLDOWN;
|
||||
|
||||
p->from = c->pos;
|
||||
p->to = vec2(0, 0);
|
||||
}
|
||||
|
||||
static void locharacter_big_warder_update_stunned_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_BIG_WARDER_BEAT;
|
||||
static const uint64_t duration = beat*4;
|
||||
|
||||
c->cache.gravity = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = (c->cache.time - c->since)*1.f/duration;
|
||||
t *= 6;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
if (t < 1) {
|
||||
t = 1-powf(1-t, 6);
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
instance->motion_time = t;
|
||||
} else {
|
||||
t = (t-1)/5;
|
||||
if (t > 1) t = 1;
|
||||
t = t*t*(3-2*t);
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = t;
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (c->since + duration <= c->cache.time) {
|
||||
locharacter_big_warder_start_cooldown_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_big_warder_start_stunned_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_STUNNED;
|
||||
|
||||
loeffect_recipient_apply_effect(
|
||||
&c->recipient, &loeffect_immediate_damage(1.f));
|
||||
}
|
||||
|
||||
static void locharacter_big_warder_update_dead_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const uint64_t anime_duration = 4000;
|
||||
static const uint64_t duration = 30000;
|
||||
|
||||
c->cache.gravity = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = (c->cache.time - c->since)*1.f/anime_duration;
|
||||
if (t > 1) t = 1;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
instance->motion_time = t*t;
|
||||
instance->color.w *= 1-t;
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (c->since+duration < c->cache.time) {
|
||||
locharacter_big_warder_start_wait_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_big_warder_start_dead_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_DEAD;
|
||||
|
||||
loplayer_gain_faith(c->player, .8f);
|
||||
}
|
||||
|
||||
bool locharacter_big_warder_update(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const vec2_t size = locharacter_big_warder_size_;
|
||||
static const float height = size.y * 1.4f;
|
||||
static const float drawsz = MATH_MAX(size.x, size.y);
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
loeffect_recipient_update(
|
||||
&base->recipient, &locharacter_big_warder_base_status_);
|
||||
|
||||
if (!locharacter_event_holder_update(&p->event)) {
|
||||
locharacter_big_warder_start_wait_state_(base);
|
||||
}
|
||||
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_WARDER,
|
||||
.marker_offset = vec2(0, height - drawsz),
|
||||
.pos = vec2(0, drawsz - height),
|
||||
.size = vec2(drawsz, drawsz),
|
||||
.color = vec4(.2f, 0, 0, 1),
|
||||
};
|
||||
|
||||
switch (base->state) {
|
||||
case LOCHARACTER_STATE_WAIT:
|
||||
locharacter_big_warder_update_wait_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_WALK:
|
||||
locharacter_big_warder_update_walk_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_SHOOT:
|
||||
locharacter_big_warder_update_shoot_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_COMBO:
|
||||
locharacter_big_warder_update_combo_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_THRUST:
|
||||
locharacter_big_warder_update_thrust_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_COOLDOWN:
|
||||
locharacter_big_warder_update_cooldown_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_STUNNED:
|
||||
locharacter_big_warder_update_stunned_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_DEAD:
|
||||
locharacter_big_warder_update_dead_state_(base);
|
||||
break;
|
||||
default:
|
||||
locharacter_big_warder_start_wait_state_(base);
|
||||
}
|
||||
locharacter_big_warder_update_passive_action_(base);
|
||||
|
||||
base->cache.height = height;
|
||||
|
||||
base->cache.instance.marker = !!base->cache.bullet_hittest;
|
||||
base->cache.instance.size.x *= base->direction;
|
||||
return true;
|
||||
}
|
||||
|
||||
void locharacter_big_warder_build(
|
||||
locharacter_base_t* base, loentity_id_t ground) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->type = LOCHARACTER_TYPE_BIG_WARDER;
|
||||
|
||||
base->ground = ground;
|
||||
|
||||
base->pos = vec2(.7f, 0);
|
||||
base->direction = 1;
|
||||
|
||||
base->state = LOCHARACTER_STATE_WAIT;
|
||||
base->since = base->cache.time;
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) base->data;
|
||||
*p = (typeof(*p)) {0};
|
||||
|
||||
locharacter_event_holder_initialize(
|
||||
&p->event,
|
||||
&base->res->music.boss_big_warder,
|
||||
base,
|
||||
LOCHARACTER_BIG_WARDER_MUSIC_DURATION,
|
||||
0);
|
||||
}
|
||||
|
||||
void locharacter_big_warder_tear_down(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) base->data;
|
||||
locharacter_event_holder_deinitialize(&p->event);
|
||||
}
|
||||
|
||||
void locharacter_big_warder_pack_data(
|
||||
const locharacter_base_t* base, msgpack_packer* packer) {
|
||||
assert(base != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
const locharacter_big_warder_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
msgpack_pack_map(packer, LOCHARACTER_BIG_WARDER_PARAM_TO_PACK_COUNT);
|
||||
|
||||
# define pack_(name, var) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, &p->var); \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_BIG_WARDER_PARAM_TO_PACK_EACH_(pack_);
|
||||
|
||||
# undef pack_
|
||||
}
|
||||
|
||||
bool locharacter_big_warder_unpack_data(
|
||||
locharacter_base_t* base, const msgpack_object* obj) {
|
||||
assert(base != NULL);
|
||||
|
||||
locharacter_big_warder_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
# define unpack_(name, var) do { \
|
||||
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(name), &p->var)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_BIG_WARDER_PARAM_TO_PACK_EACH_(unpack_);
|
||||
|
||||
# undef unpack_
|
||||
|
||||
# undef item_
|
||||
|
||||
locharacter_event_holder_initialize(
|
||||
&p->event,
|
||||
&base->res->music.boss_big_warder,
|
||||
base,
|
||||
LOCHARACTER_BIG_WARDER_MUSIC_DURATION,
|
||||
p->event.start_time);
|
||||
return true;
|
||||
}
|
37
core/locharacter/big_warder.h
Normal file
37
core/locharacter/big_warder.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/loentity/entity.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
bool
|
||||
locharacter_big_warder_update(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_big_warder_build(
|
||||
locharacter_base_t* base,
|
||||
loentity_id_t ground
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_big_warder_tear_down(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_big_warder_pack_data(
|
||||
const locharacter_base_t* base,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
locharacter_big_warder_unpack_data(
|
||||
locharacter_base_t* base,
|
||||
const msgpack_object* obj
|
||||
);
|
157
core/locharacter/big_warder.private.h
Normal file
157
core/locharacter/big_warder.private.h
Normal file
@ -0,0 +1,157 @@
|
||||
static void locharacter_big_warder_update_passive_action_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_BIG_WARDER_BEAT;
|
||||
|
||||
const locharacter_big_warder_param_t* p = (typeof(p)) c->data;
|
||||
if (!locharacter_event_holder_has_control(&p->event)) return;
|
||||
|
||||
const uint64_t dt = c->cache.time - c->last_update_time;
|
||||
const uint64_t t = c->cache.time - p->event.start_time;
|
||||
|
||||
const float beats = t/beat;
|
||||
const float last_beats = t > dt? (t-dt)/beat: 0;
|
||||
|
||||
# define name_pos_(name, x, y) \
|
||||
locommon_position_t name = c->cache.ground->super.pos; \
|
||||
vec2_addeq(&name.fract, &vec2(x, y)); \
|
||||
locommon_position_reduce(&name);
|
||||
|
||||
name_pos_(top, 0, .8f);
|
||||
name_pos_(center, 0, .25f);
|
||||
name_pos_(left, -.3f, .2f);
|
||||
name_pos_(right, .3f, .2f);
|
||||
name_pos_(leftbottom, -.4f, .1f);
|
||||
name_pos_(rightbottom, .4f, .1f);
|
||||
name_pos_(leftbottom_off, -.6f, .1f);
|
||||
name_pos_(rightbottom_off, .6f, .1f);
|
||||
|
||||
# undef name_pos_
|
||||
|
||||
# define trigger_on_(x) (last_beats < (x) && beats >= (x))
|
||||
|
||||
/* ---- intro -> A melody ---- */
|
||||
if (trigger_on_(12)) {
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_square_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = center,
|
||||
.size = vec2(.4f, .4f),
|
||||
.angle = MATH_PI/4,
|
||||
.color = vec4(1, 1, 1, .4f),
|
||||
.silent = true,
|
||||
.beat = beat,
|
||||
.step = 4,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/4),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
|
||||
/* ---- A melody ---- */
|
||||
for (size_t i = 48; i < 80; i+=8) {
|
||||
for (size_t j = 0; j < 2; ++j) {
|
||||
if (trigger_on_(i-4 + j*4)) {
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_triangle_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = j? leftbottom: rightbottom,
|
||||
.size = vec2(.05f, .15f),
|
||||
.angle = j? 0: MATH_PI,
|
||||
.color = vec4(1, 1, 1, .6f),
|
||||
.silent = true,
|
||||
.beat = beat,
|
||||
.step = 4,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/2),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
if (trigger_on_(i + j*4)) {
|
||||
static const float speed = 1.4f;
|
||||
static const float accel = .7f / (beat*2);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_linear_triangle_build(b, (&(lobullet_linear_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = j? leftbottom_off: rightbottom_off,
|
||||
.size = vec2(.05f, .15f),
|
||||
.velocity = vec2(j? speed: -speed, 0),
|
||||
.acceleration = vec2(j? -accel: accel, 0),
|
||||
.color = vec4(1, 1, 1, .8f),
|
||||
.duration = beat*2,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/2),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- B melody ---- */
|
||||
static const int32_t bmelo_trigger_beats[] = {92, 108};
|
||||
static const size_t bmelo_trigger_counts =
|
||||
sizeof(bmelo_trigger_beats)/sizeof(bmelo_trigger_beats[0]);
|
||||
for (size_t i = 0; i < bmelo_trigger_counts; ++i) {
|
||||
const int32_t st = bmelo_trigger_beats[i];
|
||||
for (int32_t j = 0; j < 4; ++j) {
|
||||
if (trigger_on_(st + j/2.f)) {
|
||||
for (int32_t x = -2; x <= 2; ++x) {
|
||||
locommon_position_t pos = center;
|
||||
vec2_addeq(&pos.fract, &vec2(x/2.f*.45f, (j-1)/4.f*.3f));
|
||||
locommon_position_reduce(&pos);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_square_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = pos,
|
||||
.size = vec2(.1f, .1f),
|
||||
.angle = MATH_PI/4,
|
||||
.color = vec4(1, 1, 1, .6f),
|
||||
.silent = true,
|
||||
.beat = beat*2,
|
||||
.step = 2,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/2),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- C melody ---- */
|
||||
for (int32_t i = 0; i < 8; ++i) {
|
||||
for (int32_t x = -10; x <= 10; ++x) {
|
||||
if (trigger_on_(112 + i*4 + (x+10)/100.f)) {
|
||||
locommon_position_t pos = center;
|
||||
pos.fract.x += x/10.f*.47f;
|
||||
pos.fract.y -= .13f;
|
||||
locommon_position_reduce(&pos);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_square_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = pos,
|
||||
.size = vec2(.06f, .06f),
|
||||
.angle = MATH_PI/4,
|
||||
.color = vec4(1, 1, 1, .4f),
|
||||
.silent = true,
|
||||
.beat = beat,
|
||||
.step = 4,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/2),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# undef trigger_on_
|
||||
}
|
265
core/locharacter/cavia.c
Normal file
265
core/locharacter/cavia.c
Normal file
@ -0,0 +1,265 @@
|
||||
#include "./cavia.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/loeffect/recipient.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loplayer/event.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./misc.h"
|
||||
|
||||
static const vec2_t locharacter_cavia_size_ = vec2(.02f, .05f);
|
||||
|
||||
static const loeffect_recipient_status_t locharacter_cavia_base_status_ = {
|
||||
.attack = .2f,
|
||||
.defence = .1f,
|
||||
.speed = .05f,
|
||||
};
|
||||
|
||||
static void
|
||||
locharacter_cavia_start_walk_state_(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
static bool
|
||||
locharacter_cavia_start_thrust_state_(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
static void
|
||||
locharacter_cavia_start_cooldown_state_(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
static void
|
||||
locharacter_cavia_start_dead_state_(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
static void locharacter_cavia_update_walk_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
/* ---- movement ---- */
|
||||
const vec2_t* gsize = &base->cache.ground->size;
|
||||
const float s = base->recipient.status.speed;
|
||||
base->pos.x += base->ticker->delta_f * base->direction * s / gsize->x;
|
||||
|
||||
if (MATH_ABS(base->pos.x) > 1) base->direction *= -1;
|
||||
|
||||
/* ---- motion ---- */
|
||||
loshader_character_drawer_instance_t* instance = &base->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_WALK;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
|
||||
const int32_t p = 70/s;
|
||||
const float t = (base->ticker->time - base->since)%p*2.0f/p - 1;
|
||||
instance->motion_time = MATH_ABS(t);
|
||||
|
||||
/* ---- dead ---- */
|
||||
if (base->recipient.madness <= 0) {
|
||||
locharacter_cavia_start_dead_state_(base);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---- trigger thrust ---- */
|
||||
if (loplayer_event_get_param(base->player->event) == NULL) {
|
||||
vec2_t dist;
|
||||
locommon_position_sub(
|
||||
&dist, &base->player->entity.super.super.pos, &base->super.super.pos);
|
||||
const float sdist_x = dist.x * base->direction;
|
||||
if (MATH_ABS(dist.y) < locharacter_cavia_size_.y &&
|
||||
sdist_x >= locharacter_cavia_size_.x &&
|
||||
sdist_x <= locharacter_cavia_size_.x*2) {
|
||||
if (locharacter_cavia_start_thrust_state_(base)) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void locharacter_cavia_start_walk_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->since = base->ticker->time;
|
||||
base->state = LOCHARACTER_STATE_WALK;
|
||||
}
|
||||
|
||||
static void locharacter_cavia_update_thrust_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const uint64_t premotion = 1200;
|
||||
static const uint64_t duration = 1500;
|
||||
|
||||
/* ---- motion ---- */
|
||||
loshader_character_drawer_instance_t* instance = &base->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
|
||||
float t = (base->ticker->time - base->since)*1.0f/premotion;
|
||||
if (t > 1) t = 1;
|
||||
instance->motion_time = t*t*t*t;
|
||||
|
||||
/* ---- cooldown ---- */
|
||||
if (base->since+duration <= base->ticker->time) {
|
||||
/* TODO(catfoot): go to cooldown */
|
||||
locharacter_cavia_start_cooldown_state_(base);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static bool locharacter_cavia_start_thrust_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
const loplayer_combat_attack_t attack = {
|
||||
.attacker = base->super.super.id,
|
||||
.start = base->ticker->time + 1000,
|
||||
.duration = 500,
|
||||
.knockback = vec2(base->direction*.1f, 0),
|
||||
.effect = loeffect_immediate_damage(base->recipient.status.attack),
|
||||
};
|
||||
if (!loplayer_attack(base->player, &attack)) return false;
|
||||
|
||||
base->since = base->ticker->time;
|
||||
base->state = LOCHARACTER_STATE_THRUST;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void locharacter_cavia_update_cooldown_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const uint64_t duration = 500;
|
||||
|
||||
/* ---- motion ---- */
|
||||
loshader_character_drawer_instance_t* instance = &base->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
|
||||
float t = (base->ticker->time - base->since)*1.0f/duration;
|
||||
if (t > 1) t = 1;
|
||||
instance->motion_time = t*t*(3-2*t);
|
||||
|
||||
/* ---- cooldown ---- */
|
||||
if (base->since+duration <= base->ticker->time) {
|
||||
if (base->recipient.madness <= 0) {
|
||||
locharacter_cavia_start_dead_state_(base);
|
||||
return;
|
||||
} else {
|
||||
locharacter_cavia_start_walk_state_(base);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void locharacter_cavia_start_cooldown_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->since = base->ticker->time;
|
||||
base->state = LOCHARACTER_STATE_COOLDOWN;
|
||||
}
|
||||
|
||||
static void locharacter_cavia_update_dead_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const uint64_t anime = 500;
|
||||
static const uint64_t duration = 30000;
|
||||
|
||||
/* ---- motion ---- */
|
||||
loshader_character_drawer_instance_t* instance = &base->cache.instance;
|
||||
if (base->since+anime > base->ticker->time) {
|
||||
const float t = (base->ticker->time - base->since)*1.0f/anime;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
instance->motion_time = t*t;
|
||||
|
||||
} else if (base->since+anime*2 > base->ticker->time) {
|
||||
const float t = (base->ticker->time - anime - base->since)*1.0f/anime;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
instance->motion_time = t*t;
|
||||
|
||||
} else if (base->ticker->time+anime > base->since+duration) {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time =
|
||||
1 - (base->since + duration - base->ticker->time)*1.0f/anime;
|
||||
|
||||
} else {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
}
|
||||
|
||||
/* ---- revive ---- */
|
||||
if (base->since+duration <= base->ticker->time) {
|
||||
loeffect_recipient_reset(&base->recipient);
|
||||
locharacter_cavia_start_walk_state_(base);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_cavia_start_dead_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->since = base->ticker->time;
|
||||
base->state = LOCHARACTER_STATE_DEAD;
|
||||
|
||||
loplayer_gain_faith(base->player, .1f);
|
||||
}
|
||||
|
||||
bool locharacter_cavia_update(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const vec2_t size = locharacter_cavia_size_;
|
||||
static const vec4_t color = vec4(.1f, .1f, .1f, 1);
|
||||
static const float height = size.y*1.4f;
|
||||
static const float drawsz = MATH_MAX(size.x, size.y);
|
||||
|
||||
loeffect_recipient_update(&base->recipient, &locharacter_cavia_base_status_);
|
||||
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_CAVIA,
|
||||
.marker_offset = vec2(0, height - drawsz),
|
||||
.pos = vec2(0, drawsz - height),
|
||||
.size = vec2(drawsz, drawsz),
|
||||
.color = color,
|
||||
};
|
||||
|
||||
switch (base->state) {
|
||||
case LOCHARACTER_STATE_WALK:
|
||||
locharacter_cavia_update_walk_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_THRUST:
|
||||
locharacter_cavia_update_thrust_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_COOLDOWN:
|
||||
locharacter_cavia_update_cooldown_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_DEAD:
|
||||
locharacter_cavia_update_dead_state_(base);
|
||||
break;
|
||||
default:
|
||||
locharacter_cavia_start_walk_state_(base);
|
||||
}
|
||||
|
||||
base->cache.bullet_hittest = base->state != LOCHARACTER_STATE_DEAD;
|
||||
|
||||
base->cache.height = height;
|
||||
|
||||
base->cache.instance.size.x *= base->direction;
|
||||
base->cache.instance.marker = !!base->cache.bullet_hittest;
|
||||
return true;
|
||||
}
|
||||
|
||||
void locharacter_cavia_build(
|
||||
locharacter_base_t* base, const locharacter_cavia_param_t* param) {
|
||||
assert(base != NULL);
|
||||
assert(param != NULL);
|
||||
|
||||
base->type = LOCHARACTER_TYPE_CAVIA;
|
||||
|
||||
base->ground = param->ground;
|
||||
|
||||
base->pos = vec2(param->pos, 0);
|
||||
base->direction = param->direction == 1? 1: -1;
|
||||
|
||||
base->state = LOCHARACTER_STATE_WALK;
|
||||
base->since = base->ticker->time;
|
||||
}
|
36
core/locharacter/cavia.h
Normal file
36
core/locharacter/cavia.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/loentity/entity.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
typedef struct {
|
||||
loentity_id_t ground;
|
||||
float pos;
|
||||
float direction;
|
||||
} locharacter_cavia_param_t;
|
||||
|
||||
bool
|
||||
locharacter_cavia_update(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_cavia_build(
|
||||
locharacter_base_t* base,
|
||||
const locharacter_cavia_param_t* param
|
||||
);
|
||||
|
||||
#define locharacter_cavia_tear_down(base)
|
||||
|
||||
#define locharacter_cavia_pack_data(base, packer) \
|
||||
msgpack_pack_nil(packer)
|
||||
|
||||
#define locharacter_cavia_unpack_data(base, obj) \
|
||||
(obj != NULL)
|
138
core/locharacter/encephalon.c
Normal file
138
core/locharacter/encephalon.c
Normal file
@ -0,0 +1,138 @@
|
||||
#include "./encephalon.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/chaos/xorshift.h"
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/locommon/msgpack.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/sound.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./misc.h"
|
||||
|
||||
typedef struct {
|
||||
float progress;
|
||||
} locharacter_encephalon_param_t;
|
||||
|
||||
_Static_assert(
|
||||
sizeof(locharacter_encephalon_param_t) <= LOCHARACTER_BASE_DATA_MAX_SIZE);
|
||||
|
||||
static const vec2_t locharacter_encephalon_size_ = vec2(.1f, .1f);
|
||||
|
||||
#define LOCHARACTER_ENCEPHALON_PARAM_TO_PACK_EACH_(PROC) do { \
|
||||
PROC("progress", progress); \
|
||||
} while (0)
|
||||
#define LOCHARACTER_ENCEPHALON_PARAM_TO_PACK_COUNT 1
|
||||
|
||||
static void locharacter_encephalon_update_progress_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
locharacter_encephalon_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
const vec2_t* player = &base->cache.player_pos;
|
||||
const bool near =
|
||||
MATH_ABS(player->x) < 1 &&
|
||||
0 <= player->y && player->y < locharacter_encephalon_size_.y;
|
||||
|
||||
if (near && base->state == LOCHARACTER_STATE_WAIT) {
|
||||
p->progress += base->ticker->delta_f;
|
||||
if (p->progress >= 1) {
|
||||
loplayer_touch_encephalon(base->player);
|
||||
base->state = LOCHARACTER_STATE_COOLDOWN;
|
||||
loresource_sound_play(base->res->sound, "touch_gate");
|
||||
}
|
||||
} else {
|
||||
p->progress -= base->ticker->delta_f * 2;
|
||||
if (!near) base->state = LOCHARACTER_STATE_WAIT;
|
||||
}
|
||||
p->progress = MATH_CLAMP(p->progress, 0, 1);
|
||||
}
|
||||
|
||||
bool locharacter_encephalon_update(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
locharacter_encephalon_update_progress_(base);
|
||||
|
||||
const locharacter_encephalon_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
base->pos = vec2(0, locharacter_encephalon_size_.y);
|
||||
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_ENCEPHALON,
|
||||
.from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1,
|
||||
.color = vec4(p->progress*.5f, 0, 0, .95f),
|
||||
.size = locharacter_encephalon_size_,
|
||||
};
|
||||
if (base->state == LOCHARACTER_STATE_COOLDOWN && p->progress > 0) {
|
||||
base->cache.instance.color.w *=
|
||||
chaos_xorshift(base->ticker->time)%100/100.f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void locharacter_encephalon_build(
|
||||
locharacter_base_t* base, loentity_id_t ground) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->type = LOCHARACTER_TYPE_ENCEPHALON;
|
||||
|
||||
base->ground = ground;
|
||||
base->pos = vec2(0, 0);
|
||||
|
||||
base->state = LOCHARACTER_STATE_WAIT;
|
||||
}
|
||||
|
||||
void locharacter_encephalon_pack_data(
|
||||
const locharacter_base_t* base, msgpack_packer* packer) {
|
||||
assert(base != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
const locharacter_encephalon_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
msgpack_pack_map(packer, LOCHARACTER_ENCEPHALON_PARAM_TO_PACK_COUNT);
|
||||
|
||||
# define pack_(name, var) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, &p->var); \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_ENCEPHALON_PARAM_TO_PACK_EACH_(pack_);
|
||||
|
||||
# undef pack_
|
||||
}
|
||||
|
||||
bool locharacter_encephalon_unpack_data(
|
||||
locharacter_base_t* base, const msgpack_object* obj) {
|
||||
assert(base != NULL);
|
||||
|
||||
locharacter_encephalon_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
# define unpack_(name, var) do { \
|
||||
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(name), &p->var)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_ENCEPHALON_PARAM_TO_PACK_EACH_(unpack_);
|
||||
return true;
|
||||
|
||||
# undef unpack_
|
||||
|
||||
# undef item_
|
||||
}
|
34
core/locharacter/encephalon.h
Normal file
34
core/locharacter/encephalon.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/loentity/entity.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
bool
|
||||
locharacter_encephalon_update(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_encephalon_build(
|
||||
locharacter_base_t* base,
|
||||
loentity_id_t ground
|
||||
);
|
||||
|
||||
#define locharacter_encephalon_tear_down(base)
|
||||
|
||||
void
|
||||
locharacter_encephalon_pack_data(
|
||||
const locharacter_base_t* base,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
locharacter_encephalon_unpack_data(
|
||||
locharacter_base_t* base,
|
||||
const msgpack_object* obj
|
||||
);
|
607
core/locharacter/greedy_scientist.c
Normal file
607
core/locharacter/greedy_scientist.c
Normal file
@ -0,0 +1,607 @@
|
||||
#include "./greedy_scientist.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/chaos/xorshift.h"
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/constant.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/lobullet/base.h"
|
||||
#include "core/lobullet/bomb.h"
|
||||
#include "core/lobullet/linear.h"
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/easing.h"
|
||||
#include "core/locommon/msgpack.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loplayer/combat.h"
|
||||
#include "core/loplayer/event.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/music.h"
|
||||
#include "core/loresource/text.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./misc.h"
|
||||
#include "./util.h"
|
||||
|
||||
typedef struct {
|
||||
locharacter_event_holder_t event;
|
||||
|
||||
int32_t phase;
|
||||
vec2_t from;
|
||||
vec2_t to;
|
||||
} locharacter_greedy_scientist_param_t;
|
||||
|
||||
_Static_assert(
|
||||
sizeof(locharacter_greedy_scientist_param_t) <= LOCHARACTER_BASE_DATA_MAX_SIZE);
|
||||
|
||||
#define LOCHARACTER_GREEDY_SCIENTIST_PARAM_TO_PACK_EACH_(PROC) do { \
|
||||
PROC("event-start-time", event.start_time); \
|
||||
PROC("phase", phase); \
|
||||
PROC("from", from); \
|
||||
PROC("to", to); \
|
||||
} while (0)
|
||||
#define LOCHARACTER_GREEDY_SCIENTIST_PARAM_TO_PACK_COUNT 4
|
||||
|
||||
static const vec2_t locharacter_greedy_scientist_size_ = vec2(.03f, .07f);
|
||||
|
||||
static const loeffect_recipient_status_t
|
||||
locharacter_greedy_scientist_base_status_ = {
|
||||
.attack = .2f,
|
||||
.defence = .85f,
|
||||
.speed = .1f,
|
||||
.jump = .1f,
|
||||
};
|
||||
|
||||
#define LOCHARACTER_GREEDY_SCIENTIST_BEAT (60000/140.f) /* 140 BPM */
|
||||
#define LOCHARACTER_GREEDY_SCIENTIST_MUSIC_DURATION \
|
||||
((uint64_t) LOCHARACTER_GREEDY_SCIENTIST_BEAT*128)
|
||||
|
||||
#define LOCHARACTER_GREEDY_SCIENTIST_MELODY_B_BEAT 52
|
||||
|
||||
#include "./greedy_scientist.private.h"
|
||||
|
||||
static void
|
||||
locharacter_greedy_scientist_start_wait_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_greedy_scientist_start_standup_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_greedy_scientist_start_combo_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_greedy_scientist_start_cooldown_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_greedy_scientist_start_stunned_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_greedy_scientist_start_dead_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
|
||||
static void locharacter_greedy_scientist_finalize_event_(locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
/* This function must start next state. */
|
||||
|
||||
locharacter_greedy_scientist_param_t* p = (typeof(p)) c->data;
|
||||
assert(p != NULL);
|
||||
|
||||
locharacter_event_holder_release_control(&p->event);
|
||||
|
||||
if (c->recipient.madness > 0) {
|
||||
loentity_character_apply_effect(
|
||||
&c->player->entity.super, &loeffect_curse_trigger());
|
||||
locharacter_greedy_scientist_start_wait_state_(c);
|
||||
} else {
|
||||
loplayer_gain_stance(c->player, LOEFFECT_STANCE_ID_PHILOSOPHER);
|
||||
locharacter_greedy_scientist_start_dead_state_(c);
|
||||
}
|
||||
}
|
||||
|
||||
static bool locharacter_greedy_scientist_reset_if_player_left_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
if (MATH_ABS(c->cache.player_pos.x) < 1 &&
|
||||
0 < c->cache.player_pos.y && c->cache.player_pos.y < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
locharacter_greedy_scientist_param_t* p = (typeof(p)) c->data;
|
||||
locharacter_event_holder_release_control(&p->event);
|
||||
|
||||
locharacter_greedy_scientist_start_wait_state_(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void locharacter_greedy_scientist_update_wait_state_(locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const uint64_t min_duration = LOCHARACTER_GREEDY_SCIENTIST_BEAT*4;
|
||||
|
||||
c->cache.gravity = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = 0;
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (c->since + min_duration <= c->cache.time) {
|
||||
if (MATH_ABS(c->cache.player_pos.x) < 1 &&
|
||||
0 < c->cache.player_pos.y && c->cache.player_pos.y < 1) {
|
||||
vec2_t diff;
|
||||
vec2_sub(&diff, &c->cache.player_pos, &c->pos);
|
||||
if (vec2_pow_length(&diff) < .5f*.5f) {
|
||||
locharacter_greedy_scientist_start_standup_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static void locharacter_greedy_scientist_start_wait_state_(locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_WAIT;
|
||||
}
|
||||
|
||||
static void locharacter_greedy_scientist_update_standup_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_GREEDY_SCIENTIST_BEAT;
|
||||
static const uint64_t period = beat*4;
|
||||
|
||||
locharacter_greedy_scientist_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
const bool event = locharacter_event_holder_has_control(&p->event);
|
||||
const uint64_t duration = event? beat*20: beat*8;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = (c->cache.time - c->since)%period*1.f/period;
|
||||
t = t*2-1;
|
||||
t = MATH_ABS(t);
|
||||
t = t*t*(3-2*t);
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND2;
|
||||
instance->motion_time = t;
|
||||
|
||||
/* ---- event ---- */
|
||||
if (event) {
|
||||
p->event.param->cinescope = true;
|
||||
p->event.param->hide_hud = true;
|
||||
|
||||
if (c->since+(p->phase*8+4)*beat < c->cache.time) {
|
||||
static const char* text[] = {
|
||||
"boss_greedy_scientist_line0",
|
||||
"boss_greedy_scientist_line1",
|
||||
};
|
||||
if (p->phase < (int32_t) (sizeof(text)/sizeof(text[0]))) {
|
||||
const char* v = loresource_text_get(
|
||||
c->res->lang, text[(size_t) p->phase]);
|
||||
loplayer_event_param_set_line(p->event.param, v, strlen(v));
|
||||
} else {
|
||||
loplayer_event_param_set_line(p->event.param, "", 0);
|
||||
}
|
||||
++p->phase;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (locharacter_greedy_scientist_reset_if_player_left_(c)) return;
|
||||
|
||||
if (c->since + duration <= c->cache.time) {
|
||||
if (event) {
|
||||
p->event.param->cinescope = false;
|
||||
p->event.param->hide_hud = false;
|
||||
loplayer_event_param_set_line(p->event.param, "", 0);
|
||||
}
|
||||
locharacter_greedy_scientist_start_combo_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_greedy_scientist_start_standup_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
locharacter_greedy_scientist_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_STANDUP;
|
||||
|
||||
loeffect_recipient_reset(&c->recipient);
|
||||
|
||||
if (!loeffect_stance_set_has(
|
||||
&c->player->status.stances, LOEFFECT_STANCE_ID_PHILOSOPHER)) {
|
||||
locharacter_event_holder_take_control(&p->event);
|
||||
}
|
||||
p->phase = 0;
|
||||
}
|
||||
|
||||
static void locharacter_greedy_scientist_update_combo_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float offset_y = locharacter_greedy_scientist_size_.y*1.5f;
|
||||
|
||||
static const float beat = LOCHARACTER_GREEDY_SCIENTIST_BEAT;
|
||||
static const uint64_t step_dur = beat;
|
||||
static const uint64_t attack_dur = beat*3;
|
||||
|
||||
const locharacter_greedy_scientist_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
c->cache.bullet_hittest = true;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND2;
|
||||
if (c->since + step_dur > c->cache.time) {
|
||||
const float t = (c->cache.time - c->since)*1.f/step_dur;
|
||||
|
||||
vec2_t to = p->to;
|
||||
to.y += offset_y;
|
||||
|
||||
/* ---- position ---- */
|
||||
vec2_t dist;
|
||||
vec2_sub(&dist, &to, &p->from);
|
||||
vec2_muleq(&dist, t*t*(3-2*t));
|
||||
c->pos = p->from;
|
||||
vec2_addeq(&c->pos, &dist);
|
||||
|
||||
/* ---- motion ---- */
|
||||
instance->motion_time = 1-powf(2*t-1, 4);
|
||||
} else {
|
||||
float t = (c->cache.time - c->since - step_dur)*1.f/attack_dur;
|
||||
t *= 3;
|
||||
t -= (uint64_t) t;
|
||||
t = t*t;
|
||||
|
||||
/* ---- position ---- */
|
||||
c->pos.y -= c->ticker->delta_f*t/3 * offset_y;
|
||||
|
||||
/* ---- motion ---- */
|
||||
instance->motion_time = t;
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (locharacter_greedy_scientist_reset_if_player_left_(c)) return;
|
||||
|
||||
if (c->since + step_dur + attack_dur <= c->cache.time) {
|
||||
locharacter_greedy_scientist_start_cooldown_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_greedy_scientist_start_combo_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_GREEDY_SCIENTIST_BEAT;
|
||||
static const uint64_t parry = 100;
|
||||
|
||||
locharacter_greedy_scientist_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
if (c->last_knockback_time+parry > c->cache.time) {
|
||||
locharacter_greedy_scientist_start_stunned_state_(c);
|
||||
return;
|
||||
}
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_COMBO;
|
||||
|
||||
c->direction = c->cache.player_pos.x - c->pos.x;
|
||||
c->direction = c->direction > 0? 1: -1;
|
||||
|
||||
c->gravity = 0;
|
||||
|
||||
p->from = c->pos;
|
||||
p->to = c->cache.player_pos;
|
||||
|
||||
const size_t delay_index = chaos_xorshift(c->cache.time)&1? 2: 3;
|
||||
for (size_t i = 1; i < 4; ++i) {
|
||||
const uint64_t delay = i >= delay_index? beat/2: 0;
|
||||
loplayer_attack(c->player, &(loplayer_combat_attack_t) {
|
||||
.attacker = c->super.super.id,
|
||||
.start = c->ticker->time + (uint64_t) (beat*i) + delay,
|
||||
.duration = beat/2,
|
||||
.effect = loeffect_immediate_damage(c->recipient.status.attack),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void locharacter_greedy_scientist_update_cooldown_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_GREEDY_SCIENTIST_BEAT;
|
||||
static const uint64_t duration = beat*4;
|
||||
|
||||
const uint64_t ti = c->cache.time - c->since;
|
||||
|
||||
const locharacter_greedy_scientist_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
c->cache.bullet_hittest = true;
|
||||
c->cache.gravity = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = ti*1.f/duration;
|
||||
if (t > 1) t = 1;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND2;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = t;
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (locharacter_greedy_scientist_reset_if_player_left_(c)) return;
|
||||
|
||||
if (c->since + duration <= c->cache.time) {
|
||||
locharacter_greedy_scientist_start_combo_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_greedy_scientist_start_cooldown_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_GREEDY_SCIENTIST_BEAT;
|
||||
static const uint64_t bmelo = LOCHARACTER_GREEDY_SCIENTIST_MELODY_B_BEAT*beat;
|
||||
|
||||
locharacter_greedy_scientist_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
if (locharacter_event_holder_has_control(&p->event)) {
|
||||
static const uint64_t dur = LOCHARACTER_GREEDY_SCIENTIST_MUSIC_DURATION;
|
||||
if (p->event.start_time+dur < c->cache.time) {
|
||||
locharacter_greedy_scientist_finalize_event_(c);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (c->recipient.madness <= 0) {
|
||||
locharacter_greedy_scientist_start_dead_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_COOLDOWN;
|
||||
|
||||
if (locharacter_event_holder_has_control(&p->event)) {
|
||||
locharacter_greedy_scientist_trigger_chained_mines_(c);
|
||||
if (c->cache.time - p->event.start_time > bmelo) {
|
||||
locharacter_greedy_scientist_shoot_amnesia_bullet_(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void locharacter_greedy_scientist_update_stunned_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_GREEDY_SCIENTIST_BEAT;
|
||||
static const uint64_t duration = beat*4;
|
||||
|
||||
c->cache.gravity = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = (c->cache.time - c->since)*1.f/duration;
|
||||
t *= 6;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
if (t < 1) {
|
||||
t = 1-powf(1-t, 6);
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
instance->motion_time = t;
|
||||
} else {
|
||||
t = (t-1)/5;
|
||||
if (t > 1) t = 1;
|
||||
t = t*t*(3-2*t);
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = t;
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (c->since + duration <= c->cache.time) {
|
||||
locharacter_greedy_scientist_start_cooldown_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_greedy_scientist_start_stunned_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_STUNNED;
|
||||
|
||||
loeffect_recipient_apply_effect(
|
||||
&c->recipient, &loeffect_immediate_damage(1.f));
|
||||
}
|
||||
|
||||
static void locharacter_greedy_scientist_update_dead_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const uint64_t anime_duration = 4000;
|
||||
static const uint64_t duration = 30000;
|
||||
|
||||
c->cache.gravity = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = (c->cache.time - c->since)*1.f/anime_duration;
|
||||
if (t > 1) t = 1;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
instance->motion_time = t*t;
|
||||
instance->color.w *= 1-t;
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (c->since+duration < c->cache.time) {
|
||||
locharacter_greedy_scientist_start_wait_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_greedy_scientist_start_dead_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_DEAD;
|
||||
|
||||
loplayer_gain_faith(c->player, .8f);
|
||||
}
|
||||
|
||||
bool locharacter_greedy_scientist_update(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const vec2_t size = locharacter_greedy_scientist_size_;
|
||||
static const float height = size.y * 1.4f;
|
||||
static const float drawsz = MATH_MAX(size.x, size.y);
|
||||
|
||||
locharacter_greedy_scientist_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
loeffect_recipient_update(
|
||||
&base->recipient, &locharacter_greedy_scientist_base_status_);
|
||||
|
||||
if (!locharacter_event_holder_update(&p->event)) {
|
||||
locharacter_greedy_scientist_start_wait_state_(base);
|
||||
}
|
||||
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_SCIENTIST,
|
||||
.marker_offset = vec2(0, height - drawsz),
|
||||
.pos = vec2(0, drawsz - height),
|
||||
.size = vec2(drawsz, drawsz),
|
||||
.color = vec4(.2f, 0, 0, 1),
|
||||
};
|
||||
|
||||
switch (base->state) {
|
||||
case LOCHARACTER_STATE_WAIT:
|
||||
locharacter_greedy_scientist_update_wait_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_STANDUP:
|
||||
locharacter_greedy_scientist_update_standup_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_COMBO:
|
||||
locharacter_greedy_scientist_update_combo_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_COOLDOWN:
|
||||
locharacter_greedy_scientist_update_cooldown_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_STUNNED:
|
||||
locharacter_greedy_scientist_update_stunned_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_DEAD:
|
||||
locharacter_greedy_scientist_update_dead_state_(base);
|
||||
break;
|
||||
default:
|
||||
locharacter_greedy_scientist_start_wait_state_(base);
|
||||
}
|
||||
locharacter_greedy_scientist_update_passive_action_(base);
|
||||
|
||||
base->cache.height = height;
|
||||
|
||||
base->cache.instance.marker = !!base->cache.bullet_hittest;
|
||||
base->cache.instance.size.x *= base->direction;
|
||||
return true;
|
||||
}
|
||||
|
||||
void locharacter_greedy_scientist_build(
|
||||
locharacter_base_t* base, loentity_id_t ground) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->type = LOCHARACTER_TYPE_GREEDY_SCIENTIST;
|
||||
|
||||
base->ground = ground;
|
||||
|
||||
base->pos = vec2(.7f, 0);
|
||||
base->direction = 1;
|
||||
|
||||
base->state = LOCHARACTER_STATE_WAIT;
|
||||
base->since = base->cache.time;
|
||||
|
||||
locharacter_greedy_scientist_param_t* p = (typeof(p)) base->data;
|
||||
*p = (typeof(*p)) {0};
|
||||
|
||||
locharacter_event_holder_initialize(
|
||||
&p->event,
|
||||
&base->res->music.boss_greedy_scientist,
|
||||
base,
|
||||
LOCHARACTER_GREEDY_SCIENTIST_MUSIC_DURATION,
|
||||
0);
|
||||
}
|
||||
|
||||
void locharacter_greedy_scientist_tear_down(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
locharacter_greedy_scientist_param_t* p = (typeof(p)) base->data;
|
||||
locharacter_event_holder_deinitialize(&p->event);
|
||||
}
|
||||
|
||||
void locharacter_greedy_scientist_pack_data(
|
||||
const locharacter_base_t* base, msgpack_packer* packer) {
|
||||
assert(base != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
const locharacter_greedy_scientist_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
msgpack_pack_map(packer, LOCHARACTER_GREEDY_SCIENTIST_PARAM_TO_PACK_COUNT);
|
||||
|
||||
# define pack_(name, var) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, &p->var); \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_GREEDY_SCIENTIST_PARAM_TO_PACK_EACH_(pack_);
|
||||
|
||||
# undef pack_
|
||||
}
|
||||
|
||||
bool locharacter_greedy_scientist_unpack_data(
|
||||
locharacter_base_t* base, const msgpack_object* obj) {
|
||||
assert(base != NULL);
|
||||
|
||||
locharacter_greedy_scientist_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
# define unpack_(name, var) do { \
|
||||
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(name), &p->var)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_GREEDY_SCIENTIST_PARAM_TO_PACK_EACH_(unpack_);
|
||||
|
||||
# undef unpack_
|
||||
|
||||
# undef item_
|
||||
|
||||
locharacter_event_holder_initialize(
|
||||
&p->event,
|
||||
&base->res->music.boss_greedy_scientist,
|
||||
base,
|
||||
LOCHARACTER_GREEDY_SCIENTIST_MUSIC_DURATION,
|
||||
p->event.start_time);
|
||||
return true;
|
||||
}
|
37
core/locharacter/greedy_scientist.h
Normal file
37
core/locharacter/greedy_scientist.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/loentity/entity.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
bool
|
||||
locharacter_greedy_scientist_update(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_greedy_scientist_build(
|
||||
locharacter_base_t* base,
|
||||
loentity_id_t ground
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_greedy_scientist_tear_down(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_greedy_scientist_pack_data(
|
||||
const locharacter_base_t* base,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
locharacter_greedy_scientist_unpack_data(
|
||||
locharacter_base_t* base,
|
||||
const msgpack_object* obj
|
||||
);
|
228
core/locharacter/greedy_scientist.private.h
Normal file
228
core/locharacter/greedy_scientist.private.h
Normal file
@ -0,0 +1,228 @@
|
||||
static void locharacter_greedy_scientist_trigger_chained_mines_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_GREEDY_SCIENTIST_BEAT;
|
||||
|
||||
locommon_position_t center = c->cache.ground->super.pos;
|
||||
center.fract.y += .1f;
|
||||
locommon_position_reduce(¢er);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_square_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = center,
|
||||
.size = vec2(.07f, .07f),
|
||||
.angle = 0,
|
||||
.color = vec4(1, .9f, .9f, .8f),
|
||||
.beat = beat,
|
||||
.step = 2,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/2),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
|
||||
for (int32_t i = -6; i <= 6; ++i) {
|
||||
locommon_position_t pos = center;
|
||||
pos.fract.x += i/6.f*.5f;
|
||||
locommon_position_reduce(&pos);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_square_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = pos,
|
||||
.size = vec2(.05f, .05f),
|
||||
.angle = 0,
|
||||
.color = vec4(1, 1, 1, .4f),
|
||||
.silent = true,
|
||||
.beat = beat*1.5f,
|
||||
.step = 2,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/2),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
|
||||
static void locharacter_greedy_scientist_shoot_amnesia_bullet_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_GREEDY_SCIENTIST_BEAT;
|
||||
|
||||
locommon_position_t pos = c->super.super.pos;
|
||||
pos.fract.y += locharacter_greedy_scientist_size_.y*1.5f;
|
||||
locommon_position_reduce(&pos);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_linear_light_build(b, (&(lobullet_linear_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = pos,
|
||||
.size = vec2(.06f, .06f),
|
||||
.velocity = vec2(0, .1f),
|
||||
.color = vec4(.8f, .8f, .6f, .8f),
|
||||
.acceleration = vec2(0, -2),
|
||||
.duration = 1000,
|
||||
.effect = loeffect_amnesia(c->ticker->time, beat*8),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
|
||||
static void locharacter_greedy_scientist_update_passive_action_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_GREEDY_SCIENTIST_BEAT;
|
||||
|
||||
const locharacter_greedy_scientist_param_t* p = (typeof(p)) c->data;
|
||||
if (!locharacter_event_holder_has_control(&p->event)) return;
|
||||
|
||||
const uint64_t dt = c->cache.time - c->last_update_time;
|
||||
const uint64_t t = c->cache.time - p->event.start_time;
|
||||
|
||||
const float beats = t/beat;
|
||||
const float last_beats = t > dt? (t-dt)/beat: 0;
|
||||
|
||||
# define name_pos_(name, x, y) \
|
||||
locommon_position_t name = c->cache.ground->super.pos; \
|
||||
vec2_addeq(&name.fract, &vec2(x, y)); \
|
||||
locommon_position_reduce(&name);
|
||||
|
||||
name_pos_(top, 0, .8f);
|
||||
name_pos_(lefttop, -.3f, .8f);
|
||||
name_pos_(righttop, .3f, .8f);
|
||||
name_pos_(center, 0, .4f);
|
||||
name_pos_(left, -.3f, .2f);
|
||||
name_pos_(right, .3f, .2f);
|
||||
|
||||
# undef name_pos_
|
||||
|
||||
# define trigger_on_(x) (last_beats < (x) && beats >= (x))
|
||||
|
||||
/* ---- intro -> A melody ---- */
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
if (trigger_on_(16)) {
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_triangle_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = i? left: right,
|
||||
.size = vec2(.1f*cos(MATH_PI/6), .1f),
|
||||
.angle = -MATH_PI/2,
|
||||
.color = vec4(1, 1, 1, .4f),
|
||||
.silent = true,
|
||||
.beat = beat*2,
|
||||
.step = 1,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/4),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
if (trigger_on_(16.5f)) {
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_triangle_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = i? left: right,
|
||||
.size = vec2(.1f*cos(MATH_PI/6), .1f),
|
||||
.angle = MATH_PI/2,
|
||||
.color = vec4(1, 1, 1, .4f),
|
||||
.silent = true,
|
||||
.beat = beat*2,
|
||||
.step = 1,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/4),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
if (trigger_on_(17)) {
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_square_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = i? left: right,
|
||||
.size = vec2(.12f, .12f),
|
||||
.angle = MATH_PI/4,
|
||||
.color = vec4(1, 1, 1, .4f),
|
||||
.silent = true,
|
||||
.beat = beat*2,
|
||||
.step = 1,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/4),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
if (trigger_on_(18 + i*.5f)) {
|
||||
locommon_position_t pos = center;
|
||||
pos.fract.y -= .1f * i;
|
||||
locommon_position_reduce(&pos);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_triangle_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = pos,
|
||||
.size = vec2(.05f, .2f),
|
||||
.angle = -MATH_PI/2,
|
||||
.color = vec4(1, 1, 1, .4f),
|
||||
.silent = true,
|
||||
.beat = beat,
|
||||
.step = 1,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/2),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- B melody ---- */
|
||||
for (size_t i = 52, cnt = 0; i < 84; i+=4, ++cnt) {
|
||||
if (trigger_on_(i)) {
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_linear_triangle_build(b, (&(lobullet_linear_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = cnt%2? lefttop: righttop,
|
||||
.size = vec2(.1f, .3f),
|
||||
.velocity = vec2(0, -1/(beat*4)*1000),
|
||||
.color = vec4(1, 1, 1, .8f),
|
||||
.duration = beat*4,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- C melody ---- */
|
||||
for (size_t i = 84, cnt = 0; i < 156; i+=8, ++cnt) {
|
||||
if (trigger_on_(i)) {
|
||||
for (int32_t x = -1-cnt%2; x <= 2; x+=2) {
|
||||
locommon_position_t pos = top;
|
||||
pos.fract.x += .18f*x;
|
||||
locommon_position_reduce(&pos);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_linear_triangle_build(b, (&(lobullet_linear_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = pos,
|
||||
.size = vec2(.05f, .1f),
|
||||
.velocity = vec2(0, -1/(beat*4)*1000),
|
||||
.color = vec4(1, 1, 1, .8f),
|
||||
.duration = beat*4,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# undef trigger_on_
|
||||
}
|
68
core/locharacter/misc.c
Normal file
68
core/locharacter/misc.c
Normal file
@ -0,0 +1,68 @@
|
||||
#include "./misc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
const char* locharacter_type_stringify(locharacter_type_t type) {
|
||||
# define each_(NAME, name) do { \
|
||||
if (type == LOCHARACTER_TYPE_##NAME) return #name; \
|
||||
} while(0)
|
||||
|
||||
LOCHARACTER_TYPE_EACH_(each_);
|
||||
|
||||
assert(false);
|
||||
return NULL;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
bool locharacter_type_unstringify(
|
||||
locharacter_type_t* type, const char* v, size_t len) {
|
||||
assert(type != NULL);
|
||||
assert(v != NULL || len == 0);
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (strncmp(v, #name, len) == 0 && #name[len] == 0) { \
|
||||
*type = LOCHARACTER_TYPE_##NAME; \
|
||||
return true; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
LOCHARACTER_TYPE_EACH_(each_);
|
||||
return false;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
const char* locharacter_state_stringify(locharacter_state_t state) {
|
||||
# define each_(NAME, name) do { \
|
||||
if (state == LOCHARACTER_STATE_##NAME) return #name; \
|
||||
} while(0)
|
||||
|
||||
LOCHARACTER_STATE_EACH_(each_);
|
||||
|
||||
assert(false);
|
||||
return NULL;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
bool locharacter_state_unstringify(
|
||||
locharacter_state_t* state, const char* v, size_t len) {
|
||||
assert(state != NULL);
|
||||
assert(v != NULL || len == 0);
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (strncmp(v, #name, len) == 0 && #name[len] == 0) { \
|
||||
*state = LOCHARACTER_STATE_##NAME; \
|
||||
return true; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
LOCHARACTER_STATE_EACH_(each_);
|
||||
return false;
|
||||
|
||||
# undef each_
|
||||
}
|
76
core/locharacter/misc.h
Normal file
76
core/locharacter/misc.h
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* dont forget to update EACH macro */
|
||||
typedef enum {
|
||||
LOCHARACTER_TYPE_ENCEPHALON,
|
||||
LOCHARACTER_TYPE_CAVIA,
|
||||
LOCHARACTER_TYPE_SCIENTIST,
|
||||
LOCHARACTER_TYPE_WARDER,
|
||||
LOCHARACTER_TYPE_THEISTS_CHILD,
|
||||
LOCHARACTER_TYPE_BIG_WARDER,
|
||||
LOCHARACTER_TYPE_GREEDY_SCIENTIST,
|
||||
} locharacter_type_t;
|
||||
|
||||
#define LOCHARACTER_TYPE_EACH_(PROC) do { \
|
||||
PROC(ENCEPHALON, encephalon); \
|
||||
PROC(CAVIA, cavia); \
|
||||
PROC(SCIENTIST, scientist); \
|
||||
PROC(WARDER, warder); \
|
||||
PROC(THEISTS_CHILD, theists_child); \
|
||||
PROC(BIG_WARDER, big_warder); \
|
||||
PROC(GREEDY_SCIENTIST, greedy_scientist); \
|
||||
} while (0)
|
||||
|
||||
/* dont forget to update EACH macro */
|
||||
typedef enum {
|
||||
LOCHARACTER_STATE_WAIT,
|
||||
LOCHARACTER_STATE_STANDUP,
|
||||
LOCHARACTER_STATE_WALK,
|
||||
LOCHARACTER_STATE_SHOOT,
|
||||
LOCHARACTER_STATE_RUSH,
|
||||
LOCHARACTER_STATE_THRUST,
|
||||
LOCHARACTER_STATE_COMBO,
|
||||
LOCHARACTER_STATE_COOLDOWN,
|
||||
LOCHARACTER_STATE_STUNNED,
|
||||
LOCHARACTER_STATE_DEAD,
|
||||
} locharacter_state_t;
|
||||
|
||||
#define LOCHARACTER_STATE_EACH_(PROC) do { \
|
||||
PROC(WAIT, wait); \
|
||||
PROC(STANDUP, standup); \
|
||||
PROC(WALK, walk); \
|
||||
PROC(SHOOT, shoot); \
|
||||
PROC(RUSH, rush); \
|
||||
PROC(THRUST, thrust); \
|
||||
PROC(COMBO, combo); \
|
||||
PROC(COOLDOWN, cooldown); \
|
||||
PROC(STUNNED, stunned); \
|
||||
PROC(DEAD, dead); \
|
||||
} while (0)
|
||||
|
||||
const char*
|
||||
locharacter_type_stringify(
|
||||
locharacter_type_t type
|
||||
);
|
||||
|
||||
bool
|
||||
locharacter_type_unstringify(
|
||||
locharacter_type_t* type,
|
||||
const char* v,
|
||||
size_t len
|
||||
);
|
||||
|
||||
const char*
|
||||
locharacter_state_stringify(
|
||||
locharacter_state_t state
|
||||
);
|
||||
|
||||
bool
|
||||
locharacter_state_unstringify(
|
||||
locharacter_state_t* state,
|
||||
const char* v,
|
||||
size_t len
|
||||
);
|
122
core/locharacter/pool.c
Normal file
122
core/locharacter/pool.c
Normal file
@ -0,0 +1,122 @@
|
||||
#include "./pool.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
#include "util/memory/memory.h"
|
||||
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/counter.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
struct locharacter_pool_t {
|
||||
loresource_set_t* res;
|
||||
loshader_character_drawer_t* drawer;
|
||||
locommon_counter_t* idgen;
|
||||
const locommon_ticker_t* ticker;
|
||||
lobullet_pool_t* bullets;
|
||||
loentity_store_t* entities;
|
||||
loplayer_t* player;
|
||||
|
||||
size_t length;
|
||||
locharacter_base_t items[1];
|
||||
};
|
||||
|
||||
static size_t locharacter_pool_find_unused_item_index_(
|
||||
const locharacter_pool_t* pool) {
|
||||
assert(pool != NULL);
|
||||
|
||||
for (size_t i = 0; i < pool->length; ++i) {
|
||||
if (!pool->items[i].used) return i;
|
||||
}
|
||||
fprintf(stderr, "character pool overflow\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
locharacter_pool_t* locharacter_pool_new(
|
||||
loresource_set_t* res,
|
||||
loshader_character_drawer_t* drawer,
|
||||
locommon_counter_t* idgen,
|
||||
const locommon_ticker_t* ticker,
|
||||
lobullet_pool_t* bullets,
|
||||
loentity_store_t* entities,
|
||||
loplayer_t* player,
|
||||
size_t length) {
|
||||
assert(res != NULL);
|
||||
assert(drawer != NULL);
|
||||
assert(idgen != NULL);
|
||||
assert(ticker != NULL);
|
||||
assert(bullets != NULL);
|
||||
assert(entities != NULL);
|
||||
assert(player != NULL);
|
||||
assert(length > 0);
|
||||
|
||||
locharacter_pool_t* pool = memory_new(
|
||||
sizeof(*pool) + (length-1)*sizeof(pool->items[0]));
|
||||
*pool = (typeof(*pool)) {
|
||||
.res = res,
|
||||
.drawer = drawer,
|
||||
.idgen = idgen,
|
||||
.ticker = ticker,
|
||||
.bullets = bullets,
|
||||
.entities = entities,
|
||||
.player = player,
|
||||
.length = length,
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < pool->length; ++i) {
|
||||
locharacter_base_initialize(
|
||||
&pool->items[i],
|
||||
res,
|
||||
drawer,
|
||||
ticker,
|
||||
bullets,
|
||||
entities,
|
||||
player);
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
void locharacter_pool_delete(locharacter_pool_t* pool) {
|
||||
assert(pool != NULL);
|
||||
|
||||
for (size_t i = 0; i < pool->length; ++i) {
|
||||
locharacter_base_deinitialize(&pool->items[i]);
|
||||
}
|
||||
memory_delete(pool);
|
||||
}
|
||||
|
||||
locharacter_base_t* locharacter_pool_create(locharacter_pool_t* pool) {
|
||||
assert(pool != NULL);
|
||||
|
||||
const size_t i = locharacter_pool_find_unused_item_index_(pool);
|
||||
|
||||
locharacter_base_reinitialize(
|
||||
&pool->items[i], locommon_counter_count(pool->idgen));
|
||||
|
||||
pool->items[i].used = true;
|
||||
return &pool->items[i];
|
||||
}
|
||||
|
||||
locharacter_base_t* locharacter_pool_unpack_item(
|
||||
locharacter_pool_t* pool, const msgpack_object* obj) {
|
||||
assert(pool != NULL);
|
||||
|
||||
const size_t i = locharacter_pool_find_unused_item_index_(pool);
|
||||
|
||||
if (!locharacter_base_unpack(&pool->items[i], obj)) return NULL;
|
||||
|
||||
pool->items[i].used = true;
|
||||
return &pool->items[i];
|
||||
}
|
48
core/locharacter/pool.h
Normal file
48
core/locharacter/pool.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/counter.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
struct locharacter_pool_t;
|
||||
typedef struct locharacter_pool_t locharacter_pool_t;
|
||||
|
||||
locharacter_pool_t* /* OWNERSHIP */
|
||||
locharacter_pool_new(
|
||||
loresource_set_t* res,
|
||||
loshader_character_drawer_t* drawer,
|
||||
locommon_counter_t* idgen,
|
||||
const locommon_ticker_t* ticker,
|
||||
lobullet_pool_t* bullets,
|
||||
loentity_store_t* entities,
|
||||
loplayer_t* player,
|
||||
size_t length
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_pool_delete(
|
||||
locharacter_pool_t* pool /* OWNERSHIP */
|
||||
);
|
||||
|
||||
locharacter_base_t*
|
||||
locharacter_pool_create(
|
||||
locharacter_pool_t* pool
|
||||
);
|
||||
|
||||
locharacter_base_t*
|
||||
locharacter_pool_unpack_item(
|
||||
locharacter_pool_t* pool,
|
||||
const msgpack_object* obj
|
||||
);
|
305
core/locharacter/scientist.c
Normal file
305
core/locharacter/scientist.c
Normal file
@ -0,0 +1,305 @@
|
||||
#include "./scientist.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/lobullet/base.h"
|
||||
#include "core/lobullet/bomb.h"
|
||||
#include "core/loeffect/recipient.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loplayer/event.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/sound.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./misc.h"
|
||||
|
||||
static const vec2_t locharacter_scientist_size_ = vec2(.02f, .05f);
|
||||
|
||||
static const loeffect_recipient_status_t locharacter_scientist_base_status_ = {
|
||||
.attack = .2f,
|
||||
.defence = .1f,
|
||||
};
|
||||
|
||||
static void locharacter_scientist_trigger_bomb_(locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_square_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = c->player->entity.super.super.pos,
|
||||
.size = vec2(.15f, .15f),
|
||||
.angle = 0,
|
||||
.color = vec4(1, 1, 1, .6f),
|
||||
.beat = 500,
|
||||
.step = 2,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(c->recipient.status.attack),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
|
||||
loresource_sound_play(c->res->sound, "enemy_trigger");
|
||||
}
|
||||
|
||||
static void
|
||||
locharacter_scientist_start_wait_state_(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
static void
|
||||
locharacter_scientist_start_shoot_state_(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
static void
|
||||
locharacter_scientist_start_combo_state_(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
static void
|
||||
locharacter_scientist_start_dead_state_(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
static void locharacter_scientist_update_wait_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const uint64_t duration = 1000;
|
||||
static const uint64_t period = 1000;
|
||||
|
||||
const uint64_t elapsed = base->ticker->time - base->since;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = elapsed%period*1.f/period;
|
||||
t = t*2 - 1;
|
||||
t = MATH_ABS(t);
|
||||
t = t*t*(3-2*t);
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &base->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND2;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = t;
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (elapsed >= duration) {
|
||||
if (base->recipient.madness <= 0) {
|
||||
locharacter_scientist_start_dead_state_(base);
|
||||
return;
|
||||
}
|
||||
|
||||
if (loplayer_event_get_param(base->player->event) == NULL) {
|
||||
vec2_t disp;
|
||||
vec2_sub(&disp, &base->cache.player_pos, &base->pos);
|
||||
disp.x *= base->cache.ground->size.x;
|
||||
|
||||
const float pdist = vec2_pow_length(&disp);
|
||||
if (MATH_ABS(disp.y) < locharacter_scientist_size_.y &&
|
||||
pdist < .2f*.2f) {
|
||||
static const float r = locharacter_scientist_size_.x*3;
|
||||
if (pdist < r*r) {
|
||||
locharacter_scientist_start_combo_state_(base);
|
||||
} else if (disp.x*base->direction > 0) {
|
||||
locharacter_scientist_start_shoot_state_(base);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void locharacter_scientist_start_wait_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->since = base->ticker->time;
|
||||
base->state = LOCHARACTER_STATE_WAIT;
|
||||
}
|
||||
|
||||
static void locharacter_scientist_update_shoot_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const uint64_t duration = 500;
|
||||
|
||||
const uint64_t elapsed = base->ticker->time - base->since;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = elapsed*1.f / duration;
|
||||
if (t > 1) t = 1;
|
||||
t = t*t;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &base->cache.instance;
|
||||
if (t < .5f) {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->motion_time = t*2;
|
||||
} else {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = (t-.5f)*2;
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (elapsed >= duration) {
|
||||
locharacter_scientist_trigger_bomb_(base);
|
||||
locharacter_scientist_start_wait_state_(base);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_scientist_start_shoot_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->since = base->ticker->time;
|
||||
base->state = LOCHARACTER_STATE_SHOOT;
|
||||
}
|
||||
|
||||
static void locharacter_scientist_update_combo_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const uint64_t duration = 1000;
|
||||
|
||||
const uint64_t elapsed = base->ticker->time - base->since;
|
||||
|
||||
base->cache.gravity = false;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = elapsed*1.f/duration;
|
||||
if (t > 1) t = 1;
|
||||
t = t*t;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &base->cache.instance;
|
||||
if (t < .5f) {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->motion_time = t*2;
|
||||
} else {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = (t-.5f)*2;
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (elapsed >= duration) {
|
||||
locharacter_scientist_start_wait_state_(base);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_scientist_start_combo_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->since = base->ticker->time;
|
||||
base->state = LOCHARACTER_STATE_COMBO;
|
||||
|
||||
const float diff = base->cache.player_pos.x - base->pos.x;
|
||||
base->direction = MATH_SIGN(diff);
|
||||
|
||||
const loplayer_combat_attack_t attack = {
|
||||
.attacker = base->super.super.id,
|
||||
.start = base->ticker->time + 600,
|
||||
.duration = 400,
|
||||
.knockback = vec2(base->direction*.1f, 0),
|
||||
.effect = loeffect_immediate_damage(base->recipient.status.attack),
|
||||
};
|
||||
loplayer_attack(base->player, &attack);
|
||||
}
|
||||
|
||||
static void locharacter_scientist_update_dead_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const uint64_t anime_duration = 500;
|
||||
static const uint64_t duration = 30000;
|
||||
|
||||
const uint64_t elapsed = base->ticker->time - base->since;
|
||||
|
||||
/* ---- motion ---- */
|
||||
loshader_character_drawer_instance_t* instance = &base->cache.instance;
|
||||
if (elapsed > duration - anime_duration) { /* wake up */
|
||||
float t = 1-(duration - elapsed)*1.f/anime_duration;
|
||||
if (t < 0) t = 0;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = 1-powf(1-t, 2);
|
||||
} else { /* down */
|
||||
float t = elapsed*1.f/anime_duration;
|
||||
if (t > 1) t = 1;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
instance->motion_time = powf(t, 2.);
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (elapsed >= duration) {
|
||||
loeffect_recipient_reset(&base->recipient);
|
||||
locharacter_scientist_start_wait_state_(base);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_scientist_start_dead_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->since = base->ticker->time;
|
||||
base->state = LOCHARACTER_STATE_DEAD;
|
||||
|
||||
loplayer_gain_faith(base->player, .3f);
|
||||
}
|
||||
|
||||
bool locharacter_scientist_update(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const vec2_t size = locharacter_scientist_size_;
|
||||
static const vec4_t color = vec4(.1f, .1f, .1f, 1);
|
||||
static const float height = size.y*1.4f;
|
||||
static const float drawsz = MATH_MAX(size.x, size.y);
|
||||
|
||||
loeffect_recipient_update(&base->recipient, &locharacter_scientist_base_status_);
|
||||
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_SCIENTIST,
|
||||
.marker_offset = vec2(0, height - drawsz),
|
||||
.pos = vec2(0, drawsz - height),
|
||||
.size = vec2(drawsz, drawsz),
|
||||
.color = color,
|
||||
};
|
||||
|
||||
base->cache.gravity = true;
|
||||
|
||||
switch (base->state) {
|
||||
case LOCHARACTER_STATE_WAIT:
|
||||
locharacter_scientist_update_wait_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_SHOOT:
|
||||
locharacter_scientist_update_shoot_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_COMBO:
|
||||
locharacter_scientist_update_combo_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_DEAD:
|
||||
locharacter_scientist_update_dead_state_(base);
|
||||
break;
|
||||
default:
|
||||
locharacter_scientist_start_wait_state_(base);
|
||||
}
|
||||
|
||||
base->cache.bullet_hittest = base->state != LOCHARACTER_STATE_DEAD;
|
||||
|
||||
base->cache.height = height;
|
||||
|
||||
base->cache.instance.size.x *= base->direction;
|
||||
base->cache.instance.marker = !!base->cache.bullet_hittest;
|
||||
return true;
|
||||
}
|
||||
|
||||
void locharacter_scientist_build(
|
||||
locharacter_base_t* base, const locharacter_scientist_param_t* param) {
|
||||
assert(base != NULL);
|
||||
assert(param != NULL);
|
||||
|
||||
base->type = LOCHARACTER_TYPE_SCIENTIST;
|
||||
|
||||
base->ground = param->ground;
|
||||
|
||||
base->pos = vec2(param->pos, 0);
|
||||
base->direction = param->direction == 1? 1: -1;
|
||||
|
||||
locharacter_scientist_start_wait_state_(base);
|
||||
}
|
34
core/locharacter/scientist.h
Normal file
34
core/locharacter/scientist.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/loentity/entity.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
typedef struct {
|
||||
loentity_id_t ground;
|
||||
float pos;
|
||||
float direction;
|
||||
} locharacter_scientist_param_t;
|
||||
|
||||
bool
|
||||
locharacter_scientist_update(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_scientist_build(
|
||||
locharacter_base_t* base,
|
||||
const locharacter_scientist_param_t* param
|
||||
);
|
||||
|
||||
#define locharacter_scientist_tear_down(base)
|
||||
|
||||
#define locharacter_scientist_pack_data(base, packer) \
|
||||
msgpack_pack_nil(packer)
|
||||
|
||||
#define locharacter_scientist_unpack_data(base, obj) \
|
||||
(obj != NULL)
|
763
core/locharacter/theists_child.c
Normal file
763
core/locharacter/theists_child.c
Normal file
@ -0,0 +1,763 @@
|
||||
#include "./theists_child.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/chaos/xorshift.h"
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/constant.h"
|
||||
#include "util/math/rational.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/lobullet/base.h"
|
||||
#include "core/lobullet/bomb.h"
|
||||
#include "core/lobullet/linear.h"
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/msgpack.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loeffect/recipient.h"
|
||||
#include "core/loeffect/stance.h"
|
||||
#include "core/loentity/bullet.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loplayer/event.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/music.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loresource/text.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./misc.h"
|
||||
#include "./util.h"
|
||||
|
||||
typedef struct {
|
||||
locharacter_event_holder_t event;
|
||||
|
||||
uint64_t phase;
|
||||
vec2_t from;
|
||||
vec2_t to;
|
||||
} locharacter_theists_child_param_t;
|
||||
|
||||
#define LOCHARACTER_THEISTS_CHILD_PARAM_TO_PACK_EACH_(PROC) do { \
|
||||
PROC("event-start-time", event.start_time); \
|
||||
PROC("phase", phase); \
|
||||
PROC("from", from); \
|
||||
PROC("to", to); \
|
||||
} while (0)
|
||||
#define LOCHARACTER_THEISTS_CHILD_PARAM_TO_PACK_COUNT 4
|
||||
|
||||
static const vec2_t locharacter_theists_child_size_ = vec2(.02f, .06f);
|
||||
|
||||
static const loeffect_recipient_status_t
|
||||
locharacter_theists_child_base_status_ = {
|
||||
.attack = .1f,
|
||||
.defence = .9f,
|
||||
.speed = .1f,
|
||||
.jump = .2f,
|
||||
};
|
||||
|
||||
#define LOCHARACTER_THEISTS_CHILD_BEAT (60000/140.f) /* 140 BPM */
|
||||
#define LOCHARACTER_THEISTS_CHILD_MUSIC_DURATION \
|
||||
((uint64_t) LOCHARACTER_THEISTS_CHILD_BEAT*236)
|
||||
|
||||
#define LOCHARACTER_THEISTS_CHILD_MELODY_B_START_BEAT 128
|
||||
#define LOCHARACTER_THEISTS_CHILD_MELODY_B_END_BEAT 192
|
||||
|
||||
#include "./theists_child.private.h"
|
||||
|
||||
static void
|
||||
locharacter_theists_child_start_wait_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_theists_child_start_standup_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_theists_child_start_rush_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_theists_child_start_combo_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_theists_child_start_cooldown_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_theists_child_start_stunned_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
static void
|
||||
locharacter_theists_child_start_dead_state_(
|
||||
locharacter_base_t* c
|
||||
);
|
||||
|
||||
static void locharacter_theists_child_finalize_event_(locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
/* This function must start next state. */
|
||||
|
||||
locharacter_theists_child_param_t* p = (typeof(p)) c->data;
|
||||
assert(p != NULL);
|
||||
|
||||
locharacter_event_holder_release_control(&p->event);
|
||||
|
||||
if (c->recipient.madness > 0) {
|
||||
loentity_character_apply_effect(
|
||||
&c->player->entity.super, &loeffect_curse_trigger());
|
||||
locharacter_theists_child_start_wait_state_(c);
|
||||
} else {
|
||||
loplayer_gain_stance(c->player, LOEFFECT_STANCE_ID_REVOLUTIONER);
|
||||
locharacter_theists_child_start_dead_state_(c);
|
||||
}
|
||||
}
|
||||
|
||||
static bool locharacter_theists_child_reset_if_player_left_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
locharacter_theists_child_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
if (MATH_ABS(c->cache.player_pos.x) < 1 &&
|
||||
0 < c->cache.player_pos.y && c->cache.player_pos.y < 1) {
|
||||
return false;
|
||||
}
|
||||
locharacter_event_holder_release_control(&p->event);
|
||||
locharacter_theists_child_start_wait_state_(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void locharacter_theists_child_fire_bullets_(locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float len = .3f;
|
||||
static const float accel = .6f;
|
||||
static const uint64_t dur = 1000;
|
||||
|
||||
for (size_t i = 0; i < 30; ++i) {
|
||||
const float t = MATH_PI/15*i;
|
||||
|
||||
const vec2_t v = vec2(cos(t), sin(t));
|
||||
|
||||
locommon_position_t pos = c->super.super.pos;
|
||||
pos.fract.x += v.x*len;
|
||||
pos.fract.y += v.y*len;
|
||||
locommon_position_reduce(&pos);
|
||||
|
||||
lobullet_base_t* bullet = lobullet_pool_create(c->bullets);
|
||||
lobullet_linear_light_build(bullet, (&(lobullet_linear_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = pos,
|
||||
.size = vec2(.015f, .015f),
|
||||
.acceleration = vec2(-v.x*accel, -v.y*accel),
|
||||
.color = vec4(1, 1, 1, .8f),
|
||||
.duration = dur,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(c->recipient.status.attack),
|
||||
}));
|
||||
loentity_store_add(c->entities, &bullet->super.super);
|
||||
}
|
||||
}
|
||||
|
||||
static void locharacter_theists_child_update_wait_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float standup_range = .5f;
|
||||
static const int32_t sit_duration = 4000;
|
||||
|
||||
c->cache.gravity = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = (c->cache.time - c->since)*1.f/sit_duration;
|
||||
if (t > 1) t = 1;
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
instance->motion_time = t;
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (c->since+sit_duration <= c->cache.time) {
|
||||
if (MATH_ABS(c->cache.player_pos.x) < 1 &&
|
||||
0 < c->cache.player_pos.y && c->cache.player_pos.y < 1) {
|
||||
vec2_t diff;
|
||||
vec2_sub(&diff, &c->cache.player_pos, &c->pos);
|
||||
if (vec2_pow_length(&diff) < standup_range*standup_range) {
|
||||
locharacter_theists_child_start_standup_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static void locharacter_theists_child_start_wait_state_(locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_WAIT;
|
||||
}
|
||||
|
||||
static void locharacter_theists_child_update_standup_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_THEISTS_CHILD_BEAT;
|
||||
static const uint64_t line_duration = beat*10;
|
||||
|
||||
locharacter_theists_child_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
const bool event = locharacter_event_holder_has_control(&p->event);
|
||||
const uint64_t standup_duration = event? beat*64: 1000;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = (c->cache.time - c->since)*1.0f/standup_duration;
|
||||
if (t > 1) t = 1;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
if (t < .5f) {
|
||||
t *= 2;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
instance->motion_time = t*t*(3-2*t);
|
||||
} else {
|
||||
t = (t-.5f)*2;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = t*t*(3-2*t);
|
||||
}
|
||||
|
||||
/* ---- event ---- */
|
||||
if (event) {
|
||||
p->event.param->cinescope = true;
|
||||
p->event.param->hide_hud = true;
|
||||
|
||||
if (c->since+(p->phase+2)*line_duration < c->cache.time) {
|
||||
static const char* text[] = {
|
||||
"boss_theists_child_line0",
|
||||
"boss_theists_child_line1",
|
||||
"boss_theists_child_line2",
|
||||
};
|
||||
if (p->phase < sizeof(text)/sizeof(text[0])) {
|
||||
const char* v = loresource_text_get(
|
||||
c->res->lang, text[(size_t) p->phase]);
|
||||
loplayer_event_param_set_line(p->event.param, v, strlen(v));
|
||||
} else {
|
||||
loplayer_event_param_set_line(p->event.param, "", 0);
|
||||
}
|
||||
++p->phase;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (locharacter_theists_child_reset_if_player_left_(c)) return;
|
||||
|
||||
if (c->since+standup_duration < c->cache.time) {
|
||||
if (event) {
|
||||
p->event.param->hide_hud = false;
|
||||
p->event.param->cinescope = false;
|
||||
loplayer_event_param_set_line(p->event.param, "", 0);
|
||||
}
|
||||
locharacter_theists_child_start_rush_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_theists_child_start_standup_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
locharacter_theists_child_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_STANDUP;
|
||||
|
||||
p->phase = 0;
|
||||
loeffect_recipient_reset(&c->recipient);
|
||||
|
||||
if (!loeffect_stance_set_has(
|
||||
&c->player->status.stances, LOEFFECT_STANCE_ID_REVOLUTIONER)) {
|
||||
locharacter_event_holder_take_control(&p->event);
|
||||
}
|
||||
}
|
||||
|
||||
static void locharacter_theists_child_update_rush_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_THEISTS_CHILD_BEAT;
|
||||
|
||||
static const uint64_t premotion_duration = beat*2;
|
||||
static const uint64_t whole_duration = beat*4;
|
||||
|
||||
const locharacter_theists_child_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
const uint64_t elapsed = c->cache.time - c->since;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = elapsed*1.f/premotion_duration;
|
||||
if (t > 1) t = 1;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
if (t < .1f) {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_WALK;
|
||||
instance->motion_time = 1-powf(1-t*10, 2);
|
||||
} else if (t < .5f) {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_WALK;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->motion_time = 1-powf(1-(t-.1f)/4*10, 2);
|
||||
} else {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
instance->motion_time = powf((t-.5f)*2, 4);
|
||||
}
|
||||
|
||||
/* ---- position ---- */
|
||||
vec2_sub(&c->pos, &p->to, &p->from);
|
||||
c->direction = MATH_SIGN(c->pos.x);
|
||||
vec2_muleq(&c->pos, powf(t, 2));
|
||||
c->pos.y += (1-MATH_ABS(t*2-1))*c->recipient.status.jump*.1f;
|
||||
vec2_addeq(&c->pos, &p->from);
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (locharacter_theists_child_reset_if_player_left_(c)) return;
|
||||
|
||||
if (c->since+whole_duration < c->cache.time) {
|
||||
locharacter_theists_child_start_cooldown_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_theists_child_start_rush_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_THEISTS_CHILD_BEAT;
|
||||
|
||||
static const uint64_t parry = 300;
|
||||
|
||||
locharacter_theists_child_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
if (c->last_knockback_time + parry > c->cache.time) {
|
||||
locharacter_theists_child_start_stunned_state_(c);
|
||||
return;
|
||||
}
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_RUSH;
|
||||
|
||||
const vec2_t* player = &c->cache.player_pos;
|
||||
|
||||
const float diffx = player->x - c->pos.x;
|
||||
p->from = c->pos;
|
||||
p->to = vec2(
|
||||
player->x - MATH_SIGN(diffx)*locharacter_theists_child_size_.x*2,
|
||||
player->y - .02f);
|
||||
|
||||
const loplayer_combat_attack_t attack = {
|
||||
.attacker = c->super.super.id,
|
||||
.start = c->ticker->time + (uint64_t) beat,
|
||||
.duration = beat*3,
|
||||
.knockback = vec2(MATH_SIGN(player->x)*.2f, 0),
|
||||
.effect = loeffect_immediate_damage(c->recipient.status.attack*2),
|
||||
};
|
||||
loplayer_attack(c->player, &attack);
|
||||
}
|
||||
|
||||
static void locharacter_theists_child_update_combo_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_THEISTS_CHILD_BEAT;
|
||||
static const uint64_t premotion_duration = beat;
|
||||
static const uint64_t attack_duration = beat;
|
||||
static const uint64_t whole_duration = beat*4;
|
||||
|
||||
const locharacter_theists_child_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
const uint64_t elapsed = c->cache.time - c->since;
|
||||
|
||||
/* ---- motion ---- */
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
if (elapsed < premotion_duration) {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->motion_time = elapsed*1.f / premotion_duration;
|
||||
} else {
|
||||
const uint64_t attack_elapsed = elapsed - premotion_duration;
|
||||
|
||||
float t = 1;
|
||||
if (attack_elapsed < attack_duration*p->phase) {
|
||||
t = attack_elapsed%attack_duration*1.f / attack_duration;
|
||||
}
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
instance->motion_time = 1-powf(1-t, 4);
|
||||
}
|
||||
|
||||
/* ---- position ---- */
|
||||
if (elapsed < premotion_duration) {
|
||||
const float t = elapsed*1.f/premotion_duration;
|
||||
vec2_sub(&c->pos, &p->to, &p->from);
|
||||
vec2_muleq(&c->pos, t*t);
|
||||
vec2_addeq(&c->pos, &p->from);
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (locharacter_theists_child_reset_if_player_left_(c)) return;
|
||||
|
||||
if (elapsed >= whole_duration) {
|
||||
locharacter_theists_child_start_cooldown_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_theists_child_start_combo_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_THEISTS_CHILD_BEAT;
|
||||
static const uint64_t parry = 200;
|
||||
|
||||
locharacter_theists_child_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
if (c->last_knockback_time + parry > c->cache.time) {
|
||||
locharacter_theists_child_start_stunned_state_(c);
|
||||
return;
|
||||
}
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_COMBO;
|
||||
|
||||
const float diffx = c->cache.player_pos.x - c->pos.x;
|
||||
c->direction = MATH_SIGN(diffx);
|
||||
|
||||
p->phase = 2 + chaos_xorshift(c->since)%2;
|
||||
p->from = c->pos;
|
||||
p->to = c->cache.player_pos;
|
||||
p->to.x -= c->direction*locharacter_theists_child_size_.x*2;
|
||||
p->to.y -= .02f;
|
||||
|
||||
const loplayer_combat_attack_t attack1 = {
|
||||
.attacker = c->super.super.id,
|
||||
.start = c->ticker->time + (uint64_t) beat,
|
||||
.duration = beat/2,
|
||||
.knockback = vec2(c->direction*.1f, 0),
|
||||
.effect = loeffect_immediate_damage(c->recipient.status.attack*.8f),
|
||||
};
|
||||
loplayer_attack(c->player, &attack1);
|
||||
|
||||
const loplayer_combat_attack_t attack2 = {
|
||||
.attacker = c->super.super.id,
|
||||
.start = c->ticker->time + (uint64_t) (beat*2),
|
||||
.duration = p->phase == 2? beat*1.5: beat/2,
|
||||
.knockback = vec2(c->direction*.1f, 0),
|
||||
.effect = loeffect_immediate_damage(c->recipient.status.attack*1.1f),
|
||||
};
|
||||
loplayer_attack(c->player, &attack2);
|
||||
|
||||
if (p->phase >= 3) {
|
||||
const loplayer_combat_attack_t attack3 = {
|
||||
.attacker = c->super.super.id,
|
||||
.start = c->ticker->time + (uint64_t) (beat*3),
|
||||
.duration = beat/2,
|
||||
.knockback = vec2(c->direction*.1f, 0),
|
||||
.effect = loeffect_immediate_damage(c->recipient.status.attack*1.3f),
|
||||
};
|
||||
loplayer_attack(c->player, &attack3);
|
||||
}
|
||||
}
|
||||
|
||||
static void locharacter_theists_child_update_cooldown_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_THEISTS_CHILD_BEAT;
|
||||
|
||||
static const uint64_t duration = beat*4;
|
||||
|
||||
const locharacter_theists_child_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
c->cache.bullet_hittest = true;
|
||||
c->cache.gravity = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = (c->cache.time - c->since)*1.0f/duration;
|
||||
if (t > 1) t = 1;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = t;
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (locharacter_theists_child_reset_if_player_left_(c)) return;
|
||||
|
||||
if (c->since+duration < c->cache.time) {
|
||||
if (locharacter_event_holder_has_control(&p->event)) {
|
||||
static const uint64_t dur = LOCHARACTER_THEISTS_CHILD_MUSIC_DURATION;
|
||||
if (p->event.start_time+dur < c->cache.time) {
|
||||
locharacter_theists_child_finalize_event_(c);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (c->recipient.madness <= 0) {
|
||||
locharacter_theists_child_start_dead_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vec2_t diff;
|
||||
vec2_sub(&diff, &c->cache.player_pos, &c->pos);
|
||||
if (vec2_pow_length(&diff) < .5f*.5f) {
|
||||
locharacter_theists_child_start_combo_state_(c);
|
||||
return;
|
||||
}
|
||||
|
||||
locharacter_theists_child_start_rush_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_theists_child_start_cooldown_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
const locharacter_theists_child_param_t* p = (typeof(p)) c->data;
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_COOLDOWN;
|
||||
|
||||
bool skip_firing = false;
|
||||
if (locharacter_event_holder_has_control(&p->event)) {
|
||||
const float beat =
|
||||
(c->cache.time - p->event.start_time)/LOCHARACTER_THEISTS_CHILD_BEAT;
|
||||
skip_firing =
|
||||
LOCHARACTER_THEISTS_CHILD_MELODY_B_START_BEAT <= beat &&
|
||||
beat < LOCHARACTER_THEISTS_CHILD_MELODY_B_END_BEAT;
|
||||
}
|
||||
if (!skip_firing) locharacter_theists_child_fire_bullets_(c);
|
||||
}
|
||||
|
||||
static void locharacter_theists_child_update_stunned_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_THEISTS_CHILD_BEAT;
|
||||
static const uint64_t duration = beat*4;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = (c->cache.time - c->since)*1.f/duration;
|
||||
t *= 6;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
if (t < 1) {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
instance->motion_time = 1-powf(1-t, 6);
|
||||
} else {
|
||||
t = (t-1)/5;
|
||||
if (t > 1) t = 1;
|
||||
t = t*t*(3-2*t);
|
||||
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_SIT;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
instance->motion_time = t;
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (c->since+duration < c->cache.time) {
|
||||
locharacter_theists_child_start_cooldown_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_theists_child_start_stunned_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_STUNNED;
|
||||
|
||||
loeffect_recipient_apply_effect(
|
||||
&c->recipient, &loeffect_immediate_damage(1.f));
|
||||
}
|
||||
|
||||
static void locharacter_theists_child_update_dead_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const uint64_t anime_duration = 4000;
|
||||
static const uint64_t duration = 30000;
|
||||
|
||||
c->cache.gravity = true;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = (c->cache.time - c->since)*1.f/anime_duration;
|
||||
if (t > 1) t = 1;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &c->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
instance->motion_time = t*t;
|
||||
instance->color.w = 1-t;
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (c->since+duration < c->cache.time) {
|
||||
c->pos = vec2(0, 0);
|
||||
locharacter_theists_child_start_wait_state_(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_theists_child_start_dead_state_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
c->since = c->cache.time;
|
||||
c->state = LOCHARACTER_STATE_DEAD;
|
||||
|
||||
loplayer_gain_faith(c->player, .5f);
|
||||
}
|
||||
|
||||
bool locharacter_theists_child_update(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const vec2_t size = locharacter_theists_child_size_;
|
||||
static const vec4_t color = vec4(.05f, 0, 0, 1);
|
||||
static const float height = size.y * 1.4f;
|
||||
static const float drawsz = MATH_MAX(size.x, size.y);
|
||||
|
||||
locharacter_theists_child_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
loeffect_recipient_update(
|
||||
&base->recipient, &locharacter_theists_child_base_status_);
|
||||
|
||||
if (!locharacter_event_holder_update(&p->event)) {
|
||||
locharacter_theists_child_start_wait_state_(base);
|
||||
}
|
||||
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_CAVIA,
|
||||
.marker_offset = vec2(0, height - drawsz),
|
||||
.pos = vec2(0, drawsz - height),
|
||||
.size = vec2(drawsz, drawsz),
|
||||
.color = color,
|
||||
};
|
||||
|
||||
switch (base->state) {
|
||||
case LOCHARACTER_STATE_WAIT:
|
||||
locharacter_theists_child_update_wait_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_STANDUP:
|
||||
locharacter_theists_child_update_standup_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_RUSH:
|
||||
locharacter_theists_child_update_rush_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_COMBO:
|
||||
locharacter_theists_child_update_combo_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_COOLDOWN:
|
||||
locharacter_theists_child_update_cooldown_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_STUNNED:
|
||||
locharacter_theists_child_update_stunned_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_DEAD:
|
||||
locharacter_theists_child_update_dead_state_(base);
|
||||
break;
|
||||
default:
|
||||
locharacter_theists_child_start_wait_state_(base);
|
||||
}
|
||||
locharacter_theists_child_update_passive_action_(base);
|
||||
|
||||
base->cache.height = height;
|
||||
|
||||
base->cache.instance.marker = !!base->cache.bullet_hittest;
|
||||
base->cache.instance.size.x *= base->direction;
|
||||
return true;
|
||||
}
|
||||
|
||||
void locharacter_theists_child_build(locharacter_base_t* base, loentity_id_t ground) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->type = LOCHARACTER_TYPE_THEISTS_CHILD;
|
||||
|
||||
base->ground = ground;
|
||||
|
||||
base->pos = vec2(0, 0);
|
||||
base->direction = 1;
|
||||
|
||||
base->state = LOCHARACTER_STATE_WAIT;
|
||||
base->since = base->cache.time;
|
||||
|
||||
locharacter_theists_child_param_t* p = (typeof(p)) base->data;
|
||||
*p = (typeof(*p)) {0};
|
||||
|
||||
locharacter_event_holder_initialize(
|
||||
&p->event,
|
||||
&base->res->music.boss_theists_child,
|
||||
base,
|
||||
LOCHARACTER_THEISTS_CHILD_MUSIC_DURATION,
|
||||
0);
|
||||
}
|
||||
|
||||
void locharacter_theists_child_tear_down(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
locharacter_theists_child_param_t* p = (typeof(p)) base->data;
|
||||
locharacter_event_holder_deinitialize(&p->event);
|
||||
}
|
||||
|
||||
void locharacter_theists_child_pack_data(
|
||||
const locharacter_base_t* base, msgpack_packer* packer) {
|
||||
assert(base != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
const locharacter_theists_child_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
msgpack_pack_map(packer, LOCHARACTER_THEISTS_CHILD_PARAM_TO_PACK_COUNT);
|
||||
|
||||
# define pack_(name, var) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, &p->var); \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_THEISTS_CHILD_PARAM_TO_PACK_EACH_(pack_);
|
||||
|
||||
# undef pack_
|
||||
}
|
||||
|
||||
bool locharacter_theists_child_unpack_data(
|
||||
locharacter_base_t* base, const msgpack_object* obj) {
|
||||
assert(base != NULL);
|
||||
|
||||
locharacter_theists_child_param_t* p = (typeof(p)) base->data;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
# define unpack_(name, var) do { \
|
||||
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(name), &p->var)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOCHARACTER_THEISTS_CHILD_PARAM_TO_PACK_EACH_(unpack_);
|
||||
|
||||
# undef unpack_
|
||||
|
||||
# undef item_
|
||||
|
||||
locharacter_event_holder_initialize(
|
||||
&p->event,
|
||||
&base->res->music.boss_theists_child,
|
||||
base,
|
||||
LOCHARACTER_THEISTS_CHILD_MUSIC_DURATION,
|
||||
p->event.start_time);
|
||||
return true;
|
||||
}
|
37
core/locharacter/theists_child.h
Normal file
37
core/locharacter/theists_child.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/loentity/entity.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
bool
|
||||
locharacter_theists_child_update(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_theists_child_build(
|
||||
locharacter_base_t* base,
|
||||
loentity_id_t ground
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_theists_child_tear_down(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_theists_child_pack_data(
|
||||
const locharacter_base_t* base,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
locharacter_theists_child_unpack_data(
|
||||
locharacter_base_t* base,
|
||||
const msgpack_object* obj
|
||||
);
|
150
core/locharacter/theists_child.private.h
Normal file
150
core/locharacter/theists_child.private.h
Normal file
@ -0,0 +1,150 @@
|
||||
static void locharacter_theists_child_update_passive_action_(
|
||||
locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
static const float beat = LOCHARACTER_THEISTS_CHILD_BEAT;
|
||||
|
||||
const locharacter_theists_child_param_t* p = (typeof(p)) c->data;
|
||||
if (!locharacter_event_holder_has_control(&p->event)) return;
|
||||
|
||||
const uint64_t dt = c->cache.time - c->last_update_time;
|
||||
const uint64_t t = c->cache.time - p->event.start_time;
|
||||
|
||||
const float beats = t/beat;
|
||||
const float last_beats = t > dt? (t-dt)/beat: 0;
|
||||
|
||||
# define name_pos_(name, x, y) \
|
||||
locommon_position_t name = c->cache.ground->super.pos; \
|
||||
vec2_addeq(&name.fract, &vec2(x, y)); \
|
||||
locommon_position_reduce(&name);
|
||||
|
||||
name_pos_(top, 0, .8f);
|
||||
name_pos_(lefttop, -.25f, .8f);
|
||||
name_pos_(righttop, .25f, .8f);
|
||||
name_pos_(center, 0, .25f);
|
||||
name_pos_(left, -.3f, .2f);
|
||||
name_pos_(right, .3f, .2f);
|
||||
|
||||
# undef name_pos_
|
||||
|
||||
# define trigger_on_(x) (last_beats < (x) && beats >= (x))
|
||||
|
||||
/* ---- intro -> A melody ---- */
|
||||
if (trigger_on_(56)) {
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_triangle_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = i? left: right,
|
||||
.size = vec2(.05f, .15f),
|
||||
.angle = -MATH_PI/2,
|
||||
.color = vec4(1, 1, 1, .8f),
|
||||
.silent = true,
|
||||
.beat = beat,
|
||||
.step = 8,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/2),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
if (trigger_on_(64)) {
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_linear_triangle_build(b, (&(lobullet_linear_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = i? lefttop: righttop,
|
||||
.size = vec2(.05f, .15f),
|
||||
.velocity = vec2(0, -1.4f/(beat/1000*2)),
|
||||
.acceleration = vec2(0, 1/(beat/1000*2)),
|
||||
.color = vec4(1, 1, 1, .8f),
|
||||
.duration = beat*2,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- B melody ---- */
|
||||
for (size_t i = 128, cnt = 0; i < 192; i+=4, ++cnt) {
|
||||
if (trigger_on_(i)) {
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_square_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = cnt%2 == 0? left: right,
|
||||
.size = vec2(.13f, .13f),
|
||||
.angle = MATH_PI/4,
|
||||
.color = vec4(1, 1, 1, .8f),
|
||||
.silent = true,
|
||||
.beat = LOCHARACTER_THEISTS_CHILD_BEAT,
|
||||
.step = 4,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack/2),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
for (size_t i = 128; i < 192; i+=4) {
|
||||
if (trigger_on_(i)) {
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_linear_triangle_build(b, (&(lobullet_linear_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = top,
|
||||
.size = vec2(.05f, .2f),
|
||||
.velocity = vec2(0, -1.4f/(beat/1000*2)),
|
||||
.acceleration = vec2(0, 1/(beat/1000*2)),
|
||||
.color = vec4(1, 1, 1, .8f),
|
||||
.duration = beat*2,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
c->recipient.status.attack),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- fill-in ---- */
|
||||
if (trigger_on_(192)) {
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_square_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = center,
|
||||
.size = vec2(.2f, .2f),
|
||||
.angle = MATH_PI/4,
|
||||
.color = vec4(1, 1, .4f, .8f),
|
||||
.silent = true,
|
||||
.beat = LOCHARACTER_THEISTS_CHILD_BEAT,
|
||||
.step = 8,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_amnesia(
|
||||
c->ticker->time + (uint64_t) (8*beat), beat*4),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
|
||||
/* ---- C melody ---- */
|
||||
for (size_t i = 200, cnt = 0; i < 232; i+=2, ++cnt) {
|
||||
if (trigger_on_(i)) {
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_bomb_square_build(b, (&(lobullet_bomb_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.size = vec2(.16f, .16f),
|
||||
.pos = cnt%2 == 0? left: right,
|
||||
.angle = MATH_PI/4,
|
||||
.color = vec4(1, 1, 1, .8f),
|
||||
.silent = true,
|
||||
.beat = LOCHARACTER_THEISTS_CHILD_BEAT,
|
||||
.step = 2,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(c->recipient.status.attack),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
}
|
||||
}
|
||||
|
||||
# undef trigger_on_
|
||||
}
|
139
core/locharacter/util.c
Normal file
139
core/locharacter/util.c
Normal file
@ -0,0 +1,139 @@
|
||||
#include "./util.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/math/rational.h"
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/loresource/music.h"
|
||||
#include "core/loplayer/event.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
static void locharacter_event_holder_handle_control_lost_(
|
||||
locharacter_event_holder_t* holder) {
|
||||
assert(holder != NULL);
|
||||
|
||||
if (holder->music != NULL) {
|
||||
jukebox_amp_change_volume(&holder->music->amp, 0, &rational(1, 1));
|
||||
jukebox_decoder_stop_after(holder->music->decoder, &rational(1, 1));
|
||||
}
|
||||
|
||||
holder->param = NULL;
|
||||
holder->start_time = 0;
|
||||
}
|
||||
|
||||
void locharacter_event_holder_initialize(
|
||||
locharacter_event_holder_t* holder,
|
||||
loresource_music_player_t* music,
|
||||
locharacter_base_t* owner,
|
||||
uint64_t duration,
|
||||
uint64_t start_time) {
|
||||
assert(holder != NULL);
|
||||
assert(music != NULL);
|
||||
assert(owner != NULL);
|
||||
assert(duration > 0);
|
||||
|
||||
*holder = (typeof(*holder)) {
|
||||
.music = music,
|
||||
.owner = owner,
|
||||
.duration = duration,
|
||||
.start_time = start_time,
|
||||
};
|
||||
}
|
||||
|
||||
void locharacter_event_holder_deinitialize(
|
||||
locharacter_event_holder_t* holder) {
|
||||
assert(holder != NULL);
|
||||
|
||||
locharacter_event_holder_release_control(holder);
|
||||
}
|
||||
|
||||
bool locharacter_event_holder_take_control(
|
||||
locharacter_event_holder_t* holder) {
|
||||
assert(holder != NULL);
|
||||
assert(holder->owner != NULL);
|
||||
assert(holder->owner->cache.ground != NULL);
|
||||
|
||||
const locharacter_base_t* owner = holder->owner;
|
||||
|
||||
const bool recover = holder->start_time > 0;
|
||||
|
||||
const uint64_t t = recover? owner->cache.time - holder->start_time: 0;
|
||||
if (recover && t >= holder->duration) return false;
|
||||
|
||||
holder->param = loplayer_event_take_control(
|
||||
owner->player->event, owner->super.super.id);
|
||||
if (holder->param == NULL) return false;
|
||||
|
||||
loplayer_event_param_t* p = holder->param;
|
||||
|
||||
p->area_pos = owner->cache.ground->super.pos;
|
||||
p->area_pos.fract.y += .4f;
|
||||
locommon_position_reduce(&p->area_pos);
|
||||
|
||||
p->area_size = vec2(.45f, .45f);
|
||||
p->music = holder->music;
|
||||
|
||||
if (!recover) {
|
||||
loentity_character_apply_effect(
|
||||
&owner->player->entity.super,
|
||||
&loeffect_curse(owner->ticker->time, holder->duration));
|
||||
holder->start_time = owner->cache.time;
|
||||
}
|
||||
if (holder->music != NULL) {
|
||||
jukebox_decoder_play(holder->music->decoder, &rational(t, 1000), false);
|
||||
jukebox_amp_change_volume(&holder->music->amp, .8f, &rational(1, 1));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void locharacter_event_holder_release_control(
|
||||
locharacter_event_holder_t* holder) {
|
||||
assert(holder != NULL);
|
||||
|
||||
if (holder->param == NULL) return;
|
||||
|
||||
loplayer_event_param_release_control(holder->param);
|
||||
locharacter_event_holder_handle_control_lost_(holder);
|
||||
}
|
||||
|
||||
bool locharacter_event_holder_update(locharacter_event_holder_t* holder) {
|
||||
assert(holder != NULL);
|
||||
|
||||
if (holder->start_time > holder->owner->ticker->time) {
|
||||
holder->start_time = 0;
|
||||
}
|
||||
|
||||
loplayer_event_param_t* p = holder->param;
|
||||
if (p == NULL) {
|
||||
if (holder->start_time > 0) {
|
||||
return locharacter_event_holder_take_control(holder);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!p->controlled || p->controlled_by != holder->owner->super.super.id) {
|
||||
locharacter_event_holder_handle_control_lost_(holder);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (holder->music != NULL) {
|
||||
rational_t r;
|
||||
jukebox_decoder_get_seek_position(holder->music->decoder, &r);
|
||||
rational_normalize(&r, 1000);
|
||||
holder->owner->cache.time = r.num + holder->start_time;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool locharacter_event_holder_has_control(
|
||||
const locharacter_event_holder_t* holder) {
|
||||
assert(holder != NULL);
|
||||
|
||||
return holder->param != NULL;
|
||||
}
|
54
core/locharacter/util.h
Normal file
54
core/locharacter/util.h
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loresource/music.h"
|
||||
#include "core/loplayer/event.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
typedef struct {
|
||||
loresource_music_player_t* music;
|
||||
locharacter_base_t* owner;
|
||||
|
||||
uint64_t duration;
|
||||
|
||||
loplayer_event_param_t* param;
|
||||
uint64_t start_time;
|
||||
} locharacter_event_holder_t;
|
||||
|
||||
void
|
||||
locharacter_event_holder_initialize(
|
||||
locharacter_event_holder_t* holder,
|
||||
loresource_music_player_t* music,
|
||||
locharacter_base_t* owner,
|
||||
uint64_t duration,
|
||||
uint64_t start_time
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_event_holder_deinitialize(
|
||||
locharacter_event_holder_t* holder
|
||||
);
|
||||
|
||||
bool
|
||||
locharacter_event_holder_take_control(
|
||||
locharacter_event_holder_t* holder
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_event_holder_release_control(
|
||||
locharacter_event_holder_t* holder
|
||||
);
|
||||
|
||||
bool /* false: event was aborted by others */
|
||||
locharacter_event_holder_update(
|
||||
locharacter_event_holder_t* holder
|
||||
);
|
||||
|
||||
bool
|
||||
locharacter_event_holder_has_control(
|
||||
const locharacter_event_holder_t* holder
|
||||
);
|
299
core/locharacter/warder.c
Normal file
299
core/locharacter/warder.c
Normal file
@ -0,0 +1,299 @@
|
||||
#include "./warder.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/lobullet/base.h"
|
||||
#include "core/lobullet/linear.h"
|
||||
#include "core/loeffect/recipient.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loplayer/event.h"
|
||||
#include "core/loplayer/player.h"
|
||||
#include "core/loresource/sound.h"
|
||||
#include "core/loshader/character.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./misc.h"
|
||||
|
||||
static const vec2_t locharacter_warder_size_ = vec2(.02f, .05f);
|
||||
|
||||
static const loeffect_recipient_status_t locharacter_warder_base_status_ = {
|
||||
.attack = .1f,
|
||||
.defence = -.8f,
|
||||
};
|
||||
|
||||
static void locharacter_warder_shoot_(locharacter_base_t* c) {
|
||||
assert(c != NULL);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(c->bullets);
|
||||
lobullet_linear_light_build(b, (&(lobullet_linear_param_t) {
|
||||
.owner = c->super.super.id,
|
||||
.pos = c->super.super.pos,
|
||||
.size = vec2(.04f, .04f),
|
||||
.velocity = vec2(c->direction*.5f, 0),
|
||||
.color = vec4(.6f, .6f, .6f, .8f),
|
||||
.acceleration = vec2(0, 0),
|
||||
.duration = 2000,
|
||||
.knockback = .4f,
|
||||
.effect = loeffect_immediate_damage(c->recipient.status.attack),
|
||||
}));
|
||||
loentity_store_add(c->entities, &b->super.super);
|
||||
|
||||
loresource_sound_play(c->res->sound, "enemy_shoot");
|
||||
}
|
||||
|
||||
static void
|
||||
locharacter_warder_start_wait_state_(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
static void
|
||||
locharacter_warder_start_shoot_state_(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
static void
|
||||
locharacter_warder_start_combo_state_(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
static void
|
||||
locharacter_warder_start_dead_state_(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
static void locharacter_warder_update_wait_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const uint64_t duration = 1000;
|
||||
|
||||
const uint64_t elapsed = base->ticker->time - base->since;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = elapsed*1.f / duration;
|
||||
if (t > 1) t = 1;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &base->cache.instance;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = t*t*(3-2*t);
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (elapsed >= duration) {
|
||||
if (base->recipient.madness <= 0) {
|
||||
locharacter_warder_start_dead_state_(base);
|
||||
return;
|
||||
}
|
||||
|
||||
if (loplayer_event_get_param(base->player->event) == NULL) {
|
||||
vec2_t disp;
|
||||
vec2_sub(&disp, &base->cache.player_pos, &base->pos);
|
||||
disp.x *= base->cache.ground->size.x;
|
||||
|
||||
const float pdist = vec2_pow_length(&disp);
|
||||
if (MATH_ABS(disp.y) < locharacter_warder_size_.y && pdist < .4f*.4f) {
|
||||
static const float r = locharacter_warder_size_.x*3;
|
||||
if (pdist < r*r) {
|
||||
locharacter_warder_start_combo_state_(base);
|
||||
} else if (disp.x*base->direction > 0) {
|
||||
locharacter_warder_start_shoot_state_(base);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void locharacter_warder_start_wait_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->since = base->ticker->time;
|
||||
base->state = LOCHARACTER_STATE_WAIT;
|
||||
}
|
||||
|
||||
static void locharacter_warder_update_shoot_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const uint64_t duration = 500;
|
||||
|
||||
const uint64_t elapsed = base->ticker->time - base->since;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = elapsed*1.f / duration;
|
||||
if (t > 1) t = 1;
|
||||
t = t*t;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &base->cache.instance;
|
||||
if (t < .5f) {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->motion_time = t*2;
|
||||
} else {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
instance->motion_time = (t-.5f)*2;
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (elapsed >= duration) {
|
||||
locharacter_warder_shoot_(base);
|
||||
locharacter_warder_start_wait_state_(base);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_warder_start_shoot_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->since = base->ticker->time;
|
||||
base->state = LOCHARACTER_STATE_SHOOT;
|
||||
}
|
||||
|
||||
static void locharacter_warder_update_combo_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const uint64_t duration = 1000;
|
||||
|
||||
const uint64_t elapsed = base->ticker->time - base->since;
|
||||
|
||||
/* ---- motion ---- */
|
||||
float t = elapsed*1.f/duration;
|
||||
if (t > 1) t = 1;
|
||||
t = t*t;
|
||||
|
||||
loshader_character_drawer_instance_t* instance = &base->cache.instance;
|
||||
if (t < .5f) {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->motion_time = t*2;
|
||||
} else {
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
instance->motion_time = (t-.5f)*2;
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (elapsed >= duration) {
|
||||
locharacter_warder_start_wait_state_(base);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_warder_start_combo_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->since = base->ticker->time;
|
||||
base->state = LOCHARACTER_STATE_COMBO;
|
||||
|
||||
const float diff = base->cache.player_pos.x - base->pos.x;
|
||||
base->direction = MATH_SIGN(diff);
|
||||
|
||||
const loplayer_combat_attack_t attack = {
|
||||
.attacker = base->super.super.id,
|
||||
.start = base->ticker->time + 500,
|
||||
.duration = 500,
|
||||
.knockback = vec2(base->direction*.1f, 0),
|
||||
.effect = loeffect_immediate_damage(base->recipient.status.attack),
|
||||
};
|
||||
loplayer_attack(base->player, &attack);
|
||||
}
|
||||
|
||||
static void locharacter_warder_update_dead_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const uint64_t anime_duration = 500;
|
||||
static const uint64_t duration = 30000;
|
||||
|
||||
const uint64_t elapsed = base->ticker->time - base->since;
|
||||
|
||||
/* ---- motion ---- */
|
||||
loshader_character_drawer_instance_t* instance = &base->cache.instance;
|
||||
if (elapsed > duration - anime_duration) { /* wake up */
|
||||
float t = 1-(duration - elapsed)*1.f/anime_duration;
|
||||
if (t < 0) t = 0;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
instance->motion_time = 1-powf(1-t, 2);
|
||||
} else { /* down */
|
||||
float t = elapsed*1.f/anime_duration;
|
||||
if (t > 1) t = 1;
|
||||
instance->from_motion_id = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
|
||||
instance->to_motion_id = LOSHADER_CHARACTER_MOTION_ID_DOWN;
|
||||
instance->motion_time = t;
|
||||
}
|
||||
|
||||
/* ---- state transition ---- */
|
||||
if (elapsed >= duration) {
|
||||
loeffect_recipient_reset(&base->recipient);
|
||||
locharacter_warder_start_wait_state_(base);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void locharacter_warder_start_dead_state_(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->since = base->ticker->time;
|
||||
base->state = LOCHARACTER_STATE_DEAD;
|
||||
|
||||
loplayer_gain_faith(base->player, .1f);
|
||||
}
|
||||
|
||||
bool locharacter_warder_update(locharacter_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
static const vec2_t size = locharacter_warder_size_;
|
||||
static const vec4_t color = vec4(.1f, .1f, .1f, 1);
|
||||
static const float height = size.y*1.4f;
|
||||
static const float drawsz = MATH_MAX(size.x, size.y);
|
||||
|
||||
loeffect_recipient_update(&base->recipient, &locharacter_warder_base_status_);
|
||||
|
||||
base->cache.instance = (loshader_character_drawer_instance_t) {
|
||||
.character_id = LOSHADER_CHARACTER_ID_WARDER,
|
||||
.marker_offset = vec2(0, height - drawsz),
|
||||
.pos = vec2(0, drawsz - height),
|
||||
.size = vec2(drawsz, drawsz),
|
||||
.color = color,
|
||||
};
|
||||
|
||||
switch (base->state) {
|
||||
case LOCHARACTER_STATE_WAIT:
|
||||
locharacter_warder_update_wait_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_SHOOT:
|
||||
locharacter_warder_update_shoot_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_COMBO:
|
||||
locharacter_warder_update_combo_state_(base);
|
||||
break;
|
||||
case LOCHARACTER_STATE_DEAD:
|
||||
locharacter_warder_update_dead_state_(base);
|
||||
break;
|
||||
default:
|
||||
locharacter_warder_start_wait_state_(base);
|
||||
}
|
||||
|
||||
base->cache.bullet_hittest = base->state != LOCHARACTER_STATE_DEAD;
|
||||
base->cache.gravity = true;
|
||||
|
||||
base->cache.height = height;
|
||||
|
||||
base->cache.instance.size.x *= base->direction;
|
||||
base->cache.instance.marker = !!base->cache.bullet_hittest;
|
||||
return true;
|
||||
}
|
||||
|
||||
void locharacter_warder_build(
|
||||
locharacter_base_t* base, const locharacter_warder_param_t* param) {
|
||||
assert(base != NULL);
|
||||
assert(param != NULL);
|
||||
|
||||
base->type = LOCHARACTER_TYPE_WARDER;
|
||||
|
||||
base->ground = param->ground;
|
||||
|
||||
base->pos = vec2(param->pos, 0);
|
||||
base->direction = -MATH_SIGN(param->pos);
|
||||
if (base->direction == 0) base->direction = 1;
|
||||
|
||||
locharacter_warder_start_wait_state_(base);
|
||||
}
|
33
core/locharacter/warder.h
Normal file
33
core/locharacter/warder.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/loentity/entity.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
typedef struct {
|
||||
loentity_id_t ground;
|
||||
float pos;
|
||||
} locharacter_warder_param_t;
|
||||
|
||||
bool
|
||||
locharacter_warder_update(
|
||||
locharacter_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
locharacter_warder_build(
|
||||
locharacter_base_t* base,
|
||||
const locharacter_warder_param_t* param
|
||||
);
|
||||
|
||||
#define locharacter_warder_tear_down(base)
|
||||
|
||||
#define locharacter_warder_pack_data(base, packer) \
|
||||
msgpack_pack_nil(packer)
|
||||
|
||||
#define locharacter_warder_unpack_data(base, obj) \
|
||||
(obj != NULL)
|
15
core/locommon/CMakeLists.txt
Normal file
15
core/locommon/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
add_library(locommon
|
||||
counter.c
|
||||
easing.c
|
||||
null.c
|
||||
physics.c
|
||||
position.c
|
||||
ticker.c
|
||||
)
|
||||
target_link_libraries(locommon
|
||||
msgpackc
|
||||
|
||||
coly2d
|
||||
math
|
||||
mpkutil
|
||||
)
|
45
core/locommon/counter.c
Normal file
45
core/locommon/counter.c
Normal file
@ -0,0 +1,45 @@
|
||||
#include "./counter.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/mpkutil/get.h"
|
||||
|
||||
void locommon_counter_initialize(locommon_counter_t* counter, size_t first) {
|
||||
assert(counter != NULL);
|
||||
|
||||
*counter = (typeof(*counter)) {
|
||||
.next = first,
|
||||
};
|
||||
}
|
||||
|
||||
void locommon_counter_deinitialize(locommon_counter_t* counter) {
|
||||
assert(counter != NULL);
|
||||
|
||||
}
|
||||
|
||||
size_t locommon_counter_count(locommon_counter_t* counter) {
|
||||
assert(counter != NULL);
|
||||
|
||||
return counter->next++;
|
||||
}
|
||||
|
||||
void locommon_counter_pack(
|
||||
const locommon_counter_t* counter, msgpack_packer* packer) {
|
||||
assert(counter != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_uint64(packer, counter->next);
|
||||
}
|
||||
|
||||
bool locommon_counter_unpack(
|
||||
locommon_counter_t* counter, const msgpack_object* obj) {
|
||||
assert(counter != NULL);
|
||||
|
||||
if (!mpkutil_get_uint64(obj, &counter->next)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
38
core/locommon/counter.h
Normal file
38
core/locommon/counter.h
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
typedef struct {
|
||||
size_t next;
|
||||
} locommon_counter_t;
|
||||
|
||||
void
|
||||
locommon_counter_initialize(
|
||||
locommon_counter_t* counter,
|
||||
size_t first
|
||||
);
|
||||
|
||||
void
|
||||
locommon_counter_deinitialize(
|
||||
locommon_counter_t* counter
|
||||
);
|
||||
|
||||
size_t
|
||||
locommon_counter_count(
|
||||
locommon_counter_t* counter
|
||||
);
|
||||
|
||||
void
|
||||
locommon_counter_pack(
|
||||
const locommon_counter_t* counter,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
locommon_counter_unpack(
|
||||
locommon_counter_t* counter,
|
||||
const msgpack_object* obj /* NULLABLE */
|
||||
);
|
40
core/locommon/easing.c
Normal file
40
core/locommon/easing.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include "./easing.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
|
||||
void locommon_easing_linear_float(float* v, float ed, float delta) {
|
||||
assert(v != NULL);
|
||||
assert(MATH_FLOAT_VALID(*v));
|
||||
assert(MATH_FLOAT_VALID(delta));
|
||||
assert(delta >= 0);
|
||||
|
||||
const float flag = MATH_SIGN(ed - *v);
|
||||
*v += flag * delta;
|
||||
if ((ed-*v)*flag < 0) *v = ed;
|
||||
}
|
||||
|
||||
void locommon_easing_smooth_float(float* v, float ed, float delta) {
|
||||
assert(v != NULL);
|
||||
assert(MATH_FLOAT_VALID(*v));
|
||||
assert(MATH_FLOAT_VALID(delta));
|
||||
assert(delta >= 0);
|
||||
|
||||
*v += (ed - *v) * MATH_MIN(delta, 1);
|
||||
}
|
||||
|
||||
void locommon_easing_smooth_position(
|
||||
locommon_position_t* pos, const locommon_position_t* ed, float delta) {
|
||||
assert(locommon_position_valid(pos));
|
||||
assert(locommon_position_valid(ed));
|
||||
assert(MATH_FLOAT_VALID(delta));
|
||||
|
||||
vec2_t diff;
|
||||
locommon_position_sub(&diff, ed, pos);
|
||||
vec2_muleq(&diff, MATH_MIN(delta, 1));
|
||||
|
||||
vec2_addeq(&pos->fract, &diff);
|
||||
locommon_position_reduce(pos);
|
||||
}
|
24
core/locommon/easing.h
Normal file
24
core/locommon/easing.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "./position.h"
|
||||
|
||||
void
|
||||
locommon_easing_linear_float(
|
||||
float* v,
|
||||
float ed,
|
||||
float delta
|
||||
);
|
||||
|
||||
void
|
||||
locommon_easing_smooth_float(
|
||||
float* v,
|
||||
float ed,
|
||||
float delta
|
||||
);
|
||||
|
||||
void
|
||||
locommon_easing_smooth_position(
|
||||
locommon_position_t* pos,
|
||||
const locommon_position_t* ed,
|
||||
float delta
|
||||
);
|
27
core/locommon/input.h
Normal file
27
core/locommon/input.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
typedef enum {
|
||||
LOCOMMON_INPUT_BUTTON_LEFT = 1 << 0,
|
||||
LOCOMMON_INPUT_BUTTON_RIGHT = 1 << 1,
|
||||
LOCOMMON_INPUT_BUTTON_UP = 1 << 2,
|
||||
LOCOMMON_INPUT_BUTTON_DOWN = 1 << 3,
|
||||
LOCOMMON_INPUT_BUTTON_DASH = 1 << 4,
|
||||
LOCOMMON_INPUT_BUTTON_JUMP = 1 << 5,
|
||||
LOCOMMON_INPUT_BUTTON_ATTACK = 1 << 6,
|
||||
LOCOMMON_INPUT_BUTTON_GUARD = 1 << 7,
|
||||
LOCOMMON_INPUT_BUTTON_MENU = 1 << 8,
|
||||
} locommon_input_button_t;
|
||||
|
||||
typedef uint16_t locommon_input_buttons_t;
|
||||
|
||||
typedef struct {
|
||||
locommon_input_buttons_t buttons;
|
||||
|
||||
vec2_t resolution; /* in pixels */
|
||||
vec2_t dpi;
|
||||
vec2_t cursor; /* -1~1 */
|
||||
} locommon_input_t;
|
118
core/locommon/msgpack.h
Normal file
118
core/locommon/msgpack.h
Normal file
@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/locommon/counter.h"
|
||||
#include "core/locommon/null.h"
|
||||
|
||||
/* THE FOLLOWING INCLUDES DESTROY DEPENDENCY STRUCTURE BETWEEN MODULES. :( */
|
||||
#include "core/loeffect/effect.h"
|
||||
#include "core/loeffect/generic.h"
|
||||
#include "core/loeffect/recipient.h"
|
||||
#include "core/loeffect/stance.h"
|
||||
#include "core/loentity/entity.h"
|
||||
|
||||
#define LOCOMMON_MSGPACK_PACK_ANY_(packer, v) _Generic((v), \
|
||||
const int32_t*: locommon_msgpack_pack_int32_, \
|
||||
const uint64_t*: locommon_msgpack_pack_uint64_, \
|
||||
const float*: locommon_msgpack_pack_float_, \
|
||||
const bool*: locommon_msgpack_pack_bool_, \
|
||||
const char*: locommon_msgpack_pack_str_, \
|
||||
const vec2_t*: locommon_msgpack_pack_vec2_, \
|
||||
const vec4_t*: locommon_msgpack_pack_vec4_, \
|
||||
\
|
||||
const locommon_counter_t*: locommon_counter_pack, \
|
||||
const locommon_null_t*: locommon_null_pack, \
|
||||
const locommon_position_t*: locommon_position_pack, \
|
||||
const locommon_ticker_t*: locommon_ticker_pack, \
|
||||
\
|
||||
const loeffect_t*: loeffect_pack, \
|
||||
const loeffect_generic_immediate_param_t*: loeffect_generic_immediate_param_pack, \
|
||||
const loeffect_generic_lasting_param_t*: loeffect_generic_lasting_param_pack, \
|
||||
const loeffect_recipient_effect_param_t*: loeffect_recipient_effect_param_pack, \
|
||||
const loeffect_stance_set_t*: loeffect_stance_set_pack \
|
||||
)(v, packer)
|
||||
|
||||
#define LOCOMMON_MSGPACK_PACK_ANY(packer, v) \
|
||||
LOCOMMON_MSGPACK_PACK_ANY_(packer, (const typeof(*v)*) (v))
|
||||
|
||||
#define LOCOMMON_MSGPACK_UNPACK_ANY(obj, v) _Generic((v), \
|
||||
int32_t*: locommon_msgpack_unpack_int32_, \
|
||||
uint64_t*: locommon_msgpack_unpack_uint64_, \
|
||||
float*: locommon_msgpack_unpack_float_, \
|
||||
bool*: locommon_msgpack_unpack_bool_, \
|
||||
vec2_t*: locommon_msgpack_unpack_vec2_, \
|
||||
vec4_t*: locommon_msgpack_unpack_vec4_, \
|
||||
\
|
||||
locommon_counter_t*: locommon_counter_unpack, \
|
||||
locommon_null_t*: locommon_null_unpack, \
|
||||
locommon_position_t*: locommon_position_unpack, \
|
||||
locommon_ticker_t*: locommon_ticker_unpack, \
|
||||
\
|
||||
loeffect_t*: loeffect_unpack, \
|
||||
loeffect_generic_immediate_param_t*: loeffect_generic_immediate_param_unpack, \
|
||||
loeffect_generic_lasting_param_t*: loeffect_generic_lasting_param_unpack, \
|
||||
loeffect_recipient_effect_param_t*: loeffect_recipient_effect_param_unpack, \
|
||||
loeffect_stance_set_t*: loeffect_stance_set_unpack \
|
||||
)(v, obj)
|
||||
|
||||
static inline void locommon_msgpack_pack_int32_(
|
||||
const int32_t* v, msgpack_packer* packer) {
|
||||
msgpack_pack_int32(packer, *v);
|
||||
}
|
||||
static inline void locommon_msgpack_pack_uint64_(
|
||||
const uint64_t* v, msgpack_packer* packer) {
|
||||
msgpack_pack_uint64(packer, *v);
|
||||
}
|
||||
static inline void locommon_msgpack_pack_float_(
|
||||
const float* v, msgpack_packer* packer) {
|
||||
msgpack_pack_double(packer, *v);
|
||||
}
|
||||
static inline void locommon_msgpack_pack_bool_(
|
||||
const bool* v, msgpack_packer* packer) {
|
||||
mpkutil_pack_bool(packer, *v);
|
||||
}
|
||||
static inline void locommon_msgpack_pack_str_(
|
||||
const char* str, msgpack_packer* packer) {
|
||||
mpkutil_pack_str(packer, str);
|
||||
}
|
||||
static inline void locommon_msgpack_pack_vec2_(
|
||||
const vec2_t* v, msgpack_packer* packer) {
|
||||
mpkutil_pack_vec2(packer, v);
|
||||
}
|
||||
static inline void locommon_msgpack_pack_vec4_(
|
||||
const vec4_t* v, msgpack_packer* packer) {
|
||||
mpkutil_pack_vec4(packer, v);
|
||||
}
|
||||
|
||||
static inline bool locommon_msgpack_unpack_int32_(
|
||||
int32_t* v, const msgpack_object* obj) {
|
||||
return mpkutil_get_int32(obj, v);
|
||||
}
|
||||
static inline bool locommon_msgpack_unpack_uint64_(
|
||||
uint64_t* v, const msgpack_object* obj) {
|
||||
return mpkutil_get_uint64(obj, v);
|
||||
}
|
||||
static inline bool locommon_msgpack_unpack_float_(
|
||||
float* v, const msgpack_object* obj) {
|
||||
return mpkutil_get_float(obj, v);
|
||||
}
|
||||
static inline bool locommon_msgpack_unpack_bool_(
|
||||
bool* v, const msgpack_object* obj) {
|
||||
return mpkutil_get_bool(obj, v);
|
||||
}
|
||||
static inline bool locommon_msgpack_unpack_vec2_(
|
||||
vec2_t* v, const msgpack_object* obj) {
|
||||
return mpkutil_get_vec2(obj, v);
|
||||
}
|
||||
static inline bool locommon_msgpack_unpack_vec4_(
|
||||
vec4_t* v, const msgpack_object* obj) {
|
||||
return mpkutil_get_vec4(obj, v);
|
||||
}
|
20
core/locommon/null.c
Normal file
20
core/locommon/null.c
Normal file
@ -0,0 +1,20 @@
|
||||
#include "./null.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
void locommon_null_pack(const locommon_null_t* null, msgpack_packer* packer) {
|
||||
assert(null != NULL);
|
||||
assert(null != NULL);
|
||||
|
||||
msgpack_pack_nil(packer);
|
||||
}
|
||||
|
||||
bool locommon_null_unpack(locommon_null_t* null, const msgpack_object* obj) {
|
||||
assert(null != NULL);
|
||||
|
||||
return obj != NULL;
|
||||
}
|
20
core/locommon/null.h
Normal file
20
core/locommon/null.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
/* used in some macro templates */
|
||||
typedef struct {} locommon_null_t;
|
||||
|
||||
void
|
||||
locommon_null_pack(
|
||||
const locommon_null_t* null,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
locommon_null_unpack(
|
||||
locommon_null_t* null,
|
||||
const msgpack_object* obj
|
||||
);
|
94
core/locommon/physics.c
Normal file
94
core/locommon/physics.c
Normal file
@ -0,0 +1,94 @@
|
||||
#include "./physics.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/coly2d/hittest.h"
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "./position.h"
|
||||
|
||||
bool locommon_physics_entity_valid(const locommon_physics_entity_t* e) {
|
||||
return
|
||||
e != NULL &&
|
||||
locommon_position_valid(&e->pos) &&
|
||||
vec2_valid(&e->velocity) &&
|
||||
vec2_valid(&e->size);
|
||||
}
|
||||
|
||||
bool locommon_physics_solve_collision_with_fixed_one(
|
||||
locommon_physics_entity_t* e1,
|
||||
const locommon_physics_entity_t* e2,
|
||||
float dt) {
|
||||
assert(locommon_physics_entity_valid(e1));
|
||||
assert(locommon_physics_entity_valid(e2));
|
||||
assert(vec2_pow_length(&e2->velocity) == 0);
|
||||
assert(MATH_FLOAT_VALID(dt));
|
||||
|
||||
vec2_t size;
|
||||
vec2_add(&size, &e1->size, &e2->size);
|
||||
|
||||
vec2_t pos;
|
||||
locommon_position_sub(&pos, &e1->pos, &e2->pos);
|
||||
pos.x /= size.x;
|
||||
pos.y /= size.y;
|
||||
|
||||
vec2_t velocity = e1->velocity;
|
||||
velocity.x /= size.x;
|
||||
velocity.y /= size.y;
|
||||
|
||||
vec2_t disp;
|
||||
vec2_mul(&disp, &velocity, dt);
|
||||
|
||||
vec2_t spos;
|
||||
vec2_sub(&spos, &pos, &disp);
|
||||
|
||||
static const vec2_t origin = vec2(0, 0);
|
||||
static const vec2_t sz = vec2(1, 1);
|
||||
if (!coly2d_hittest_lineseg_and_rect(&spos, &pos, &origin, &sz)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (MATH_ABS(spos.x) < 1 && MATH_ABS(spos.y) < 1) {
|
||||
float* f = MATH_ABS(spos.x) > MATH_ABS(spos.y)? &spos.x: &spos.y;
|
||||
*f = MATH_SIGN(*f);
|
||||
}
|
||||
|
||||
vec2_t vt = vec2(MATH_INF, MATH_INF);
|
||||
if (velocity.x != 0) {
|
||||
vt.x = -(MATH_SIGN(velocity.x)+spos.x) / velocity.x;
|
||||
}
|
||||
if (velocity.y != 0) {
|
||||
vt.y = -(MATH_SIGN(velocity.y)+spos.y) / velocity.y;
|
||||
}
|
||||
|
||||
/* ---- simulation ---- */
|
||||
float t = MATH_MIN(vt.x, vt.y);
|
||||
if (t < 0) t = MATH_MAX(vt.x, vt.y);
|
||||
if (t < 0 || t >= dt) return false;
|
||||
|
||||
vec2_t d, v = velocity, p = spos;
|
||||
vec2_mul(&d, &v, t);
|
||||
vec2_addeq(&p, &d);
|
||||
|
||||
if (t == vt.x) v.x = 0;
|
||||
if (t == vt.y) v.y = 0;
|
||||
vec2_mul(&d, &v, dt-t);
|
||||
vec2_addeq(&p, &d);
|
||||
|
||||
/* ---- return result ---- */
|
||||
p.x *= size.x;
|
||||
p.y *= size.y;
|
||||
v.x *= size.x;
|
||||
v.y *= size.y;
|
||||
|
||||
e1->pos = e2->pos;
|
||||
vec2_addeq(&e1->pos.fract, &p);
|
||||
locommon_position_reduce(&e1->pos);
|
||||
|
||||
e1->velocity = v;
|
||||
|
||||
return true;
|
||||
}
|
28
core/locommon/physics.h
Normal file
28
core/locommon/physics.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "./position.h"
|
||||
|
||||
typedef struct {
|
||||
/* input */
|
||||
vec2_t size;
|
||||
|
||||
/* input/output */
|
||||
locommon_position_t pos;
|
||||
vec2_t velocity;
|
||||
} locommon_physics_entity_t;
|
||||
|
||||
bool
|
||||
locommon_physics_entity_valid(
|
||||
const locommon_physics_entity_t* e
|
||||
);
|
||||
|
||||
bool /* whether they were collided */
|
||||
locommon_physics_solve_collision_with_fixed_one(
|
||||
locommon_physics_entity_t* e1,
|
||||
const locommon_physics_entity_t* e2,
|
||||
float dt
|
||||
);
|
83
core/locommon/position.c
Normal file
83
core/locommon/position.c
Normal file
@ -0,0 +1,83 @@
|
||||
#include "./position.h"
|
||||
|
||||
#include <assert.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"
|
||||
|
||||
int32_t locommon_position_get_district_from_chunk(int32_t x) {
|
||||
static const int32_t cpd = LOCOMMON_POSITION_CHUNKS_PER_DISTRICT;
|
||||
return x >= 0? x/cpd: x/cpd-1;
|
||||
}
|
||||
|
||||
int32_t locommon_position_get_chunk_index_in_district(int32_t x) {
|
||||
static const int32_t cpd = LOCOMMON_POSITION_CHUNKS_PER_DISTRICT;
|
||||
return x >= 0? x%cpd: cpd-1-(-x-1)%cpd;
|
||||
}
|
||||
|
||||
bool locommon_position_valid(const locommon_position_t* a) {
|
||||
return a != NULL && vec2_valid(&a->fract);
|
||||
}
|
||||
|
||||
void locommon_position_sub(
|
||||
vec2_t* a, const locommon_position_t* b, const locommon_position_t* c) {
|
||||
assert(a != NULL);
|
||||
assert(locommon_position_valid(b));
|
||||
assert(locommon_position_valid(c));
|
||||
|
||||
vec2_sub(a, &b->fract, &c->fract);
|
||||
a->x += b->chunk.x - c->chunk.x;
|
||||
a->y += b->chunk.y - c->chunk.y;
|
||||
}
|
||||
|
||||
void locommon_position_reduce(locommon_position_t* a) {
|
||||
assert(locommon_position_valid(a));
|
||||
|
||||
# define reduce_(e) do { \
|
||||
a->chunk.e += (int) a->fract.e; \
|
||||
a->fract.e -= (int) a->fract.e; \
|
||||
if (a->fract.e < 0) { \
|
||||
--a->chunk.e; \
|
||||
++a->fract.e; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
reduce_(x);
|
||||
reduce_(y);
|
||||
|
||||
# undef reduce_
|
||||
}
|
||||
|
||||
void locommon_position_pack(
|
||||
const locommon_position_t* pos, msgpack_packer* packer) {
|
||||
assert(locommon_position_valid(pos));
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_array(packer, 4);
|
||||
|
||||
msgpack_pack_int32(packer, pos->chunk.x);
|
||||
msgpack_pack_int32(packer, pos->chunk.y);
|
||||
msgpack_pack_double(packer, pos->fract.x);
|
||||
msgpack_pack_double(packer, pos->fract.y);
|
||||
}
|
||||
|
||||
bool locommon_position_unpack(
|
||||
locommon_position_t* pos, const msgpack_object* obj) {
|
||||
assert(pos != NULL);
|
||||
|
||||
const msgpack_object_array* root = mpkutil_get_array(obj);
|
||||
if (root == NULL || root->size != 4 ||
|
||||
!mpkutil_get_int32(&root->ptr[0], &pos->chunk.x) ||
|
||||
!mpkutil_get_int32(&root->ptr[1], &pos->chunk.y) ||
|
||||
!mpkutil_get_float(&root->ptr[2], &pos->fract.x) ||
|
||||
!mpkutil_get_float(&root->ptr[3], &pos->fract.y)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
59
core/locommon/position.h
Normal file
59
core/locommon/position.h
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
int32_t x, y;
|
||||
} chunk;
|
||||
vec2_t fract;
|
||||
} locommon_position_t;
|
||||
|
||||
#define locommon_position(chunk_x, chunk_y, fract) \
|
||||
((locommon_position_t) { {chunk_x, chunk_y, }, fract })
|
||||
|
||||
#define LOCOMMON_POSITION_CHUNKS_PER_DISTRICT 7
|
||||
|
||||
int32_t
|
||||
locommon_position_get_district_from_chunk(
|
||||
int32_t x
|
||||
);
|
||||
|
||||
int32_t
|
||||
locommon_position_get_chunk_index_in_district(
|
||||
int32_t x
|
||||
);
|
||||
|
||||
bool
|
||||
locommon_position_valid(
|
||||
const locommon_position_t* a
|
||||
);
|
||||
|
||||
void
|
||||
locommon_position_sub(
|
||||
vec2_t* a,
|
||||
const locommon_position_t* b,
|
||||
const locommon_position_t* c
|
||||
);
|
||||
|
||||
void
|
||||
locommon_position_reduce(
|
||||
locommon_position_t* a
|
||||
);
|
||||
|
||||
void
|
||||
locommon_position_pack(
|
||||
const locommon_position_t* pos,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
locommon_position_unpack(
|
||||
locommon_position_t* pos,
|
||||
const msgpack_object* obj
|
||||
);
|
46
core/locommon/ticker.c
Normal file
46
core/locommon/ticker.c
Normal file
@ -0,0 +1,46 @@
|
||||
#include "./ticker.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/mpkutil/get.h"
|
||||
|
||||
void locommon_ticker_initialize(locommon_ticker_t* ticker, uint64_t time) {
|
||||
assert(ticker != NULL);
|
||||
|
||||
*ticker = (typeof(*ticker)) { .time = time, };
|
||||
}
|
||||
|
||||
void locommon_ticker_deinitialize(locommon_ticker_t* ticker) {
|
||||
(void) ticker;
|
||||
}
|
||||
|
||||
void locommon_ticker_tick(locommon_ticker_t* ticker, uint64_t time) {
|
||||
assert(ticker != NULL);
|
||||
assert(ticker->time <= time);
|
||||
|
||||
ticker->delta = time - ticker->time;
|
||||
ticker->delta_f = ticker->delta*1.f / LOCOMMON_TICKER_UNIT;
|
||||
ticker->time = time;
|
||||
}
|
||||
|
||||
void locommon_ticker_pack(
|
||||
const locommon_ticker_t* ticker, msgpack_packer* packer) {
|
||||
assert(ticker != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_uint64(packer, ticker->time);
|
||||
}
|
||||
|
||||
bool locommon_ticker_unpack(
|
||||
locommon_ticker_t* ticker, const msgpack_object* obj) {
|
||||
assert(ticker != NULL);
|
||||
|
||||
if (!mpkutil_get_uint64(obj, &ticker->time)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
44
core/locommon/ticker.h
Normal file
44
core/locommon/ticker.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#define LOCOMMON_TICKER_UNIT 1000
|
||||
|
||||
typedef struct {
|
||||
uint64_t time;
|
||||
|
||||
int64_t delta;
|
||||
float delta_f;
|
||||
} locommon_ticker_t;
|
||||
|
||||
void
|
||||
locommon_ticker_initialize(
|
||||
locommon_ticker_t* ticker,
|
||||
uint64_t time
|
||||
);
|
||||
|
||||
void
|
||||
locommon_ticker_deinitialize(
|
||||
locommon_ticker_t* ticker
|
||||
);
|
||||
|
||||
void
|
||||
locommon_ticker_tick(
|
||||
locommon_ticker_t* ticker,
|
||||
uint64_t time
|
||||
);
|
||||
|
||||
void
|
||||
locommon_ticker_pack(
|
||||
const locommon_ticker_t* ticker,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
locommon_ticker_unpack(
|
||||
locommon_ticker_t* ticker,
|
||||
const msgpack_object* obj
|
||||
);
|
14
core/loeffect/CMakeLists.txt
Normal file
14
core/loeffect/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
add_library(loeffect
|
||||
effect.c
|
||||
generic.c
|
||||
recipient.c
|
||||
stance.c
|
||||
)
|
||||
target_link_libraries(loeffect
|
||||
msgpackc
|
||||
|
||||
mpkutil
|
||||
|
||||
locommon
|
||||
loshader
|
||||
)
|
99
core/loeffect/effect.c
Normal file
99
core/loeffect/effect.c
Normal file
@ -0,0 +1,99 @@
|
||||
#include "./effect.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/locommon/msgpack.h"
|
||||
|
||||
#include "./generic.h"
|
||||
|
||||
#define LOEFFECT_ID_EACH_(PROC) do { \
|
||||
PROC(IMMEDIATE_DAMAGE, "imm-damage", imm); \
|
||||
PROC(CURSE, "curse", lasting); \
|
||||
PROC(CURSE_TRIGGER, "curse-trigger", null); \
|
||||
PROC(AMNESIA, "amnesia", lasting); \
|
||||
} while (0)
|
||||
|
||||
const char* loeffect_id_stringify(loeffect_id_t id) {
|
||||
# define each_(NAME, s, d) do { \
|
||||
if (LOEFFECT_ID_##NAME == id) return s; \
|
||||
} while(0)
|
||||
|
||||
LOEFFECT_ID_EACH_(each_);
|
||||
|
||||
assert(false);
|
||||
return NULL;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
bool loeffect_id_unstringify(loeffect_id_t* id, const char* str, size_t len) {
|
||||
assert(id != NULL);
|
||||
assert(str != NULL || len == 0);
|
||||
|
||||
# define each_(NAME, s, d) do { \
|
||||
if (strncmp(str, s, len) == 0 && s[len] == 0) { \
|
||||
*id = LOEFFECT_ID_##NAME; \
|
||||
return true; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOEFFECT_ID_EACH_(each_);
|
||||
return false;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
void loeffect_pack(const loeffect_t* effect, msgpack_packer* packer) {
|
||||
assert(effect != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 2);
|
||||
|
||||
mpkutil_pack_str(packer, "id");
|
||||
mpkutil_pack_str(packer, loeffect_id_stringify(effect->id));
|
||||
|
||||
mpkutil_pack_str(packer, "data");
|
||||
|
||||
# define each_(NAME, s, d) do { \
|
||||
if (effect->id == LOEFFECT_ID_##NAME) { \
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, &effect->data.d); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOEFFECT_ID_EACH_(each_);
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
bool loeffect_unpack(loeffect_t* effect, const msgpack_object* obj) {
|
||||
assert(effect != NULL);
|
||||
|
||||
if (obj == NULL) return false;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
const char* idstr;
|
||||
size_t idstr_len;
|
||||
if (!mpkutil_get_str(item_("id"), &idstr, &idstr_len) ||
|
||||
!loeffect_id_unstringify(&effect->id, idstr, idstr_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# define each_(NAME, s, d) do { \
|
||||
if (effect->id == LOEFFECT_ID_##NAME) { \
|
||||
return LOCOMMON_MSGPACK_UNPACK_ANY(item_("data"), &effect->data.d); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOEFFECT_ID_EACH_(each_);
|
||||
|
||||
# undef each_
|
||||
return false;
|
||||
}
|
87
core/loeffect/effect.h
Normal file
87
core/loeffect/effect.h
Normal file
@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/locommon/null.h"
|
||||
|
||||
#include "./generic.h"
|
||||
|
||||
typedef enum {
|
||||
LOEFFECT_ID_IMMEDIATE_DAMAGE,
|
||||
|
||||
LOEFFECT_ID_CURSE,
|
||||
/* The curse effect actually does nothing and is just for HUD.
|
||||
* To kill player immediately, use curse trigger effect.*/
|
||||
LOEFFECT_ID_CURSE_TRIGGER,
|
||||
|
||||
LOEFFECT_ID_AMNESIA,
|
||||
LOEFFECT_ID_LOST,
|
||||
} loeffect_id_t;
|
||||
|
||||
typedef struct {
|
||||
loeffect_id_t id;
|
||||
union {
|
||||
locommon_null_t null;
|
||||
loeffect_generic_immediate_param_t imm;
|
||||
loeffect_generic_lasting_param_t lasting;
|
||||
} data;
|
||||
} loeffect_t;
|
||||
|
||||
#define loeffect_immediate_damage(a) \
|
||||
((loeffect_t) { \
|
||||
.id = LOEFFECT_ID_IMMEDIATE_DAMAGE, \
|
||||
.data = { .imm = { \
|
||||
.amount = a, \
|
||||
}, }, \
|
||||
} )
|
||||
|
||||
#define loeffect_curse(b, dur) \
|
||||
((loeffect_t) { \
|
||||
.id = LOEFFECT_ID_CURSE, \
|
||||
.data = { .lasting = { \
|
||||
.begin = b, \
|
||||
.duration = dur, \
|
||||
}, }, \
|
||||
} )
|
||||
|
||||
#define loeffect_curse_trigger() \
|
||||
((loeffect_t) { \
|
||||
.id = LOEFFECT_ID_CURSE_TRIGGER, \
|
||||
} )
|
||||
|
||||
#define loeffect_amnesia(b, dur) \
|
||||
((loeffect_t) { \
|
||||
.id = LOEFFECT_ID_AMNESIA, \
|
||||
.data = { .lasting = { \
|
||||
.begin = b, \
|
||||
.duration = dur, \
|
||||
}, }, \
|
||||
} )
|
||||
|
||||
const char*
|
||||
loeffect_id_stringify(
|
||||
loeffect_id_t id
|
||||
);
|
||||
|
||||
bool
|
||||
loeffect_id_unstringify(
|
||||
loeffect_id_t* id,
|
||||
const char* str,
|
||||
size_t len
|
||||
);
|
||||
|
||||
void
|
||||
loeffect_pack(
|
||||
const loeffect_t* effect,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
loeffect_unpack(
|
||||
loeffect_t* effect,
|
||||
const msgpack_object* obj
|
||||
);
|
81
core/loeffect/generic.c
Normal file
81
core/loeffect/generic.c
Normal file
@ -0,0 +1,81 @@
|
||||
#include "./generic.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
void loeffect_generic_immediate_param_pack(
|
||||
const loeffect_generic_immediate_param_t* param,
|
||||
msgpack_packer* packer) {
|
||||
assert(param != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 1);
|
||||
|
||||
mpkutil_pack_str(packer, "amount");
|
||||
msgpack_pack_double(packer, param->amount);
|
||||
}
|
||||
|
||||
bool loeffect_generic_immediate_param_unpack(
|
||||
loeffect_generic_immediate_param_t* param,
|
||||
const msgpack_object* obj) {
|
||||
assert(param != NULL);
|
||||
|
||||
if (obj == NULL) return false;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
const msgpack_object* amount = mpkutil_get_map_item_by_str(root, "amount");
|
||||
if (!mpkutil_get_float(amount, ¶m->amount)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void loeffect_generic_lasting_param_pack(
|
||||
const loeffect_generic_lasting_param_t* param, msgpack_packer* packer) {
|
||||
assert(param != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 3);
|
||||
|
||||
mpkutil_pack_str(packer, "begin");
|
||||
msgpack_pack_uint64(packer, param->begin);
|
||||
|
||||
mpkutil_pack_str(packer, "duration");
|
||||
msgpack_pack_uint64(packer, param->duration);
|
||||
|
||||
mpkutil_pack_str(packer, "amount");
|
||||
msgpack_pack_double(packer, param->amount);
|
||||
}
|
||||
|
||||
bool loeffect_generic_lasting_param_unpack(
|
||||
loeffect_generic_lasting_param_t* param, const msgpack_object* obj) {
|
||||
assert(param != NULL);
|
||||
assert(obj != NULL);
|
||||
|
||||
if (obj == NULL) return false;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
if (!mpkutil_get_uint64(item_("begin"), ¶m->begin)) {
|
||||
return false;
|
||||
}
|
||||
if (!mpkutil_get_uint64(item_("duration"), ¶m->duration)) {
|
||||
return false;
|
||||
}
|
||||
if (!mpkutil_get_float(item_("amount"), ¶m->amount)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# undef item_
|
||||
return true;
|
||||
}
|
40
core/loeffect/generic.h
Normal file
40
core/loeffect/generic.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
typedef struct {
|
||||
float amount;
|
||||
} loeffect_generic_immediate_param_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t begin;
|
||||
uint64_t duration;
|
||||
float amount;
|
||||
} loeffect_generic_lasting_param_t;
|
||||
|
||||
void
|
||||
loeffect_generic_immediate_param_pack(
|
||||
const loeffect_generic_immediate_param_t* param,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
loeffect_generic_immediate_param_unpack(
|
||||
loeffect_generic_immediate_param_t* param,
|
||||
const msgpack_object* obj
|
||||
);
|
||||
|
||||
void
|
||||
loeffect_generic_lasting_param_pack(
|
||||
const loeffect_generic_lasting_param_t* param,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
loeffect_generic_lasting_param_unpack(
|
||||
loeffect_generic_lasting_param_t* param,
|
||||
const msgpack_object* obj
|
||||
);
|
132
core/loeffect/recipient.c
Normal file
132
core/loeffect/recipient.c
Normal file
@ -0,0 +1,132 @@
|
||||
#include "./recipient.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/locommon/msgpack.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
|
||||
#include "./generic.h"
|
||||
|
||||
#define LOEFFECT_RECIPIENT_EFFECT_PARAM_EACH_(PROC) do { \
|
||||
PROC(curse); \
|
||||
} while (0)
|
||||
#define LOEFFECT_RECIPIENT_EFFECT_PARAM_COUNT 1
|
||||
|
||||
void loeffect_recipient_initialize(
|
||||
loeffect_recipient_t* recipient, const locommon_ticker_t* ticker) {
|
||||
assert(recipient != NULL);
|
||||
assert(ticker != NULL);
|
||||
|
||||
*recipient = (typeof(*recipient)) {
|
||||
.ticker = ticker,
|
||||
};
|
||||
loeffect_recipient_reset(recipient);
|
||||
}
|
||||
|
||||
void loeffect_recipient_deinitialize(loeffect_recipient_t* recipient) {
|
||||
assert(recipient != NULL);
|
||||
|
||||
}
|
||||
|
||||
void loeffect_recipient_reset(loeffect_recipient_t* recipient) {
|
||||
assert(recipient != NULL);
|
||||
|
||||
recipient->madness = 1;
|
||||
recipient->faith = 1;
|
||||
|
||||
recipient->effects = (typeof(recipient->effects)) {0};
|
||||
}
|
||||
|
||||
void loeffect_recipient_apply_effect(
|
||||
loeffect_recipient_t* recipient, const loeffect_t* effect) {
|
||||
assert(recipient != NULL);
|
||||
assert(effect != NULL);
|
||||
|
||||
if (recipient->madness <= 0) return;
|
||||
|
||||
switch (effect->id) {
|
||||
case LOEFFECT_ID_IMMEDIATE_DAMAGE:
|
||||
recipient->madness -=
|
||||
effect->data.imm.amount * (1-recipient->status.defence);
|
||||
recipient->last_damage = LOEFFECT_ID_IMMEDIATE_DAMAGE;
|
||||
break;
|
||||
case LOEFFECT_ID_CURSE:
|
||||
recipient->effects.curse = effect->data.lasting;
|
||||
break;
|
||||
case LOEFFECT_ID_CURSE_TRIGGER:
|
||||
recipient->madness = 0;
|
||||
recipient->last_damage = LOEFFECT_ID_CURSE;
|
||||
break;
|
||||
case LOEFFECT_ID_AMNESIA:
|
||||
recipient->effects.amnesia = effect->data.lasting;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void loeffect_recipient_update(
|
||||
loeffect_recipient_t* recipient, const loeffect_recipient_status_t* base) {
|
||||
assert(recipient != NULL);
|
||||
assert(base != NULL);
|
||||
|
||||
recipient->status = *base;
|
||||
|
||||
if (recipient->madness > 0 && recipient->faith <= 0) {
|
||||
recipient->madness -= recipient->ticker->delta_f / 30;
|
||||
recipient->last_damage = LOEFFECT_ID_LOST;
|
||||
}
|
||||
|
||||
recipient->madness = MATH_CLAMP(recipient->madness, 0, 1);
|
||||
recipient->faith = MATH_CLAMP(recipient->faith, 0, 1);
|
||||
}
|
||||
|
||||
void loeffect_recipient_effect_param_pack(
|
||||
const loeffect_recipient_effect_param_t* param,
|
||||
msgpack_packer* packer) {
|
||||
assert(param != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, LOEFFECT_RECIPIENT_EFFECT_PARAM_COUNT);
|
||||
|
||||
# define each_(name) do { \
|
||||
mpkutil_pack_str(packer, #name); \
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, ¶m->name); \
|
||||
} while (0)
|
||||
|
||||
LOEFFECT_RECIPIENT_EFFECT_PARAM_EACH_(each_);
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
bool loeffect_recipient_effect_param_unpack(
|
||||
loeffect_recipient_effect_param_t* param, const msgpack_object* obj) {
|
||||
assert(param != NULL);
|
||||
|
||||
if (obj == NULL) return false;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
# define each_(name) do { \
|
||||
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(#name), ¶m->name)) { \
|
||||
param->name = (typeof(param->name)) {0}; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOEFFECT_RECIPIENT_EFFECT_PARAM_EACH_(each_);
|
||||
|
||||
# undef each_
|
||||
# undef item_
|
||||
return true;
|
||||
}
|
76
core/loeffect/recipient.h
Normal file
76
core/loeffect/recipient.h
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/locommon/ticker.h"
|
||||
|
||||
#include "./effect.h"
|
||||
#include "./generic.h"
|
||||
|
||||
typedef struct {
|
||||
float attack;
|
||||
float defence;
|
||||
|
||||
float speed; /* [chunks/sec] */
|
||||
float jump; /* [chunks/sec^2] */
|
||||
} loeffect_recipient_status_t;
|
||||
|
||||
typedef struct {
|
||||
loeffect_generic_lasting_param_t curse;
|
||||
loeffect_generic_lasting_param_t amnesia;
|
||||
} loeffect_recipient_effect_param_t;
|
||||
|
||||
typedef struct {
|
||||
const locommon_ticker_t* ticker;
|
||||
|
||||
float madness;
|
||||
float faith;
|
||||
loeffect_id_t last_damage;
|
||||
|
||||
loeffect_recipient_effect_param_t effects;
|
||||
|
||||
loeffect_recipient_status_t status;
|
||||
|
||||
} loeffect_recipient_t;
|
||||
|
||||
void
|
||||
loeffect_recipient_initialize(
|
||||
loeffect_recipient_t* recipient,
|
||||
const locommon_ticker_t* ticker
|
||||
);
|
||||
|
||||
void
|
||||
loeffect_recipient_deinitialize(
|
||||
loeffect_recipient_t* recipient
|
||||
);
|
||||
|
||||
void
|
||||
loeffect_recipient_reset(
|
||||
loeffect_recipient_t* recipient
|
||||
);
|
||||
|
||||
void
|
||||
loeffect_recipient_apply_effect(
|
||||
loeffect_recipient_t* recipient,
|
||||
const loeffect_t* effect
|
||||
);
|
||||
|
||||
void
|
||||
loeffect_recipient_update(
|
||||
loeffect_recipient_t* recipient,
|
||||
const loeffect_recipient_status_t* base
|
||||
);
|
||||
|
||||
void
|
||||
loeffect_recipient_effect_param_pack(
|
||||
const loeffect_recipient_effect_param_t* recipient,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
loeffect_recipient_effect_param_unpack(
|
||||
loeffect_recipient_effect_param_t* recipient,
|
||||
const msgpack_object* obj /* NULLABLE */
|
||||
);
|
143
core/loeffect/stance.c
Normal file
143
core/loeffect/stance.c
Normal file
@ -0,0 +1,143 @@
|
||||
#include "./stance.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/loshader/menu_stance.h"
|
||||
|
||||
#include "./recipient.h"
|
||||
|
||||
const char* loeffect_stance_stringify(loeffect_stance_id_t id) {
|
||||
# define each_(NAME, name) \
|
||||
if (id == LOEFFECT_STANCE_ID_##NAME) return #name;
|
||||
|
||||
LOEFFECT_STANCE_EACH(each_);
|
||||
|
||||
assert(false);
|
||||
return NULL;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
bool loeffect_stance_unstringify(
|
||||
loeffect_stance_id_t* id, const char* str, size_t len) {
|
||||
assert(id != NULL);
|
||||
assert(str != NULL || len == 0);
|
||||
|
||||
# define each_(NAME, name) do {\
|
||||
if (strncmp(str, #name, len) == 0 && #name[len] == 0) { \
|
||||
*id = LOEFFECT_STANCE_ID_##NAME; \
|
||||
return true; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOEFFECT_STANCE_EACH(each_);
|
||||
return false;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
loshader_menu_stance_id_t loeffect_stance_get_id_for_menu_shader(
|
||||
loeffect_stance_id_t id) {
|
||||
# define each_(NAME, name) do {\
|
||||
if (id == LOEFFECT_STANCE_ID_##NAME) { \
|
||||
return LOSHADER_MENU_STANCE_ID_##NAME; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOEFFECT_STANCE_EACH(each_);
|
||||
|
||||
assert(false);
|
||||
return LOSHADER_MENU_STANCE_ID_EMPTY;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
void loeffect_stance_set_initialize(loeffect_stance_set_t* set) {
|
||||
assert(set != NULL);
|
||||
|
||||
*set = 1 << LOEFFECT_STANCE_ID_MISSIONARY;
|
||||
}
|
||||
|
||||
void loeffect_stance_set_deinitialize(loeffect_stance_set_t* set) {
|
||||
assert(set != NULL);
|
||||
|
||||
}
|
||||
|
||||
void loeffect_stance_set_add(
|
||||
loeffect_stance_set_t* set, loeffect_stance_id_t id) {
|
||||
assert(set != NULL);
|
||||
|
||||
*set |= 1 << id;
|
||||
}
|
||||
|
||||
void loeffect_stance_set_remove(
|
||||
loeffect_stance_set_t* set, loeffect_stance_id_t id) {
|
||||
assert(set != NULL);
|
||||
|
||||
*set &= ~(1 << id);
|
||||
}
|
||||
|
||||
bool loeffect_stance_set_has(
|
||||
const loeffect_stance_set_t* set, loeffect_stance_id_t id) {
|
||||
assert(set != NULL);
|
||||
|
||||
return *set & (1 << id);
|
||||
}
|
||||
|
||||
void loeffect_stance_set_affect_base_status(
|
||||
const loeffect_stance_set_t* set,
|
||||
loeffect_recipient_status_t* status) {
|
||||
assert(set != NULL);
|
||||
assert(status != NULL);
|
||||
|
||||
}
|
||||
|
||||
void loeffect_stance_set_pack(
|
||||
const loeffect_stance_set_t* set, msgpack_packer* packer) {
|
||||
assert(set != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
loeffect_stance_id_t mask = 1;
|
||||
size_t len = 0;
|
||||
while (mask <= *set) {
|
||||
len += !!(*set & mask);
|
||||
mask <<= 1;
|
||||
}
|
||||
msgpack_pack_array(packer, len);
|
||||
|
||||
mask = 1;
|
||||
size_t i = 0;
|
||||
while (*set >= mask) {
|
||||
if (*set & mask) {
|
||||
mpkutil_pack_str(packer, loeffect_stance_stringify(i));
|
||||
}
|
||||
++i;
|
||||
mask <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool loeffect_stance_set_unpack(
|
||||
loeffect_stance_set_t* set, const msgpack_object* obj) {
|
||||
assert(set != NULL);
|
||||
|
||||
const msgpack_object_array* array = mpkutil_get_array(obj);
|
||||
if (array == NULL) return false;
|
||||
|
||||
for (size_t i = 0; i < array->size; ++i) {
|
||||
size_t len;
|
||||
const char* name;
|
||||
if (!mpkutil_get_str(&array->ptr[i], &name, &len)) continue;
|
||||
|
||||
loeffect_stance_id_t stance;
|
||||
if (!loeffect_stance_unstringify(&stance, name, len)) continue;
|
||||
*set |= 1 << stance;
|
||||
}
|
||||
return true;
|
||||
}
|
84
core/loeffect/stance.h
Normal file
84
core/loeffect/stance.h
Normal file
@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/loshader/menu_stance.h"
|
||||
|
||||
typedef enum {
|
||||
LOEFFECT_STANCE_ID_MISSIONARY,
|
||||
LOEFFECT_STANCE_ID_REVOLUTIONER,
|
||||
LOEFFECT_STANCE_ID_UNFINISHER,
|
||||
LOEFFECT_STANCE_ID_PHILOSOPHER,
|
||||
LOEFFECT_STANCE_ID_LENGTH_,
|
||||
} loeffect_stance_id_t;
|
||||
_Static_assert(LOEFFECT_STANCE_ID_LENGTH_ < 16);
|
||||
|
||||
typedef uint16_t loeffect_stance_set_t;
|
||||
|
||||
#define LOEFFECT_STANCE_EACH(PROC) do { \
|
||||
PROC(MISSIONARY, missionary); \
|
||||
PROC(REVOLUTIONER, revolutioner); \
|
||||
PROC(UNFINISHER, unfinisher); \
|
||||
PROC(PHILOSOPHER, philosopher); \
|
||||
} while (0)
|
||||
|
||||
const char*
|
||||
loeffect_stance_stringify(
|
||||
loeffect_stance_id_t id
|
||||
);
|
||||
|
||||
bool
|
||||
loeffect_stance_unstringify(
|
||||
loeffect_stance_id_t* id,
|
||||
const char* str,
|
||||
size_t len
|
||||
);
|
||||
|
||||
loshader_menu_stance_id_t
|
||||
loeffect_stance_get_id_for_menu_shader(
|
||||
loeffect_stance_id_t id
|
||||
);
|
||||
|
||||
void
|
||||
loeffect_stance_set_initialize(
|
||||
loeffect_stance_set_t* set
|
||||
);
|
||||
|
||||
void
|
||||
loeffect_stance_set_deinitialize(
|
||||
loeffect_stance_set_t* set
|
||||
);
|
||||
|
||||
void
|
||||
loeffect_stance_set_add(
|
||||
loeffect_stance_set_t* set,
|
||||
loeffect_stance_id_t id
|
||||
);
|
||||
|
||||
void
|
||||
loeffect_stance_set_remove(
|
||||
loeffect_stance_set_t* set,
|
||||
loeffect_stance_id_t id
|
||||
);
|
||||
|
||||
bool
|
||||
loeffect_stance_set_has(
|
||||
const loeffect_stance_set_t* set,
|
||||
loeffect_stance_id_t id
|
||||
);
|
||||
|
||||
void
|
||||
loeffect_stance_set_pack(
|
||||
const loeffect_stance_set_t* set,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
loeffect_stance_set_unpack(
|
||||
loeffect_stance_set_t* set,
|
||||
const msgpack_object* obj
|
||||
);
|
16
core/loentity/CMakeLists.txt
Normal file
16
core/loentity/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
add_library(loentity
|
||||
bullet.c
|
||||
character.c
|
||||
entity.c
|
||||
store.c
|
||||
)
|
||||
target_link_libraries(loentity
|
||||
msgpackc
|
||||
|
||||
coly2d
|
||||
container
|
||||
math
|
||||
memory
|
||||
|
||||
locommon
|
||||
)
|
43
core/loentity/bullet.c
Normal file
43
core/loentity/bullet.c
Normal file
@ -0,0 +1,43 @@
|
||||
#include "./bullet.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/coly2d/shape.h"
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
|
||||
#include "./decl.private.h"
|
||||
|
||||
bool loentity_bullet_affect(
|
||||
loentity_bullet_t* bullet, loentity_character_t* chara) {
|
||||
assert(bullet != NULL);
|
||||
assert(chara != NULL);
|
||||
|
||||
assert(bullet->vtable.affect != NULL);
|
||||
return bullet->vtable.affect(bullet, chara);
|
||||
}
|
||||
|
||||
bool loentity_bullet_hittest(
|
||||
const loentity_bullet_t* bullet,
|
||||
const locommon_position_t* point,
|
||||
const vec2_t* velocity,
|
||||
float dt) {
|
||||
assert(bullet != NULL);
|
||||
assert(locommon_position_valid(point));
|
||||
assert(vec2_valid(velocity));
|
||||
assert(MATH_FLOAT_VALID(dt));
|
||||
|
||||
vec2_t st;
|
||||
locommon_position_sub(&st, point, &bullet->super.pos);
|
||||
|
||||
vec2_t ed;
|
||||
vec2_sub(&ed, velocity, &bullet->velocity);
|
||||
vec2_muleq(&ed, dt);
|
||||
vec2_addeq(&ed, &st);
|
||||
|
||||
return coly2d_shape_hittest_lineseg(&bullet->shape, &st, &ed);
|
||||
}
|
45
core/loentity/bullet.h
Normal file
45
core/loentity/bullet.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "util/coly2d/shape.h"
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
|
||||
#include "./entity.h"
|
||||
|
||||
#include "./decl.private.h"
|
||||
|
||||
typedef struct {
|
||||
bool
|
||||
(*affect)(
|
||||
loentity_bullet_t* bullet,
|
||||
loentity_character_t* chara
|
||||
);
|
||||
} loentity_bullet_vtable_t;
|
||||
|
||||
struct loentity_bullet_t {
|
||||
loentity_t super;
|
||||
|
||||
loentity_bullet_vtable_t vtable;
|
||||
|
||||
loentity_id_t owner;
|
||||
vec2_t velocity;
|
||||
|
||||
coly2d_shape_t shape;
|
||||
};
|
||||
|
||||
bool
|
||||
loentity_bullet_affect(
|
||||
loentity_bullet_t* bullet,
|
||||
loentity_character_t* chara
|
||||
);
|
||||
|
||||
bool
|
||||
loentity_bullet_hittest(
|
||||
const loentity_bullet_t* bullet,
|
||||
const locommon_position_t* point,
|
||||
const vec2_t* velocity,
|
||||
float dt
|
||||
);
|
28
core/loentity/character.c
Normal file
28
core/loentity/character.c
Normal file
@ -0,0 +1,28 @@
|
||||
#include "./character.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "core/loeffect/effect.h"
|
||||
|
||||
#include "./entity.h"
|
||||
|
||||
#include "./decl.private.h"
|
||||
|
||||
void loentity_character_apply_effect(
|
||||
loentity_character_t* chara, const loeffect_t* effect) {
|
||||
assert(chara != NULL);
|
||||
assert(effect != NULL);
|
||||
|
||||
assert(chara->vtable.apply_effect != NULL);
|
||||
chara->vtable.apply_effect(chara, effect);
|
||||
}
|
||||
|
||||
void loentity_character_knockback(
|
||||
loentity_character_t* chara, const vec2_t* v) {
|
||||
assert(chara != NULL);
|
||||
assert(v != NULL);
|
||||
|
||||
assert(chara->vtable.knockback != NULL);
|
||||
chara->vtable.knockback(chara, v);
|
||||
}
|
40
core/loentity/character.h
Normal file
40
core/loentity/character.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/loeffect/effect.h"
|
||||
|
||||
#include "./entity.h"
|
||||
|
||||
#include "./decl.private.h"
|
||||
|
||||
typedef struct {
|
||||
void
|
||||
(*apply_effect)(
|
||||
loentity_character_t* chara,
|
||||
const loeffect_t* effect
|
||||
);
|
||||
void
|
||||
(*knockback)(
|
||||
loentity_character_t* chara,
|
||||
const vec2_t* v
|
||||
);
|
||||
} loentity_character_vtable_t;
|
||||
|
||||
struct loentity_character_t {
|
||||
loentity_t super;
|
||||
|
||||
loentity_character_vtable_t vtable;
|
||||
};
|
||||
|
||||
void
|
||||
loentity_character_apply_effect(
|
||||
loentity_character_t* chara,
|
||||
const loeffect_t* effect
|
||||
);
|
||||
|
||||
void
|
||||
loentity_character_knockback(
|
||||
loentity_character_t* chara,
|
||||
const vec2_t* v
|
||||
);
|
13
core/loentity/decl.private.h
Normal file
13
core/loentity/decl.private.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
struct loentity_t;
|
||||
typedef struct loentity_t loentity_t;
|
||||
|
||||
struct loentity_ground_t;
|
||||
typedef struct loentity_ground_t loentity_ground_t;
|
||||
|
||||
struct loentity_bullet_t;
|
||||
typedef struct loentity_bullet_t loentity_bullet_t;
|
||||
|
||||
struct loentity_character_t;
|
||||
typedef struct loentity_character_t loentity_character_t;
|
44
core/loentity/entity.c
Normal file
44
core/loentity/entity.c
Normal file
@ -0,0 +1,44 @@
|
||||
#include "./entity.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "./decl.private.h"
|
||||
|
||||
void loentity_delete(loentity_t* entity) {
|
||||
assert(entity != NULL);
|
||||
|
||||
assert(entity->vtable.delete != NULL);
|
||||
entity->vtable.delete(entity);
|
||||
}
|
||||
|
||||
void loentity_die(loentity_t* entity) {
|
||||
assert(entity != NULL);
|
||||
|
||||
assert(entity->vtable.die != NULL);
|
||||
entity->vtable.die(entity);
|
||||
}
|
||||
|
||||
bool loentity_update(loentity_t* entity) {
|
||||
assert(entity != NULL);
|
||||
|
||||
assert(entity->vtable.update != NULL);
|
||||
return entity->vtable.update(entity);
|
||||
}
|
||||
|
||||
void loentity_draw(loentity_t* entity, const locommon_position_t* basepos) {
|
||||
assert(entity != NULL);
|
||||
assert(locommon_position_valid(basepos));
|
||||
|
||||
assert(entity->vtable.draw != NULL);
|
||||
entity->vtable.draw(entity, basepos);
|
||||
}
|
||||
|
||||
void loentity_pack(const loentity_t* entity, msgpack_packer* packer) {
|
||||
assert(entity != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
assert(entity->vtable.pack != NULL);
|
||||
entity->vtable.pack(entity, packer);
|
||||
}
|
88
core/loentity/entity.h
Normal file
88
core/loentity/entity.h
Normal file
@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
|
||||
#include "./decl.private.h"
|
||||
|
||||
typedef struct {
|
||||
void
|
||||
(*delete)(
|
||||
loentity_t* entity
|
||||
);
|
||||
|
||||
void
|
||||
(*die)(
|
||||
loentity_t* entity
|
||||
);
|
||||
|
||||
bool
|
||||
(*update)(
|
||||
loentity_t* entity
|
||||
);
|
||||
void
|
||||
(*draw)(
|
||||
loentity_t* entity,
|
||||
const locommon_position_t* basepos
|
||||
);
|
||||
|
||||
void
|
||||
(*pack)(
|
||||
const loentity_t* entity,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
} loentity_vtable_t;
|
||||
|
||||
typedef enum {
|
||||
LOENTITY_SUBCLASS_NONE,
|
||||
LOENTITY_SUBCLASS_GROUND,
|
||||
LOENTITY_SUBCLASS_BULLET,
|
||||
LOENTITY_SUBCLASS_CHARACTER,
|
||||
} loentity_subclass_t;
|
||||
|
||||
typedef uint64_t loentity_id_t;
|
||||
|
||||
struct loentity_t {
|
||||
loentity_vtable_t vtable;
|
||||
loentity_subclass_t subclass;
|
||||
|
||||
loentity_id_t id;
|
||||
|
||||
locommon_position_t pos;
|
||||
|
||||
bool dont_save;
|
||||
};
|
||||
|
||||
void
|
||||
loentity_delete(
|
||||
loentity_t* entity /* OWNERSHIP */
|
||||
);
|
||||
|
||||
void
|
||||
loentity_die(
|
||||
loentity_t* entity
|
||||
);
|
||||
|
||||
bool
|
||||
loentity_update(
|
||||
loentity_t* entity
|
||||
);
|
||||
|
||||
void
|
||||
loentity_draw(
|
||||
loentity_t* entity,
|
||||
const locommon_position_t* basepos
|
||||
);
|
||||
|
||||
void
|
||||
loentity_pack(
|
||||
const loentity_t* entity,
|
||||
msgpack_packer* packer
|
||||
);
|
18
core/loentity/ground.h
Normal file
18
core/loentity/ground.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "./entity.h"
|
||||
|
||||
#include "./decl.private.h"
|
||||
|
||||
typedef struct {
|
||||
} loentity_ground_vtable_t;
|
||||
|
||||
struct loentity_ground_t {
|
||||
loentity_t super;
|
||||
|
||||
loentity_ground_vtable_t vtable;
|
||||
|
||||
vec2_t size;
|
||||
};
|
211
core/loentity/store.c
Normal file
211
core/loentity/store.c
Normal file
@ -0,0 +1,211 @@
|
||||
#include "./store.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/container/array.h"
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/memory/memory.h"
|
||||
|
||||
#include "core/locommon/physics.h"
|
||||
#include "core/locommon/position.h"
|
||||
|
||||
#include "./bullet.h"
|
||||
#include "./character.h"
|
||||
#include "./entity.h"
|
||||
#include "./ground.h"
|
||||
|
||||
struct loentity_store_t {
|
||||
CONTAINER_ARRAY loentity_t** items;
|
||||
};
|
||||
|
||||
static void loentity_store_iterator_assign_by_index_(
|
||||
loentity_store_t* store, loentity_store_iterator_t* itr) {
|
||||
assert(store != NULL);
|
||||
assert(itr != NULL);
|
||||
assert(itr->index < container_array_get_length(store->items));
|
||||
|
||||
itr->entity = NULL;
|
||||
itr->ground = NULL;
|
||||
itr->bullet = NULL;
|
||||
itr->character = NULL;
|
||||
|
||||
itr->entity = store->items[itr->index];
|
||||
switch (itr->entity->subclass) {
|
||||
case LOENTITY_SUBCLASS_GROUND:
|
||||
itr->ground = (loentity_ground_t*) itr->entity;
|
||||
break;
|
||||
case LOENTITY_SUBCLASS_BULLET:
|
||||
itr->bullet = (loentity_bullet_t*) itr->entity;
|
||||
break;
|
||||
case LOENTITY_SUBCLASS_CHARACTER:
|
||||
itr->character = (loentity_character_t*) itr->entity;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
loentity_store_t* loentity_store_new(size_t reserve) {
|
||||
loentity_store_t* store = memory_new(sizeof(*store));
|
||||
*store = (typeof(*store)) {0};
|
||||
|
||||
container_array_reserve(store->items, reserve);
|
||||
return store;
|
||||
}
|
||||
|
||||
void loentity_store_delete(loentity_store_t* store) {
|
||||
if (store == NULL) return;
|
||||
|
||||
container_array_delete(store->items);
|
||||
memory_delete(store);
|
||||
}
|
||||
|
||||
void loentity_store_add(
|
||||
loentity_store_t* store, loentity_t* entity) {
|
||||
assert(store != NULL);
|
||||
assert(entity != NULL);
|
||||
|
||||
const size_t len = container_array_get_length(store->items);
|
||||
size_t index = 0;
|
||||
for (; index < len; ++index) {
|
||||
if (store->items[index] == NULL) break;
|
||||
}
|
||||
if (index == len) container_array_insert(store->items, index);
|
||||
store->items[index] = entity;
|
||||
}
|
||||
|
||||
loentity_t* loentity_store_remove(
|
||||
loentity_store_t* store, const loentity_store_iterator_t* itr) {
|
||||
assert(store != NULL);
|
||||
assert(itr != NULL);
|
||||
|
||||
assert(itr->index < container_array_get_length(store->items));
|
||||
assert(itr->entity != NULL);
|
||||
assert(itr->entity == store->items[itr->index]);
|
||||
|
||||
store->items[itr->index] = NULL;
|
||||
return itr->entity;
|
||||
}
|
||||
|
||||
void loentity_store_clear(loentity_store_t* store) {
|
||||
assert(store != NULL);
|
||||
|
||||
const size_t len = container_array_get_length(store->items);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
loentity_t** e = &store->items[i];
|
||||
if (*e == NULL) continue;
|
||||
loentity_delete(*e);
|
||||
*e = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool loentity_store_iterate_next(
|
||||
loentity_store_t* store, loentity_store_iterator_t* itr) {
|
||||
assert(store != NULL);
|
||||
assert(itr != NULL);
|
||||
|
||||
++itr->index;
|
||||
if (itr->entity == NULL) {
|
||||
itr->index = 0;
|
||||
}
|
||||
itr->entity = NULL;
|
||||
itr->ground = NULL;
|
||||
itr->bullet = NULL;
|
||||
itr->character = NULL;
|
||||
|
||||
const size_t len = container_array_get_length(store->items);
|
||||
for (; itr->index < len; ++itr->index) {
|
||||
if (store->items[itr->index] != NULL) break;
|
||||
}
|
||||
if (itr->index >= len) return false;
|
||||
|
||||
loentity_store_iterator_assign_by_index_(store, itr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loentity_store_find_item_by_id(
|
||||
loentity_store_t* store, loentity_store_iterator_t* itr, loentity_id_t id) {
|
||||
assert(store != NULL);
|
||||
assert(itr != NULL);
|
||||
|
||||
const size_t len = container_array_get_length(store->items);
|
||||
for (itr->index = 0; itr->index < len; ++itr->index) {
|
||||
loentity_t* e = store->items[itr->index];
|
||||
if (e != NULL && e->id == id) {
|
||||
loentity_store_iterator_assign_by_index_(store, itr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool loentity_store_solve_collision_between_ground(
|
||||
loentity_store_t* store,
|
||||
locommon_physics_entity_t* e,
|
||||
float dt) {
|
||||
assert(store != NULL);
|
||||
assert(e != NULL);
|
||||
|
||||
bool solved = false;
|
||||
|
||||
loentity_store_iterator_t itr = {0};
|
||||
while (loentity_store_iterate_next(store, &itr)) {
|
||||
if (itr.ground == NULL) continue;
|
||||
|
||||
const locommon_physics_entity_t g = {
|
||||
.pos = itr.entity->pos,
|
||||
.velocity = vec2(0, 0),
|
||||
.size = itr.ground->size,
|
||||
};
|
||||
if (locommon_physics_solve_collision_with_fixed_one(e, &g, dt)) {
|
||||
solved = true;
|
||||
}
|
||||
}
|
||||
return solved;
|
||||
}
|
||||
|
||||
bool loentity_store_affect_bullets_shot_by_others(
|
||||
loentity_store_t* store,
|
||||
loentity_character_t* chara,
|
||||
const vec2_t* velocity,
|
||||
float dt) {
|
||||
assert(store != NULL);
|
||||
assert(chara != NULL);
|
||||
assert(vec2_valid(velocity));
|
||||
assert(MATH_FLOAT_VALID(dt));
|
||||
|
||||
loentity_store_iterator_t itr = {0};
|
||||
while (loentity_store_iterate_next(store, &itr)) {
|
||||
if (itr.bullet == NULL || itr.bullet->owner == chara->super.id) continue;
|
||||
|
||||
if (loentity_bullet_hittest(itr.bullet, &chara->super.pos, velocity, dt)) {
|
||||
if (loentity_bullet_affect(itr.bullet, chara)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool loentity_store_affect_bullets_shot_by_one(
|
||||
loentity_store_t* store,
|
||||
loentity_character_t* chara,
|
||||
loentity_id_t shooter,
|
||||
const vec2_t* velocity,
|
||||
float dt) {
|
||||
assert(store != NULL);
|
||||
assert(chara != NULL);
|
||||
assert(vec2_valid(velocity));
|
||||
assert(MATH_FLOAT_VALID(dt));
|
||||
|
||||
loentity_store_iterator_t itr = {0};
|
||||
while (loentity_store_iterate_next(store, &itr)) {
|
||||
if (itr.bullet == NULL || itr.bullet->owner != shooter) continue;
|
||||
|
||||
if (loentity_bullet_hittest(itr.bullet, &chara->super.pos, velocity, dt)) {
|
||||
if (loentity_bullet_affect(itr.bullet, chara)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
90
core/loentity/store.h
Normal file
90
core/loentity/store.h
Normal file
@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/physics.h"
|
||||
#include "core/locommon/position.h"
|
||||
|
||||
#include "./bullet.h"
|
||||
#include "./character.h"
|
||||
#include "./entity.h"
|
||||
#include "./ground.h"
|
||||
|
||||
struct loentity_store_t;
|
||||
typedef struct loentity_store_t loentity_store_t;
|
||||
|
||||
typedef struct {
|
||||
loentity_t* entity;
|
||||
loentity_ground_t* ground;
|
||||
loentity_bullet_t* bullet;
|
||||
loentity_character_t* character;
|
||||
|
||||
size_t index;
|
||||
} loentity_store_iterator_t;
|
||||
|
||||
loentity_store_t* /* OWNERSHIP */
|
||||
loentity_store_new(
|
||||
size_t reserve
|
||||
);
|
||||
|
||||
void
|
||||
loentity_store_delete(
|
||||
loentity_store_t* store /* OWNERSHIP */
|
||||
);
|
||||
|
||||
void
|
||||
loentity_store_add(
|
||||
loentity_store_t* store,
|
||||
loentity_t* entity /* OWNERSHIP */
|
||||
);
|
||||
|
||||
loentity_t* /* OWNERSHIP */
|
||||
loentity_store_remove(
|
||||
loentity_store_t* store,
|
||||
const loentity_store_iterator_t* itr
|
||||
);
|
||||
|
||||
void
|
||||
loentity_store_clear(
|
||||
loentity_store_t* store
|
||||
);
|
||||
|
||||
bool
|
||||
loentity_store_iterate_next(
|
||||
loentity_store_t* store,
|
||||
loentity_store_iterator_t* itr
|
||||
);
|
||||
|
||||
bool
|
||||
loentity_store_find_item_by_id(
|
||||
loentity_store_t* store,
|
||||
loentity_store_iterator_t* itr,
|
||||
loentity_id_t id
|
||||
);
|
||||
|
||||
bool /* whether the entitiy was collided */
|
||||
loentity_store_solve_collision_between_ground(
|
||||
loentity_store_t* store,
|
||||
locommon_physics_entity_t* e,
|
||||
float dt
|
||||
);
|
||||
|
||||
bool
|
||||
loentity_store_affect_bullets_shot_by_others(
|
||||
loentity_store_t* store,
|
||||
loentity_character_t* chara,
|
||||
const vec2_t* velocity,
|
||||
float dt
|
||||
);
|
||||
|
||||
bool
|
||||
loentity_store_affect_bullets_shot_by_one(
|
||||
loentity_store_t* store,
|
||||
loentity_character_t* chara,
|
||||
loentity_id_t shooter,
|
||||
const vec2_t* velocity,
|
||||
float dt
|
||||
);
|
17
core/loground/CMakeLists.txt
Normal file
17
core/loground/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
add_library(loground
|
||||
base.c
|
||||
island.c
|
||||
misc.c
|
||||
pool.c
|
||||
)
|
||||
target_link_libraries(loground
|
||||
msgpackc
|
||||
|
||||
math
|
||||
memory
|
||||
mpkutil
|
||||
|
||||
locommon
|
||||
loentity
|
||||
loshader
|
||||
)
|
228
core/loground/base.c
Normal file
228
core/loground/base.c
Normal file
@ -0,0 +1,228 @@
|
||||
#include "./base.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/locommon/msgpack.h"
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loentity/ground.h"
|
||||
#include "core/loshader/ground.h"
|
||||
|
||||
#include "./island.h"
|
||||
#include "./misc.h"
|
||||
|
||||
#define LOGROUND_BASE_PARAM_TO_PACK_EACH_(PROC, PROC_str, PROC_type) do { \
|
||||
PROC_str ("subclass", "ground"); \
|
||||
PROC_type ("type", type); \
|
||||
PROC ("id", super.super.id); \
|
||||
PROC ("pos", super.super.pos); \
|
||||
PROC ("size", super.size); \
|
||||
} while (0)
|
||||
#define LOGROUND_BASE_PARAM_TO_PACK_COUNT 5
|
||||
|
||||
static void loground_base_delete_(loentity_t* entity) {
|
||||
assert(entity != NULL);
|
||||
|
||||
loground_base_t* base = (typeof(base)) entity;
|
||||
if (!base->used) return;
|
||||
|
||||
base->used = false;
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (base->type == LOGROUND_TYPE_##NAME) { \
|
||||
loground_##name##_tear_down(base); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOGROUND_TYPE_EACH_(each_);
|
||||
assert(false);
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
static void loground_base_die_(loentity_t* entity) {
|
||||
assert(entity != NULL);
|
||||
|
||||
}
|
||||
|
||||
static bool loground_base_update_(loentity_t* entity) {
|
||||
assert(entity != NULL);
|
||||
|
||||
loground_base_t* base = (typeof(base)) entity;
|
||||
base->cache = (typeof(base->cache)) {0};
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (base->type == LOGROUND_TYPE_##NAME) { \
|
||||
return loground_##name##_update(base); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOGROUND_TYPE_EACH_(each_);
|
||||
return false;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
static void loground_base_draw_(
|
||||
loentity_t* entity, const locommon_position_t* basepos) {
|
||||
assert(entity != NULL);
|
||||
assert(locommon_position_valid(basepos));
|
||||
|
||||
loground_base_t* base = (typeof(base)) entity;
|
||||
|
||||
vec2_t p;
|
||||
locommon_position_sub(&p, &base->super.super.pos, basepos);
|
||||
vec2_addeq(&base->cache.instance.pos, &p);
|
||||
|
||||
loshader_ground_drawer_add_instance(base->drawer, &base->cache.instance);
|
||||
}
|
||||
|
||||
static void loground_base_pack_(
|
||||
const loentity_t* entity, msgpack_packer* packer) {
|
||||
assert(entity != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
const loground_base_t* base = (typeof(base)) entity;
|
||||
|
||||
msgpack_pack_map(packer, LOGROUND_BASE_PARAM_TO_PACK_COUNT+1);
|
||||
|
||||
# define pack_(name, var) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, &base->var); \
|
||||
} while (0)
|
||||
# define pack_str_(name, str) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
mpkutil_pack_str(packer, str); \
|
||||
} while (0)
|
||||
# define pack_type_(name, var) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
mpkutil_pack_str(packer, loground_type_stringify(base->var)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
LOGROUND_BASE_PARAM_TO_PACK_EACH_(pack_, pack_str_, pack_type_);
|
||||
|
||||
# undef pack_type_
|
||||
# undef pack_str_
|
||||
# undef pack_
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (base->type == LOGROUND_TYPE_##NAME) { \
|
||||
loground_##name##_pack_data(base, packer); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
mpkutil_pack_str(packer, "data");
|
||||
LOGROUND_TYPE_EACH_(each_);
|
||||
assert(false);
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
void loground_base_initialize(
|
||||
loground_base_t* base, loshader_ground_drawer_t* drawer) {
|
||||
assert(base != NULL);
|
||||
assert(drawer != NULL);
|
||||
|
||||
*base = (typeof(*base)) {
|
||||
.super = {
|
||||
.super = {
|
||||
.vtable = {
|
||||
.delete = loground_base_delete_,
|
||||
.die = loground_base_die_,
|
||||
.update = loground_base_update_,
|
||||
.draw = loground_base_draw_,
|
||||
.pack = loground_base_pack_,
|
||||
},
|
||||
.subclass = LOENTITY_SUBCLASS_GROUND,
|
||||
},
|
||||
},
|
||||
.drawer = drawer,
|
||||
};
|
||||
}
|
||||
|
||||
void loground_base_reinitialize(loground_base_t* base, loentity_id_t id) {
|
||||
assert(base != NULL);
|
||||
|
||||
# define reset_(name, var) do { \
|
||||
base->var = (typeof(base->var)) {0}; \
|
||||
} while (0)
|
||||
# define reset_str_(name, str)
|
||||
|
||||
LOGROUND_BASE_PARAM_TO_PACK_EACH_(reset_, reset_str_, reset_);
|
||||
|
||||
# undef reset_str_
|
||||
# undef reset_
|
||||
|
||||
base->super.super.id = id;
|
||||
}
|
||||
|
||||
void loground_base_deinitialize(loground_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
loground_base_delete_(&base->super.super);
|
||||
}
|
||||
|
||||
bool loground_base_unpack(loground_base_t* base, const msgpack_object *obj) {
|
||||
assert(base != NULL);
|
||||
assert(obj != NULL);
|
||||
|
||||
loground_base_reinitialize(base, 0);
|
||||
/* id will be overwritten below */
|
||||
|
||||
const char* v;
|
||||
size_t vlen;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
# define unpack_(name, var) do { \
|
||||
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(name), &base->var)) { \
|
||||
return NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
# define unpack_type_(name, var) do { \
|
||||
if (!mpkutil_get_str(item_(name), &v, &vlen) || \
|
||||
!loground_type_unstringify(&base->var, v, vlen)) { \
|
||||
return NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
# define unpack_str_(name, str) do { \
|
||||
if (!mpkutil_get_str(item_(name), &v, &vlen) || \
|
||||
!(strncmp(v, str, vlen) == 0 && str[vlen] == 0)) { \
|
||||
return NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOGROUND_BASE_PARAM_TO_PACK_EACH_(unpack_, unpack_str_, unpack_type_);
|
||||
|
||||
# undef unpack_str_
|
||||
# undef unpack_type_
|
||||
# undef unpack_
|
||||
|
||||
const msgpack_object* data = item_("data");
|
||||
# define each_(NAME, name) do { \
|
||||
if (base->type == LOGROUND_TYPE_##NAME) { \
|
||||
return loground_##name##_unpack_data(base, data); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOGROUND_TYPE_EACH_(each_);
|
||||
return false;
|
||||
|
||||
# undef each_
|
||||
|
||||
# undef item_
|
||||
}
|
54
core/loground/base.h
Normal file
54
core/loground/base.h
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/loentity/entity.h"
|
||||
#include "core/loentity/ground.h"
|
||||
#include "core/loshader/ground.h"
|
||||
|
||||
#include "./misc.h"
|
||||
|
||||
typedef struct {
|
||||
loentity_ground_t super;
|
||||
bool used;
|
||||
|
||||
/* injected deps */
|
||||
loshader_ground_drawer_t* drawer;
|
||||
|
||||
/* params not to be packed */
|
||||
struct {
|
||||
loshader_ground_drawer_instance_t instance;
|
||||
} cache;
|
||||
|
||||
/* params to be packed (includes id, pos, and size) */
|
||||
loground_type_t type;
|
||||
|
||||
# define LOGROUND_BASE_DATA_MAX_SIZE 256
|
||||
uint8_t data[LOGROUND_BASE_DATA_MAX_SIZE];
|
||||
} loground_base_t;
|
||||
|
||||
void
|
||||
loground_base_initialize(
|
||||
loground_base_t* base,
|
||||
loshader_ground_drawer_t* drawer
|
||||
);
|
||||
|
||||
void
|
||||
loground_base_reinitialize(
|
||||
loground_base_t* base,
|
||||
loentity_id_t id
|
||||
);
|
||||
|
||||
void
|
||||
loground_base_deinitialize(
|
||||
loground_base_t* base
|
||||
);
|
||||
|
||||
bool
|
||||
loground_base_unpack(
|
||||
loground_base_t* base,
|
||||
const msgpack_object *obj
|
||||
);
|
35
core/loground/island.c
Normal file
35
core/loground/island.c
Normal file
@ -0,0 +1,35 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
|
||||
#include "./base.h"
|
||||
#include "./misc.h"
|
||||
|
||||
bool loground_island_update(loground_base_t* base) {
|
||||
assert(base != NULL);
|
||||
|
||||
base->cache.instance = (loshader_ground_drawer_instance_t) {
|
||||
.ground_id = LOSHADER_GROUND_ID_ISLAND,
|
||||
.size = base->super.size,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
void loground_island_build(
|
||||
loground_base_t* base,
|
||||
const locommon_position_t* pos,
|
||||
const vec2_t* size) {
|
||||
assert(base != NULL);
|
||||
assert(locommon_position_valid(pos));
|
||||
assert(vec2_valid(size));
|
||||
assert(size->x >= 0 && size->y >= 0);
|
||||
|
||||
base->type = LOGROUND_TYPE_ISLAND;
|
||||
|
||||
base->super.super.pos = *pos;
|
||||
base->super.size = *size;
|
||||
}
|
31
core/loground/island.h
Normal file
31
core/loground/island.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
bool
|
||||
loground_island_update(
|
||||
loground_base_t* base
|
||||
);
|
||||
|
||||
void
|
||||
loground_island_build(
|
||||
loground_base_t* base,
|
||||
const locommon_position_t* pos,
|
||||
const vec2_t* size
|
||||
);
|
||||
|
||||
#define loground_island_tear_down(base)
|
||||
|
||||
#define loground_island_pack_data(base, packer) \
|
||||
msgpack_pack_nil(packer)
|
||||
|
||||
#define loground_island_unpack_data(base, obj) \
|
||||
(obj != NULL)
|
37
core/loground/misc.c
Normal file
37
core/loground/misc.c
Normal file
@ -0,0 +1,37 @@
|
||||
#include "./misc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
const char* loground_type_stringify(loground_type_t type) {
|
||||
# define each_(NAME, name) do { \
|
||||
if (type == LOGROUND_TYPE_##NAME) return #name; \
|
||||
} while (0)
|
||||
|
||||
LOGROUND_TYPE_EACH_(each_);
|
||||
|
||||
assert(false);
|
||||
return NULL;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
bool loground_type_unstringify(
|
||||
loground_type_t* type, const char* v, size_t len) {
|
||||
assert(type != NULL);
|
||||
assert(v != NULL || len == 0);
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (strncmp(v, #name, len) == 0 && #name[len] == 0) { \
|
||||
*type = LOGROUND_TYPE_##NAME; \
|
||||
return true; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOGROUND_TYPE_EACH_(each_);
|
||||
return false;
|
||||
|
||||
# undef each_
|
||||
}
|
25
core/loground/misc.h
Normal file
25
core/loground/misc.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* dont forget to update EACH macro */
|
||||
typedef enum {
|
||||
LOGROUND_TYPE_ISLAND,
|
||||
} loground_type_t;
|
||||
|
||||
#define LOGROUND_TYPE_EACH_(PROC) do { \
|
||||
PROC(ISLAND, island); \
|
||||
} while (0)
|
||||
|
||||
const char*
|
||||
loground_type_stringify(
|
||||
loground_type_t type
|
||||
);
|
||||
|
||||
bool
|
||||
loground_type_unstringify(
|
||||
loground_type_t* type,
|
||||
const char* v,
|
||||
size_t len
|
||||
);
|
89
core/loground/pool.c
Normal file
89
core/loground/pool.c
Normal file
@ -0,0 +1,89 @@
|
||||
#include "./pool.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/memory/memory.h"
|
||||
|
||||
#include "core/locommon/counter.h"
|
||||
#include "core/loshader/ground.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
struct loground_pool_t {
|
||||
loshader_ground_drawer_t* drawer;
|
||||
locommon_counter_t* idgen;
|
||||
|
||||
size_t length;
|
||||
loground_base_t items[1];
|
||||
};
|
||||
|
||||
static size_t loground_pool_find_unused_item_index_(
|
||||
const loground_pool_t* pool) {
|
||||
assert(pool != NULL);
|
||||
|
||||
for (size_t i = 0; i < pool->length; ++i) {
|
||||
if (!pool->items[i].used) return i;
|
||||
}
|
||||
fprintf(stderr, "ground pool overflow\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
loground_pool_t* loground_pool_new(
|
||||
loshader_ground_drawer_t* drawer,
|
||||
locommon_counter_t* idgen,
|
||||
size_t length) {
|
||||
assert(drawer != NULL);
|
||||
assert(idgen != NULL);
|
||||
assert(length > 0);
|
||||
|
||||
loground_pool_t* pool = memory_new(
|
||||
sizeof(*pool) + (length-1)*sizeof(pool->items[0]));
|
||||
*pool = (typeof(*pool)) {
|
||||
.drawer = drawer,
|
||||
.idgen = idgen,
|
||||
.length = length,
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < pool->length; ++i) {
|
||||
loground_base_initialize(&pool->items[i], drawer);
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
void loground_pool_delete(loground_pool_t* pool) {
|
||||
assert(pool != NULL);
|
||||
|
||||
for (size_t i = 0; i < pool->length; ++i) {
|
||||
loground_base_deinitialize(&pool->items[i]);
|
||||
}
|
||||
memory_delete(pool);
|
||||
}
|
||||
|
||||
loground_base_t* loground_pool_create(loground_pool_t* pool) {
|
||||
assert(pool != NULL);
|
||||
|
||||
const size_t i = loground_pool_find_unused_item_index_(pool);
|
||||
|
||||
loground_base_reinitialize(
|
||||
&pool->items[i], locommon_counter_count(pool->idgen));
|
||||
|
||||
pool->items[i].used = true;
|
||||
return &pool->items[i];
|
||||
}
|
||||
|
||||
loground_base_t* loground_pool_unpack_item(
|
||||
loground_pool_t* pool, const msgpack_object* obj) {
|
||||
assert(pool != NULL);
|
||||
|
||||
const size_t i = loground_pool_find_unused_item_index_(pool);
|
||||
|
||||
if (!loground_base_unpack(&pool->items[i], obj)) return NULL;
|
||||
|
||||
pool->items[i].used = true;
|
||||
return &pool->items[i];
|
||||
}
|
39
core/loground/pool.h
Normal file
39
core/loground/pool.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/counter.h"
|
||||
#include "core/loshader/ground.h"
|
||||
|
||||
#include "./base.h"
|
||||
|
||||
struct loground_pool_t;
|
||||
typedef struct loground_pool_t loground_pool_t;
|
||||
|
||||
loground_pool_t* /* OWNERSHIP */
|
||||
loground_pool_new(
|
||||
loshader_ground_drawer_t* drawer,
|
||||
locommon_counter_t* idgen,
|
||||
size_t length
|
||||
);
|
||||
|
||||
void
|
||||
loground_pool_delete(
|
||||
loground_pool_t* pool /* OWNERSHIP */
|
||||
);
|
||||
|
||||
loground_base_t* /* OWNERSHIP */
|
||||
loground_pool_create(
|
||||
loground_pool_t* pool
|
||||
);
|
||||
|
||||
loground_base_t* /* OWNERSHIP/NULLABLE */
|
||||
loground_pool_unpack_item(
|
||||
loground_pool_t* pool,
|
||||
const msgpack_object* obj
|
||||
);
|
28
core/loplayer/CMakeLists.txt
Normal file
28
core/loplayer/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
||||
add_library(loplayer
|
||||
action.c
|
||||
camera.c
|
||||
combat.c
|
||||
controller.c
|
||||
entity.c
|
||||
event.c
|
||||
hud.c
|
||||
menu.c
|
||||
player.c
|
||||
status.c
|
||||
)
|
||||
target_link_libraries(loplayer
|
||||
msgpackc
|
||||
|
||||
conv
|
||||
glyphas
|
||||
math
|
||||
memory
|
||||
mpkutil
|
||||
|
||||
lobullet
|
||||
locommon
|
||||
loeffect
|
||||
loentity
|
||||
loresource
|
||||
loshader
|
||||
)
|
880
core/loplayer/action.c
Normal file
880
core/loplayer/action.c
Normal file
@ -0,0 +1,880 @@
|
||||
#include "./action.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/memory/memory.h"
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/lobullet/base.h"
|
||||
#include "core/lobullet/linear.h"
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/easing.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loeffect/effect.h"
|
||||
#include "core/loeffect/stance.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loresource/set.h"
|
||||
#include "core/loresource/sound.h"
|
||||
#include "core/loresource/text.h"
|
||||
|
||||
#include "./camera.h"
|
||||
#include "./combat.h"
|
||||
#include "./controller.h"
|
||||
#include "./entity.h"
|
||||
#include "./event.h"
|
||||
#include "./hud.h"
|
||||
#include "./menu.h"
|
||||
#include "./status.h"
|
||||
|
||||
struct loplayer_action_t {
|
||||
loresource_set_t* res;
|
||||
const locommon_ticker_t* ticker;
|
||||
lobullet_pool_t* bullets;
|
||||
loentity_store_t* entities;
|
||||
|
||||
loplayer_event_t* event;
|
||||
loplayer_status_t* status;
|
||||
loplayer_entity_t* entity;
|
||||
const loplayer_controller_t* controller;
|
||||
loplayer_combat_t* combat;
|
||||
loplayer_camera_t* camera;
|
||||
loplayer_hud_t* hud;
|
||||
loplayer_menu_t* menu;
|
||||
|
||||
union {
|
||||
struct {
|
||||
} stand;
|
||||
struct {
|
||||
float direction;
|
||||
} moving;
|
||||
struct {
|
||||
float direction;
|
||||
} dodge;
|
||||
struct {
|
||||
} combat;
|
||||
struct {
|
||||
} shoot;
|
||||
struct {
|
||||
} dead;
|
||||
struct {
|
||||
bool invincible;
|
||||
} menu;
|
||||
} state;
|
||||
uint64_t since;
|
||||
|
||||
void
|
||||
(*execute)(
|
||||
loplayer_action_t* action
|
||||
);
|
||||
void
|
||||
(*pack)(
|
||||
const loplayer_action_t* action,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
};
|
||||
|
||||
#define LOPLAYER_ACTION_STATE_EACH_(PROC) do { \
|
||||
PROC(stand); \
|
||||
PROC(moving); \
|
||||
PROC(dodge); \
|
||||
PROC(combat); \
|
||||
PROC(shoot); \
|
||||
PROC(dead); \
|
||||
PROC(menu); \
|
||||
} while (0)
|
||||
|
||||
static void
|
||||
loplayer_action_start_stand_state_(
|
||||
loplayer_action_t* action
|
||||
);
|
||||
static void
|
||||
loplayer_action_start_moving_state_(
|
||||
loplayer_action_t* action,
|
||||
float direction
|
||||
);
|
||||
static void
|
||||
loplayer_action_start_dodge_state_(
|
||||
loplayer_action_t* action,
|
||||
float direction
|
||||
);
|
||||
static void
|
||||
loplayer_action_start_combat_state_(
|
||||
loplayer_action_t* action
|
||||
);
|
||||
static bool
|
||||
loplayer_action_start_shoot_state_(
|
||||
loplayer_action_t* action
|
||||
);
|
||||
static void
|
||||
loplayer_action_start_dead_state_(
|
||||
loplayer_action_t* action
|
||||
);
|
||||
static void
|
||||
loplayer_action_start_menu_state_(
|
||||
loplayer_action_t* action,
|
||||
bool invincible
|
||||
);
|
||||
|
||||
static void loplayer_action_affect_bullet_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
if (action->status->bullet_immune_until > action->ticker->time) {
|
||||
return;
|
||||
}
|
||||
if (loplayer_entity_affect_bullet(action->entity)) {
|
||||
action->status->bullet_immune_until = action->ticker->time + 200;
|
||||
}
|
||||
}
|
||||
static bool loplayer_action_shoot_bullet_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
static const float consume = .05f;
|
||||
|
||||
float* f = &action->status->recipient.faith;
|
||||
if (*f <= 0) return false;
|
||||
|
||||
vec2_t v;
|
||||
locommon_position_sub(
|
||||
&v, &action->controller->looking, &action->entity->super.super.pos);
|
||||
const float vlen = vec2_length(&v);
|
||||
if (vlen == 0) {
|
||||
v = vec2(action->entity->direction, 0);
|
||||
} else {
|
||||
vec2_diveq(&v, vec2_length(&v));
|
||||
}
|
||||
|
||||
/* TODO(catfoot): diffusion */
|
||||
vec2_muleq(&v, 1.f);
|
||||
vec2_addeq(&v, &action->entity->last_velocity);
|
||||
|
||||
lobullet_base_t* b = lobullet_pool_create(action->bullets);
|
||||
lobullet_linear_light_build(b, (&(lobullet_linear_param_t) {
|
||||
.owner = action->entity->super.super.id,
|
||||
.pos = action->entity->super.super.pos,
|
||||
.size = vec2(.015f, .015f),
|
||||
.velocity = v,
|
||||
.acceleration = vec2(0, -.1f),
|
||||
.color = vec4(.8f, .8f, .8f, .8f),
|
||||
.duration = 2000,
|
||||
.knockback = .1f,
|
||||
.effect = loeffect_immediate_damage(
|
||||
action->status->recipient.status.attack/2),
|
||||
}));
|
||||
loentity_store_add(action->entities, &b->super.super);
|
||||
|
||||
*f -= consume;
|
||||
if (*f < 0) *f = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void loplayer_action_show_tutorial_after_death_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
# define text_(name) loresource_text_get(action->res->lang, name)
|
||||
# define popup_(name) \
|
||||
loplayer_menu_popup( \
|
||||
action->menu, \
|
||||
text_("tutorial_title_"name), \
|
||||
text_("tutorial_text_" name))
|
||||
|
||||
switch (action->status->recipient.last_damage) {
|
||||
case LOEFFECT_ID_IMMEDIATE_DAMAGE:
|
||||
popup_("dead_by_combat");
|
||||
break;
|
||||
case LOEFFECT_ID_CURSE:
|
||||
popup_("dead_by_curse");
|
||||
break;
|
||||
case LOEFFECT_ID_LOST:
|
||||
popup_("dead_by_lost");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
# undef popup_
|
||||
# undef text_
|
||||
|
||||
loplayer_action_start_menu_popup_state(action);
|
||||
}
|
||||
|
||||
static void loplayer_action_execute_stand_state_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
const float max_acceleration_ = action->entity->on_ground? 2.0f: 0.5f;
|
||||
|
||||
loplayer_entity_aim(action->entity, &action->controller->looking);
|
||||
|
||||
if (action->status->recipient.madness <= 0) {
|
||||
loplayer_action_start_dead_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
loplayer_action_affect_bullet_(action);
|
||||
if (loplayer_combat_accept_all_attacks(action->combat)) {
|
||||
loplayer_action_start_combat_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action->controller->action) {
|
||||
case LOPLAYER_CONTROLLER_ACTION_NONE:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_ATTACK:
|
||||
if (loplayer_action_start_shoot_state_(action)) return;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_DODGE:
|
||||
loplayer_action_start_dodge_state_(action, action->entity->direction);
|
||||
return;
|
||||
case LOPLAYER_CONTROLLER_ACTION_GUARD:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_UNGUARD:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_MENU:
|
||||
if (action->entity->movement.x == 0) {
|
||||
loplayer_menu_show_status(action->menu);
|
||||
loplayer_action_start_menu_state_(action, false /* INVINCIBLE */);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (action->controller->movement) {
|
||||
case LOPLAYER_CONTROLLER_MOVEMENT_NONE:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_MOVEMENT_JUMP:
|
||||
if (action->entity->on_ground) {
|
||||
action->entity->gravity += action->status->recipient.status.jump;
|
||||
}
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_MOVEMENT_WALK_LEFT:
|
||||
case LOPLAYER_CONTROLLER_MOVEMENT_DASH_LEFT:
|
||||
loplayer_action_start_moving_state_(action, -1);
|
||||
return;
|
||||
case LOPLAYER_CONTROLLER_MOVEMENT_WALK_RIGHT:
|
||||
case LOPLAYER_CONTROLLER_MOVEMENT_DASH_RIGHT:
|
||||
loplayer_action_start_moving_state_(action, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const float t = (action->ticker->time - action->since)%2000/1000.0f - 1;
|
||||
action->entity->motion.time = t*t*(3-2*MATH_ABS(t));
|
||||
action->entity->motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
action->entity->motion.to = LOSHADER_CHARACTER_MOTION_ID_STAND2;
|
||||
|
||||
locommon_easing_linear_float(
|
||||
&action->entity->movement.x,
|
||||
0,
|
||||
max_acceleration_ * action->ticker->delta_f);
|
||||
}
|
||||
static void loplayer_action_pack_stand_state_(
|
||||
const loplayer_action_t* action, msgpack_packer* packer) {
|
||||
assert(action != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 1);
|
||||
|
||||
mpkutil_pack_str(packer, "name");
|
||||
mpkutil_pack_str(packer, "stand");
|
||||
}
|
||||
static bool loplayer_action_unpack_stand_state_(
|
||||
loplayer_action_t* action, const msgpack_object_map* root) {
|
||||
assert(action != NULL);
|
||||
|
||||
return root != NULL;
|
||||
}
|
||||
static void loplayer_action_start_stand_state_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
action->since = action->ticker->time;
|
||||
action->execute = loplayer_action_execute_stand_state_;
|
||||
action->pack = loplayer_action_pack_stand_state_;
|
||||
|
||||
action->camera->state = LOPLAYER_CAMERA_STATE_DEFAULT;
|
||||
loplayer_hud_show(action->hud);
|
||||
}
|
||||
|
||||
static void loplayer_action_execute_moving_state_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
static const float backwalk_attenuation_ = 0.8f;
|
||||
static const float dash_speed_ = 1.4f;
|
||||
|
||||
const float max_acceleration_ = action->entity->on_ground? 2.4f: 0.8f;
|
||||
|
||||
const float dir = action->state.moving.direction;
|
||||
|
||||
loplayer_entity_aim(action->entity, &action->controller->looking);
|
||||
|
||||
if (action->status->recipient.madness <= 0) {
|
||||
loplayer_action_start_dead_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
loplayer_action_affect_bullet_(action);
|
||||
if (loplayer_combat_accept_all_attacks(action->combat)) {
|
||||
loplayer_action_start_combat_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action->controller->action) {
|
||||
case LOPLAYER_CONTROLLER_ACTION_NONE:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_ATTACK:
|
||||
if (loplayer_action_start_shoot_state_(action)) return;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_DODGE:
|
||||
loplayer_action_start_dodge_state_(action, dir);
|
||||
return;
|
||||
case LOPLAYER_CONTROLLER_ACTION_GUARD:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_UNGUARD:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_MENU:
|
||||
break;
|
||||
}
|
||||
|
||||
float max_speed = action->status->recipient.status.speed;
|
||||
float control_dir = dir;
|
||||
|
||||
switch (action->controller->movement) {
|
||||
case LOPLAYER_CONTROLLER_MOVEMENT_NONE:
|
||||
loplayer_action_start_stand_state_(action);
|
||||
return;
|
||||
case LOPLAYER_CONTROLLER_MOVEMENT_JUMP:
|
||||
if (action->entity->on_ground) {
|
||||
action->entity->gravity += action->status->recipient.status.jump;
|
||||
}
|
||||
return;
|
||||
case LOPLAYER_CONTROLLER_MOVEMENT_WALK_LEFT:
|
||||
control_dir = -1;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_MOVEMENT_WALK_RIGHT:
|
||||
control_dir = 1;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_MOVEMENT_DASH_LEFT:
|
||||
max_speed *= dash_speed_;
|
||||
control_dir = -1;
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_MOVEMENT_DASH_RIGHT:
|
||||
max_speed *= dash_speed_;
|
||||
control_dir = 1;
|
||||
break;
|
||||
}
|
||||
if (control_dir * dir < 0) {
|
||||
loplayer_action_start_stand_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dir * action->entity->direction < 0) {
|
||||
max_speed *= backwalk_attenuation_;
|
||||
}
|
||||
|
||||
if (action->entity->on_ground) {
|
||||
const int32_t p = 70/max_speed;
|
||||
const float t = (action->ticker->time - action->since)%p*2.0f/p - 1;
|
||||
action->entity->motion.time = MATH_ABS(t);
|
||||
action->entity->motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
action->entity->motion.to = LOSHADER_CHARACTER_MOTION_ID_WALK;
|
||||
}
|
||||
|
||||
locommon_easing_linear_float(
|
||||
&action->entity->movement.x,
|
||||
max_speed*dir,
|
||||
max_acceleration_ * action->ticker->delta_f);
|
||||
}
|
||||
static void loplayer_action_pack_moving_state_(
|
||||
const loplayer_action_t* action, msgpack_packer* packer) {
|
||||
assert(action != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 2);
|
||||
|
||||
mpkutil_pack_str(packer, "name");
|
||||
mpkutil_pack_str(packer, "moving");
|
||||
|
||||
mpkutil_pack_str(packer, "direction");
|
||||
msgpack_pack_double(packer, action->state.moving.direction);
|
||||
}
|
||||
static bool loplayer_action_unpack_moving_state_(
|
||||
loplayer_action_t* action, const msgpack_object_map* root) {
|
||||
assert(action != NULL);
|
||||
|
||||
const msgpack_object* direction =
|
||||
mpkutil_get_map_item_by_str(root, "direction");
|
||||
if (!mpkutil_get_float(direction, &action->state.moving.direction)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static void loplayer_action_start_moving_state_(
|
||||
loplayer_action_t* action, float dir) {
|
||||
assert(action != NULL);
|
||||
assert(MATH_FLOAT_VALID(dir));
|
||||
|
||||
action->since = action->ticker->time;
|
||||
action->execute = loplayer_action_execute_moving_state_;
|
||||
action->pack = loplayer_action_pack_moving_state_;
|
||||
|
||||
action->state = (typeof(action->state)) {
|
||||
.moving = {
|
||||
.direction = dir,
|
||||
},
|
||||
};
|
||||
action->camera->state = LOPLAYER_CAMERA_STATE_DEFAULT;
|
||||
loplayer_hud_show(action->hud);
|
||||
}
|
||||
|
||||
static void loplayer_action_execute_dodge_state_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
static const uint64_t duration_ = 200;
|
||||
static const float start_speed_ = 0.6f;
|
||||
static const float end_speed_ = 0.1f;
|
||||
|
||||
if (action->since + duration_ <= action->ticker->time) {
|
||||
loplayer_combat_drop_all_attacks(action->combat);
|
||||
loplayer_action_start_stand_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
const float dir = action->state.dodge.direction;
|
||||
|
||||
vec2_t* v = &action->entity->movement;
|
||||
|
||||
const float t = (action->ticker->time - action->since)*1.0f/duration_;
|
||||
const float r = 1 - powf(1-t, 1.5);
|
||||
v->x = (r * (start_speed_-end_speed_) + end_speed_) * dir;
|
||||
v->y = 0;
|
||||
|
||||
action->entity->motion.time = 1-powf(1-t, 2);
|
||||
action->entity->motion.from = LOSHADER_CHARACTER_MOTION_ID_WALK;
|
||||
action->entity->motion.to = LOSHADER_CHARACTER_MOTION_ID_STAND1;
|
||||
}
|
||||
static void loplayer_action_pack_dodge_state_(
|
||||
const loplayer_action_t* action, msgpack_packer* packer) {
|
||||
assert(action != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 2);
|
||||
|
||||
mpkutil_pack_str(packer, "name");
|
||||
mpkutil_pack_str(packer, "dodge");
|
||||
|
||||
mpkutil_pack_str(packer, "direction");
|
||||
msgpack_pack_double(packer, action->state.dodge.direction);
|
||||
}
|
||||
static bool loplayer_action_unpack_dodge_state_(
|
||||
loplayer_action_t* action, const msgpack_object_map* root) {
|
||||
assert(action != NULL);
|
||||
|
||||
const msgpack_object* direction =
|
||||
mpkutil_get_map_item_by_str(root, "direction");
|
||||
if (!mpkutil_get_float(direction, &action->state.moving.direction)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static void loplayer_action_start_dodge_state_(
|
||||
loplayer_action_t* action, float dir) {
|
||||
assert(action != NULL);
|
||||
assert(MATH_FLOAT_VALID(dir));
|
||||
|
||||
action->since = action->ticker->time;
|
||||
action->state = (typeof(action->state)) {
|
||||
.moving = {
|
||||
.direction = dir,
|
||||
},
|
||||
};
|
||||
action->execute = loplayer_action_execute_dodge_state_;
|
||||
action->pack = loplayer_action_pack_dodge_state_;
|
||||
|
||||
action->camera->state = LOPLAYER_CAMERA_STATE_DEFAULT;
|
||||
loplayer_hud_show(action->hud);
|
||||
loplayer_combat_drop_all_attacks(action->combat);
|
||||
|
||||
loresource_sound_play(action->res->sound, "dodge");
|
||||
}
|
||||
|
||||
static void loplayer_action_execute_combat_state_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
if (action->status->recipient.madness <= 0) {
|
||||
loplayer_action_start_dead_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
loplayer_action_affect_bullet_(action);
|
||||
if (!loplayer_combat_accept_all_attacks(action->combat)) {
|
||||
loplayer_action_start_stand_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action->controller->action) {
|
||||
case LOPLAYER_CONTROLLER_ACTION_NONE:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_ATTACK:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_DODGE:
|
||||
loplayer_action_start_dodge_state_(action, action->entity->direction);
|
||||
return;
|
||||
case LOPLAYER_CONTROLLER_ACTION_GUARD:
|
||||
loplayer_combat_guard(action->combat);
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_UNGUARD:
|
||||
loplayer_combat_unguard(action->combat);
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_MENU:
|
||||
break;
|
||||
}
|
||||
|
||||
action->entity->gravity = 0;
|
||||
|
||||
const float klen = vec2_length(&action->entity->knockback);
|
||||
if (klen > .1f) vec2_muleq(&action->entity->knockback, .1f/klen);
|
||||
}
|
||||
static void loplayer_action_pack_combat_state_(
|
||||
const loplayer_action_t* action, msgpack_packer* packer) {
|
||||
assert(action != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 1);
|
||||
|
||||
mpkutil_pack_str(packer, "name");
|
||||
mpkutil_pack_str(packer, "combat");
|
||||
}
|
||||
static bool loplayer_action_unpack_combat_state_(
|
||||
loplayer_action_t* action, const msgpack_object_map* root) {
|
||||
assert(action != NULL);
|
||||
|
||||
return root != NULL;
|
||||
}
|
||||
static void loplayer_action_start_combat_state_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
action->since = action->ticker->time;
|
||||
action->execute = loplayer_action_execute_combat_state_;
|
||||
action->pack = loplayer_action_pack_combat_state_;
|
||||
|
||||
action->entity->movement = vec2(0, 0);
|
||||
|
||||
action->camera->state = LOPLAYER_CAMERA_STATE_COMBAT;
|
||||
loplayer_hud_show(action->hud);
|
||||
}
|
||||
|
||||
static void loplayer_action_execute_shoot_state_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
static const uint64_t duration = 300;
|
||||
static const float max_acceleration = 1.f;
|
||||
|
||||
loplayer_entity_aim(action->entity, &action->controller->looking);
|
||||
|
||||
if (action->status->recipient.madness <= 0) {
|
||||
loplayer_action_start_dead_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
loplayer_action_affect_bullet_(action);
|
||||
if (loplayer_combat_accept_all_attacks(action->combat)) {
|
||||
loplayer_action_start_combat_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action->since+duration <= action->ticker->time) {
|
||||
if (loplayer_action_shoot_bullet_(action)) {
|
||||
loresource_sound_play(action->res->sound, "player_shoot");
|
||||
}
|
||||
loplayer_action_start_stand_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
const float a = max_acceleration * action->ticker->delta_f;
|
||||
locommon_easing_linear_float(&action->entity->movement.x, 0, a);
|
||||
locommon_easing_linear_float(&action->entity->movement.y, 0, a);
|
||||
}
|
||||
static void loplayer_action_pack_shoot_state_(
|
||||
const loplayer_action_t* action, msgpack_packer* packer) {
|
||||
assert(action != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 1);
|
||||
|
||||
mpkutil_pack_str(packer, "name");
|
||||
mpkutil_pack_str(packer, "shoot");
|
||||
}
|
||||
static bool loplayer_action_unpack_shoot_state_(
|
||||
loplayer_action_t* action, const msgpack_object_map* root) {
|
||||
assert(action != NULL);
|
||||
|
||||
return root != NULL;
|
||||
}
|
||||
static bool loplayer_action_start_shoot_state_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
if (!loeffect_stance_set_has(
|
||||
&action->status->stances, LOEFFECT_STANCE_ID_REVOLUTIONER)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
action->since = action->ticker->time;
|
||||
action->execute = loplayer_action_execute_shoot_state_;
|
||||
action->pack = loplayer_action_pack_shoot_state_;
|
||||
|
||||
action->camera->state = LOPLAYER_CAMERA_STATE_DEFAULT;
|
||||
loplayer_hud_show(action->hud);
|
||||
|
||||
loresource_sound_play(action->res->sound, "player_trigger");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void loplayer_action_execute_dead_state_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
static const uint64_t duration_ = 3000;
|
||||
|
||||
if (action->since + duration_ <= action->ticker->time) {
|
||||
loplayer_entity_move(action->entity, &action->status->respawn_pos);
|
||||
loplayer_status_reset(action->status);
|
||||
loplayer_combat_drop_all_attacks(action->combat);
|
||||
|
||||
loplayer_action_start_stand_state_(action);
|
||||
loplayer_action_show_tutorial_after_death_(action);
|
||||
loplayer_event_abort(action->event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void loplayer_action_pack_dead_state_(
|
||||
const loplayer_action_t* action, msgpack_packer* packer) {
|
||||
assert(action != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 1);
|
||||
|
||||
mpkutil_pack_str(packer, "name");
|
||||
mpkutil_pack_str(packer, "dead");
|
||||
}
|
||||
static bool loplayer_action_unpack_dead_state_(
|
||||
loplayer_action_t* action, const msgpack_object_map* root) {
|
||||
assert(action != NULL);
|
||||
|
||||
return root != NULL;
|
||||
}
|
||||
static void loplayer_action_start_dead_state_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
action->since = action->ticker->time;
|
||||
action->execute = loplayer_action_execute_dead_state_;
|
||||
action->pack = loplayer_action_pack_dead_state_;
|
||||
|
||||
action->entity->movement = vec2(0, 0);
|
||||
|
||||
action->camera->state = LOPLAYER_CAMERA_STATE_DEAD;
|
||||
loplayer_hud_hide(action->hud);
|
||||
loplayer_combat_drop_all_attacks(action->combat);
|
||||
|
||||
/* Deny all event requests. */
|
||||
loplayer_event_abort(action->event);
|
||||
loplayer_event_take_control(action->event, action->entity->super.super.id);
|
||||
}
|
||||
|
||||
static void loplayer_action_execute_menu_state_(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
if (action->status->recipient.madness <= 0) {
|
||||
loplayer_menu_hide(action->menu);
|
||||
loplayer_action_start_dead_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
loplayer_action_affect_bullet_(action);
|
||||
if (!action->state.menu.invincible &&
|
||||
loplayer_combat_accept_all_attacks(action->combat)) {
|
||||
loplayer_menu_hide(action->menu);
|
||||
loplayer_action_start_combat_state_(action);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action->controller->action) {
|
||||
case LOPLAYER_CONTROLLER_ACTION_NONE:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_ATTACK:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_DODGE:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_GUARD:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_UNGUARD:
|
||||
break;
|
||||
case LOPLAYER_CONTROLLER_ACTION_MENU:
|
||||
loplayer_menu_hide(action->menu);
|
||||
loplayer_action_start_stand_state_(action);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static void loplayer_action_pack_menu_state_(
|
||||
const loplayer_action_t* action, msgpack_packer* packer) {
|
||||
assert(action != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 2);
|
||||
|
||||
mpkutil_pack_str(packer, "name");
|
||||
mpkutil_pack_str(packer, "menu");
|
||||
|
||||
mpkutil_pack_str(packer, "invincible");
|
||||
mpkutil_pack_bool(packer, action->state.menu.invincible);
|
||||
}
|
||||
static bool loplayer_action_unpack_menu_state_(
|
||||
loplayer_action_t* action, const msgpack_object_map* root) {
|
||||
assert(action != NULL);
|
||||
|
||||
const msgpack_object* invincible =
|
||||
mpkutil_get_map_item_by_str(root, "invincible");
|
||||
if (!mpkutil_get_bool(invincible, &action->state.menu.invincible)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static void loplayer_action_start_menu_state_(
|
||||
loplayer_action_t* action, bool invincible) {
|
||||
assert(action != NULL);
|
||||
|
||||
action->since = action->ticker->time;
|
||||
action->execute = loplayer_action_execute_menu_state_;
|
||||
action->pack = loplayer_action_pack_menu_state_;
|
||||
|
||||
action->entity->movement = vec2(0, 0);
|
||||
|
||||
action->state.menu = (typeof(action->state.menu)) {
|
||||
.invincible = invincible,
|
||||
};
|
||||
|
||||
action->camera->state = LOPLAYER_CAMERA_STATE_MENU;
|
||||
}
|
||||
|
||||
loplayer_action_t* loplayer_action_new(
|
||||
loresource_set_t* res,
|
||||
const locommon_ticker_t* ticker,
|
||||
lobullet_pool_t* bullets,
|
||||
loentity_store_t* entities,
|
||||
loplayer_event_t* event,
|
||||
loplayer_status_t* status,
|
||||
loplayer_entity_t* entity,
|
||||
loplayer_combat_t* combat,
|
||||
const loplayer_controller_t* controller,
|
||||
loplayer_camera_t* camera,
|
||||
loplayer_hud_t* hud,
|
||||
loplayer_menu_t* menu) {
|
||||
assert(res != NULL);
|
||||
assert(ticker != NULL);
|
||||
assert(bullets != NULL);
|
||||
assert(entities != NULL);
|
||||
assert(event != NULL);
|
||||
assert(status != NULL);
|
||||
assert(entity != NULL);
|
||||
assert(combat != NULL);
|
||||
assert(controller != NULL);
|
||||
assert(camera != NULL);
|
||||
assert(hud != NULL);
|
||||
assert(menu != NULL);
|
||||
|
||||
loplayer_action_t* action = memory_new(sizeof(*action));
|
||||
*action = (typeof(*action)) {
|
||||
.res = res,
|
||||
.ticker = ticker,
|
||||
.bullets = bullets,
|
||||
.entities = entities,
|
||||
.event = event,
|
||||
.status = status,
|
||||
.entity = entity,
|
||||
.combat = combat,
|
||||
.controller = controller,
|
||||
.camera = camera,
|
||||
.hud = hud,
|
||||
.menu = menu,
|
||||
};
|
||||
loplayer_action_start_stand_state_(action);
|
||||
return action;
|
||||
}
|
||||
|
||||
void loplayer_action_delete(loplayer_action_t* action) {
|
||||
if (action == NULL) return;
|
||||
|
||||
memory_delete(action);
|
||||
}
|
||||
|
||||
void loplayer_action_start_menu_popup_state(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
loplayer_action_start_menu_state_(action, true /* invincible */);
|
||||
}
|
||||
|
||||
void loplayer_action_execute(loplayer_action_t* action) {
|
||||
assert(action != NULL);
|
||||
|
||||
assert(action->execute != NULL);
|
||||
action->execute(action);
|
||||
}
|
||||
|
||||
void loplayer_action_pack(const loplayer_action_t* action, msgpack_packer* packer) {
|
||||
assert(action != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 2);
|
||||
|
||||
mpkutil_pack_str(packer, "since");
|
||||
msgpack_pack_uint64(packer, action->since);
|
||||
|
||||
assert(action->pack != NULL);
|
||||
|
||||
mpkutil_pack_str(packer, "state");
|
||||
action->pack(action, packer);
|
||||
}
|
||||
|
||||
bool loplayer_action_unpack(
|
||||
loplayer_action_t* action, const msgpack_object* obj) {
|
||||
assert(action != NULL);
|
||||
|
||||
if (obj == NULL) return false;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
const msgpack_object* since = mpkutil_get_map_item_by_str(root, "since");
|
||||
if (!mpkutil_get_uint64(since, &action->since)) return false;
|
||||
|
||||
const msgpack_object_map* state = mpkutil_get_map(
|
||||
mpkutil_get_map_item_by_str(root, "state"));
|
||||
|
||||
bool state_loaded = false;
|
||||
|
||||
const msgpack_object* name = mpkutil_get_map_item_by_str(state, "name");
|
||||
const char* v;
|
||||
size_t len;
|
||||
if (!mpkutil_get_str(name, &v, &len)) {
|
||||
loplayer_action_start_stand_state_(action);
|
||||
state_loaded = true;
|
||||
}
|
||||
|
||||
# define unpack_state_(name_) do { \
|
||||
if (!state_loaded && strncmp(v, #name_, len) == 0 && #name_[len] == 0) { \
|
||||
action->execute = loplayer_action_execute_##name_##_state_; \
|
||||
action->pack = loplayer_action_pack_##name_##_state_; \
|
||||
if (!loplayer_action_unpack_##name_##_state_(action, state)) { \
|
||||
loplayer_action_start_stand_state_(action); \
|
||||
} \
|
||||
state_loaded = true; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOPLAYER_ACTION_STATE_EACH_(unpack_state_);
|
||||
|
||||
# undef unpack_state_
|
||||
|
||||
return true;
|
||||
}
|
64
core/loplayer/action.h
Normal file
64
core/loplayer/action.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "core/lobullet/pool.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loresource/set.h"
|
||||
|
||||
#include "./camera.h"
|
||||
#include "./combat.h"
|
||||
#include "./controller.h"
|
||||
#include "./entity.h"
|
||||
#include "./event.h"
|
||||
#include "./hud.h"
|
||||
#include "./menu.h"
|
||||
#include "./status.h"
|
||||
|
||||
struct loplayer_action_t;
|
||||
typedef struct loplayer_action_t loplayer_action_t;
|
||||
|
||||
loplayer_action_t* /* OWNERSHIP */
|
||||
loplayer_action_new(
|
||||
loresource_set_t* res,
|
||||
const locommon_ticker_t* ticker,
|
||||
lobullet_pool_t* bullets,
|
||||
loentity_store_t* entities,
|
||||
loplayer_event_t* event,
|
||||
loplayer_status_t* status,
|
||||
loplayer_entity_t* entity,
|
||||
loplayer_combat_t* combat,
|
||||
const loplayer_controller_t* controller,
|
||||
loplayer_camera_t* camera,
|
||||
loplayer_hud_t* hud,
|
||||
loplayer_menu_t* menu
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_action_delete(
|
||||
loplayer_action_t* action /* OWNERSHIP */
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_action_start_menu_popup_state(
|
||||
loplayer_action_t* action
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_action_execute(
|
||||
loplayer_action_t* action
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_action_pack(
|
||||
const loplayer_action_t* action,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
loplayer_action_unpack(
|
||||
loplayer_action_t* action,
|
||||
const msgpack_object* obj /* NULLABLE */
|
||||
);
|
273
core/loplayer/camera.c
Normal file
273
core/loplayer/camera.c
Normal file
@ -0,0 +1,273 @@
|
||||
#include "./camera.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/matrix.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/locommon/easing.h"
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
|
||||
#include "./entity.h"
|
||||
#include "./event.h"
|
||||
#include "./status.h"
|
||||
|
||||
#define LOPLAYER_CAMERA_STATE_EACH_(PROC) do { \
|
||||
PROC(DEFAULT, default); \
|
||||
PROC(COMBAT, combat); \
|
||||
PROC(DEAD, dead); \
|
||||
PROC(MENU, menu); \
|
||||
} while (0)
|
||||
|
||||
static void loplayer_camera_bind_position_in_area_(
|
||||
const loplayer_camera_t* camera,
|
||||
locommon_position_t* pos,
|
||||
const locommon_position_t* areapos,
|
||||
const vec2_t* areasize) {
|
||||
assert(camera != NULL);
|
||||
assert(locommon_position_valid(pos));
|
||||
assert(locommon_position_valid(areapos));
|
||||
assert(vec2_valid(areasize));
|
||||
|
||||
vec2_t szoffset = camera->display_chunksz;
|
||||
vec2_diveq(&szoffset, camera->scale);
|
||||
|
||||
vec2_t sz;
|
||||
vec2_sub(&sz, areasize, &szoffset);
|
||||
|
||||
vec2_t v;
|
||||
locommon_position_sub(&v, pos, areapos);
|
||||
|
||||
# define fix_coordinate_(axis) do { \
|
||||
if (sz.axis > 0) { \
|
||||
if (MATH_ABS(v.axis) > sz.axis) v.axis = MATH_SIGN(v.axis)*sz.axis; \
|
||||
} else { \
|
||||
v.axis = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
fix_coordinate_(x);
|
||||
fix_coordinate_(y);
|
||||
|
||||
# undef fix_coordinate_
|
||||
|
||||
*pos = *areapos;
|
||||
vec2_addeq(&pos->fract, &v);
|
||||
locommon_position_reduce(pos);
|
||||
}
|
||||
|
||||
const char* loplayer_camera_state_stringify(loplayer_camera_state_t state) {
|
||||
# define each_(NAME, name) do { \
|
||||
if (state == LOPLAYER_CAMERA_STATE_##NAME) return #name; \
|
||||
} while (0)
|
||||
|
||||
LOPLAYER_CAMERA_STATE_EACH_(each_);
|
||||
|
||||
assert(false);
|
||||
return NULL;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
bool loplayer_camera_state_unstringify(
|
||||
loplayer_camera_state_t* state, const char* str, size_t len) {
|
||||
assert(state != NULL);
|
||||
|
||||
# define each_(NAME, name) do { \
|
||||
if (strncmp(str, #name, len) == 0 && #name[len] == 0) { \
|
||||
*state = LOPLAYER_CAMERA_STATE_##NAME; \
|
||||
return true; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOPLAYER_CAMERA_STATE_EACH_(each_);
|
||||
return false;
|
||||
|
||||
# undef each_
|
||||
}
|
||||
|
||||
void loplayer_camera_initialize(
|
||||
loplayer_camera_t* camera,
|
||||
loshader_set_t* shaders,
|
||||
const locommon_ticker_t* ticker,
|
||||
const loplayer_event_t* event,
|
||||
const loplayer_status_t* status,
|
||||
const loplayer_entity_t* entity,
|
||||
const mat4_t* proj) {
|
||||
assert(camera != NULL);
|
||||
assert(shaders != NULL);
|
||||
assert(ticker != NULL);
|
||||
assert(event != NULL);
|
||||
assert(status != NULL);
|
||||
assert(entity != NULL);
|
||||
assert(mat4_valid(proj));
|
||||
|
||||
mat4_t inv_proj;
|
||||
mat4_inv(&inv_proj, proj);
|
||||
|
||||
static const vec4_t chunk = vec4(1, 1, 0, 0);
|
||||
vec4_t chunksz;
|
||||
mat4_mul_vec4(&chunksz, &inv_proj, &chunk);
|
||||
|
||||
*camera = (typeof(*camera)) {
|
||||
.shaders = shaders,
|
||||
.ticker = ticker,
|
||||
.event = event,
|
||||
.status = status,
|
||||
.entity = entity,
|
||||
|
||||
.display_chunksz = chunksz.xy,
|
||||
|
||||
.matrix = mat4_scale(1, 1, 1),
|
||||
.scale = 1.0f,
|
||||
|
||||
.cinesco = {
|
||||
.color = vec4(0, 0, 0, 1),
|
||||
},
|
||||
|
||||
.state = LOPLAYER_CAMERA_STATE_DEFAULT,
|
||||
.brightness = 1,
|
||||
};
|
||||
}
|
||||
|
||||
void loplayer_camera_deinitialize(loplayer_camera_t* camera) {
|
||||
assert(camera != NULL);
|
||||
|
||||
}
|
||||
|
||||
void loplayer_camera_update(loplayer_camera_t* camera) {
|
||||
assert(camera != NULL);
|
||||
|
||||
const float d = camera->ticker->delta_f;
|
||||
const loplayer_status_t* stat = camera->status;
|
||||
|
||||
locommon_position_t target = camera->entity->super.super.pos;
|
||||
|
||||
/* ---- movement ---- */
|
||||
const loplayer_event_param_t* e = loplayer_event_get_param(camera->event);
|
||||
if (e != NULL && e->area_size.x > 0 && e->area_size.y > 0) {
|
||||
loplayer_camera_bind_position_in_area_(
|
||||
camera, &target, &e->area_pos, &e->area_size);
|
||||
}
|
||||
|
||||
vec2_t dist;
|
||||
locommon_position_sub(&dist, &target, &camera->pos);
|
||||
if (vec2_pow_length(&dist) > 2) camera->pos = target;
|
||||
|
||||
locommon_easing_smooth_position(&camera->pos, &target, d*10);
|
||||
|
||||
# define ease_float_(name, ed, speed) \
|
||||
locommon_easing_smooth_float(&camera->name, ed, d*(speed))
|
||||
|
||||
/* ---- cinema scope ---- */
|
||||
ease_float_(cinesco.size, !!(e != NULL && e->cinescope)*.3f, 2);
|
||||
|
||||
/* ---- damage effect ---- */
|
||||
const bool damaged =
|
||||
stat->last_damage_time > 0 &&
|
||||
stat->last_damage_time+500 > camera->ticker->time;
|
||||
ease_float_(pe.raster, !!damaged*.5f, damaged? 5: 3);
|
||||
|
||||
/* ---- amnesia effect ---- */
|
||||
const uint64_t amnesia_st = stat->recipient.effects.amnesia.begin;
|
||||
const uint64_t amnesia_dur = stat->recipient.effects.amnesia.duration;
|
||||
ease_float_(
|
||||
pe.amnesia_displacement,
|
||||
!!(amnesia_st+amnesia_dur > camera->ticker->time), 5);
|
||||
|
||||
/* ---- dying effect ---- */
|
||||
const float dying = stat->dying_effect;
|
||||
camera->pixsort = dying < .1f? dying/.1f: powf(1-(dying-.1f), 1.5f);
|
||||
if (camera->pixsort < 0) camera->pixsort = 0;
|
||||
|
||||
/* ---- switch by current state ---- */
|
||||
switch (camera->state) {
|
||||
case LOPLAYER_CAMERA_STATE_DEFAULT:
|
||||
ease_float_(scale, 1.0f, 10);
|
||||
ease_float_(pe.whole_blur, 0.0f, 1);
|
||||
ease_float_(pe.radial_displacement, 0.0f, 5);
|
||||
ease_float_(pe.radial_fade, 0.5f, 3);
|
||||
break;
|
||||
case LOPLAYER_CAMERA_STATE_COMBAT:
|
||||
ease_float_(scale, 1.5f, 8);
|
||||
ease_float_(pe.whole_blur, 0.0f, 1);
|
||||
ease_float_(pe.radial_displacement, 0.6f, 3);
|
||||
ease_float_(pe.radial_fade, 0.8f, 1);
|
||||
break;
|
||||
case LOPLAYER_CAMERA_STATE_DEAD:
|
||||
ease_float_(scale, 2.0f, 1);
|
||||
ease_float_(pe.whole_blur, 1.0f, 1);
|
||||
ease_float_(pe.radial_displacement, 0.3f, .7f);
|
||||
ease_float_(pe.radial_fade, 1.5f, .7f);
|
||||
break;
|
||||
case LOPLAYER_CAMERA_STATE_MENU:
|
||||
ease_float_(scale, 1.0f, 10);
|
||||
ease_float_(pe.whole_blur, 0.9f, 1);
|
||||
ease_float_(pe.radial_displacement, 0.0f, 10);
|
||||
ease_float_(pe.radial_fade, 0.6f, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
# undef ease_float_
|
||||
|
||||
/* ---- fixed params ---- */
|
||||
camera->pe.brightness = camera->brightness;
|
||||
|
||||
/* ---- matrix ---- */
|
||||
camera->matrix = mat4_scale(camera->scale, camera->scale, 1);
|
||||
}
|
||||
|
||||
void loplayer_camera_draw(const loplayer_camera_t* camera) {
|
||||
assert(camera != NULL);
|
||||
|
||||
loshader_pixsort_drawer_set_intensity(
|
||||
camera->shaders->drawer.pixsort, camera->pixsort);
|
||||
|
||||
loshader_posteffect_drawer_set_param(
|
||||
camera->shaders->drawer.posteffect, &camera->pe);
|
||||
|
||||
loshader_cinescope_drawer_set_param(
|
||||
camera->shaders->drawer.cinescope, &camera->cinesco);
|
||||
}
|
||||
|
||||
void loplayer_camera_pack(
|
||||
const loplayer_camera_t* camera, msgpack_packer* packer) {
|
||||
assert(camera != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 2);
|
||||
|
||||
mpkutil_pack_str(packer, "state");
|
||||
mpkutil_pack_str(packer, loplayer_camera_state_stringify(camera->state));
|
||||
|
||||
mpkutil_pack_str(packer, "pos");
|
||||
locommon_position_pack(&camera->pos, packer);
|
||||
}
|
||||
|
||||
bool loplayer_camera_unpack(
|
||||
loplayer_camera_t* camera, const msgpack_object* obj) {
|
||||
assert(camera != NULL);
|
||||
|
||||
if (obj == NULL) return false;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
const char* v;
|
||||
size_t len;
|
||||
if (!mpkutil_get_str(item_("state"), &v, &len) ||
|
||||
!loplayer_camera_state_unstringify(&camera->state, v, len)) {
|
||||
return false;
|
||||
}
|
||||
if (!locommon_position_unpack(&camera->pos, item_("pos"))) {
|
||||
return false;
|
||||
}
|
||||
# undef item_
|
||||
return true;
|
||||
}
|
101
core/loplayer/camera.h
Normal file
101
core/loplayer/camera.h
Normal file
@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <msgpack.h>
|
||||
|
||||
#include "util/math/matrix.h"
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "core/locommon/position.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loshader/cinescope.h"
|
||||
#include "core/loshader/posteffect.h"
|
||||
#include "core/loshader/set.h"
|
||||
|
||||
#include "./entity.h"
|
||||
#include "./event.h"
|
||||
#include "./status.h"
|
||||
|
||||
typedef enum {
|
||||
LOPLAYER_CAMERA_STATE_DEFAULT,
|
||||
LOPLAYER_CAMERA_STATE_COMBAT,
|
||||
LOPLAYER_CAMERA_STATE_DEAD,
|
||||
LOPLAYER_CAMERA_STATE_MENU,
|
||||
} loplayer_camera_state_t;
|
||||
|
||||
typedef struct {
|
||||
/* injected deps */
|
||||
loshader_set_t* shaders;
|
||||
const locommon_ticker_t* ticker;
|
||||
const loplayer_event_t* event;
|
||||
const loplayer_status_t* status;
|
||||
const loplayer_entity_t* entity;
|
||||
|
||||
/* immutable params */
|
||||
vec2_t display_chunksz;
|
||||
|
||||
/* read-only mutable params */
|
||||
locommon_position_t pos;
|
||||
mat4_t matrix;
|
||||
|
||||
float scale;
|
||||
float pixsort;
|
||||
loshader_posteffect_drawer_param_t pe;
|
||||
loshader_cinescope_drawer_param_t cinesco;
|
||||
|
||||
/* public params */
|
||||
loplayer_camera_state_t state;
|
||||
float brightness;
|
||||
|
||||
} loplayer_camera_t;
|
||||
|
||||
const char*
|
||||
loplayer_camera_state_stringify(
|
||||
loplayer_camera_state_t state
|
||||
);
|
||||
|
||||
bool
|
||||
loplayer_camera_state_unstringify(
|
||||
loplayer_camera_state_t* state,
|
||||
const char* str,
|
||||
size_t len
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_camera_initialize(
|
||||
loplayer_camera_t* camera,
|
||||
loshader_set_t* shaders,
|
||||
const locommon_ticker_t* ticker,
|
||||
const loplayer_event_t* event,
|
||||
const loplayer_status_t* status,
|
||||
const loplayer_entity_t* entity,
|
||||
const mat4_t* proj
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_camera_deinitialize(
|
||||
loplayer_camera_t* camera
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_camera_update(
|
||||
loplayer_camera_t* camera
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_camera_draw(
|
||||
const loplayer_camera_t* camera
|
||||
);
|
||||
|
||||
void
|
||||
loplayer_camera_pack(
|
||||
const loplayer_camera_t* camera,
|
||||
msgpack_packer* packer
|
||||
);
|
||||
|
||||
bool
|
||||
loplayer_camera_unpack(
|
||||
loplayer_camera_t* camera,
|
||||
const msgpack_object* packer
|
||||
);
|
503
core/loplayer/combat.c
Normal file
503
core/loplayer/combat.c
Normal file
@ -0,0 +1,503 @@
|
||||
#include "./combat.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/memory/memory.h"
|
||||
#include "util/mpkutil/get.h"
|
||||
#include "util/mpkutil/pack.h"
|
||||
|
||||
#include "core/locommon/easing.h"
|
||||
#include "core/locommon/msgpack.h"
|
||||
#include "core/locommon/ticker.h"
|
||||
#include "core/loeffect/effect.h"
|
||||
#include "core/loentity/character.h"
|
||||
#include "core/loentity/store.h"
|
||||
#include "core/loresource/sound.h"
|
||||
#include "core/loshader/combat_ring.h"
|
||||
|
||||
#include "./entity.h"
|
||||
#include "./status.h"
|
||||
|
||||
struct loplayer_combat_t {
|
||||
/* injected deps */
|
||||
loresource_sound_t* sound;
|
||||
const locommon_ticker_t* ticker;
|
||||
loshader_combat_ring_drawer_t* drawer;
|
||||
loentity_store_t* entities;
|
||||
|
||||
loplayer_status_t* status;
|
||||
loplayer_entity_t* entity;
|
||||
|
||||
/* temporary cache for drawing */
|
||||
uint64_t ring_start;
|
||||
uint64_t ring_end;
|
||||
|
||||
uint64_t guard_start;
|
||||
uint64_t guard_end;
|
||||
|
||||
uint64_t attack_end;
|
||||
float alpha;
|
||||
|
||||
/* params */
|
||||
bool accepted;
|
||||
|
||||
size_t length;
|
||||
loplayer_combat_attack_t attacks[1];
|
||||
};
|
||||
|
||||
#define LOPLAYER_COMBAT_GUARD_ERROR_THRESHOLD 0.55f
|
||||
|
||||
#define LOPLAYER_COMBAT_ATTACK_PARAM_TO_PACK_EACH_(PROC) do { \
|
||||
PROC("attacker", attacker); \
|
||||
PROC("start", start); \
|
||||
PROC("duration", duration); \
|
||||
PROC("knockback", knockback); \
|
||||
PROC("effect", effect); \
|
||||
} while (0)
|
||||
#define LOPLAYER_COMBAT_ATTACK_PARAM_TO_PACK_COUNT 5
|
||||
|
||||
static bool loplayer_combat_find_attack_index_in_period_(
|
||||
loplayer_combat_t* combat, size_t* index, uint64_t st, uint64_t ed) {
|
||||
assert(combat != NULL);
|
||||
assert(st <= ed);
|
||||
|
||||
static size_t dummy_;
|
||||
if (index == NULL) index = &dummy_;
|
||||
|
||||
for (size_t i = 0; i < combat->length; ++i) {
|
||||
const uint64_t ist = combat->attacks[i].start;
|
||||
const uint64_t ied = combat->attacks[i].duration + ist;
|
||||
|
||||
if (ist < ed && ied > st) {
|
||||
*index = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool loplayer_combat_find_unused_attack_index_(
|
||||
loplayer_combat_t* combat, size_t *index) {
|
||||
assert(combat != NULL);
|
||||
|
||||
for (size_t i = 0; i < combat->length; ++i) {
|
||||
const uint64_t ed = combat->attacks[i].start + combat->attacks[i].duration;
|
||||
if (ed <= combat->ticker->time) {
|
||||
*index = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void loplayer_combat_execute_reflective_attack_(
|
||||
loplayer_combat_t* combat,
|
||||
loplayer_combat_attack_t* attack,
|
||||
loentity_character_t* chara) {
|
||||
assert(combat != NULL);
|
||||
assert(attack != NULL);
|
||||
|
||||
if (chara == NULL) return;
|
||||
|
||||
const loeffect_t dmg =
|
||||
loeffect_immediate_damage(combat->status->recipient.status.attack);
|
||||
loentity_character_apply_effect(chara, &dmg);
|
||||
|
||||
vec2_t knockback = attack->knockback;
|
||||
vec2_muleq(&knockback, -1);
|
||||
loentity_character_knockback(chara, &knockback);
|
||||
|
||||
loresource_sound_play(combat->sound, "reflection");
|
||||
}
|
||||
|
||||
static void loplayer_combat_execute_enemy_attack_(
|
||||
loplayer_combat_t* combat,
|
||||
loplayer_combat_attack_t* attack,
|
||||
loentity_character_t* chara) {
|
||||
assert(combat != NULL);
|
||||
assert(attack != NULL);
|
||||
|
||||
/* chara can be NULL */
|
||||
(void) chara;
|
||||
|
||||
loplayer_status_apply_effect(combat->status, &attack->effect);
|
||||
loentity_character_knockback(&combat->entity->super, &attack->knockback);
|
||||
}
|
||||
|
||||
static void loplayer_combat_handle_attack_(
|
||||
loplayer_combat_t* combat,
|
||||
loplayer_combat_attack_t* attack,
|
||||
loentity_character_t* chara) {
|
||||
assert(combat != NULL);
|
||||
assert(attack != NULL);
|
||||
|
||||
if (combat->guard_start < combat->guard_end) {
|
||||
const uint64_t atked = attack->start + attack->duration;
|
||||
|
||||
const uint64_t stdiff =
|
||||
MATH_MAX(attack->start, combat->guard_start) -
|
||||
MATH_MIN(attack->start, combat->guard_start);
|
||||
const uint64_t eddiff =
|
||||
MATH_MAX(atked, combat->guard_end) -
|
||||
MATH_MIN(atked, combat->guard_end);
|
||||
|
||||
const float guard_error = (stdiff + eddiff)*1.0f / attack->duration;
|
||||
|
||||
const bool back_attack =
|
||||
attack->knockback.x * combat->entity->direction > 0;
|
||||
|
||||
float guard_error_thresh = LOPLAYER_COMBAT_GUARD_ERROR_THRESHOLD;
|
||||
if (back_attack) guard_error_thresh /= 10;
|
||||
|
||||
if (guard_error < guard_error_thresh) {
|
||||
loplayer_combat_execute_reflective_attack_(combat, attack, chara);
|
||||
return;
|
||||
}
|
||||
}
|
||||
loplayer_combat_execute_enemy_attack_(combat, attack, chara);
|
||||
}
|
||||
|
||||
static void loplayer_combat_draw_ring_base_(
|
||||
const loplayer_combat_t* combat) {
|
||||
assert(combat != NULL);
|
||||
|
||||
loshader_combat_ring_drawer_add_instance(
|
||||
combat->drawer, &(loshader_combat_ring_drawer_instance_t) {
|
||||
.range = -1, /* = draw ring base */
|
||||
.color = vec4(0, 0, 0, .8f*combat->alpha),
|
||||
});
|
||||
}
|
||||
|
||||
static void loplayer_combat_draw_attacks_(const loplayer_combat_t* combat) {
|
||||
assert(combat != NULL);
|
||||
|
||||
const uint64_t ring_st = combat->ring_start;
|
||||
const uint64_t ring_ed = combat->ring_end;
|
||||
assert(ring_st <= ring_ed);
|
||||
|
||||
const uint64_t ring_dur = ring_ed - ring_st;
|
||||
|
||||
for (size_t i = 0; i < combat->length; ++i) {
|
||||
const uint64_t st = combat->attacks[i].start;
|
||||
const uint64_t ed = combat->attacks[i].duration + st;
|
||||
|
||||
if (st < ring_ed && ring_st < ed) {
|
||||
const uint64_t rst = st - MATH_MIN(ring_st, st);
|
||||
const uint64_t red = ed - ring_st;
|
||||
assert(rst <= red);
|
||||
|
||||
loshader_combat_ring_drawer_add_instance(
|
||||
combat->drawer, &(loshader_combat_ring_drawer_instance_t) {
|
||||
.range = .8f,
|
||||
.start = rst*1.f/ring_dur,
|
||||
.end = MATH_MIN(red, ring_dur)*1.f/ring_dur,
|
||||
.color = vec4(.7f, .1f, .1f, combat->alpha),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void loplayer_combat_draw_guard_(const loplayer_combat_t* combat) {
|
||||
assert(combat != NULL);
|
||||
|
||||
const bool now_guarding = (combat->guard_start > combat->guard_end);
|
||||
if (!now_guarding && combat->guard_end <= combat->ring_start) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float ring_dur = combat->ring_end - combat->ring_start;
|
||||
assert(ring_dur > 0);
|
||||
|
||||
const uint64_t st = combat->guard_start -
|
||||
MATH_MIN(combat->ring_start, combat->guard_start);
|
||||
const uint64_t ed =
|
||||
(now_guarding? combat->ticker->time: combat->guard_end) -
|
||||
combat->ring_start;
|
||||
|
||||
loshader_combat_ring_drawer_add_instance(
|
||||
combat->drawer, &(loshader_combat_ring_drawer_instance_t) {
|
||||
.range = .7f,
|
||||
.start = st/ring_dur,
|
||||
.end = ed/ring_dur,
|
||||
.color = vec4(.1f, .1f, .7f, combat->alpha),
|
||||
});
|
||||
}
|
||||
|
||||
static void loplayer_combat_draw_clockhand_(
|
||||
const loplayer_combat_t* combat) {
|
||||
assert(combat != NULL);
|
||||
|
||||
const uint64_t ring_dur = combat->ring_end - combat->ring_start;
|
||||
assert(ring_dur > 0);
|
||||
|
||||
assert(combat->ticker->time >= combat->ring_start);
|
||||
const uint64_t cur = combat->ticker->time - combat->ring_start;
|
||||
const float curf = cur*1.f/ring_dur;
|
||||
|
||||
loshader_combat_ring_drawer_add_instance(
|
||||
combat->drawer, &(loshader_combat_ring_drawer_instance_t) {
|
||||
.range = 0, /* = draw clockhand */
|
||||
.start = curf,
|
||||
.color = vec4(1, 1, 1, combat->alpha),
|
||||
});
|
||||
}
|
||||
|
||||
loplayer_combat_t* loplayer_combat_new(
|
||||
loresource_sound_t* sound,
|
||||
loshader_combat_ring_drawer_t* drawer,
|
||||
const locommon_ticker_t* ticker,
|
||||
loentity_store_t* entities,
|
||||
loplayer_status_t* status,
|
||||
loplayer_entity_t* entity,
|
||||
size_t length) {
|
||||
assert(sound != NULL);
|
||||
assert(drawer != NULL);
|
||||
assert(ticker != NULL);
|
||||
assert(entities != NULL);
|
||||
assert(status != NULL);
|
||||
assert(entity != NULL);
|
||||
assert(length > 0);
|
||||
|
||||
loplayer_combat_t* combat = memory_new(
|
||||
sizeof(*combat) + (length-1)*sizeof(combat->attacks[0]));
|
||||
*combat = (typeof(*combat)) {
|
||||
.sound = sound,
|
||||
.drawer = drawer,
|
||||
.ticker = ticker,
|
||||
.entities = entities,
|
||||
.status = status,
|
||||
.entity = entity,
|
||||
.length = length,
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < combat->length; ++i) {
|
||||
combat->attacks[i] = (typeof(combat->attacks[0])) {0};
|
||||
}
|
||||
return combat;
|
||||
}
|
||||
|
||||
void loplayer_combat_delete(loplayer_combat_t* combat) {
|
||||
if (combat == NULL) return;
|
||||
|
||||
memory_delete(combat);
|
||||
}
|
||||
|
||||
bool loplayer_combat_add_attack(
|
||||
loplayer_combat_t* combat, const loplayer_combat_attack_t* attack) {
|
||||
assert(combat != NULL);
|
||||
assert(attack != NULL);
|
||||
|
||||
if (loplayer_combat_find_attack_index_in_period_(
|
||||
combat, NULL, attack->start, attack->start + attack->duration)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t index;
|
||||
if (!loplayer_combat_find_unused_attack_index_(combat, &index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
combat->attacks[index] = *attack;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loplayer_combat_accept_all_attacks(loplayer_combat_t* combat) {
|
||||
assert(combat != NULL);
|
||||
|
||||
if (combat->accepted) return true;
|
||||
|
||||
if (!loplayer_combat_find_attack_index_in_period_(
|
||||
combat, NULL, combat->ticker->time, SIZE_MAX)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
combat->accepted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void loplayer_combat_drop_all_attacks(loplayer_combat_t* combat) {
|
||||
assert(combat != NULL);
|
||||
|
||||
for (size_t i = 0; i < combat->length; ++i) {
|
||||
combat->attacks[i] = (typeof(combat->attacks[0])) {0};
|
||||
}
|
||||
combat->accepted = false;
|
||||
combat->ring_start = 0;
|
||||
combat->ring_end = 0;
|
||||
combat->guard_start = 0;
|
||||
combat->guard_end = 0;
|
||||
combat->attack_end = 0;
|
||||
}
|
||||
|
||||
void loplayer_combat_guard(loplayer_combat_t* combat) {
|
||||
assert(combat != NULL);
|
||||
|
||||
if (combat->ring_end <= combat->ticker->time ||
|
||||
combat->guard_start > combat->guard_end) {
|
||||
return;
|
||||
}
|
||||
combat->guard_start = combat->ticker->time;
|
||||
|
||||
loresource_sound_play(combat->sound, "guard");
|
||||
}
|
||||
|
||||
void loplayer_combat_unguard(loplayer_combat_t* combat) {
|
||||
assert(combat != NULL);
|
||||
|
||||
if (combat->guard_start <= combat->guard_end) return;
|
||||
|
||||
combat->guard_end = combat->ticker->time;
|
||||
}
|
||||
|
||||
void loplayer_combat_update(loplayer_combat_t* combat) {
|
||||
assert(combat != NULL);
|
||||
|
||||
if (!combat->accepted) return;
|
||||
|
||||
const uint64_t cur = combat->ticker->time;
|
||||
const uint64_t pre = cur - combat->ticker->delta;
|
||||
|
||||
combat->ring_end = 0;
|
||||
for (size_t i = 0; i < combat->length; ++i) {
|
||||
loentity_store_iterator_t itr = (typeof(itr)) {0};
|
||||
|
||||
if (!loentity_store_find_item_by_id(
|
||||
combat->entities, &itr, combat->attacks[i].attacker)) {
|
||||
combat->attacks[i].start = 0;
|
||||
combat->attacks[i].duration = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint64_t st = combat->attacks[i].start;
|
||||
const uint64_t ed = combat->attacks[i].duration + st;
|
||||
|
||||
const bool pre_active = st <= pre && pre < ed;
|
||||
const bool cur_active = st <= cur && cur < ed;
|
||||
if (!pre_active && cur_active) {
|
||||
combat->attack_end = ed;
|
||||
} else if (pre_active && !cur_active) {
|
||||
loplayer_combat_handle_attack_(
|
||||
combat, &combat->attacks[i], itr.character);
|
||||
}
|
||||
combat->ring_end = MATH_MAX(combat->ring_end, ed);
|
||||
}
|
||||
if (combat->ring_end > cur) {
|
||||
if (combat->ring_start == 0) {
|
||||
combat->ring_start = cur;
|
||||
}
|
||||
locommon_easing_smooth_float(
|
||||
&combat->alpha, 1, combat->ticker->delta_f*10);
|
||||
} else {
|
||||
combat->alpha = 0;
|
||||
loplayer_combat_drop_all_attacks(combat);
|
||||
}
|
||||
}
|
||||
|
||||
void loplayer_combat_draw_ui(const loplayer_combat_t* combat) {
|
||||
assert(combat != NULL);
|
||||
|
||||
if (!combat->accepted || combat->ring_end <= combat->ticker->time) {
|
||||
return;
|
||||
}
|
||||
loplayer_combat_draw_ring_base_(combat);
|
||||
loplayer_combat_draw_attacks_(combat);
|
||||
loplayer_combat_draw_guard_(combat);
|
||||
loplayer_combat_draw_clockhand_(combat);
|
||||
}
|
||||
|
||||
void loplayer_combat_pack(
|
||||
const loplayer_combat_t* combat, msgpack_packer* packer) {
|
||||
assert(combat != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, 1);
|
||||
|
||||
mpkutil_pack_str(packer, "attacks");
|
||||
|
||||
size_t len = 0;
|
||||
for (size_t i = 0; i < combat->length; ++i) {
|
||||
const uint64_t st = combat->attacks[i].start;
|
||||
const uint64_t ed = combat->attacks[i].duration + st;
|
||||
const uint64_t cur = combat->ticker->time;
|
||||
|
||||
if (st <= cur && cur < ed) ++len;
|
||||
}
|
||||
msgpack_pack_array(packer, len);
|
||||
|
||||
for (size_t i = 0; i < combat->length; ++i) {
|
||||
const uint64_t st = combat->attacks[i].start;
|
||||
const uint64_t ed = combat->attacks[i].duration + st;
|
||||
const uint64_t cur = combat->ticker->time;
|
||||
|
||||
if (st <= cur && cur < ed) {
|
||||
loplayer_combat_attack_pack(&combat->attacks[i], packer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool loplayer_combat_unpack(
|
||||
loplayer_combat_t* combat, const msgpack_object* obj) {
|
||||
assert(combat != NULL);
|
||||
|
||||
loplayer_combat_drop_all_attacks(combat);
|
||||
|
||||
if (obj == NULL) return false;
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
const msgpack_object_array* array =
|
||||
mpkutil_get_array(mpkutil_get_map_item_by_str(root, "attacks"));
|
||||
|
||||
size_t src = 0, dst = 0;
|
||||
while (src < array->size && dst < combat->length) {
|
||||
if (loplayer_combat_attack_unpack(
|
||||
&combat->attacks[dst], &array->ptr[src++])) {
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void loplayer_combat_attack_pack(
|
||||
const loplayer_combat_attack_t* attack,
|
||||
msgpack_packer* packer) {
|
||||
assert(attack != NULL);
|
||||
assert(packer != NULL);
|
||||
|
||||
msgpack_pack_map(packer, LOPLAYER_COMBAT_ATTACK_PARAM_TO_PACK_COUNT);
|
||||
|
||||
# define pack_(name, var) do { \
|
||||
mpkutil_pack_str(packer, name); \
|
||||
LOCOMMON_MSGPACK_PACK_ANY(packer, &attack->var); \
|
||||
} while (0)
|
||||
|
||||
LOPLAYER_COMBAT_ATTACK_PARAM_TO_PACK_EACH_(pack_);
|
||||
|
||||
# undef pack_
|
||||
}
|
||||
|
||||
bool loplayer_combat_attack_unpack(
|
||||
loplayer_combat_attack_t* attack,
|
||||
const msgpack_object* obj) {
|
||||
assert(attack != NULL);
|
||||
|
||||
const msgpack_object_map* root = mpkutil_get_map(obj);
|
||||
|
||||
# define item_(v) mpkutil_get_map_item_by_str(root, v)
|
||||
|
||||
# define unpack_(name, var) do { \
|
||||
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(name), &attack->var)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LOPLAYER_COMBAT_ATTACK_PARAM_TO_PACK_EACH_(unpack_);
|
||||
return true;
|
||||
|
||||
# undef unpack_
|
||||
# undef item_
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user