[RELEASE] u22-v04

This version is submitted for U22 final presentation. (squashed 158 commits)
This commit is contained in:
falsycat 2020-10-09 00:00:00 +00:00
parent 84c3a02b9a
commit 80b3b82332
277 changed files with 12154 additions and 13836 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
/.build
/.rbuild
*.swp

5
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "thirdparty/miniaudio"]
path = thirdparty/miniaudio/repo
url = https://github.com/dr-soft/miniaudio
url = https://github.com/mackron/miniaudio
[submodule "thirdparty/tinycthread"]
path = thirdparty/tinycthread
url = https://github.com/tinycthread/tinycthread

View File

@ -2,35 +2,37 @@ cmake_minimum_required(VERSION 3.16)
project(leftone C)
if (BUILD_TESTING)
enable_testing()
endif()
include(CTest)
set(LEFTONE_C_FLAGS
-Werror -Wall -Wextra -Wno-missing-field-initializers
$<$<CONFIG:Release>:-Wno-unused-parameter>
$<$<PLATFORM_ID:Windows>:-mconsole>
)
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)
set(SDL2_TARGET SDL2::SDL2)
if (NOT TARGET SDL2::SDL2)
set(SDL2_TARGET ${SDL2_LIBRARIES})
endif()
include_directories(SYSTEM
${FREETYPE_INCLUDE_DIRS}
${GLEW_INCLUDE_DIRS}
${OPENGL_INCLUDE_DIR}
${SDL2_INCLUDE_DIRS}
)
include_directories(.)
include_directories(. ${CMAKE_CURRENT_BINARY_DIR})
include(cmake/anysrc.cmake)
include(cmake/benum.cmake)
include(cmake/crial.cmake)
include(cmake/sos.cmake)
add_subdirectory(app)

361
LICENSE Normal file
View File

@ -0,0 +1,361 @@
Creative Commons Legal Code
Attribution-NonCommercial-ShareAlike 3.0 Unported
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
DAMAGES RESULTING FROM ITS USE.
License
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
CONDITIONS.
1. Definitions
a. "Adaptation" means a work based upon the Work, or upon the Work and
other pre-existing works, such as a translation, adaptation,
derivative work, arrangement of music or other alterations of a
literary or artistic work, or phonogram or performance and includes
cinematographic adaptations or any other form in which the Work may be
recast, transformed, or adapted including in any form recognizably
derived from the original, except that a work that constitutes a
Collection will not be considered an Adaptation for the purpose of
this License. For the avoidance of doubt, where the Work is a musical
work, performance or phonogram, the synchronization of the Work in
timed-relation with a moving image ("synching") will be considered an
Adaptation for the purpose of this License.
b. "Collection" means a collection of literary or artistic works, such as
encyclopedias and anthologies, or performances, phonograms or
broadcasts, or other works or subject matter other than works listed
in Section 1(g) below, which, by reason of the selection and
arrangement of their contents, constitute intellectual creations, in
which the Work is included in its entirety in unmodified form along
with one or more other contributions, each constituting separate and
independent works in themselves, which together are assembled into a
collective whole. A work that constitutes a Collection will not be
considered an Adaptation (as defined above) for the purposes of this
License.
c. "Distribute" means to make available to the public the original and
copies of the Work or Adaptation, as appropriate, through sale or
other transfer of ownership.
d. "License Elements" means the following high-level license attributes
as selected by Licensor and indicated in the title of this License:
Attribution, Noncommercial, ShareAlike.
e. "Licensor" means the individual, individuals, entity or entities that
offer(s) the Work under the terms of this License.
f. "Original Author" means, in the case of a literary or artistic work,
the individual, individuals, entity or entities who created the Work
or if no individual or entity can be identified, the publisher; and in
addition (i) in the case of a performance the actors, singers,
musicians, dancers, and other persons who act, sing, deliver, declaim,
play in, interpret or otherwise perform literary or artistic works or
expressions of folklore; (ii) in the case of a phonogram the producer
being the person or legal entity who first fixes the sounds of a
performance or other sounds; and, (iii) in the case of broadcasts, the
organization that transmits the broadcast.
g. "Work" means the literary and/or artistic work offered under the terms
of this License including without limitation any production in the
literary, scientific and artistic domain, whatever may be the mode or
form of its expression including digital form, such as a book,
pamphlet and other writing; a lecture, address, sermon or other work
of the same nature; a dramatic or dramatico-musical work; a
choreographic work or entertainment in dumb show; a musical
composition with or without words; a cinematographic work to which are
assimilated works expressed by a process analogous to cinematography;
a work of drawing, painting, architecture, sculpture, engraving or
lithography; a photographic work to which are assimilated works
expressed by a process analogous to photography; a work of applied
art; an illustration, map, plan, sketch or three-dimensional work
relative to geography, topography, architecture or science; a
performance; a broadcast; a phonogram; a compilation of data to the
extent it is protected as a copyrightable work; or a work performed by
a variety or circus performer to the extent it is not otherwise
considered a literary or artistic work.
h. "You" means an individual or entity exercising rights under this
License who has not previously violated the terms of this License with
respect to the Work, or who has received express permission from the
Licensor to exercise rights under this License despite a previous
violation.
i. "Publicly Perform" means to perform public recitations of the Work and
to communicate to the public those public recitations, by any means or
process, including by wire or wireless means or public digital
performances; to make available to the public Works in such a way that
members of the public may access these Works from a place and at a
place individually chosen by them; to perform the Work to the public
by any means or process and the communication to the public of the
performances of the Work, including by public digital performance; to
broadcast and rebroadcast the Work by any means including signs,
sounds or images.
j. "Reproduce" means to make copies of the Work by any means including
without limitation by sound or visual recordings and the right of
fixation and reproducing fixations of the Work, including storage of a
protected performance or phonogram in digital form or other electronic
medium.
2. Fair Dealing Rights. Nothing in this License is intended to reduce,
limit, or restrict any uses free from copyright or rights arising from
limitations or exceptions that are provided for in connection with the
copyright protection under copyright law or other applicable laws.
3. License Grant. Subject to the terms and conditions of this License,
Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
perpetual (for the duration of the applicable copyright) license to
exercise the rights in the Work as stated below:
a. to Reproduce the Work, to incorporate the Work into one or more
Collections, and to Reproduce the Work as incorporated in the
Collections;
b. to create and Reproduce Adaptations provided that any such Adaptation,
including any translation in any medium, takes reasonable steps to
clearly label, demarcate or otherwise identify that changes were made
to the original Work. For example, a translation could be marked "The
original work was translated from English to Spanish," or a
modification could indicate "The original work has been modified.";
c. to Distribute and Publicly Perform the Work including as incorporated
in Collections; and,
d. to Distribute and Publicly Perform Adaptations.
The above rights may be exercised in all media and formats whether now
known or hereafter devised. The above rights include the right to make
such modifications as are technically necessary to exercise the rights in
other media and formats. Subject to Section 8(f), all rights not expressly
granted by Licensor are hereby reserved, including but not limited to the
rights described in Section 4(e).
4. Restrictions. The license granted in Section 3 above is expressly made
subject to and limited by the following restrictions:
a. You may Distribute or Publicly Perform the Work only under the terms
of this License. You must include a copy of, or the Uniform Resource
Identifier (URI) for, this License with every copy of the Work You
Distribute or Publicly Perform. You may not offer or impose any terms
on the Work that restrict the terms of this License or the ability of
the recipient of the Work to exercise the rights granted to that
recipient under the terms of the License. You may not sublicense the
Work. You must keep intact all notices that refer to this License and
to the disclaimer of warranties with every copy of the Work You
Distribute or Publicly Perform. When You Distribute or Publicly
Perform the Work, You may not impose any effective technological
measures on the Work that restrict the ability of a recipient of the
Work from You to exercise the rights granted to that recipient under
the terms of the License. This Section 4(a) applies to the Work as
incorporated in a Collection, but this does not require the Collection
apart from the Work itself to be made subject to the terms of this
License. If You create a Collection, upon notice from any Licensor You
must, to the extent practicable, remove from the Collection any credit
as required by Section 4(d), as requested. If You create an
Adaptation, upon notice from any Licensor You must, to the extent
practicable, remove from the Adaptation any credit as required by
Section 4(d), as requested.
b. You may Distribute or Publicly Perform an Adaptation only under: (i)
the terms of this License; (ii) a later version of this License with
the same License Elements as this License; (iii) a Creative Commons
jurisdiction license (either this or a later license version) that
contains the same License Elements as this License (e.g.,
Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License").
You must include a copy of, or the URI, for Applicable License with
every copy of each Adaptation You Distribute or Publicly Perform. You
may not offer or impose any terms on the Adaptation that restrict the
terms of the Applicable License or the ability of the recipient of the
Adaptation to exercise the rights granted to that recipient under the
terms of the Applicable License. You must keep intact all notices that
refer to the Applicable License and to the disclaimer of warranties
with every copy of the Work as included in the Adaptation You
Distribute or Publicly Perform. When You Distribute or Publicly
Perform the Adaptation, You may not impose any effective technological
measures on the Adaptation that restrict the ability of a recipient of
the Adaptation from You to exercise the rights granted to that
recipient under the terms of the Applicable License. This Section 4(b)
applies to the Adaptation as incorporated in a Collection, but this
does not require the Collection apart from the Adaptation itself to be
made subject to the terms of the Applicable License.
c. You may not exercise any of the rights granted to You in Section 3
above in any manner that is primarily intended for or directed toward
commercial advantage or private monetary compensation. The exchange of
the Work for other copyrighted works by means of digital file-sharing
or otherwise shall not be considered to be intended for or directed
toward commercial advantage or private monetary compensation, provided
there is no payment of any monetary compensation in con-nection with
the exchange of copyrighted works.
d. If You Distribute, or Publicly Perform the Work or any Adaptations or
Collections, You must, unless a request has been made pursuant to
Section 4(a), keep intact all copyright notices for the Work and
provide, reasonable to the medium or means You are utilizing: (i) the
name of the Original Author (or pseudonym, if applicable) if supplied,
and/or if the Original Author and/or Licensor designate another party
or parties (e.g., a sponsor institute, publishing entity, journal) for
attribution ("Attribution Parties") in Licensor's copyright notice,
terms of service or by other reasonable means, the name of such party
or parties; (ii) the title of the Work if supplied; (iii) to the
extent reasonably practicable, the URI, if any, that Licensor
specifies to be associated with the Work, unless such URI does not
refer to the copyright notice or licensing information for the Work;
and, (iv) consistent with Section 3(b), in the case of an Adaptation,
a credit identifying the use of the Work in the Adaptation (e.g.,
"French translation of the Work by Original Author," or "Screenplay
based on original Work by Original Author"). The credit required by
this Section 4(d) may be implemented in any reasonable manner;
provided, however, that in the case of a Adaptation or Collection, at
a minimum such credit will appear, if a credit for all contributing
authors of the Adaptation or Collection appears, then as part of these
credits and in a manner at least as prominent as the credits for the
other contributing authors. For the avoidance of doubt, You may only
use the credit required by this Section for the purpose of attribution
in the manner set out above and, by exercising Your rights under this
License, You may not implicitly or explicitly assert or imply any
connection with, sponsorship or endorsement by the Original Author,
Licensor and/or Attribution Parties, as appropriate, of You or Your
use of the Work, without the separate, express prior written
permission of the Original Author, Licensor and/or Attribution
Parties.
e. For the avoidance of doubt:
i. Non-waivable Compulsory License Schemes. In those jurisdictions in
which the right to collect royalties through any statutory or
compulsory licensing scheme cannot be waived, the Licensor
reserves the exclusive right to collect such royalties for any
exercise by You of the rights granted under this License;
ii. Waivable Compulsory License Schemes. In those jurisdictions in
which the right to collect royalties through any statutory or
compulsory licensing scheme can be waived, the Licensor reserves
the exclusive right to collect such royalties for any exercise by
You of the rights granted under this License if Your exercise of
such rights is for a purpose or use which is otherwise than
noncommercial as permitted under Section 4(c) and otherwise waives
the right to collect royalties through any statutory or compulsory
licensing scheme; and,
iii. Voluntary License Schemes. The Licensor reserves the right to
collect royalties, whether individually or, in the event that the
Licensor is a member of a collecting society that administers
voluntary licensing schemes, via that society, from any exercise
by You of the rights granted under this License that is for a
purpose or use which is otherwise than noncommercial as permitted
under Section 4(c).
f. Except as otherwise agreed in writing by the Licensor or as may be
otherwise permitted by applicable law, if You Reproduce, Distribute or
Publicly Perform the Work either by itself or as part of any
Adaptations or Collections, You must not distort, mutilate, modify or
take other derogatory action in relation to the Work which would be
prejudicial to the Original Author's honor or reputation. Licensor
agrees that in those jurisdictions (e.g. Japan), in which any exercise
of the right granted in Section 3(b) of this License (the right to
make Adaptations) would be deemed to be a distortion, mutilation,
modification or other derogatory action prejudicial to the Original
Author's honor and reputation, the Licensor will waive or not assert,
as appropriate, this Section, to the fullest extent permitted by the
applicable national law, to enable You to reasonably exercise Your
right under Section 3(b) of this License (right to make Adaptations)
but not otherwise.
5. Representations, Warranties and Disclaimer
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE
FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS
AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE
WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT
LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT
DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED
WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU.
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. Termination
a. This License and the rights granted hereunder will terminate
automatically upon any breach by You of the terms of this License.
Individuals or entities who have received Adaptations or Collections
from You under this License, however, will not have their licenses
terminated provided such individuals or entities remain in full
compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
survive any termination of this License.
b. Subject to the above terms and conditions, the license granted here is
perpetual (for the duration of the applicable copyright in the Work).
Notwithstanding the above, Licensor reserves the right to release the
Work under different license terms or to stop distributing the Work at
any time; provided, however that any such election will not serve to
withdraw this License (or any other license that has been, or is
required to be, granted under the terms of this License), and this
License will continue in full force and effect unless terminated as
stated above.
8. Miscellaneous
a. Each time You Distribute or Publicly Perform the Work or a Collection,
the Licensor offers to the recipient a license to the Work on the same
terms and conditions as the license granted to You under this License.
b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
offers to the recipient a license to the original Work on the same
terms and conditions as the license granted to You under this License.
c. If any provision of this License is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this License, and without further action
by the parties to this agreement, such provision shall be reformed to
the minimum extent necessary to make such provision valid and
enforceable.
d. No term or provision of this License shall be deemed waived and no
breach consented to unless such waiver or consent shall be in writing
and signed by the party to be charged with such waiver or consent.
e. This License constitutes the entire agreement between the parties with
respect to the Work licensed here. There are no understandings,
agreements or representations with respect to the Work not specified
here. Licensor shall not be bound by any additional provisions that
may appear in any communication from You. This License may not be
modified without the mutual written agreement of the Licensor and You.
f. The rights granted under, and the subject matter referenced, in this
License were drafted utilizing the terminology of the Berne Convention
for the Protection of Literary and Artistic Works (as amended on
September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
and the Universal Copyright Convention (as revised on July 24, 1971).
These rights and subject matter take effect in the relevant
jurisdiction in which the License terms are sought to be enforced
according to the corresponding provisions of the implementation of
those treaty provisions in the applicable national law. If the
standard suite of rights granted under applicable copyright law
includes additional rights not granted under this License, such
additional rights are deemed to be included in the License; this
License is not intended to restrict the license of any rights under
applicable law.
Creative Commons Notice
Creative Commons is not a party to this License, and makes no warranty
whatsoever in connection with the Work. Creative Commons will not be
liable to You or any party on any legal theory for any damages
whatsoever, including without limitation any general, special,
incidental or consequential damages arising in connection to this
license. Notwithstanding the foregoing two (2) sentences, if Creative
Commons has expressly identified itself as the Licensor hereunder, it
shall have all rights and obligations of Licensor.
Except for the limited purpose of indicating to the public that the
Work is licensed under the CCPL, Creative Commons does not authorize
the use by either party of the trademark "Creative Commons" or any
related trademark or logo of Creative Commons without the prior
written consent of Creative Commons. Any permitted use will be in
compliance with Creative Commons' then-current trademark usage
guidelines, as may be published on its website or otherwise made
available upon request from time to time. For the avoidance of doubt,
this trademark restriction does not form part of this License.
Creative Commons may be contacted at https://creativecommons.org/.

39
README.md Normal file
View File

@ -0,0 +1,39 @@
# LEFTONE
LEFTONE is 2d action game got a prize on [2020 U22 Programming Contest](https://u22procon.com)
I'm noob at composing so don't expect the BGM quality. :(
## AUTHOR
falsycat
[portfolio](https://falsy.cat/) | [twitter](https://twitter.com/falsycat)
## LICENSE
***CC-BY-NC-SA-3.0***
### Libraries
- [freetype2](https://www.freetype.org/index.html) - FreeType License
- [glew](http://glew.sourceforge.net) - Modified BSD License
- [miniaudio](https://github.com/mackron/miniaudio) - public domain
- [msgpack-c](https://github.com/msgpack/msgpack-c) - Boost Software License
- [SDL2](https://www.libsdl.org/index.php) - zlib License
- [tinycthread](https://github.com/tinycthread/tinycthread) - zlib License
### Resources
- [Otologic](https://otologic.jp) - CC-BY-4.0
- [99sounds](https://99sounds.org) - [license](https://99sounds.org/license/)
- They replied me it's no problem to put their works on GitHub.
- [minimized Noto Font](https://github.com/hirofumii/Noto-Sans-CJK-JP.min) - SIL OPEN FONT LICENSE
### Why CC?
Because some of my shaders are inspired by the followings on ShaderToy, whose default license is CC-BY-NC-SA-3.0.
***YOU HAD BETTER NOT OPEN THESE LINKS BY POOR MACHINES.***
- [fractal cage - gaz](https://www.shadertoy.com/view/3l2yDd)
- [Ray Marching: Part 2 - jlfwong](https://www.shadertoy.com/view/lt33z7)
- [Raymarching - Primitives - iq](https://www.shadertoy.com/view/Xds3zN)

View File

@ -1 +1,3 @@
add_compile_options(${LEFTONE_C_FLAGS})
add_subdirectory(sdl)

View File

@ -5,7 +5,7 @@ add_executable(app-sdl
)
target_link_libraries(app-sdl
GLEW::GLEW
SDL2::SDL2
${SDL2_TARGET}
conv
parsarg

View File

@ -75,9 +75,6 @@ void app_args_parse(app_args_t* args, int argc, char** argv) {
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);

View File

@ -10,15 +10,13 @@
#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_LSHIFT? LOCOMMON_INPUT_BUTTON_DODGE: \
(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_LEFT? LOCOMMON_INPUT_BUTTON_SHOOT: \
(m) == SDL_BUTTON_RIGHT? LOCOMMON_INPUT_BUTTON_GUARD: \
0)

View File

@ -3,7 +3,9 @@
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <SDL2/SDL.h>
@ -128,7 +130,8 @@ int main(int argc, char** argv) {
app_get_dpi_(&libs, &args);
loscene_context_t* ctx = loscene_context_new(&args.scene);
loscene_context_t ctx;
loscene_context_initialize(&ctx, &args.scene);
locommon_input_t input = {
.resolution = vec2(args.scene.width, args.scene.height),
@ -147,10 +150,10 @@ int main(int argc, char** argv) {
if (!app_event_handle(&input, &e)) goto EXIT;
}
if (!loscene_context_update(ctx, &input, base_time)) goto EXIT;
if (!loscene_context_update(&ctx, &input, base_time)) goto EXIT;
glClear(GL_COLOR_BUFFER_BIT);
loscene_context_draw(ctx);
loscene_context_draw(&ctx);
SDL_GL_SwapWindow(libs.win);
# ifndef NDEBUG
@ -166,7 +169,7 @@ int main(int argc, char** argv) {
}
EXIT:
loscene_context_delete(ctx);
loscene_context_deinitialize(&ctx);
app_deinitialize_libraries_(&libs);
return 0;
return EXIT_SUCCESS;
}

View File

@ -1,8 +1,6 @@
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})
@ -12,9 +10,10 @@ function(target_any_sources target)
set(out ${CMAKE_CURRENT_BINARY_DIR}/anysrc/${path})
add_custom_command(
OUTPUT ${out}.c ${out}.h
COMMAND cat ${in} | ${bin2c} ${name} ${out}
COMMAND cat ${in} | bash ${bin2c} ${name} ${out}
DEPENDS ${path} ${bin2c}
COMMENT "converting ${path} to C header")
COMMENT "converting ${path} to C header"
VERBATIM)
target_sources(${target} PRIVATE ${out}.c ${out}.h)
endforeach()
endfunction()

29
cmake/benum.cmake Normal file
View File

@ -0,0 +1,29 @@
add_custom_target(benum-generated)
function(target_benum_sources target)
set(benum ${LEFTONE_TOOL_DIR}/benum.sh)
set(output_files "")
foreach (path ${ARGN})
get_filename_component(dirname ${path} DIRECTORY)
get_filename_component(basename ${path} NAME_WE)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/benum/${dirname})
set(in ${CMAKE_CURRENT_SOURCE_DIR}/${path})
set(out ${CMAKE_CURRENT_BINARY_DIR}/benum/${dirname}/${basename})
add_custom_command(
OUTPUT ${out}.c ${out}.h
COMMAND bash ${benum} ${out} ${in} < ${in}
DEPENDS ${path} ${benum}
COMMENT "generating benum utilities for ${path}"
VERBATIM)
target_sources(${target} PRIVATE ${out}.c)
list(APPEND output_files ${out}.c ${out}.h)
endforeach()
add_custom_target(${target}-benum-generated DEPENDS ${output_files})
add_dependencies(${target} ${target}-benum-generated)
add_dependencies(benum-generated ${target}-benum-generated)
endfunction()

19
cmake/crial.cmake Normal file
View File

@ -0,0 +1,19 @@
function(target_crial_sources target)
set(crial ${LEFTONE_TOOL_DIR}/crial.sh)
foreach (path ${ARGN})
get_filename_component(dirname ${path} DIRECTORY)
get_filename_component(basename ${path} NAME_WE)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/crial/${dirname})
set(in ${CMAKE_CURRENT_SOURCE_DIR}/${path})
set(out ${CMAKE_CURRENT_BINARY_DIR}/crial/${dirname}/${basename}.h)
add_custom_command(
OUTPUT ${out}
COMMAND bash ${crial} < ${in} > ${out}
DEPENDS ${path} ${crial}
COMMENT "generating serializer from ${path}"
VERBATIM)
target_sources(${target} PRIVATE ${out})
endforeach()
endfunction()

View File

@ -1,20 +1,22 @@
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)
foreach (path ${ARGN})
get_filename_component(dirname ${path} DIRECTORY)
get_filename_component(basename ${path} NAME_WE)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/sos/${dirname})
set(sos_target sos-${target}-${name})
set(in ${CMAKE_CURRENT_SOURCE_DIR}/${file})
set(out ${CMAKE_CURRENT_BINARY_DIR}/sos/${file})
set(sos_target ${target}-${basename}-sos-generator)
set(in ${CMAKE_CURRENT_SOURCE_DIR}/${path})
set(out ${CMAKE_CURRENT_BINARY_DIR}/sos/${path})
add_executable(${sos_target} ${in})
add_custom_command(
OUTPUT ${out}
COMMAND ${sos_target} > ${out}
DEPENDS ${sos_target}
COMMENT "generating ${file}")
COMMENT "generating ${path}"
VERBATIM)
target_sources(${target} PRIVATE ${out})
endforeach()
endfunction()

View File

@ -1,13 +1,15 @@
include_directories(.)
add_compile_options(${LEFTONE_C_FLAGS})
add_subdirectory(lobullet)
add_subdirectory(locharacter)
add_subdirectory(lochara)
add_subdirectory(locommon)
add_subdirectory(loeffect)
add_subdirectory(loentity)
add_subdirectory(loground)
add_subdirectory(loparticle)
add_subdirectory(loplayer)
add_subdirectory(loresource)
add_subdirectory(loscene)
add_subdirectory(loshader)
add_subdirectory(loui)
add_subdirectory(loworld)

View File

@ -1,10 +1,14 @@
add_library(lobullet
base.c
bomb.c
linear.c
misc.c
pool.c
)
target_benum_sources(lobullet
type.h
)
target_crial_sources(lobullet
base.crial
)
target_link_libraries(lobullet
msgpackc

View File

@ -12,6 +12,7 @@
#include "util/mpkutil/get.h"
#include "util/mpkutil/pack.h"
#include "core/locommon/msgpack.h"
#include "core/locommon/position.h"
#include "core/locommon/ticker.h"
#include "core/loentity/bullet.h"
@ -20,10 +21,20 @@
#include "core/loentity/store.h"
#include "core/loresource/set.h"
#include "core/loshader/bullet.h"
#include "core/loshader/set.h"
#include "./bomb.h"
#include "./linear.h"
#include "./misc.h"
#include "./type.h"
/* generated serializer */
#include "core/lobullet/crial/base.h"
static bool
(*const update_function_vtable_[LOBULLET_TYPE_COUNT])(lobullet_base_t* base) = {
[LOBULLET_TYPE_LINEAR_CIRCLE] = lobullet_linear_circle_update,
[LOBULLET_TYPE_LINEAR_TRIANGLE] = lobullet_linear_triangle_update,
[LOBULLET_TYPE_LINEAR_SQUARE] = lobullet_linear_square_update,
};
static void lobullet_base_delete_(loentity_t* entity) {
assert(entity != NULL);
@ -32,18 +43,6 @@ static void lobullet_base_delete_(loentity_t* 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) {
@ -55,18 +54,22 @@ static bool lobullet_base_update_(loentity_t* entity) {
assert(entity != NULL);
lobullet_base_t* base = (typeof(base)) entity;
base->cache = (typeof(base->cache)) {0};
base->super.owner = base->param.owner;
base->super.velocity = vec2(0, 0);
# define each_(NAME, name) do { \
if (base->type == LOBULLET_TYPE_##NAME) { \
return lobullet_##name##_update(base); \
} \
} while (0)
const locommon_position_t oldpos = base->super.super.pos;
LOBULLET_TYPE_EACH_(each_);
return false;
# undef each_
assert(update_function_vtable_[base->param.type] != NULL);
if (!update_function_vtable_[base->param.type](base)) {
return false;
}
if (base->cache.velocity_calc) {
locommon_position_sub(
&base->super.velocity, &base->super.super.pos, &oldpos);
}
return true;
}
static void lobullet_base_draw_(
@ -80,7 +83,8 @@ static void lobullet_base_draw_(
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);
loshader_bullet_drawer_add_instance(
&base->shaders->drawer.bullet, &base->cache.instance);
}
static void lobullet_base_pack_(
@ -90,29 +94,8 @@ static void lobullet_base_pack_(
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_
msgpack_pack_map(packer, CRIAL_PROPERTY_COUNT_);
CRIAL_SERIALIZER_;
}
static bool lobullet_base_affect_(
@ -122,57 +105,31 @@ static bool lobullet_base_affect_(
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);
}
locommon_position_sub(&v, &chara->super.pos, &base->super.super.pos);
vec2_muleq(&v, base->cache.knockback);
loentity_character_knockback(chara, &v);
if (base->cache.toxic) {
loentity_character_apply_effect(chara, &base->cache.effect);
loentity_character_apply_effect(chara, &base->param.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) {
lobullet_base_t* base,
loresource_set_t* res,
loshader_set_t* shaders,
const locommon_ticker_t* ticker,
loentity_store_t* entities) {
assert(base != NULL);
assert(res != NULL);
assert(drawer != NULL);
assert(shaders != 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,
.shaders = shaders,
.ticker = ticker,
.entities = entities,
};
@ -180,12 +137,31 @@ void lobullet_base_initialize(
void lobullet_base_reinitialize(lobullet_base_t* base, loentity_id_t id) {
assert(base != NULL);
assert(!base->used);
base->super.super.id = id;
base->super = (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_,
},
.id = id,
.subclass = LOENTITY_SUBCLASS_BULLET,
},
.vtable = {
.affect = lobullet_base_affect_,
},
};
base->param = (typeof(base->param)) {0};
}
void lobullet_base_deinitialize(lobullet_base_t* base) {
assert(base != NULL);
assert(!base->used);
lobullet_base_delete_(&base->super.super);
}
@ -195,41 +171,13 @@ bool lobullet_base_unpack(lobullet_base_t* base, const msgpack_object* obj) {
lobullet_base_reinitialize(base, 0);
const char* v;
size_t vlen;
const msgpack_object_map* root = mpkutil_get_map(obj);
if (root == NULL) goto FAIL;
# 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_
CRIAL_DESERIALIZER_;
return true;
FAIL:
lobullet_base_delete_(&base->super.super);
return false;
}

44
core/lobullet/base.crial Normal file
View File

@ -0,0 +1,44 @@
/* CRIAL
SERIALIZER_BEGIN
mpkutil_pack_str(packer, "$name");
mpkutil_pack_str(packer, $code);
END
DESERIALIZER_BEGIN
const char* v;
size_t vlen;
if (!mpkutil_get_str(
mpkutil_get_map_item_by_str(root, "$name"), &v, &vlen) ||
strncmp(v, $code, vlen) != 0 || $code[vlen] != 0) {
goto FAIL;
}
END
PROPERTY subclass = "bullet"
SERIALIZER_BEGIN
mpkutil_pack_str(packer, "$name");
LOCOMMON_MSGPACK_PACK_ANY(packer, &base->$code);
END
DESERIALIZER_BEGIN
if (!LOCOMMON_MSGPACK_UNPACK_ANY(
mpkutil_get_map_item_by_str(root, "$name"), &base->$code)) {
goto FAIL;
}
END
PROPERTY id = super.super.id
PROPERTY pos = super.super.pos
PROPERTY owner = param.owner
PROPERTY type = param.type
PROPERTY target = param.target
PROPERTY basepos = param.basepos
PROPERTY size = param.size
PROPERTY color = param.color
PROPERTY velocity = param.velocity
PROPERTY acceleration = param.acceleration
PROPERTY angle = param.angle
PROPERTY angular_velocity = param.angular_velocity
PROPERTY quiet = param.quiet
PROPERTY knockback = param.knockback
PROPERTY effect = param.effect
PROPERTY since = param.since
PROPERTY duration = param.duration
*/

View File

@ -13,54 +13,61 @@
#include "core/loentity/store.h"
#include "core/loresource/set.h"
#include "core/loshader/bullet.h"
#include "core/loshader/set.h"
#include "./misc.h"
#include "./type.h"
typedef enum {
LOBULLET_BASE_KNOCKBACK_ALGORITHM_VELOCITY,
LOBULLET_BASE_KNOCKBACK_ALGORITHM_POSITION,
} lobullet_base_knockback_algorithm_t;
typedef struct {
lobullet_type_t type;
loentity_id_t owner;
loentity_id_t target;
locommon_position_t basepos;
vec2_t size;
vec4_t color;
vec2_t velocity;
vec2_t acceleration;
float angle;
float angular_velocity;
bool quiet;
float knockback;
loeffect_t effect;
uint64_t since;
uint64_t duration;
} lobullet_base_param_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;
loresource_set_t* res;
loshader_set_t* shaders;
const locommon_ticker_t* ticker;
loentity_store_t* entities;
lobullet_base_param_t param;
/* 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;
bool toxic;
float knockback;
bool velocity_calc;
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
lobullet_base_t* base,
loresource_set_t* res,
loshader_set_t* shaders,
const locommon_ticker_t* ticker,
loentity_store_t* entities
);
void

View File

@ -1,205 +0,0 @@
#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(&param->pos) &&
vec2_valid(&param->size) &&
MATH_FLOAT_VALID(param->angle) &&
vec4_valid(&param->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;
}

View File

@ -1,82 +0,0 @@
#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)

View File

@ -1,197 +1,100 @@
#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/algorithm.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 "core/loshader/bullet.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);
#define FADE_DURATION_ 200
static bool lobullet_linear_update_(lobullet_base_t* base) {
assert(base != NULL);
const lobullet_linear_param_t* p = (typeof(p)) base->data;
const uint64_t t = base->ticker->time - base->param.since;
const float tf = t/1000.f;
const float t = (base->ticker->time - p->since)/1000.f;
base->super.owner = p->owner;
if (t >= base->param.duration) {
return false;
}
const uint64_t rt = base->param.duration - t;
const float alpha = rt < FADE_DURATION_? rt*1.f/FADE_DURATION_: 1;
/* ---- movement ---- */
vec2_t v1;
vec2_mul(&v1, &p->velocity, t);
vec2_mul(&v1, &base->param.velocity, tf);
vec2_t v2;
vec2_mul(&v2, &p->acceleration, t*t/2);
vec2_mul(&v2, &base->param.acceleration, tf*tf/2);
base->super.super.pos = p->pos;
base->super.super.pos = base->param.basepos;
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);
vec2_mul(&base->super.velocity, &base->param.acceleration, tf);
vec2_addeq(&base->super.velocity, &base->param.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;
/* ---- parameters ---- */
const float angle = base->param.angle + base->param.angular_velocity*tf;
base->cache = (typeof(base->cache)) {
.toxic = true,
.knockback = base->param.knockback,
/* ---- 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,
.instance = {
.size = base->param.size,
.theta = angle,
.color = base->param.color,
.time = alpha,
},
};
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(&param->pos) &&
vec2_valid(&param->size) &&
vec2_valid(&param->velocity) &&
vec2_valid(&param->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->super.shape = (coly2d_shape_t) {
.size = base->param.size,
.angle = angle,
};
base->cache.instance.color.w = alpha;
return true;
}
bool lobullet_linear_circle_update(lobullet_base_t* base) {
assert(base != NULL);
if (!lobullet_linear_update_(base)) return false;
base->cache.instance.bullet_id = LOSHADER_BULLET_ID_LIGHT;
base->super.shape.type = COLY2D_SHAPE_TYPE_ELLIPSE;
return true;
}
bool lobullet_linear_triangle_update(lobullet_base_t* base) {
assert(base != NULL);
if (!lobullet_linear_update_(base)) return false;
base->cache.instance.bullet_id = LOSHADER_BULLET_ID_TRIANGLE;
base->super.shape.type = COLY2D_SHAPE_TYPE_TRIANGLE;
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;
}
bool lobullet_linear_square_update(lobullet_base_t* base) {
assert(base != NULL);
if (!lobullet_linear_update_(base)) return false;
base->cache.instance.bullet_id = LOSHADER_BULLET_ID_SQUARE;
base->super.shape.type = COLY2D_SHAPE_TYPE_RECT;
return true;
}
void lobullet_linear_build_(
lobullet_base_t* base,
const lobullet_base_param_t* param) {
assert(base != NULL);
assert(param != NULL);
base->super.super.pos = param->basepos;
base->param = *param;
base->param.since = base->ticker->time;
}

View File

@ -1,81 +1,51 @@
#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_linear_circle_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)
bool
lobullet_linear_square_update(
lobullet_base_t* base
);
void
lobullet_linear_build_(
lobullet_base_t* base,
const lobullet_base_param_t* param
);
#define lobullet_linear_circle_build(base, ...) \
lobullet_linear_build_( \
base, \
&(lobullet_base_param_t) { \
.type = LOBULLET_TYPE_LINEAR_CIRCLE, \
__VA_ARGS__ \
})
#define lobullet_linear_triangle_build(base, ...) \
lobullet_linear_build_( \
base, \
&(lobullet_base_param_t) { \
.type = LOBULLET_TYPE_LINEAR_TRIANGLE, \
__VA_ARGS__ \
})
#define lobullet_linear_square_build(base, ...) \
lobullet_linear_build_( \
base, \
&(lobullet_base_param_t) { \
.type = LOBULLET_TYPE_LINEAR_SQUARE, \
__VA_ARGS__ \
})

View File

@ -1,37 +0,0 @@
#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_
}

View File

@ -1,31 +0,0 @@
#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
);

View File

@ -8,43 +8,24 @@
#include "core/locommon/counter.h"
#include "core/locommon/ticker.h"
#include "core/loentity/pool.h"
#include "core/loentity/store.h"
#include "core/loresource/set.h"
#include "core/loshader/bullet.h"
#include "core/loshader/set.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();
}
LOENTITY_POOL_SOURCE_TEMPLATE(lobullet)
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) {
loresource_set_t* res,
loshader_set_t* shaders,
locommon_counter_t* idgen,
const locommon_ticker_t* ticker,
loentity_store_t* entities,
size_t length) {
assert(res != NULL);
assert(drawer != NULL);
assert(shaders != NULL);
assert(idgen != NULL);
assert(ticker != NULL);
assert(entities != NULL);
@ -53,52 +34,16 @@ lobullet_pool_t* lobullet_pool_new(
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,
.idgen = idgen,
.length = length,
};
for (size_t i = 0; i < pool->length; ++i) {
lobullet_base_initialize(
&pool->items[i],
res,
drawer,
shaders,
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];
}

View File

@ -8,7 +8,7 @@
#include "core/locommon/ticker.h"
#include "core/loentity/store.h"
#include "core/loresource/set.h"
#include "core/loshader/bullet.h"
#include "core/loshader/set.h"
#include "./base.h"
@ -17,12 +17,12 @@ 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
loresource_set_t* res,
loshader_set_t* shaders,
locommon_counter_t* idgen,
const locommon_ticker_t* ticker,
loentity_store_t* entities,
size_t length
);
void

11
core/lobullet/type.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
typedef enum {
/* BENUM BEGIN lobullet_type */
LOBULLET_TYPE_LINEAR_CIRCLE,
LOBULLET_TYPE_LINEAR_TRIANGLE,
LOBULLET_TYPE_LINEAR_SQUARE,
/* BENUM END */
} lobullet_type_t;
#include "core/lobullet/benum/type.h"

View File

@ -0,0 +1,37 @@
add_library(lochara
base.c
big_warder.c
cavia.c
combat.c
encephalon.c
player.c
pool.c
state.c
strategy.c
theists_child.c
warder.c
)
target_benum_sources(lochara
state.h
strategy.h
type.h
)
target_crial_sources(lochara
base.crial
)
target_link_libraries(lochara
msgpackc
chaos
math
statman
lobullet
locommon
loeffect
loentity
loplayer
loresource
loshader
)

333
core/lochara/base.c Normal file
View File

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

60
core/lochara/base.crial Normal file
View File

@ -0,0 +1,60 @@
/* CRIAL
SERIALIZER_BEGIN
mpkutil_pack_str(packer, "$name");
mpkutil_pack_str(packer, $code);
END
DESERIALIZER_BEGIN
const char* v;
size_t vlen;
if (!mpkutil_get_str(
mpkutil_get_map_item_by_str(root, "$name"), &v, &vlen) ||
strncmp(v, $code, vlen) != 0 || $code[vlen] != 0) {
goto FAIL;
}
END
PROPERTY subclass = "character"
SERIALIZER_BEGIN
mpkutil_pack_str(packer, "$name");
LOCOMMON_MSGPACK_PACK_ANY(packer, &base->$code);
END
DESERIALIZER_BEGIN
if (!LOCOMMON_MSGPACK_UNPACK_ANY(
mpkutil_get_map_item_by_str(root, "$name"), &base->$code)) {
goto FAIL;
}
END
PROPERTY id = super.super.id
PROPERTY pos = super.super.pos
PROPERTY velocity = super.velocity
PROPERTY type = param.type
PROPERTY recipient = param.recipient
PROPERTY last_state_changed = param.last_state_changed
PROPERTY last_strategy_changed = param.last_strategy_changed
PROPERTY ground = param.ground
PROPERTY on_ground = param.on_ground
PROPERTY direction = param.direction
PROPERTY movement = param.movement
PROPERTY gravity = param.gravity
PROPERTY knockback = param.knockback
PROPERTY last_knockback = param.last_knockback
PROPERTY last_bullet_hit = param.last_bullet_hit
PROPERTY anchor_pos = param.anchor.pos
PROPERTY anchor_vec = param.anchor.vec
SERIALIZER_BEGIN
const $code v = base->param.$name;
mpkutil_pack_str(packer, "$name");
LOCOMMON_MSGPACK_PACK_ANY(packer, &v);
END
DESERIALIZER_BEGIN
$code v;
if (!LOCOMMON_MSGPACK_UNPACK_ANY(
mpkutil_get_map_item_by_str(root, "$name"), &v)) {
goto FAIL;
}
base->param.$name = v;
END
PROPERTY state = lochara_state_t
PROPERTY strategy = lochara_strategy_t
*/

116
core/lochara/base.h Normal file
View File

@ -0,0 +1,116 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <msgpack.h>
#include "util/math/vector.h"
#include "util/statman/statman.h"
#include "core/lobullet/pool.h"
#include "core/locommon/ticker.h"
#include "core/loeffect/recipient.h"
#include "core/loentity/character.h"
#include "core/loentity/entity.h"
#include "core/loentity/ground.h"
#include "core/loentity/store.h"
#include "core/loplayer/player.h"
#include "core/loresource/set.h"
#include "core/loshader/set.h"
#include "./state.h"
#include "./strategy.h"
#include "./type.h"
typedef struct lochara_base_t {
loentity_character_t super;
bool used;
loresource_set_t* res;
loshader_set_t* shaders;
const locommon_ticker_t* ticker;
loentity_store_t* entities;
loplayer_t* player;
lobullet_pool_t* bullet;
struct {
lochara_type_t type;
loeffect_recipient_t recipient;
statman_state_t state; /* actual type is lochara_state_t */
uint64_t last_state_changed;
statman_state_t strategy; /* actual type is lochara_strategy_t */
uint64_t last_strategy_changed;
loentity_id_t ground;
bool on_ground;
vec2_t direction;
vec2_t movement;
float gravity;
float knockback;
uint64_t last_knockback;
uint64_t last_bullet_hit;
/* some character uses these params for some strategy */
struct {
locommon_position_t pos;
vec2_t vec;
} anchor;
} param;
struct {
loentity_ground_t* ground;
vec2_t ground_pos;
loshader_character_drawer_instance_t instance;
} cache;
} lochara_base_t;
void
lochara_base_initialize(
lochara_base_t* base,
loresource_set_t* res,
loshader_set_t* shaders,
const locommon_ticker_t* ticker,
loentity_store_t* entities,
loplayer_t* player,
lobullet_pool_t* bullet
);
void
lochara_base_reinitialize(
lochara_base_t* base,
loentity_id_t id
);
void
lochara_base_deinitialize(
lochara_base_t* base
);
void
lochara_base_calculate_physics(
lochara_base_t* base,
const vec2_t* size,
const vec2_t* offset
);
void
lochara_base_bind_on_ground(
lochara_base_t* base,
const vec2_t* offset
);
bool
lochara_base_affect_bullets(
lochara_base_t* base
);
bool
lochara_base_unpack(
lochara_base_t* base,
const msgpack_object* obj
);

728
core/lochara/big_warder.c Normal file
View File

@ -0,0 +1,728 @@
#include "./big_warder.h"
#include <stdbool.h>
#include <stdint.h>
#include "util/chaos/xorshift.h"
#include "util/math/algorithm.h"
#include "util/math/vector.h"
#include "util/statman/statman.h"
#include "core/lobullet/linear.h"
#include "core/lobullet/pool.h"
#include "core/locommon/position.h"
#include "core/loentity/ground.h"
#include "core/loplayer/event.h"
#include "core/loplayer/stance.h"
#include "core/loresource/music.h"
#include "core/loshader/character.h"
#include "./base.h"
#include "./state.h"
#include "./strategy.h"
#include "./type.h"
#define WIDTH_ .025f
#define HEIGHT_ .06f
#define MARKER_ .03f
#define COLOR_ vec4(0, 0, 0, 1)
#define BPM_ 80
#define BEAT_ (60.f/BPM_)
#define BEAT_MS_ (BEAT_*1000)
#define MUSIC_DURATION_ (BEAT_MS_*144)
#define WAKE_UP_RANGE_ (WIDTH_*8)
#define REWARD_STANCE_ LOPLAYER_STANCE_UNFINISHER
static const loeffect_recipient_status_t base_status_ = {
.attack = .2f,
.defence = .92f,
.speed = .31f,
.jump = 1.1f,
};
static void lochara_big_warder_initialize_shoot_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
lochara_state_initialize_any_(meta, instance, state);
lochara_base_t* base = instance;
vec2_t dir;
locommon_position_sub(
&dir, &base->player->entity->super.super.pos, &base->super.super.pos);
if (dir.x == 0 && dir.y == 0) dir = vec2(1, 0);
vec2_diveq(&dir, vec2_length(&dir));
const vec2_t invdir = vec2(dir.y, -dir.x);
base->param.direction = vec2(MATH_SIGN_NOZERO(dir.x), 0);
for (int32_t i = -4; i <= 4; ++i) {
vec2_t a;
vec2_mul(&a, &dir, 1.5f-MATH_ABS(i)/4.f*.5f);
vec2_t v;
vec2_mul(&v, &dir, .5f);
vec2_t p;
vec2_mul(&p, &invdir, i*.02f);
locommon_position_t pos = base->super.super.pos;
vec2_addeq(&pos.fract, &p);
locommon_position_reduce(&pos);
lobullet_base_t* b = lobullet_pool_create(base->bullet);
lobullet_linear_circle_build(b,
.owner = base->super.super.id,
.basepos = pos,
.size = vec2(.02f, .02f),
.color = vec4(1, 1, 1, 1),
.acceleration = a,
.velocity = v,
.knockback = 1,
.effect = loeffect_damage(
base->param.recipient.status.attack*.6f),
.duration = 2000,
);
loentity_store_add(base->entities, &b->super.super);
}
}
static void lochara_big_warder_update_thrust_in_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
static const uint64_t dur = BEAT_MS_/4;
lochara_base_t* base = instance;
uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t > dur) t = dur;
const float tf = t*1.f / dur;
base->cache.instance.motion.time = powf(tf, 2);
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
}
static void lochara_big_warder_update_thrust_out_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
static const uint64_t dur = BEAT_MS_/4;
lochara_base_t* base = instance;
uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t > dur) t = dur;
const float tf = t*1.f / dur;
base->cache.instance.motion.time = 1-powf(1-tf, 2);
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
}
static void lochara_big_warder_update_down_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
const bool fast =
meta->state == LOCHARA_STATE_DOWN ||
meta->state == LOCHARA_STATE_REVIVE;
const bool reverse =
meta->state == LOCHARA_STATE_REVIVE ||
meta->state == LOCHARA_STATE_RESUSCITATE;
const uint64_t dur = fast? reverse? BEAT_MS_*3: BEAT_MS_: BEAT_MS_*4;
lochara_base_t* base = instance;
base->param.movement = vec2(0, 0);
uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t > dur) t = dur;
if (reverse) t = dur - t;
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_DOWN;
base->cache.instance.motion.time = powf(t*1.f/dur, 6);
}
static const statman_meta_t state_table_[] = {
lochara_state_stand(
.period = BEAT_MS_*2,
.acceleration = {{5, 5}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_ATTACK1,
),
lochara_state_walk(
.period = BEAT_MS_,
.acceleration = {{5, 5}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_WALK,
),
lochara_state_dodge(
.duration = BEAT_MS_/2,
.speed = 1,
.acceleration = {{3, 3}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_WALK,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
),
lochara_state_teleport(
.duration = BEAT_MS_,
.offset = {{WIDTH_*2, 0}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_ATTACK2,
),
{
.state = LOCHARA_STATE_THRUST_IN,
.name = "THRUST_IN",
.initialize = lochara_state_initialize_any_,
.update = lochara_big_warder_update_thrust_in_state_,
},
{
.state = LOCHARA_STATE_THRUST_OUT,
.name = "THRUST_OUT",
.initialize = lochara_state_initialize_any_,
.update = lochara_big_warder_update_thrust_out_state_,
},
{
.state = LOCHARA_STATE_SHOOT,
.name = "SHOOT",
.initialize = lochara_big_warder_initialize_shoot_state_,
.update = lochara_big_warder_update_thrust_in_state_,
},
{
.state = LOCHARA_STATE_DOWN,
.name = "DOWN",
.initialize = lochara_state_initialize_any_,
.update = lochara_big_warder_update_down_state_,
},
{
.state = LOCHARA_STATE_REVIVE,
.name = "REVIVE",
.initialize = lochara_state_initialize_any_,
.update = lochara_big_warder_update_down_state_,
},
{
.state = LOCHARA_STATE_DEAD,
.name = "DEAD",
.initialize = lochara_state_initialize_any_,
.update = lochara_big_warder_update_down_state_,
},
{
.state = LOCHARA_STATE_RESUSCITATE,
.name = "RESUSCITATE",
.initialize = lochara_state_initialize_any_,
.update = lochara_big_warder_update_down_state_,
},
{0},
};
static void lochara_big_warder_update_wait_strategy_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
static const float range2 = WAKE_UP_RANGE_*WAKE_UP_RANGE_;
lochara_base_t* base = instance;
vec2_t disp;
locommon_position_sub(
&disp, &base->player->entity->super.super.pos, &base->super.super.pos);
if (fabsf(disp.y) < HEIGHT_/2 && vec2_pow_length(&disp) < range2) {
loeffect_recipient_apply_effect(
&base->param.recipient, &loeffect_resuscitate());
if (loplayer_stance_set_has(&base->player->stances, REWARD_STANCE_)) {
*next = LOCHARA_STRATEGY_WAKE_UP;
} else {
*next = LOCHARA_STRATEGY_WAKE_UP_EVENT;
}
return;
}
statman_transition_to(
state_table_, instance, &base->param.state, LOCHARA_STATE_DEAD);
}
static void lochara_big_warder_initialize_wake_up_strategy_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
lochara_strategy_initialize_any_(meta, instance, next);
if (meta->state != LOCHARA_STRATEGY_WAKE_UP_EVENT) return;
lochara_base_t* base = instance;
loentity_character_apply_effect(
&base->super, &loeffect_fanatic(MUSIC_DURATION_));
loentity_character_apply_effect(
&base->player->entity->super, &loeffect_curse(MUSIC_DURATION_));
}
static void lochara_big_warder_update_wake_up_strategy_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
const bool ev = meta->state == LOCHARA_STRATEGY_WAKE_UP_EVENT;
const uint64_t dur = (ev? BEAT_MS_*16: BEAT_MS_*4);
lochara_base_t* base = instance;
const uint64_t t = base->ticker->time - base->param.last_strategy_changed;
if (t >= dur) {
*next = LOCHARA_STRATEGY_APPROACH;
return;
}
statman_transition_to(
state_table_, instance, &base->param.state, LOCHARA_STATE_RESUSCITATE);
}
static void lochara_big_warder_update_approach_strategy_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
lochara_base_t* base = instance;
if (!loeffect_recipient_is_alive(&base->param.recipient)) {
*next = LOCHARA_STRATEGY_DEAD;
return;
}
const uint64_t since = base->param.last_strategy_changed;
uint64_t until = since + BEAT_MS_;
if (base->player->event.executor == base->super.super.id) {
const uint64_t msince = base->player->event.ctx.music.since;
if (msince < since) {
const uint64_t beats = (since - msince)/BEAT_MS_ + 1;
until = msince + beats*BEAT_MS_;
}
}
/* ---- strategy transition ---- */
const locommon_position_t* player = &base->player->entity->super.super.pos;
vec2_t disp;
locommon_position_sub(&disp, player, &base->super.super.pos);
if (player->chunk.x != base->super.super.pos.chunk.x ||
player->chunk.y != base->super.super.pos.chunk.y ||
disp.y < -HEIGHT_) {
*next = LOCHARA_STRATEGY_WAIT;
return;
}
const float dist = MATH_ABS(disp.x);
if (base->ticker->time >= until) {
if (MATH_ABS(disp.y) > HEIGHT_) {
*next = LOCHARA_STRATEGY_SHOOT1;
} else if (dist < WIDTH_*5) {
*next = LOCHARA_STRATEGY_COMBO1;
if (chaos_xorshift(base->ticker->time)%3 == 0) {
*next = LOCHARA_STRATEGY_COMBO2;
}
} else if (dist < WIDTH_*10) {
*next = LOCHARA_STRATEGY_COMBO2;
} else {
*next = LOCHARA_STRATEGY_SHOOT1;
}
return;
}
if (dist > WIDTH_*6) {
base->param.direction = vec2(MATH_SIGN_NOZERO(disp.x), 0);
}
}
static const lochara_combat_action_t combo1_[] = {
lochara_combat_action_rest(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_THRUST_IN,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = BEAT_MS_,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1.2f,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_DODGE_LEFT,
),
{0},
};
static const lochara_combat_action_t combo2_[] = {
lochara_combat_action_rest(
.duration = BEAT_MS_,
.state = LOCHARA_STATE_TELEPORT_FRONT,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_THRUST_IN,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_DODGE_RIGHT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_,
.state = LOCHARA_STATE_TELEPORT_BEHIND,
),
{0},
};
static const lochara_combat_action_t combo3_[] = {
lochara_combat_action_rest(
.duration = BEAT_MS_,
.state = LOCHARA_STATE_THRUST_IN,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_DODGE_RIGHT,
),
{0},
};
static const lochara_combat_action_t shoot1_[] = {
lochara_combat_action_rest(
.duration = BEAT_MS_,
.state = LOCHARA_STATE_SHOOT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_DODGE_RIGHT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_,
.state = LOCHARA_STATE_SHOOT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_DODGE_LEFT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/2*3,
.state = LOCHARA_STATE_STAND,
),
{0},
};
static const lochara_combat_action_t down_[] = {
lochara_combat_action_rest(
.duration = BEAT_MS_,
.state = LOCHARA_STATE_DOWN,
),
lochara_combat_action_rest(
.duration = BEAT_MS_*3,
.state = LOCHARA_STATE_REVIVE,
),
{0},
};
static const lochara_combat_action_t kill_[] = {
lochara_combat_action_rest(
.duration = BEAT_MS_*4,
.state = LOCHARA_STATE_DEAD,
),
{0},
};
static const lochara_combat_action_t dead_[] = {
lochara_combat_action_rest(
.duration = 30000,
.state = LOCHARA_STATE_DEAD,
),
{0},
};
static const statman_meta_t strategy_table_[] = {
{
.state = LOCHARA_STRATEGY_WAIT,
.name = "WAIT",
.initialize = lochara_strategy_initialize_any_,
.update = lochara_big_warder_update_wait_strategy_,
},
{
.state = LOCHARA_STRATEGY_WAKE_UP,
.name = "WAKE_UP",
.initialize = lochara_strategy_initialize_any_,
.update = lochara_big_warder_update_wake_up_strategy_,
},
{
.state = LOCHARA_STRATEGY_WAKE_UP_EVENT,
.name = "WAKE_UP_EVENT",
.initialize = lochara_big_warder_initialize_wake_up_strategy_,
.update = lochara_big_warder_update_wake_up_strategy_,
},
{
.state = LOCHARA_STRATEGY_APPROACH,
.name = "APPROACH",
.initialize = lochara_strategy_initialize_any_,
.update = lochara_big_warder_update_approach_strategy_,
},
lochara_strategy_combat(COMBO1,
.state_table = state_table_,
.actions = combo1_,
.parry_window = 100,
.parried_next = LOCHARA_STRATEGY_COMBO3,
.next = LOCHARA_STRATEGY_APPROACH,
),
lochara_strategy_combat(COMBO2,
.state_table = state_table_,
.actions = combo2_,
.parry_window = 100,
.parried_next = LOCHARA_STRATEGY_DOWN,
.next = LOCHARA_STRATEGY_COMBO3,
),
lochara_strategy_combat(COMBO3,
.state_table = state_table_,
.actions = combo3_,
.parried_next = LOCHARA_STRATEGY_DOWN,
.next = LOCHARA_STRATEGY_APPROACH,
),
lochara_strategy_combat(SHOOT1,
.state_table = state_table_,
.actions = shoot1_,
.parry_window = 200,
.parried_next = LOCHARA_STRATEGY_DOWN,
.gravity = true,
.next = LOCHARA_STRATEGY_APPROACH,
),
lochara_strategy_combat(DOWN,
.state_table = state_table_,
.actions = down_,
.gravity = true,
.next = LOCHARA_STRATEGY_APPROACH,
),
lochara_strategy_combat(KILL,
.state_table = state_table_,
.actions = kill_,
.gravity = true,
.next = LOCHARA_STRATEGY_WAIT,
),
lochara_strategy_combat(DEAD,
.state_table = state_table_,
.actions = dead_,
.gravity = true,
.next = LOCHARA_STRATEGY_WAIT,
),
{0},
};
static void lochara_big_warder_update_event_(lochara_base_t* base) {
assert(base != NULL);
static const loplayer_event_command_t wake_up[] = {
loplayer_event_command_play_music(LORESOURCE_MUSIC_ID_BOSS_BIG_WARDER),
loplayer_event_command_set_area(.49f, .45f),
loplayer_event_command_set_cinescope(1),
loplayer_event_command_wait(BEAT_MS_*4),
loplayer_event_command_set_line("boss_big_warder_line0"),
loplayer_event_command_wait(BEAT_MS_*8),
loplayer_event_command_set_line("boss_big_warder_line1"),
loplayer_event_command_wait(BEAT_MS_*4),
loplayer_event_command_set_line(NULL),
loplayer_event_command_set_cinescope(0),
{0},
};
static const loplayer_event_command_t kill[] = {
loplayer_event_command_set_area(0, 0),
loplayer_event_command_set_cinescope(1),
loplayer_event_command_wait(BEAT_MS_),
loplayer_event_command_stop_music(),
loplayer_event_command_set_line("boss_big_warder_kill_line"),
loplayer_event_command_wait(BEAT_MS_*4),
loplayer_event_command_finalize(),
{0},
};
static const loplayer_event_command_t dead[] = {
loplayer_event_command_set_area(0, 0),
loplayer_event_command_wait(BEAT_MS_),
loplayer_event_command_stop_music(),
loplayer_event_command_finalize(),
{0},
};
const uint64_t t = base->ticker->time;
loplayer_event_t* event = &base->player->event;
const loentity_id_t id = base->super.super.id;
locommon_position_t basepos = base->super.super.pos;
basepos.fract = vec2(.5f, .5f);
if (base->param.strategy == LOCHARA_STRATEGY_WAKE_UP_EVENT) {
loplayer_event_execute_commands(event, id, &basepos, wake_up);
return;
}
if (event->executor != id) return;
if (base->player->entity->param.state == LOCHARA_STATE_DEAD) {
statman_transition_to(
strategy_table_, base, &base->param.strategy, LOCHARA_STRATEGY_KILL);
loplayer_event_execute_commands(event, id, &basepos, kill);
return;
}
if (base->param.strategy == LOCHARA_STRATEGY_DEAD) {
loplayer_event_execute_commands(event, id, &basepos, dead);
if (!loplayer_stance_set_has(&base->player->stances, REWARD_STANCE_)) {
loplayer_stance_set_add(&base->player->stances, REWARD_STANCE_);
loplayer_popup_queue_new_stance(&base->player->popup, REWARD_STANCE_);
}
return;
}
if (base->player->event.basetime+MUSIC_DURATION_ <= t &&
loeffect_recipient_is_alive(&base->param.recipient)) {
loentity_character_apply_effect(
&base->player->entity->super, &loeffect_curse_trigger());
return;
}
loplayer_event_execute_commands(event, id, &basepos, wake_up);
}
bool lochara_big_warder_update(lochara_base_t* base) {
assert(base != NULL);
loeffect_recipient_update(&base->param.recipient, &base_status_);
statman_update(strategy_table_, base, &base->param.strategy);
lochara_big_warder_update_event_(base);
const float dir = MATH_SIGN_NOZERO(base->param.direction.x);
base->cache.instance = (loshader_character_drawer_instance_t) {
.character_id = LOSHADER_CHARACTER_ID_WARDER,
.pos = vec2(0, -MARKER_),
.size = vec2(dir*HEIGHT_, HEIGHT_),
.color = COLOR_,
.marker_offset = vec2(0, MARKER_),
};
statman_update(state_table_, base, &base->param.state);
if (base->param.strategy != LOCHARA_STRATEGY_WAIT &&
base->param.strategy != LOCHARA_STRATEGY_WAKE_UP &&
base->param.strategy != LOCHARA_STRATEGY_WAKE_UP_EVENT &&
base->param.strategy != LOCHARA_STRATEGY_DEAD) {
base->cache.instance.marker = lochara_base_affect_bullets(base);
}
lochara_base_calculate_physics(
base, &vec2(WIDTH_, HEIGHT_), &vec2(0, MARKER_));
lochara_base_bind_on_ground(base, &vec2(WIDTH_, HEIGHT_+MARKER_));
return true;
}
void lochara_big_warder_build(
lochara_base_t* base, loentity_ground_t* gnd) {
assert(base != NULL);
assert(gnd != NULL);
base->super.super.pos = gnd->super.pos;
vec2_addeq(&base->super.super.pos.fract, &vec2(0, gnd->size.y));
locommon_position_reduce(&base->super.super.pos);
base->param = (typeof(base->param)) {
.type = LOCHARA_TYPE_BIG_WARDER,
.state = LOCHARA_STATE_DEAD,
.last_state_changed = base->ticker->time,
.strategy = LOCHARA_STRATEGY_WAIT,
.last_strategy_changed = base->ticker->time,
.ground = gnd->super.id,
};
loeffect_recipient_initialize(
&base->param.recipient, base->ticker, &base_status_);
}

18
core/lochara/big_warder.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <stdbool.h>
#include "core/loentity/ground.h"
#include "./base.h"
bool
lochara_big_warder_update(
lochara_base_t* base
);
void
lochara_big_warder_build(
lochara_base_t* base,
loentity_ground_t* gnd
);

305
core/lochara/cavia.c Normal file
View File

@ -0,0 +1,305 @@
#include "./cavia.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include "util/math/algorithm.h"
#include "util/math/vector.h"
#include "util/statman/statman.h"
#include "core/lobullet/base.h"
#include "core/lobullet/linear.h"
#include "core/loeffect/effect.h"
#include "core/loeffect/recipient.h"
#include "core/loentity/ground.h"
#include "core/loresource/sound.h"
#include "core/loshader/character.h"
#include "./base.h"
#include "./combat.h"
#define WIDTH_ .02f
#define HEIGHT_ .05f
#define MARKER_ .02f
#define COLOR_ vec4(0, 0, 0, 1)
static const loeffect_recipient_status_t base_status_ = {
.attack = .1f,
.defence = .3f,
.speed = .05f,
.jump = 1.f,
};
static void lochara_cavia_update_thrust_in_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
static const uint64_t dur = 100;
lochara_base_t* base = instance;
uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t > dur) t = dur;
const float tf = t*1.f / dur;
base->cache.instance.motion.time = powf(tf, 2);
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
}
static void lochara_cavia_update_thrust_out_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
static const uint64_t dur = 100;
lochara_base_t* base = instance;
uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t > dur) t = dur;
const float tf = t*1.f / dur;
base->cache.instance.motion.time = powf(tf, 2);
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
}
static void lochara_cavia_initialize_dead_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
lochara_state_initialize_any_(meta, instance, state);
lochara_base_t* base = instance;
for (size_t i = 0; i < 12; ++i) {
const float t = MATH_PI/6*i;
vec2_t v, a, p;
p = vec2(cos(t), sin(t));
vec2_mul(&v, &p, .7f);
vec2_mul(&a, &p, -1.4f);
vec2_muleq(&p, .1f);
locommon_position_t pos = base->super.super.pos;
vec2_addeq(&pos.fract, &p);
locommon_position_reduce(&pos);
lobullet_base_t* b = lobullet_pool_create(base->bullet);
lobullet_linear_triangle_build(b,
.owner = b->super.super.id,
.basepos = pos,
.velocity = v,
.acceleration = a,
.size = vec2(.01f, .045f),
.color = vec4(0, 0, 0, .8f),
.angle = t-MATH_PI,
.knockback = 1,
.effect = loeffect_damage(.1f),
.duration = 1200,
);
loentity_store_add(base->entities, &b->super.super);
}
loresource_sound_set_play(&base->res->sound, LORESOURCE_SOUND_ID_ENEMY_TRIGGER);
}
static void lochara_cavia_update_dead_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
lochara_base_t* base = instance;
base->param.movement = vec2(0, 0);
uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t > 2000) {
t = 2000;
}
if (meta->state == LOCHARA_STATE_RESUSCITATE) {
if (t == 0) {
loeffect_recipient_apply_effect(
&base->param.recipient, &loeffect_resuscitate());
}
t = 2000 - t;
}
if (t < 1000) {
const float tf = t/1000.f;
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_SIT;
base->cache.instance.motion.time = powf(tf, 6);
} else {
const float tf = (t-1000)/1000.f;
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_SIT;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_DOWN;
base->cache.instance.motion.time = powf(tf, 4);
}
}
static const statman_meta_t state_table_[] = {
lochara_state_stand(
.period = 4000,
.acceleration = {{5, 5}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND2,
),
lochara_state_walk(
.period = 1000,
.acceleration = {{5, 5}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_WALK,
),
{
.state = LOCHARA_STATE_THRUST_IN,
.name = "THRUST_IN",
.initialize = lochara_state_initialize_any_,
.update = lochara_cavia_update_thrust_in_,
},
{
.state = LOCHARA_STATE_THRUST_OUT,
.name = "THRUST_OUT",
.initialize = lochara_state_initialize_any_,
.update = lochara_cavia_update_thrust_out_,
},
{
.state = LOCHARA_STATE_DEAD,
.name = "DEAD",
.initialize = lochara_cavia_initialize_dead_,
.update = lochara_cavia_update_dead_,
},
{
.state = LOCHARA_STATE_RESUSCITATE,
.name = "RESUSCITATE",
.initialize = lochara_state_initialize_any_,
.update = lochara_cavia_update_dead_,
},
{0},
};
static const lochara_combat_action_t combo1_[] = {
lochara_combat_action_rest(
.duration = 300,
.state = LOCHARA_STATE_STAND,
),
lochara_combat_action_attack(
.duration = 300,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1.2f,
),
lochara_combat_action_rest(
.duration = 100,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_attack(
.duration = 150,
.state = LOCHARA_STATE_THRUST_IN,
.damage = .8f,
),
lochara_combat_action_rest(
.duration = 150,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_attack(
.duration = 150,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = 1000,
.state = LOCHARA_STATE_THRUST_OUT,
),
{0},
};
static const lochara_combat_action_t dead_[] = {
lochara_combat_action_rest(
.duration = 30000,
.state = LOCHARA_STATE_DEAD,
),
lochara_combat_action_rest(
.duration = 2000,
.state = LOCHARA_STATE_RESUSCITATE,
),
{0},
};
static const statman_meta_t strategy_table_[] = {
lochara_strategy_scouting(
.state_table = state_table_,
.period = 2000,
.stagger = .5f,
.range_close = WIDTH_*2,
.found_close = LOCHARA_STRATEGY_COMBO1,
.range_back = 1,
),
lochara_strategy_combat(COMBO1,
.state_table = state_table_,
.actions = combo1_,
.next = LOCHARA_STRATEGY_SCOUTING,
),
lochara_strategy_combat(DEAD,
.state_table = state_table_,
.actions = dead_,
.next = LOCHARA_STRATEGY_SCOUTING,
),
{0},
};
bool lochara_cavia_update(lochara_base_t* base) {
assert(base != NULL);
loeffect_recipient_update(&base->param.recipient, &base_status_);
statman_update(strategy_table_, base, &base->param.strategy);
const float dir = MATH_SIGN_NOZERO(base->param.direction.x);
base->cache.instance = (loshader_character_drawer_instance_t) {
.character_id = LOSHADER_CHARACTER_ID_CAVIA,
.pos = vec2(0, -MARKER_),
.size = vec2(dir*HEIGHT_, HEIGHT_),
.color = COLOR_,
.marker_offset = vec2(0, MARKER_),
};
statman_update(state_table_, base, &base->param.state);
if (base->param.state != LOCHARA_STATE_DEAD) {
base->cache.instance.marker = lochara_base_affect_bullets(base);
}
lochara_base_calculate_physics(
base, &vec2(WIDTH_, HEIGHT_), &vec2(0, MARKER_));
lochara_base_bind_on_ground(base, &vec2(WIDTH_, HEIGHT_+MARKER_));
return true;
}
void lochara_cavia_build(
lochara_base_t* base, const loentity_ground_t* gnd, float pos) {
assert(base != NULL);
assert(gnd != NULL);
assert(MATH_FLOAT_VALID(pos));
base->super.super.pos = gnd->super.pos;
vec2_addeq(
&base->super.super.pos.fract,
&vec2(gnd->size.x*pos, gnd->size.y));
locommon_position_reduce(&base->super.super.pos);
base->param = (typeof(base->param)) {
.type = LOCHARA_TYPE_CAVIA,
.state = LOCHARA_STATE_STAND,
.last_state_changed = base->ticker->time,
.strategy = LOCHARA_STRATEGY_SCOUTING,
.last_strategy_changed = base->ticker->time,
.ground = gnd->super.id,
};
loeffect_recipient_initialize(
&base->param.recipient, base->ticker, &base_status_);
}

19
core/lochara/cavia.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <stdbool.h>
#include "core/loentity/ground.h"
#include "./base.h"
bool
lochara_cavia_update(
lochara_base_t* base
);
void
lochara_cavia_build(
lochara_base_t* base,
const loentity_ground_t* gnd,
float pos
);

79
core/lochara/combat.c Normal file
View File

@ -0,0 +1,79 @@
#include "./combat.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "util/math/algorithm.h"
#include "core/loeffect/effect.h"
#include "core/loentity/character.h"
#include "core/loplayer/combat.h"
#include "./base.h"
#include "./state.h"
static void lochara_combat_action_handle_attack_(
const loplayer_combat_attack_t* attack,
loplayer_combat_attack_result_t result) {
assert(attack != NULL);
lochara_base_t* attacker = attack->data1;
/* TODO(catfoot): store the result in the attacker */
if (result != LOPLAYER_COMBAT_ATTACK_RESULT_EXECUTED) return;
const float v = attacker->param.recipient.status.attack*
MATH_SIGN_NOZERO(attacker->param.direction.x);
loentity_character_knockback(&attacker->player->entity->super, &vec2(v, 0));
const lochara_combat_action_t* action = attack->data2;
switch (action->type) {
case LOCHARA_COMBAT_ACTION_TYPE_REST:
break;
case LOCHARA_COMBAT_ACTION_TYPE_ATTACK:
loentity_character_apply_effect(
&attacker->player->entity->super,
&loeffect_damage(
action->damage*attacker->param.recipient.status.attack));
break;
case LOCHARA_COMBAT_ACTION_TYPE_ATTACK_EFFECT:
loentity_character_apply_effect(
&attacker->player->entity->super, &action->effect);
break;
}
}
void lochara_combat_action_execute_all_attacks(
const lochara_combat_action_t* actions, lochara_base_t* attacker) {
assert(actions != NULL);
assert(attacker != NULL);
loplayer_combat_t* combat = &attacker->player->combat;
uint64_t t = attacker->ticker->time;
for (; actions->duration != 0; t += actions->duration, ++actions) {
if (actions->type == LOCHARA_COMBAT_ACTION_TYPE_REST) continue;
loplayer_combat_add_attack(
combat,
&(loplayer_combat_attack_t) {
.attacker = attacker->super.super.id,
.start = t,
.duration = actions->duration,
.data1 = attacker,
.data2 = (void*) actions,
.handle = lochara_combat_action_handle_attack_,
});
}
}
const lochara_combat_action_t* lochara_combat_action_find_by_time(
const lochara_combat_action_t* actions, uint64_t time) {
assert(actions != NULL);
while (actions->duration != 0 && actions->duration <= time) {
time -= actions->duration;
++actions;
}
return actions->duration > 0? actions: NULL;
}

51
core/lochara/combat.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <stdint.h>
#include "core/loeffect/effect.h"
#include "./state.h"
typedef struct lochara_base_t lochara_base_t;
typedef enum {
LOCHARA_COMBAT_ACTION_TYPE_REST,
LOCHARA_COMBAT_ACTION_TYPE_ATTACK,
LOCHARA_COMBAT_ACTION_TYPE_ATTACK_EFFECT,
} lochara_combat_action_type_t;
typedef struct {
lochara_combat_action_type_t type;
lochara_state_t state;
uint64_t duration;
union {
float damage;
loeffect_t effect;
};
} lochara_combat_action_t;
#define lochara_combat_action_rest(...) { \
.type = LOCHARA_COMBAT_ACTION_TYPE_REST, \
__VA_ARGS__ \
}
#define lochara_combat_action_attack(...) { \
.type = LOCHARA_COMBAT_ACTION_TYPE_ATTACK, \
__VA_ARGS__ \
}
#define lochara_combat_action_attack_effect(...) { \
.type = LOCHARA_COMBAT_ACTION_TYPE_ATTACK_EFFECT, \
__VA_ARGS__ \
}
void
lochara_combat_action_execute_all_attacks(
const lochara_combat_action_t* actions,
lochara_base_t* attacker
);
const lochara_combat_action_t* /* NULLABLE */
lochara_combat_action_find_by_time(
const lochara_combat_action_t* actions,
uint64_t t
);

85
core/lochara/encephalon.c Normal file
View File

@ -0,0 +1,85 @@
#include "./encephalon.h"
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include "util/chaos/xorshift.h"
#include "util/math/vector.h"
#include "core/locommon/position.h"
#include "core/loeffect/effect.h"
#include "core/loeffect/recipient.h"
#include "core/loentity/ground.h"
#include "core/loshader/character.h"
#include "./base.h"
#define WIDTH_ .1f
#define HEIGHT_ .1f
#define RANGE_ .15f
#define COLOR_ vec4(0, 0, 0, .8f)
bool lochara_encephalon_update(lochara_base_t* base) {
assert(base != NULL);
base->cache.instance = (loshader_character_drawer_instance_t) {
.character_id = LOSHADER_CHARACTER_ID_ENCEPHALON,
.size = vec2(WIDTH_, HEIGHT_),
.color = COLOR_,
};
vec2_t disp;
locommon_position_sub(
&disp, &base->player->entity->super.super.pos, &base->super.super.pos);
const bool active = fabsf(disp.x) < RANGE_ && fabsf(disp.y) < HEIGHT_;
if (active) {
if (base->param.state != LOCHARA_STATE_SPELL) {
loresource_sound_set_play(
&base->res->sound, LORESOURCE_SOUND_ID_TOUCH_GATE);
base->param.last_state_changed = base->ticker->time;
}
base->param.state = LOCHARA_STATE_SPELL;
loentity_character_apply_effect(
&base->player->entity->super, &loeffect_resuscitate());
base->player->entity->param.anchor.pos =
base->player->entity->super.super.pos;
if (base->param.last_state_changed+500 > base->ticker->time) {
base->cache.instance.color.w = chaos_xorshift(base->ticker->time)%3 >= 1;
}
} else {
base->param.state = LOCHARA_STATE_STAND;
}
if (base->cache.ground == NULL) return false;
base->super.super.pos = base->cache.ground->super.pos;
base->super.super.pos.fract.y += base->cache.ground->size.y + HEIGHT_;
locommon_position_reduce(&base->super.super.pos);
return true;
}
void lochara_encephalon_build(
lochara_base_t* base, const loentity_ground_t* gnd) {
assert(base != NULL);
assert(gnd != NULL);
base->super.super.pos = gnd->super.pos;
base->super.super.pos.fract.y += gnd->size.y + HEIGHT_;
locommon_position_reduce(&base->super.super.pos);
base->param = (typeof(base->param)) {
.type = LOCHARA_TYPE_ENCEPHALON,
.state = LOCHARA_STATE_STAND,
.last_state_changed = base->ticker->time,
.ground = gnd->super.id,
};
loeffect_recipient_initialize(
&base->param.recipient,
base->ticker,
&(loeffect_recipient_status_t) {0});
}

18
core/lochara/encephalon.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <stdbool.h>
#include "core/loentity/ground.h"
#include "./base.h"
bool
lochara_encephalon_update(
lochara_base_t* base
);
void
lochara_encephalon_build(
lochara_base_t* base,
const loentity_ground_t* gnd
);

492
core/lochara/player.c Normal file
View File

@ -0,0 +1,492 @@
#include "./player.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "util/math/algorithm.h"
#include "util/math/vector.h"
#include "util/statman/statman.h"
#include "core/lobullet/linear.h"
#include "core/lobullet/pool.h"
#include "core/locommon/easing.h"
#include "core/locommon/position.h"
#include "core/loeffect/effect.h"
#include "core/loeffect/recipient.h"
#include "core/loentity/store.h"
#include "core/loplayer/stance.h"
#include "core/loshader/character.h"
#include "./base.h"
#include "./state.h"
#include "./type.h"
#define INITIAL_POS_ locommon_position(0, 0, vec2(.5f, .8f))
#define WIDTH_ .02f
#define HEIGHT_ .05f
#define MARKER_ .03f
#define COLOR_ vec4(0, 0, 0, 1)
#define RESPAWN_DURATION_ 4000
#define BULLET_SIZE_ vec2(.02f, .02f)
#define BULLET_COLOR_ vec4(1, .9f, .9f, 1)
#define BULLET_KNOCKBACK_ 4
#define BULLET_DAMAGE_ .2f
#define BULLET_DURATION_ 1000
#define BULLET_COST_ .06f
#define CAMERA_SPEED_ 10
#define CAMERA_SHIFT_Y_ .1f
#define CAMERA_FADE_RADIAL_ .6f
#define CAMERA_DEAD_FADE_ 2
#define CAMERA_DEAD_SPEED_ .3f
#define CAMERA_DEAD_RECOVERY_SPEED_ 10
#define CAMERA_COMBAT_SPEED_ 10
#define CAMERA_COMBAT_RECOVERY_SPEED_ 5
#define CAMERA_ENEMY_ATTACK_INTENSITY_ .2f
#define CAMERA_ENEMY_ATTACK_SPEED_ 4
#define CAMERA_DAMAGE_INTENSITY_ .5f
#define CAMERA_DAMAGE_DURATION_ 1000
#define CAMERA_ABERRATION_ 1
#define CAMERA_CORRUPTION_THRESH_ .2f
#define CAMERA_CORRUPTION_PERIOD_ 4000
#define DIRECTION_X_EPSILON_ .005f
#define GUARD_THRESHOLD_ .8f
#define BACKGUARD_THRESHOLD_ .95f
static const loeffect_recipient_status_t base_status_ = {
.attack = .2f,
.defence = .2f,
.speed = .3f,
.jump = 1.f,
};
static void lochara_player_update_dead_state_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
lochara_base_t* base = instance;
base->param.movement = vec2(0, 0);
const uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t >= RESPAWN_DURATION_ && base->player->event.executor == 0) {
loeffect_recipient_apply_effect(
&base->param.recipient, &loeffect_resuscitate());
base->super.super.pos = base->param.anchor.pos;
*next = LOCHARA_STATE_STAND;
return;
}
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
base->cache.instance.motion.time = 0;
}
static void lochara_player_update_shoot_state_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
static const uint64_t dur = 200;
lochara_base_t* base = instance;
const uint64_t t = base->ticker->time - base->param.last_state_changed;
const float dt = base->ticker->delta_f;
locommon_easing_linear_float(&base->param.movement.x, 0, dt);
locommon_easing_linear_float(&base->param.movement.y, 0, dt);
const bool has_stance = loplayer_stance_set_has(
&base->player->stances, LOPLAYER_STANCE_REVOLUTIONER);
if (has_stance && t >= dur && base->param.recipient.faith > 0) {
locommon_position_t pos = base->super.super.pos;
pos.fract.x += WIDTH_*MATH_SIGN_NOZERO(base->param.direction.x);
locommon_position_reduce(&pos);
lobullet_base_t* b = lobullet_pool_create(base->bullet);
lobullet_linear_circle_build(b,
.owner = base->super.super.id,
.basepos = pos,
.size = BULLET_SIZE_,
.color = BULLET_COLOR_,
.velocity = base->param.direction,
.knockback = BULLET_KNOCKBACK_,
.effect = loeffect_damage(
base->param.recipient.status.attack*BULLET_DAMAGE_),
.duration = BULLET_DURATION_,);
loentity_store_add(base->entities, &b->super.super);
loentity_character_apply_effect(
&base->super, &loeffect_lost(BULLET_COST_));
base->param.last_state_changed = base->ticker->time;
}
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_STAND2;
base->cache.instance.motion.time = t > dur? 1: t*1.f / dur;
}
static const statman_meta_t state_table_[] = {
lochara_state_stand(
.period = 2000,
.acceleration = {{5, 5}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND2,
),
lochara_state_walk(
.period = 350,
.acceleration = {{5, 5}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_WALK,
),
lochara_state_sprint(
.period = 300,
.acceleration = {{5, 5}},
.speed = 1.4f,
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_WALK,
),
lochara_state_dodge(
.duration = 100,
.acceleration = {{3, 3}},
.speed = 1,
.motion1 = LOSHADER_CHARACTER_MOTION_ID_WALK,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
),
lochara_state_jump(),
{
.state = LOCHARA_STATE_GUARD,
.name = "GUARD",
.data = &(lochara_state_move_param_t) {
.speed = 0,
.period = 1000,
.acceleration = {{10, 10}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
},
.initialize = lochara_state_initialize_any_,
.update = lochara_state_update_move_,
},
{
.state = LOCHARA_STATE_SHOOT,
.name = "SHOOT",
.initialize = lochara_state_initialize_any_,
.update = lochara_player_update_shoot_state_,
},
{
.state = LOCHARA_STATE_DEAD,
.name = "DEAD",
.initialize = lochara_state_initialize_any_,
.update = lochara_player_update_dead_state_,
.finalize = lochara_state_cancel_transition_,
},
{0},
};
static void lochara_player_handle_controller_(lochara_base_t* base) {
assert(base != NULL);
lochara_state_t next = base->param.state;
switch (base->player->controller.state) {
case LOPLAYER_CONTROLLER_STATE_NONE:
next = LOCHARA_STATE_STAND;
break;
case LOPLAYER_CONTROLLER_STATE_WALK_LEFT:
next = LOCHARA_STATE_WALK_LEFT;
break;
case LOPLAYER_CONTROLLER_STATE_WALK_RIGHT:
next = LOCHARA_STATE_WALK_RIGHT;
break;
case LOPLAYER_CONTROLLER_STATE_SPRINT_LEFT:
next = LOCHARA_STATE_SPRINT_LEFT;
break;
case LOPLAYER_CONTROLLER_STATE_SPRINT_RIGHT:
next = LOCHARA_STATE_SPRINT_RIGHT;
break;
case LOPLAYER_CONTROLLER_STATE_DODGE_FORWARD:
next =
base->param.direction.x < 0?
LOCHARA_STATE_DODGE_LEFT:
LOCHARA_STATE_DODGE_RIGHT;
break;
case LOPLAYER_CONTROLLER_STATE_DODGE_LEFT:
next = LOCHARA_STATE_DODGE_LEFT;
break;
case LOPLAYER_CONTROLLER_STATE_DODGE_RIGHT:
next = LOCHARA_STATE_DODGE_RIGHT;
break;
case LOPLAYER_CONTROLLER_STATE_JUMP:
if (base->param.on_ground) next = LOCHARA_STATE_JUMP;
break;
case LOPLAYER_CONTROLLER_STATE_GUARD:
next = LOCHARA_STATE_GUARD;
break;
case LOPLAYER_CONTROLLER_STATE_SHOOT:
next = LOCHARA_STATE_SHOOT;
break;
}
if (loplayer_combat_is_attack_pending(&base->player->combat)) {
if (next != LOCHARA_STATE_STAND &&
next != LOCHARA_STATE_DODGE_LEFT &&
next != LOCHARA_STATE_DODGE_RIGHT &&
next != LOCHARA_STATE_GUARD) {
next = LOCHARA_STATE_STAND;
}
} else {
vec2_t dir;
locommon_position_sub(
&dir, &base->player->controller.cursor, &base->super.super.pos);
if (fabsf(dir.x) > DIRECTION_X_EPSILON_) {
vec2_div(&base->param.direction, &dir, vec2_length(&dir));
}
}
statman_transition_to(state_table_, base, &base->param.state, next);
}
static void lochara_player_update_combat_(lochara_base_t* base) {
assert(base != NULL);
const lochara_state_t state = base->param.state;
if (state == LOCHARA_STATE_DEAD) {
loplayer_combat_set_accepting(
&base->player->combat,
false,
LOPLAYER_COMBAT_ATTACK_RESULT_ABORTED);
} else {
loplayer_combat_set_accepting(
&base->player->combat,
state != LOCHARA_STATE_DODGE_LEFT &&
state != LOCHARA_STATE_DODGE_RIGHT,
LOPLAYER_COMBAT_ATTACK_RESULT_DODGED);
}
loplayer_combat_set_guarding(
&base->player->combat,
state == LOCHARA_STATE_GUARD);
if (loplayer_combat_is_attack_pending(&base->player->combat)) {
base->param.movement = vec2(0, 0);
base->param.gravity = 0;
}
float r;
loplayer_combat_attack_t a;
if (!loplayer_combat_pop_attack(&base->player->combat, &a, &r)) return;
loentity_store_iterator_t itr;
if (!loentity_store_find_item_by_id(base->entities, &itr, a.attacker) ||
itr.character == NULL) {
return;
}
vec2_t disp;
locommon_position_sub(
&disp, &itr.character->super.pos, &base->super.super.pos);
const float dir = disp.x * base->param.direction.x;
const bool reflected =
r > (dir < 0? BACKGUARD_THRESHOLD_: GUARD_THRESHOLD_);
loplayer_combat_attack_handle(
&a,
reflected?
LOPLAYER_COMBAT_ATTACK_RESULT_REFLECTED:
LOPLAYER_COMBAT_ATTACK_RESULT_EXECUTED);
if (!reflected) return;
loresource_sound_set_play(&base->res->sound, LORESOURCE_SOUND_ID_REFLECTION);
loentity_character_apply_effect(
itr.character,
&loeffect_damage(base->param.recipient.status.attack));
loentity_character_knockback(
itr.character,
&vec2(MATH_SIGN(disp.x)*base->param.recipient.status.attack, 0));
}
static void lochara_player_update_camera_(lochara_base_t* base) {
assert(base != NULL);
const loeffect_recipient_t* r = &base->param.recipient;
const uint64_t t = base->ticker->time;
const uint64_t pt = base->ticker->prev_time;
const float dt = base->ticker->delta_f;
const bool combat = loplayer_combat_is_attack_pending(&base->player->combat);
loplayer_camera_t* camera = &base->player->camera;
vec2_t sz = base->player->camera.chunk_winsz;
vec2_diveq(&sz,
base->player->camera.scale*
(camera->posteffect.distortion_radial+1));
locommon_position_t pos = base->super.super.pos;
if (!combat) {
pos.fract.y += CAMERA_SHIFT_Y_;
locommon_position_reduce(&pos);
}
loplayer_event_bind_rect_in_area(&base->player->event, &pos, &sz);
vec2_t disp;
locommon_position_sub(&disp, &pos, &camera->pos);
if (vec2_pow_length(&disp) < 1) {
locommon_easing_smooth_position(&camera->pos, &pos, dt*CAMERA_SPEED_);
} else {
camera->pos = pos;
}
locommon_easing_smooth_float(&camera->scale, 1, dt);
/* ---- dead ---- */
if (base->param.state == LOCHARA_STATE_DEAD) {
locommon_easing_smooth_float(
&camera->posteffect.fade_radial,
CAMERA_DEAD_FADE_,
dt*CAMERA_DEAD_SPEED_);
} else {
locommon_easing_smooth_float(
&camera->posteffect.fade_radial,
CAMERA_FADE_RADIAL_,
dt*CAMERA_DEAD_RECOVERY_SPEED_);
}
/* ---- combat ---- */
if (combat) {
locommon_easing_smooth_float(
&camera->posteffect.distortion_radial,
1,
dt*CAMERA_COMBAT_SPEED_);
} else {
locommon_easing_smooth_float(
&camera->posteffect.distortion_radial,
0,
dt*CAMERA_COMBAT_RECOVERY_SPEED_);
}
/* ---- enemy attack ---- */
const loplayer_combat_attack_t* attack = base->player->combat.first_attack;
if (attack != NULL && pt < attack->start && attack->start <= t) {
camera->posteffect.distortion_urgent = CAMERA_ENEMY_ATTACK_INTENSITY_;
} else {
locommon_easing_smooth_float(
&camera->posteffect.distortion_urgent,
0,
dt*CAMERA_ENEMY_ATTACK_SPEED_);
}
/* ---- damage ----- */
if (0 < r->last_damage &&
r->last_damage < t && t < r->last_damage+CAMERA_DAMAGE_DURATION_) {
camera->posteffect.raster_whole =
(1 - (t - r->last_damage)*1.f/CAMERA_DAMAGE_DURATION_)*
CAMERA_DAMAGE_INTENSITY_;
} else {
camera->posteffect.raster_whole = 0;
}
/* ---- amnesia ---- */
locommon_easing_smooth_float(
&camera->posteffect.distortion_amnesia,
!!(r->effects.amnesia.start < t &&
t < r->effects.amnesia.start+r->effects.amnesia.duration),
dt);
/* ---- corruption ---- */
if (r->madness <= CAMERA_CORRUPTION_THRESH_) {
if (camera->corruption_since == 0) camera->corruption_since = t;
const uint64_t p =
(t - camera->corruption_since)%CAMERA_CORRUPTION_PERIOD_;
camera->pixsort = 1-powf(p*1.f/CAMERA_CORRUPTION_PERIOD_, 2);
} else {
camera->corruption_since = 0;
locommon_easing_smooth_float(&camera->pixsort, 0, dt);
}
/* ---- passive ---- */
locommon_easing_smooth_float(
&camera->posteffect.aberration_radial, CAMERA_ABERRATION_, dt);
}
bool lochara_player_update(lochara_base_t* base) {
assert(base != NULL);
loeffect_recipient_update(&base->param.recipient, &base_status_);
if (!loeffect_recipient_is_alive(&base->param.recipient)) {
statman_transition_to(
state_table_, base, &base->param.state, LOCHARA_STATE_DEAD);
}
if (loplayer_stance_set_has(
&base->player->stances, LOPLAYER_STANCE_UNFINISHER)) {
if (base->param.recipient.faith > .5f) {
loeffect_recipient_apply_effect(
&base->param.recipient, &loeffect_heal(base->ticker->delta_f*.01f));
}
}
lochara_player_handle_controller_(base);
const float dir = MATH_SIGN_NOZERO(base->param.direction.x);
base->cache.instance = (loshader_character_drawer_instance_t) {
.character_id = LOSHADER_CHARACTER_ID_PLAYER,
.pos = vec2(0, -MARKER_),
.size = vec2(dir*HEIGHT_, HEIGHT_),
.color = COLOR_,
.marker_offset = vec2(0, MARKER_),
};
statman_update(state_table_, base, &base->param.state);
lochara_player_update_combat_(base);
if (base->param.state != LOCHARA_STATE_DODGE_LEFT &&
base->param.state != LOCHARA_STATE_DODGE_RIGHT &&
base->param.state != LOCHARA_STATE_DEAD) {
base->cache.instance.marker = lochara_base_affect_bullets(base);
}
lochara_base_calculate_physics(
base, &vec2(WIDTH_, HEIGHT_), &vec2(0, MARKER_));
loplayer_event_bind_rect_in_area(
&base->player->event,
&base->super.super.pos,
&vec2(WIDTH_*.8f, HEIGHT_));
lochara_player_update_camera_(base);
return true;
}
void lochara_player_build(lochara_base_t* base) {
assert(base != NULL);
base->super.super.pos = base->param.anchor.pos = INITIAL_POS_;
base->param = (typeof(base->param)) {
.type = LOCHARA_TYPE_PLAYER,
.state = LOCHARA_STATE_STAND,
.last_state_changed = base->ticker->time,
};
loeffect_recipient_initialize(
&base->param.recipient, base->ticker, &base_status_);
}

15
core/lochara/player.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdbool.h>
#include "./base.h"
bool
lochara_player_update(
lochara_base_t* base
);
void
lochara_player_build(
lochara_base_t* base
);

58
core/lochara/pool.c Normal file
View File

@ -0,0 +1,58 @@
#include "./pool.h"
#include <assert.h>
#include <stddef.h>
#include <msgpack.h>
#include "util/memory/memory.h"
#include "core/lobullet/pool.h"
#include "core/locommon/counter.h"
#include "core/locommon/ticker.h"
#include "core/loentity/pool.h"
#include "core/loentity/entity.h"
#include "core/loplayer/player.h"
#include "core/loresource/set.h"
#include "core/loshader/set.h"
#include "./base.h"
LOENTITY_POOL_SOURCE_TEMPLATE(lochara)
lochara_pool_t* lochara_pool_new(
loresource_set_t* res,
loshader_set_t* shaders,
locommon_counter_t* idgen,
const locommon_ticker_t* ticker,
loentity_store_t* entities,
loplayer_t* player,
lobullet_pool_t* bullet,
size_t length) {
assert(res != NULL);
assert(shaders != NULL);
assert(idgen != NULL);
assert(ticker != NULL);
assert(entities != NULL);
assert(player != NULL);
assert(bullet != NULL);
assert(length > 0);
lochara_pool_t* pool =
memory_new(sizeof(*pool) + (length-1)*sizeof(pool->items[0]));
*pool = (typeof(*pool)) {
.idgen = idgen,
.length = length,
};
for (size_t i = 0; i < pool->length; ++i) {
lochara_base_initialize(
&pool->items[i],
res,
shaders,
ticker,
entities,
player,
bullet);
}
return pool;
}

46
core/lochara/pool.h Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include <stddef.h>
#include <msgpack.h>
#include "core/lobullet/pool.h"
#include "core/locommon/counter.h"
#include "core/locommon/ticker.h"
#include "core/loentity/entity.h"
#include "core/loplayer/player.h"
#include "core/loresource/set.h"
#include "core/loshader/set.h"
#include "./base.h"
struct lochara_pool_t;
typedef struct lochara_pool_t lochara_pool_t;
lochara_pool_t* /* OWNERSHIP */
lochara_pool_new(
loresource_set_t* res,
loshader_set_t* shaders,
locommon_counter_t* idgen,
const locommon_ticker_t* ticker,
loentity_store_t* entities,
loplayer_t* player,
lobullet_pool_t* bullet,
size_t length
);
void
lochara_pool_delete(
lochara_pool_t* pool /* OWNERSHIP */
);
lochara_base_t*
lochara_pool_create(
lochara_pool_t* pool
);
lochara_base_t*
lochara_pool_unpack_item(
lochara_pool_t* pool,
const msgpack_object* obj
);

179
core/lochara/state.c Normal file
View File

@ -0,0 +1,179 @@
#include "./state.h"
#include <assert.h>
#include <math.h>
#include <stddef.h>
#include <stdint.h>
#include "util/math/algorithm.h"
#include "util/math/vector.h"
#include "util/statman/statman.h"
#include "core/locommon/easing.h"
#include "core/loresource/sound.h"
#include "core/loshader/character.h"
#include "./base.h"
#define MIDAIR_SPEED_ .8f
#define BACKWALK_SPEED_ .6f
void lochara_state_initialize_any_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
lochara_base_t* base = instance;
base->param.last_state_changed = base->ticker->time;
}
void lochara_state_cancel_transition_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
*state = meta->state;
}
void lochara_state_update_move_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
const lochara_state_move_param_t* p = meta->data;
lochara_base_t* base = instance;
const uint64_t t = base->ticker->time - base->param.last_state_changed;
const float dt = base->ticker->delta_f;
assert(vec2_valid(&p->acceleration));
assert(MATH_FLOAT_VALID(p->speed));
assert(vec2_valid(&p->velocity));
assert(p->period > 0);
/* ---- acceleration ---- */
float speed = p->speed*base->param.recipient.status.speed;
if (!base->param.on_ground) {
speed *= MIDAIR_SPEED_;
}
if (vec2_dot(&p->velocity, &base->param.direction) < 0) {
speed *= BACKWALK_SPEED_;
}
vec2_t velocity = p->velocity;
vec2_muleq(&velocity, speed);
locommon_easing_linear_float(
&base->param.movement.x, velocity.x, p->acceleration.x*dt);
locommon_easing_linear_float(
&base->param.movement.y, velocity.y, p->acceleration.y*dt);
/* ---- periodic motion ---- */
base->cache.instance.motion.time =
(!!base->param.on_ground)*(1-fabs(t%p->period*1.f/p->period*2 - 1));
base->cache.instance.motion.from = p->motion1;
base->cache.instance.motion.to = p->motion2;
}
void lochara_state_initialize_dodge_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
lochara_state_initialize_any_(meta, instance, state);
lochara_base_t* base = instance;
loresource_sound_set_play(&base->res->sound, LORESOURCE_SOUND_ID_DODGE);
}
void lochara_state_update_dodge_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
const lochara_state_dodge_param_t* p = meta->data;
lochara_base_t* base = instance;
const uint64_t t = base->ticker->time - base->param.last_state_changed;
assert(p->duration > 0);
assert(vec2_valid(&p->acceleration));
assert(vec2_valid(&p->velocity));
if (t > p->duration) {
*state = LOCHARA_STATE_STAND;
return;
}
/* ---- acceleration ---- */
base->param.movement = p->acceleration;
vec2_muleq(&base->param.movement, t/1000.f);
base->param.movement.x *= -MATH_SIGN(p->velocity.x);
base->param.movement.y *= -MATH_SIGN(p->velocity.y);
vec2_addeq(&base->param.movement, &p->velocity);
/* ---- motion ---- */
base->cache.instance.motion.time = powf(t*1.f/p->duration, 1.2f);
base->cache.instance.motion.from = p->motion1;
base->cache.instance.motion.to = p->motion2;
const float a = powf(t*2.f/p->duration-1, 2);
base->cache.instance.size.x *= (1-a)*.8f+1;
base->cache.instance.color.w *= MATH_MIN(a+.2f, 1);
}
void lochara_state_initialize_jump_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
lochara_state_initialize_any_(meta, instance, next);
lochara_base_t* base = instance;
base->param.gravity += base->param.recipient.status.jump;
*next = LOCHARA_STATE_STAND;
}
void lochara_state_update_teleport_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
const lochara_state_teleport_param_t* p = meta->data;
lochara_base_t* base = instance;
base->param.movement = vec2(0, 0);
const uint64_t t = base->ticker->time - base->param.last_state_changed;
const uint64_t pt =
t == 0? 0: base->ticker->prev_time - base->param.last_state_changed;
if (pt < p->duration/2 && p->duration/2 <= t) {
const lochara_base_t* player = base->player->entity;
const float pdir =
MATH_SIGN_NOZERO(player->param.direction.x)*p->direction;
base->param.direction = vec2(-pdir, 0);
vec2_t offset = p->offset;
offset.x *= pdir;
base->super.super.pos = player->super.super.pos;
vec2_addeq(&base->super.super.pos.fract, &offset);
locommon_position_reduce(&base->super.super.pos);
}
const float tf = fabsf(t*1.f/p->duration*2 - 1);
base->cache.instance.motion.from = p->motion2;
base->cache.instance.motion.to = p->motion1;
base->cache.instance.motion.time = tf;
base->cache.instance.color.w = tf;
}

233
core/lochara/state.h Normal file
View File

@ -0,0 +1,233 @@
#pragma once
#include <stddef.h>
#include "util/math/vector.h"
#include "util/statman/statman.h"
#include "core/loshader/character.h"
typedef enum {
/* BENUM BEGIN lochara_state */
LOCHARA_STATE_STAND,
LOCHARA_STATE_WALK_LEFT,
LOCHARA_STATE_WALK_RIGHT,
LOCHARA_STATE_SPRINT_LEFT,
LOCHARA_STATE_SPRINT_RIGHT,
LOCHARA_STATE_DODGE_LEFT,
LOCHARA_STATE_DODGE_RIGHT,
LOCHARA_STATE_JUMP,
LOCHARA_STATE_TELEPORT_BEHIND,
LOCHARA_STATE_TELEPORT_FRONT,
LOCHARA_STATE_THRUST_IN,
LOCHARA_STATE_THRUST_OUT,
LOCHARA_STATE_SLASH,
LOCHARA_STATE_SPELL,
LOCHARA_STATE_SHOOT,
LOCHARA_STATE_DOWN,
LOCHARA_STATE_DEAD,
LOCHARA_STATE_REVIVE,
LOCHARA_STATE_RESUSCITATE,
LOCHARA_STATE_GUARD,
/* BENUM END */
} lochara_state_t;
/* generated benum utils */
#include "core/lochara/benum/state.h"
/* ---- default state handlers ---- */
void
lochara_state_initialize_any_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
lochara_state_cancel_transition_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
lochara_state_update_move_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
lochara_state_initialize_dodge_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
lochara_state_update_dodge_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
lochara_state_initialize_jump_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
lochara_state_update_teleport_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
/* ---- state meta constructor ---- */
typedef struct {
vec2_t acceleration;
float speed;
vec2_t velocity;
uint64_t period;
loshader_character_motion_id_t motion1;
loshader_character_motion_id_t motion2;
} lochara_state_move_param_t;
#define lochara_state_stand(...) \
(statman_meta_t) { \
.state = LOCHARA_STATE_STAND, \
.name = "STAND", \
.data = &(lochara_state_move_param_t) { \
.speed = 0, \
__VA_ARGS__ \
}, \
.initialize = lochara_state_initialize_any_, \
.update = lochara_state_update_move_, \
}
#define lochara_state_walk(...) \
(statman_meta_t) { \
.state = LOCHARA_STATE_WALK_LEFT, \
.name = "WALK_LEFT", \
.data = &(lochara_state_move_param_t) { \
.speed = 1, \
.velocity = {{-1, 0}}, \
__VA_ARGS__ \
}, \
.initialize = lochara_state_initialize_any_, \
.update = lochara_state_update_move_, \
}, \
(statman_meta_t) { \
.state = LOCHARA_STATE_WALK_RIGHT, \
.name = "WALK_RIGHT", \
.data = &(lochara_state_move_param_t) { \
.speed = 1, \
.velocity = {{1, 0}}, \
__VA_ARGS__ \
}, \
.initialize = lochara_state_initialize_any_, \
.update = lochara_state_update_move_, \
}
#define lochara_state_sprint(...) \
(statman_meta_t) { \
.state = LOCHARA_STATE_SPRINT_LEFT, \
.name = "SPRINT_LEFT", \
.data = &(lochara_state_move_param_t) { \
.velocity = {{-1, 0}}, \
__VA_ARGS__ \
}, \
.initialize = lochara_state_initialize_any_, \
.update = lochara_state_update_move_, \
}, \
(statman_meta_t) { \
.state = LOCHARA_STATE_SPRINT_RIGHT, \
.name = "SPRINT_RIGHT", \
.data = &(lochara_state_move_param_t) { \
.velocity = {{1, 0}}, \
__VA_ARGS__ \
}, \
.initialize = lochara_state_initialize_any_, \
.update = lochara_state_update_move_, \
}
typedef struct {
uint64_t duration;
float speed;
vec2_t acceleration;
vec2_t velocity;
loshader_character_motion_id_t motion1;
loshader_character_motion_id_t motion2;
} lochara_state_dodge_param_t;
#define lochara_state_dodge(...) \
(statman_meta_t) { \
.state = LOCHARA_STATE_DODGE_LEFT, \
.name = "DODGE_LEFT", \
.data = &(lochara_state_dodge_param_t) { \
.velocity = {{-1, 0}}, \
__VA_ARGS__ \
}, \
.initialize = lochara_state_initialize_dodge_, \
.update = lochara_state_update_dodge_, \
.finalize = lochara_state_cancel_transition_, \
}, \
(statman_meta_t) { \
.state = LOCHARA_STATE_DODGE_RIGHT, \
.name = "DODGE_RIGHT", \
.data = &(lochara_state_dodge_param_t) { \
.velocity = {{1, 0}}, \
__VA_ARGS__ \
}, \
.initialize = lochara_state_initialize_dodge_, \
.update = lochara_state_update_dodge_, \
.finalize = lochara_state_cancel_transition_, \
}
#define lochara_state_jump() \
(statman_meta_t) { \
.state = LOCHARA_STATE_JUMP, \
.name = "JUMP", \
.initialize = lochara_state_initialize_jump_, \
}
typedef struct {
uint64_t duration;
float direction;
vec2_t offset;
loshader_character_motion_id_t motion1;
loshader_character_motion_id_t motion2;
} lochara_state_teleport_param_t;
#define lochara_state_teleport(...) \
(statman_meta_t) { \
.state = LOCHARA_STATE_TELEPORT_FRONT, \
.name = "TELEPORT_FRONT", \
.data = &(lochara_state_teleport_param_t) { \
.direction = 1, \
__VA_ARGS__ \
}, \
.initialize = lochara_state_initialize_any_, \
.update = lochara_state_update_teleport_, \
}, \
(statman_meta_t) { \
.state = LOCHARA_STATE_TELEPORT_BEHIND, \
.name = "TELEPORT_BEHIND", \
.data = &(lochara_state_teleport_param_t) { \
.direction = -1, \
__VA_ARGS__ \
}, \
.initialize = lochara_state_initialize_any_, \
.update = lochara_state_update_teleport_, \
}

184
core/lochara/strategy.c Normal file
View File

@ -0,0 +1,184 @@
#include "./strategy.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "util/chaos/xorshift.h"
#include "util/math/algorithm.h"
#include "util/math/vector.h"
#include "util/statman/statman.h"
#include "core/loeffect/effect.h"
#include "core/loeffect/recipient.h"
#include "./base.h"
#include "./combat.h"
#include "./state.h"
#define SEED_ 3467
#define PARRY_DAMAGE_ 1
void lochara_strategy_initialize_any_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
lochara_base_t* base = instance;
base->param.last_strategy_changed = base->ticker->time;
}
void lochara_strategy_cancel_transition_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
*next = meta->state;
}
void lochara_strategy_initialize_scouting_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
lochara_strategy_initialize_any_(meta, instance, next);
const lochara_strategy_scouting_param_t* p = meta->data;
lochara_base_t* base = instance;
statman_transition_to(
p->state_table, base, &base->param.state, LOCHARA_STATE_STAND);
}
void lochara_strategy_update_scouting_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
const lochara_strategy_scouting_param_t* p = meta->data;
assert(p->state_table != NULL);
assert(p->period > 0);
lochara_base_t* base = instance;
const uint64_t t = base->ticker->time;
const bool knockback = base->param.last_knockback+500 > t;
const bool event = base->player->event.executor != 0;
/* ---- die ---- */
if (!loeffect_recipient_is_alive(&base->param.recipient)) {
*next = LOCHARA_STRATEGY_DEAD;
return;
}
/* ---- strategy transition ---- */
vec2_t disp;
locommon_position_sub(
&disp, &base->player->entity->super.super.pos, &base->super.super.pos);
if (!knockback && MATH_ABS(disp.y) < .1f && !event) {
disp.x *= MATH_SIGN_NOZERO(base->param.direction.x);
const float b = p->range_back;
if (-p->range_close*b < disp.x && disp.x < p->range_close) {
*next = p->found_close;
} else if (-p->range_mid*b < disp.x && disp.x < p->range_mid) {
*next = p->found_mid;
} else if (-p->range_long*b < disp.x && disp.x < p->range_long) {
*next = p->found_long;
}
if (*next != LOCHARA_STRATEGY_SCOUTING) return;
}
/* ---- state transition ---- */
const uint64_t since = base->param.last_state_changed;
uint64_t seed = 1+since*SEED_;
# define rand_() (seed = chaos_xorshift(seed))
const uint64_t dur = rand_()%p->period + p->period/2;
lochara_state_t state = base->param.state;
if (knockback) {
state = LOCHARA_STATE_STAND;
} else if (since+dur < t) {
seed = 1+t*SEED_;
if (state == LOCHARA_STATE_STAND && rand_()%100/100.f < p->stagger) {
const float r = .5f + base->cache.ground_pos.x*.5f;
if (rand_()%100/100.f < r) {
base->param.direction = vec2(-1, 0);
state = LOCHARA_STATE_WALK_LEFT;
} else {
base->param.direction = vec2(1, 0);
state = LOCHARA_STATE_WALK_RIGHT;
}
} else {
state = LOCHARA_STATE_STAND;
}
}
statman_transition_to(p->state_table, base, &base->param.state, state);
# undef rand_
}
void lochara_strategy_initialize_combat_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
lochara_base_t* base = instance;
const lochara_strategy_combat_param_t* p = meta->data;
assert(p->actions != NULL);
if (base->param.last_knockback+p->parry_window > base->ticker->time) {
loentity_character_apply_effect(
&base->super, &loeffect_damage(PARRY_DAMAGE_));
*next = p->parried_next;
return;
}
lochara_strategy_initialize_any_(meta, instance, next);
lochara_combat_action_execute_all_attacks(p->actions, base);
vec2_t disp;
locommon_position_sub(
&disp, &base->player->entity->super.super.pos, &base->super.super.pos);
base->param.direction = vec2(MATH_SIGN_NOZERO(disp.x), 0);
}
void lochara_strategy_update_combat_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
const lochara_strategy_combat_param_t* p = meta->data;
assert(p->actions != NULL);
assert(p->next != meta->state);
lochara_base_t* base = instance;
if (!p->gravity) base->param.gravity = 0;
const uint64_t t = base->ticker->time - base->param.last_strategy_changed;
const lochara_combat_action_t* action =
lochara_combat_action_find_by_time(p->actions, t);
if (action == NULL) {
*next = p->next;
return;
}
statman_transition_to(
p->state_table, base, &base->param.state, action->state);
}

131
core/lochara/strategy.h Normal file
View File

@ -0,0 +1,131 @@
#pragma once
#include "util/statman/statman.h"
#include "./combat.h"
typedef enum {
/* BENUM BEGIN lochara_strategy */
LOCHARA_STRATEGY_WAIT,
LOCHARA_STRATEGY_WAKE_UP,
LOCHARA_STRATEGY_WAKE_UP_EVENT,
LOCHARA_STRATEGY_SCOUTING,
LOCHARA_STRATEGY_APPROACH,
LOCHARA_STRATEGY_COMBO1,
LOCHARA_STRATEGY_COMBO2,
LOCHARA_STRATEGY_COMBO3,
LOCHARA_STRATEGY_SHOOT1,
LOCHARA_STRATEGY_SHOOT2,
LOCHARA_STRATEGY_SHOOT3,
LOCHARA_STRATEGY_SPELL1,
LOCHARA_STRATEGY_SPELL2,
LOCHARA_STRATEGY_SPELL3,
LOCHARA_STRATEGY_DOWN,
LOCHARA_STRATEGY_DEAD,
LOCHARA_STRATEGY_KILL,
/* BENUM END */
} lochara_strategy_t;
/* generated benum header */
#include "core/lochara/benum/strategy.h"
/* ---- default strategy handlers ---- */
void
lochara_strategy_initialize_any_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
lochara_strategy_cancel_transition_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
lochara_strategy_initialize_scouting_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
lochara_strategy_update_scouting_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
lochara_strategy_initialize_combat_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
void
lochara_strategy_update_combat_(
const statman_meta_t* meta,
void* instance,
statman_state_t* next
);
/* ---- default strategy constructors ---- */
typedef struct {
const statman_meta_t* state_table;
uint64_t period;
float stagger; /* 0~1 */
float range_back;
float range_close;
float range_mid;
float range_long;
lochara_strategy_t found_close;
lochara_strategy_t found_mid;
lochara_strategy_t found_long;
} lochara_strategy_scouting_param_t;
#define lochara_strategy_scouting(...) \
(statman_meta_t) { \
.state = LOCHARA_STRATEGY_SCOUTING, \
.name = "SCOUTING", \
.data = &(lochara_strategy_scouting_param_t) { \
__VA_ARGS__ \
}, \
.initialize = lochara_strategy_initialize_scouting_, \
.update = lochara_strategy_update_scouting_, \
}
typedef struct {
const statman_meta_t* state_table;
const lochara_combat_action_t* actions;
uint64_t parry_window;
lochara_strategy_t parried_next;
bool gravity;
lochara_strategy_t next;
} lochara_strategy_combat_param_t;
#define lochara_strategy_combat(NAME, ...) \
(statman_meta_t) { \
.state = LOCHARA_STRATEGY_##NAME, \
.name = #NAME, \
.data = &(lochara_strategy_combat_param_t) { \
__VA_ARGS__ \
}, \
.initialize = lochara_strategy_initialize_combat_, \
.update = lochara_strategy_update_combat_, \
.finalize = lochara_strategy_cancel_transition_, \
}

View File

@ -0,0 +1,718 @@
#include "./theists_child.h"
#include <stdbool.h>
#include <stdint.h>
#include "util/math/algorithm.h"
#include "util/math/vector.h"
#include "util/statman/statman.h"
#include "core/lobullet/linear.h"
#include "core/lobullet/pool.h"
#include "core/locommon/position.h"
#include "core/loentity/ground.h"
#include "core/loplayer/event.h"
#include "core/loplayer/stance.h"
#include "core/loresource/music.h"
#include "core/loshader/character.h"
#include "./base.h"
#include "./state.h"
#include "./strategy.h"
#include "./type.h"
#define WIDTH_ .025f
#define HEIGHT_ .06f
#define MARKER_ .03f
#define COLOR_ vec4(0, 0, 0, 1)
#define BPM_ 140
#define BEAT_ (60.f/BPM_)
#define BEAT_MS_ (BEAT_*1000)
#define MUSIC_DURATION_ (BEAT_MS_*232)
#define WAKE_UP_RANGE_ (WIDTH_*6)
#define REWARD_STANCE_ LOPLAYER_STANCE_REVOLUTIONER
static const loeffect_recipient_status_t base_status_ = {
.attack = .3f,
.defence = .92f,
.speed = .31f,
.jump = 1.1f,
};
static void lochara_theists_child_initialize_shoot_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
lochara_state_initialize_any_(meta, instance, state);
lochara_base_t* base = instance;
vec2_t dir;
locommon_position_sub(
&dir, &base->player->entity->super.super.pos, &base->super.super.pos);
if (dir.x == 0 && dir.y == 0) dir = vec2(1, 0);
vec2_diveq(&dir, vec2_length(&dir));
const vec2_t invdir = vec2(dir.y, -dir.x);
base->param.direction = vec2(MATH_SIGN_NOZERO(dir.x), 0);
for (int32_t i = -4; i <= 4; ++i) {
vec2_t v;
vec2_mul(&v, &dir, 1);
vec2_t p;
vec2_mul(&p, &invdir, i*.02f);
locommon_position_t pos = base->super.super.pos;
vec2_addeq(&pos.fract, &p);
locommon_position_reduce(&pos);
lobullet_base_t* b = lobullet_pool_create(base->bullet);
lobullet_linear_circle_build(b,
.owner = base->super.super.id,
.basepos = pos,
.size = vec2(.02f, .02f),
.color = vec4(1, 1, 1, 1),
.velocity = v,
.knockback = 1,
.effect = loeffect_damage(
base->param.recipient.status.attack*.6f),
.duration = 1000,
);
loentity_store_add(base->entities, &b->super.super);
}
}
static void lochara_theists_child_update_thrust_in_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
static const uint64_t dur = BEAT_MS_/4;
lochara_base_t* base = instance;
uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t > dur) t = dur;
const float tf = t*1.f / dur;
base->cache.instance.motion.time = powf(tf, 2);
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
}
static void lochara_theists_child_update_thrust_out_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
static const uint64_t dur = BEAT_MS_/4;
lochara_base_t* base = instance;
uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t > dur) t = dur;
const float tf = t*1.f / dur;
base->cache.instance.motion.time = powf(tf, 2);
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
}
static void lochara_theists_child_update_down_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
const bool fast =
meta->state == LOCHARA_STATE_DOWN ||
meta->state == LOCHARA_STATE_REVIVE;
const bool reverse =
meta->state == LOCHARA_STATE_REVIVE ||
meta->state == LOCHARA_STATE_RESUSCITATE;
const uint64_t dur = fast? reverse? BEAT_MS_*3: BEAT_MS_: BEAT_MS_*16;
const uint64_t key = fast? dur: dur/2;
lochara_base_t* base = instance;
base->param.movement = vec2(0, 0);
uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t > dur) t = dur;
if (reverse) t = dur - t;
if (t < key) {
const float tf = t*1.f/key;
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_SIT;
base->cache.instance.motion.time = powf(tf, 6);
} else {
const float tf = (t-key)*1.f/(dur-key);
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_SIT;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_DOWN;
base->cache.instance.motion.time = powf(tf, 4);
}
}
static const statman_meta_t state_table_[] = {
lochara_state_stand(
.period = BEAT_MS_*2,
.acceleration = {{5, 5}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_ATTACK1,
),
lochara_state_walk(
.period = BEAT_MS_,
.acceleration = {{5, 5}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_WALK,
),
lochara_state_dodge(
.duration = BEAT_MS_/2,
.speed = 1,
.acceleration = {{3, 3}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_WALK,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
),
lochara_state_jump(),
lochara_state_teleport(
.duration = BEAT_MS_*2,
.offset = {{WIDTH_*2, 0}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_ATTACK2,
),
{
.state = LOCHARA_STATE_THRUST_IN,
.name = "THRUST_IN",
.initialize = lochara_state_initialize_any_,
.update = lochara_theists_child_update_thrust_in_state_,
},
{
.state = LOCHARA_STATE_THRUST_OUT,
.name = "THRUST_OUT",
.initialize = lochara_state_initialize_any_,
.update = lochara_theists_child_update_thrust_out_state_,
},
{
.state = LOCHARA_STATE_SHOOT,
.name = "SHOOT",
.initialize = lochara_theists_child_initialize_shoot_state_,
.update = lochara_theists_child_update_thrust_in_state_,
},
{
.state = LOCHARA_STATE_DOWN,
.name = "DOWN",
.initialize = lochara_state_initialize_any_,
.update = lochara_theists_child_update_down_state_,
},
{
.state = LOCHARA_STATE_REVIVE,
.name = "REVIVE",
.initialize = lochara_state_initialize_any_,
.update = lochara_theists_child_update_down_state_,
},
{
.state = LOCHARA_STATE_DEAD,
.name = "DEAD",
.initialize = lochara_state_initialize_any_,
.update = lochara_theists_child_update_down_state_,
},
{
.state = LOCHARA_STATE_RESUSCITATE,
.name = "RESUSCITATE",
.initialize = lochara_state_initialize_any_,
.update = lochara_theists_child_update_down_state_,
},
{0},
};
static void lochara_theists_child_update_wait_strategy_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
static const float range2 = WAKE_UP_RANGE_*WAKE_UP_RANGE_;
lochara_base_t* base = instance;
vec2_t disp;
locommon_position_sub(
&disp, &base->player->entity->super.super.pos, &base->super.super.pos);
if (fabsf(disp.y) < HEIGHT_/2 && vec2_pow_length(&disp) < range2) {
loeffect_recipient_apply_effect(
&base->param.recipient, &loeffect_resuscitate());
if (loplayer_stance_set_has(&base->player->stances, REWARD_STANCE_)) {
*next = LOCHARA_STRATEGY_WAKE_UP;
} else {
*next = LOCHARA_STRATEGY_WAKE_UP_EVENT;
}
return;
}
statman_transition_to(
state_table_, instance, &base->param.state, LOCHARA_STATE_DEAD);
}
static void lochara_theists_child_initialize_wake_up_strategy_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
lochara_strategy_initialize_any_(meta, instance, next);
if (meta->state != LOCHARA_STRATEGY_WAKE_UP_EVENT) return;
lochara_base_t* base = instance;
loentity_character_apply_effect(
&base->super, &loeffect_fanatic(MUSIC_DURATION_));
loentity_character_apply_effect(
&base->player->entity->super, &loeffect_curse(MUSIC_DURATION_));
}
static void lochara_theists_child_update_wake_up_strategy_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
const bool ev = meta->state == LOCHARA_STRATEGY_WAKE_UP_EVENT;
const uint64_t dur = (ev? BEAT_MS_*64: BEAT_MS_*16);
lochara_base_t* base = instance;
const uint64_t t = base->ticker->time - base->param.last_strategy_changed;
if (t >= dur) {
*next = LOCHARA_STRATEGY_APPROACH;
return;
}
statman_transition_to(
state_table_, instance, &base->param.state, LOCHARA_STATE_RESUSCITATE);
}
static void lochara_theists_child_update_approach_strategy_(
const statman_meta_t* meta, void* instance, statman_state_t* next) {
assert(meta != NULL);
assert(instance != NULL);
assert(next != NULL);
lochara_base_t* base = instance;
if (!loeffect_recipient_is_alive(&base->param.recipient)) {
*next = LOCHARA_STRATEGY_DEAD;
return;
}
const uint64_t since = base->param.last_strategy_changed;
uint64_t until = since + BEAT_MS_;
if (base->player->event.executor == base->super.super.id) {
const uint64_t msince = base->player->event.ctx.music.since;
if (msince < since) {
const uint64_t beats = (since - msince)/BEAT_MS_ + 1;
until = msince + beats*BEAT_MS_;
}
}
/* ---- strategy transition ---- */
const locommon_position_t* player = &base->player->entity->super.super.pos;
vec2_t disp;
locommon_position_sub(&disp, player, &base->super.super.pos);
if (player->chunk.x != base->super.super.pos.chunk.x ||
player->chunk.y != base->super.super.pos.chunk.y ||
disp.y < -HEIGHT_) {
*next = LOCHARA_STRATEGY_WAIT;
return;
}
const float dist = MATH_ABS(disp.x);
if (base->ticker->time >= until) {
if (MATH_ABS(disp.y) > HEIGHT_) {
*next = LOCHARA_STRATEGY_SHOOT1;
} else if (dist < WIDTH_*4) {
*next = LOCHARA_STRATEGY_COMBO1;
} else if (dist < WIDTH_*8) {
*next = LOCHARA_STRATEGY_SHOOT1;
} else {
*next = LOCHARA_STRATEGY_COMBO2;
}
return;
}
/* ---- approaching ---- */
if (dist > WIDTH_*6) {
base->param.direction = vec2(MATH_SIGN_NOZERO(disp.x), 0);
const lochara_state_t state =
disp.x < 0? LOCHARA_STATE_WALK_LEFT: LOCHARA_STATE_WALK_RIGHT;
statman_transition_to(state_table_, base, &base->param.state, state);
}
}
static const lochara_combat_action_t combo1_[] = {
lochara_combat_action_rest(
.duration = BEAT_MS_/4*3,
.state = LOCHARA_STATE_STAND,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_IN,
),
lochara_combat_action_attack(
.duration = BEAT_MS_,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1.2f,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_IN,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_THRUST_IN,
.damage = .8f,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_IN,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/4*3,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_DODGE_LEFT,
),
{0},
};
static const lochara_combat_action_t combo2_[] = {
lochara_combat_action_rest(
.duration = BEAT_MS_*2,
.state = LOCHARA_STATE_TELEPORT_FRONT,
),
lochara_combat_action_attack(
.duration = BEAT_MS_,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1.2f,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_THRUST_IN,
.damage = .8f,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_IN,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_attack(
.duration = BEAT_MS_/4,
.state = LOCHARA_STATE_THRUST_IN,
.damage = .6f,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_/2,
.state = LOCHARA_STATE_DODGE_RIGHT,
),
{0},
};
static const lochara_combat_action_t shoot1_[] = {
lochara_combat_action_rest(
.duration = 1,
.state = LOCHARA_STATE_JUMP,
),
lochara_combat_action_rest(
.duration = BEAT_MS_-1,
.state = LOCHARA_STATE_STAND,
),
lochara_combat_action_rest(
.duration = BEAT_MS_,
.state = LOCHARA_STATE_SHOOT,
),
lochara_combat_action_rest(
.duration = BEAT_MS_*2,
.state = LOCHARA_STATE_TELEPORT_BEHIND,
),
{0},
};
static const lochara_combat_action_t down_[] = {
lochara_combat_action_rest(
.duration = BEAT_MS_,
.state = LOCHARA_STATE_DOWN,
),
lochara_combat_action_rest(
.duration = BEAT_MS_*3,
.state = LOCHARA_STATE_REVIVE,
),
{0},
};
static const lochara_combat_action_t kill_[] = {
lochara_combat_action_rest(
.duration = BEAT_MS_*12,
.state = LOCHARA_STATE_DEAD,
),
{0},
};
static const lochara_combat_action_t dead_[] = {
lochara_combat_action_rest(
.duration = 30000,
.state = LOCHARA_STATE_DEAD,
),
{0},
};
static const statman_meta_t strategy_table_[] = {
{
.state = LOCHARA_STRATEGY_WAIT,
.name = "WAIT",
.initialize = lochara_strategy_initialize_any_,
.update = lochara_theists_child_update_wait_strategy_,
},
{
.state = LOCHARA_STRATEGY_WAKE_UP,
.name = "WAKE_UP",
.initialize = lochara_strategy_initialize_any_,
.update = lochara_theists_child_update_wake_up_strategy_,
},
{
.state = LOCHARA_STRATEGY_WAKE_UP_EVENT,
.name = "WAKE_UP_EVENT",
.initialize = lochara_theists_child_initialize_wake_up_strategy_,
.update = lochara_theists_child_update_wake_up_strategy_,
},
{
.state = LOCHARA_STRATEGY_APPROACH,
.name = "APPROACH",
.initialize = lochara_strategy_initialize_any_,
.update = lochara_theists_child_update_approach_strategy_,
},
lochara_strategy_combat(COMBO1,
.state_table = state_table_,
.actions = combo1_,
.parry_window = 100,
.parried_next = LOCHARA_STRATEGY_DOWN,
.next = LOCHARA_STRATEGY_APPROACH,
),
lochara_strategy_combat(COMBO2,
.state_table = state_table_,
.actions = combo2_,
.parry_window = 100,
.parried_next = LOCHARA_STRATEGY_DOWN,
.next = LOCHARA_STRATEGY_APPROACH,
),
lochara_strategy_combat(SHOOT1,
.state_table = state_table_,
.actions = shoot1_,
.parry_window = 200,
.parried_next = LOCHARA_STRATEGY_DOWN,
.gravity = true,
.next = LOCHARA_STRATEGY_APPROACH,
),
lochara_strategy_combat(DOWN,
.state_table = state_table_,
.actions = down_,
.gravity = true,
.next = LOCHARA_STRATEGY_APPROACH,
),
lochara_strategy_combat(KILL,
.state_table = state_table_,
.actions = kill_,
.gravity = true,
.next = LOCHARA_STRATEGY_WAIT,
),
lochara_strategy_combat(DEAD,
.state_table = state_table_,
.actions = dead_,
.gravity = true,
.next = LOCHARA_STRATEGY_WAIT,
),
{0},
};
static void lochara_theists_child_update_event_(lochara_base_t* base) {
assert(base != NULL);
static const loplayer_event_command_t wake_up[] = {
loplayer_event_command_play_music(LORESOURCE_MUSIC_ID_BOSS_THEISTS_CHILD),
loplayer_event_command_set_area(.49f, .45f),
loplayer_event_command_set_cinescope(1),
loplayer_event_command_wait(BEAT_MS_*16),
loplayer_event_command_set_line("boss_theists_child_line0"),
loplayer_event_command_wait(BEAT_MS_*16),
loplayer_event_command_set_line("boss_theists_child_line1"),
loplayer_event_command_wait(BEAT_MS_*16),
loplayer_event_command_set_line("boss_theists_child_line2"),
loplayer_event_command_wait(BEAT_MS_*15),
loplayer_event_command_set_line(NULL),
loplayer_event_command_set_cinescope(0),
{0},
};
static const loplayer_event_command_t kill[] = {
loplayer_event_command_set_area(0, 0),
loplayer_event_command_set_cinescope(1),
loplayer_event_command_wait(BEAT_MS_),
loplayer_event_command_stop_music(),
loplayer_event_command_set_line("boss_theists_child_kill_line0"),
loplayer_event_command_wait(BEAT_MS_*4),
loplayer_event_command_set_line("boss_theists_child_kill_line1"),
loplayer_event_command_wait(BEAT_MS_*4),
loplayer_event_command_finalize(),
{0},
};
static const loplayer_event_command_t dead[] = {
loplayer_event_command_set_area(0, 0),
loplayer_event_command_wait(BEAT_MS_),
loplayer_event_command_stop_music(),
loplayer_event_command_set_line("boss_theists_child_dead_line"),
loplayer_event_command_wait(BEAT_MS_*8),
loplayer_event_command_finalize(),
{0},
};
const uint64_t t = base->ticker->time;
loplayer_event_t* event = &base->player->event;
const loentity_id_t id = base->super.super.id;
locommon_position_t basepos = base->super.super.pos;
basepos.fract = vec2(.5f, .5f);
if (base->param.strategy == LOCHARA_STRATEGY_WAKE_UP_EVENT) {
loplayer_event_execute_commands(event, id, &basepos, wake_up);
return;
}
if (event->executor != id) return;
if (base->player->entity->param.state == LOCHARA_STATE_DEAD) {
statman_transition_to(
strategy_table_, base, &base->param.strategy, LOCHARA_STRATEGY_KILL);
loplayer_event_execute_commands(event, id, &basepos, kill);
return;
}
if (base->param.strategy == LOCHARA_STRATEGY_DEAD) {
loplayer_event_execute_commands(event, id, &basepos, dead);
if (!loplayer_stance_set_has(&base->player->stances, REWARD_STANCE_)) {
loplayer_stance_set_add(&base->player->stances, REWARD_STANCE_);
loplayer_popup_queue_new_stance(&base->player->popup, REWARD_STANCE_);
}
return;
}
if (base->player->event.basetime+MUSIC_DURATION_ <= t &&
loeffect_recipient_is_alive(&base->param.recipient)) {
loentity_character_apply_effect(
&base->player->entity->super, &loeffect_curse_trigger());
return;
}
loplayer_event_execute_commands(event, id, &basepos, wake_up);
}
bool lochara_theists_child_update(lochara_base_t* base) {
assert(base != NULL);
loeffect_recipient_update(&base->param.recipient, &base_status_);
statman_update(strategy_table_, base, &base->param.strategy);
lochara_theists_child_update_event_(base);
const float dir = MATH_SIGN_NOZERO(base->param.direction.x);
base->cache.instance = (loshader_character_drawer_instance_t) {
.character_id = LOSHADER_CHARACTER_ID_CAVIA,
.pos = vec2(0, -MARKER_),
.size = vec2(dir*HEIGHT_, HEIGHT_),
.color = COLOR_,
.marker_offset = vec2(0, MARKER_),
};
statman_update(state_table_, base, &base->param.state);
if (base->param.strategy != LOCHARA_STRATEGY_WAIT &&
base->param.strategy != LOCHARA_STRATEGY_WAKE_UP &&
base->param.strategy != LOCHARA_STRATEGY_WAKE_UP_EVENT &&
base->param.strategy != LOCHARA_STRATEGY_DEAD) {
base->cache.instance.marker = lochara_base_affect_bullets(base);
}
lochara_base_calculate_physics(
base, &vec2(WIDTH_, HEIGHT_), &vec2(0, MARKER_));
lochara_base_bind_on_ground(base, &vec2(WIDTH_, HEIGHT_+MARKER_));
return true;
}
void lochara_theists_child_build(
lochara_base_t* base, loentity_ground_t* gnd) {
assert(base != NULL);
assert(gnd != NULL);
base->super.super.pos = gnd->super.pos;
vec2_addeq(&base->super.super.pos.fract, &vec2(0, gnd->size.y));
locommon_position_reduce(&base->super.super.pos);
base->param = (typeof(base->param)) {
.type = LOCHARA_TYPE_THEISTS_CHILD,
.state = LOCHARA_STATE_DEAD,
.last_state_changed = base->ticker->time,
.strategy = LOCHARA_STRATEGY_WAIT,
.last_strategy_changed = base->ticker->time,
.ground = gnd->super.id,
};
loeffect_recipient_initialize(
&base->param.recipient, base->ticker, &base_status_);
}

View File

@ -0,0 +1,18 @@
#pragma once
#include <stdbool.h>
#include "core/loentity/ground.h"
#include "./base.h"
bool
lochara_theists_child_update(
lochara_base_t* base
);
void
lochara_theists_child_build(
lochara_base_t* base,
loentity_ground_t* gnd
);

18
core/lochara/type.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
typedef enum {
/* BENUM BEGIN lochara_type */
LOCHARA_TYPE_PLAYER,
LOCHARA_TYPE_ENCEPHALON,
LOCHARA_TYPE_CAVIA,
LOCHARA_TYPE_WARDER,
LOCHARA_TYPE_BIG_WARDER,
LOCHARA_TYPE_THEISTS_CHILD,
/* BENUM END*/
} lochara_type_t;
/* generated benum utils */
#include "core/lochara/benum/type.h"

370
core/lochara/warder.c Normal file
View File

@ -0,0 +1,370 @@
#include "./warder.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "util/math/algorithm.h"
#include "util/math/vector.h"
#include "util/statman/statman.h"
#include "core/lobullet/base.h"
#include "core/lobullet/linear.h"
#include "core/loeffect/effect.h"
#include "core/loeffect/recipient.h"
#include "core/loentity/ground.h"
#include "core/loresource/sound.h"
#include "core/loshader/character.h"
#include "./base.h"
#include "./combat.h"
#define WIDTH_ .02f
#define HEIGHT_ .055f
#define MARKER_ .022f
#define COLOR_ vec4(0, 0, 0, 1)
#define BULLET_DAMAGE_ .9f
#define BULLET_KNOCKBACK_ 6
#define BULLET_DURATION_ 2000
#define BULLET_SPEED_ .6f
#define BULLET_COLOR_ vec4(0, 0, 0, 1)
#define BULLET_SIZE_ vec2(.03f, .03f)
#define SHOOT_DURATION_ 2000
static const loeffect_recipient_status_t base_status_ = {
.attack = .2f,
.defence = .05f,
.speed = .05f,
.jump = 1.f,
};
static void lochara_warder_update_thrust_in_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
static const uint64_t dur = 100;
lochara_base_t* base = instance;
uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t > dur) t = dur;
const float tf = t*1.f / dur;
base->cache.instance.motion.time = powf(tf, 2);
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
}
static void lochara_warder_update_thrust_out_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
static const uint64_t dur = 100;
lochara_base_t* base = instance;
uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t > dur) t = dur;
const float tf = t*1.f / dur;
base->cache.instance.motion.time = powf(tf, 2);
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
}
static void lochara_warder_update_shoot_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
lochara_base_t* base = instance;
const uint64_t since = base->param.last_state_changed;
uint64_t t = base->ticker->time - since;
if (t > SHOOT_DURATION_) t = SHOOT_DURATION_;
const uint64_t pt = t == 0? 0: base->ticker->prev_time - since;
if (pt < SHOOT_DURATION_/2 && SHOOT_DURATION_/2 <= t) {
vec2_t v = base->param.direction;
vec2_muleq(&v, BULLET_SPEED_);
lobullet_base_t* b = lobullet_pool_create(base->bullet);
lobullet_linear_circle_build(b,
.owner = base->super.super.id,
.basepos = base->super.super.pos,
.size = BULLET_SIZE_,
.color = BULLET_COLOR_,
.velocity = v,
.knockback = BULLET_KNOCKBACK_,
.effect = loeffect_damage(
base->param.recipient.status.attack*BULLET_DAMAGE_),
.duration = BULLET_DURATION_,);
loentity_store_add(base->entities, &b->super.super);
loresource_sound_set_play(
&base->res->sound, LORESOURCE_SOUND_ID_ENEMY_SHOOT);
}
float tf = t*1.f/SHOOT_DURATION_*2;
if (tf < 1) {
tf *= 2;
if (tf < 1) {
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
} else {
tf -= 1;
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK1;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
}
base->cache.instance.motion.time = powf(tf, 4);
} else {
tf -= 1;
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_ATTACK2;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_STAND1;
base->cache.instance.motion.time = tf;
}
}
static void lochara_warder_initialize_resuscitate_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
lochara_state_initialize_any_(meta, instance, state);
lochara_base_t* base = instance;
loeffect_recipient_apply_effect(
&base->param.recipient, &loeffect_resuscitate());
}
static void lochara_warder_update_dead_state_(
const statman_meta_t* meta, void* instance, statman_state_t* state) {
assert(meta != NULL);
assert(instance != NULL);
assert(state != NULL);
static uint64_t dur = 1000;
lochara_base_t* base = instance;
base->param.movement = vec2(0, 0);
uint64_t t = base->ticker->time - base->param.last_state_changed;
if (t > dur) t = dur;
if (base->param.state == LOCHARA_STATE_RESUSCITATE) t = dur - t;
base->cache.instance.motion.from = LOSHADER_CHARACTER_MOTION_ID_STAND1;
base->cache.instance.motion.to = LOSHADER_CHARACTER_MOTION_ID_DOWN;
base->cache.instance.motion.time = powf(t*1.f/dur, 6);
}
static const statman_meta_t state_table_[] = {
lochara_state_stand(
.period = 4000,
.acceleration = {{5, 5}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
),
lochara_state_walk(
.period = 1000,
.acceleration = {{5, 5}},
.motion1 = LOSHADER_CHARACTER_MOTION_ID_STAND1,
.motion2 = LOSHADER_CHARACTER_MOTION_ID_WALK,
),
{
.state = LOCHARA_STATE_THRUST_IN,
.name = "THRUST_IN",
.initialize = lochara_state_initialize_any_,
.update = lochara_warder_update_thrust_in_state_,
},
{
.state = LOCHARA_STATE_THRUST_OUT,
.name = "THRUST_OUT",
.initialize = lochara_state_initialize_any_,
.update = lochara_warder_update_thrust_out_state_,
},
{
.state = LOCHARA_STATE_SHOOT,
.name = "SHOOT",
.initialize = lochara_state_initialize_any_,
.update = lochara_warder_update_shoot_state_,
},
{
.state = LOCHARA_STATE_DEAD,
.name = "DEAD",
.initialize = lochara_state_initialize_any_,
.update = lochara_warder_update_dead_state_,
},
{
.state = LOCHARA_STATE_RESUSCITATE,
.name = "RESUSCITATE",
.initialize = lochara_warder_initialize_resuscitate_state_,
.update = lochara_warder_update_dead_state_,
},
{0},
};
static const lochara_combat_action_t combo1_[] = {
lochara_combat_action_rest(
.duration = 400,
.state = LOCHARA_STATE_STAND,
),
lochara_combat_action_attack(
.duration = 200,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = 200,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_attack(
.duration = 300,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1,
),
lochara_combat_action_rest(
.duration = 200,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_attack(
.duration = 300,
.state = LOCHARA_STATE_THRUST_IN,
.damage = 1.2f,
),
lochara_combat_action_rest(
.duration = 200,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_attack(
.duration = 300,
.state = LOCHARA_STATE_THRUST_IN,
.damage = .8f,
),
lochara_combat_action_rest(
.duration = 400,
.state = LOCHARA_STATE_THRUST_OUT,
),
lochara_combat_action_rest(
.duration = 600,
.state = LOCHARA_STATE_STAND,
),
{0},
};
static const lochara_combat_action_t shoot1_[] = {
lochara_combat_action_rest(
.duration = 100,
.state = LOCHARA_STATE_STAND,
),
lochara_combat_action_rest(
.duration = SHOOT_DURATION_,
.state = LOCHARA_STATE_SHOOT,
),
lochara_combat_action_rest(
.duration = 500,
.state = LOCHARA_STATE_STAND,
),
{0},
};
static const lochara_combat_action_t dead_[] = {
lochara_combat_action_rest(
.duration = 30000,
.state = LOCHARA_STATE_DEAD,
),
lochara_combat_action_rest(
.duration = 2000,
.state = LOCHARA_STATE_RESUSCITATE,
),
{0},
};
static const statman_meta_t strategy_table_[] = {
lochara_strategy_scouting(
.state_table = state_table_,
.period = 2000,
.stagger = .8f,
.range_back = .5f,
.range_close = WIDTH_*4,
.found_close = LOCHARA_STRATEGY_COMBO1,
.range_mid = .4f,
.found_mid = LOCHARA_STRATEGY_SHOOT1,
),
lochara_strategy_combat(COMBO1,
.state_table = state_table_,
.actions = combo1_,
.next = LOCHARA_STRATEGY_SCOUTING,
),
lochara_strategy_combat(SHOOT1,
.state_table = state_table_,
.actions = shoot1_,
.next = LOCHARA_STRATEGY_SCOUTING,
),
lochara_strategy_combat(DEAD,
.state_table = state_table_,
.actions = dead_,
.next = LOCHARA_STRATEGY_SCOUTING,
),
{0},
};
bool lochara_warder_update(lochara_base_t* base) {
assert(base != NULL);
loeffect_recipient_update(&base->param.recipient, &base_status_);
statman_update(strategy_table_, base, &base->param.strategy);
const float dir = MATH_SIGN_NOZERO(base->param.direction.x);
base->cache.instance = (loshader_character_drawer_instance_t) {
.character_id = LOSHADER_CHARACTER_ID_WARDER,
.pos = vec2(0, -MARKER_),
.size = vec2(dir*HEIGHT_, HEIGHT_),
.color = COLOR_,
.marker_offset = vec2(0, MARKER_),
};
statman_update(state_table_, base, &base->param.state);
if (base->param.state != LOCHARA_STATE_DEAD) {
base->cache.instance.marker = lochara_base_affect_bullets(base);
}
lochara_base_calculate_physics(
base, &vec2(WIDTH_, HEIGHT_), &vec2(0, MARKER_));
lochara_base_bind_on_ground(base, &vec2(WIDTH_, HEIGHT_+MARKER_));
return true;
}
void lochara_warder_build(
lochara_base_t* base, const loentity_ground_t* gnd, float pos) {
assert(base != NULL);
assert(gnd != NULL);
assert(MATH_FLOAT_VALID(pos));
base->super.super.pos = gnd->super.pos;
vec2_addeq(
&base->super.super.pos.fract,
&vec2(gnd->size.x*pos, gnd->size.y));
locommon_position_reduce(&base->super.super.pos);
base->param = (typeof(base->param)) {
.type = LOCHARA_TYPE_WARDER,
.state = LOCHARA_STATE_STAND,
.last_state_changed = base->ticker->time,
.strategy = LOCHARA_STRATEGY_SCOUTING,
.last_strategy_changed = base->ticker->time,
.ground = gnd->super.id,
};
loeffect_recipient_initialize(
&base->param.recipient, base->ticker, &base_status_);
}

19
core/lochara/warder.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <stdbool.h>
#include "core/loentity/ground.h"
#include "./base.h"
bool
lochara_warder_update(
lochara_base_t* base
);
void
lochara_warder_build(
lochara_base_t* base,
const loentity_ground_t* gnd,
float pos
);

View File

@ -1,27 +0,0 @@
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
)

View File

@ -1,434 +0,0 @@
#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_
}

View File

@ -1,100 +0,0 @@
#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
);

View File

@ -1,787 +0,0 @@
#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;
}

View File

@ -1,37 +0,0 @@
#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
);

View File

@ -1,157 +0,0 @@
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_
}

View File

@ -1,265 +0,0 @@
#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;
}

View File

@ -1,36 +0,0 @@
#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)

View File

@ -1,138 +0,0 @@
#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_
}

View File

@ -1,34 +0,0 @@
#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
);

View File

@ -1,607 +0,0 @@
#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;
}

View File

@ -1,37 +0,0 @@
#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
);

View File

@ -1,228 +0,0 @@
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(&center);
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_
}

View File

@ -1,68 +0,0 @@
#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_
}

View File

@ -1,76 +0,0 @@
#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
);

View File

@ -1,122 +0,0 @@
#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];
}

View File

@ -1,48 +0,0 @@
#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
);

View File

@ -1,305 +0,0 @@
#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);
}

View File

@ -1,34 +0,0 @@
#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)

View File

@ -1,763 +0,0 @@
#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;
}

View File

@ -1,37 +0,0 @@
#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
);

View File

@ -1,150 +0,0 @@
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_
}

View File

@ -1,139 +0,0 @@
#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;
}

View File

@ -1,54 +0,0 @@
#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
);

View File

@ -1,299 +0,0 @@
#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);
}

View File

@ -1,33 +0,0 @@
#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)

View File

@ -4,6 +4,7 @@ add_library(locommon
null.c
physics.c
position.c
screen.c
ticker.c
)
target_link_libraries(locommon
@ -13,3 +14,6 @@ target_link_libraries(locommon
math
mpkutil
)
add_dependencies(locommon
benum-generated
)

View File

@ -26,6 +26,12 @@ size_t locommon_counter_count(locommon_counter_t* counter) {
return counter->next++;
}
void locommon_counter_reset(locommon_counter_t* counter) {
assert(counter != NULL);
counter->next = 0;
}
void locommon_counter_pack(
const locommon_counter_t* counter, msgpack_packer* packer) {
assert(counter != NULL);

View File

@ -25,6 +25,11 @@ locommon_counter_count(
locommon_counter_t* counter
);
void
locommon_counter_reset(
locommon_counter_t* counter
);
void
locommon_counter_pack(
const locommon_counter_t* counter,

View File

@ -5,15 +5,16 @@
#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_LEFT = 1 << 0,
LOCOMMON_INPUT_BUTTON_RIGHT = 1 << 1,
LOCOMMON_INPUT_BUTTON_DODGE = 1 << 2,
LOCOMMON_INPUT_BUTTON_JUMP = 1 << 3,
LOCOMMON_INPUT_BUTTON_GUARD = 1 << 4,
LOCOMMON_INPUT_BUTTON_SHOOT = 1 << 5,
LOCOMMON_INPUT_BUTTON_MENU = 1 << 6,
LOCOMMON_INPUT_BUTTON_OK = LOCOMMON_INPUT_BUTTON_SHOOT,
LOCOMMON_INPUT_BUTTON_CANCEL = LOCOMMON_INPUT_BUTTON_MENU,
} locommon_input_button_t;
typedef uint16_t locommon_input_buttons_t;

View File

@ -13,11 +13,17 @@
#include "core/locommon/null.h"
/* THE FOLLOWING INCLUDES DESTROY DEPENDENCY STRUCTURE BETWEEN MODULES. :( */
#include "core/lobullet/type.h"
#include "core/lochara/state.h"
#include "core/lochara/strategy.h"
#include "core/lochara/type.h"
#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"
#include "core/loground/type.h"
#include "core/loparticle/misc.h"
#include "core/loresource/music.h"
#include "core/loresource/sound.h"
#define LOCOMMON_MSGPACK_PACK_ANY_(packer, v) _Generic((v), \
const int32_t*: locommon_msgpack_pack_int32_, \
@ -27,17 +33,30 @@
const char*: locommon_msgpack_pack_str_, \
const vec2_t*: locommon_msgpack_pack_vec2_, \
const vec4_t*: locommon_msgpack_pack_vec4_, \
\
const lobullet_type_t*: locommon_msgpack_lobullet_type_pack_, \
\
const lochara_type_t*: locommon_msgpack_lochara_type_pack_, \
const lochara_state_t*: locommon_msgpack_lochara_state_pack_, \
const lochara_strategy_t*: locommon_msgpack_lochara_strategy_pack_, \
\
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_id_t*: locommon_msgpack_loeffect_id_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 \
const loeffect_generic_lasting_param_t*: loeffect_generic_lasting_param_pack, \
const loeffect_recipient_t*: loeffect_recipient_pack, \
\
const loground_type_t*: locommon_msgpack_loground_type_pack_, \
\
const loparticle_type_t*: locommon_msgpack_loparticle_type_pack_, \
\
const loresource_music_id_t*: locommon_msgpack_loresource_music_id_pack_, \
const loresource_sound_id_t*: locommon_msgpack_loresource_sound_id_pack_ \
)(v, packer)
#define LOCOMMON_MSGPACK_PACK_ANY(packer, v) \
@ -50,17 +69,30 @@
bool*: locommon_msgpack_unpack_bool_, \
vec2_t*: locommon_msgpack_unpack_vec2_, \
vec4_t*: locommon_msgpack_unpack_vec4_, \
\
lobullet_type_t*: locommon_msgpack_lobullet_type_unpack_, \
\
lochara_type_t*: locommon_msgpack_lochara_type_unpack_, \
lochara_state_t*: locommon_msgpack_lochara_state_unpack_, \
lochara_strategy_t*: locommon_msgpack_lochara_strategy_unpack_, \
\
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_id_t*: locommon_msgpack_loeffect_id_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 \
loeffect_generic_lasting_param_t*: loeffect_generic_lasting_param_unpack, \
loeffect_recipient_t*: loeffect_recipient_unpack, \
\
loground_type_t*: locommon_msgpack_loground_type_unpack_, \
\
loparticle_type_t*: locommon_msgpack_loparticle_type_unpack_, \
\
loresource_music_id_t*: locommon_msgpack_loresource_music_id_unpack_, \
loresource_sound_id_t*: locommon_msgpack_loresource_sound_id_unpack_ \
)(v, obj)
static inline void locommon_msgpack_pack_int32_(
@ -116,3 +148,49 @@ static inline bool locommon_msgpack_unpack_vec4_(
vec4_t* v, const msgpack_object* obj) {
return mpkutil_get_vec4(obj, v);
}
#define enum_packer_(name) \
static inline void locommon_msgpack_##name##_pack_( \
const name##_t* v, msgpack_packer* packer) { \
mpkutil_pack_str(packer, name##_stringify(*v)); \
}
#define enum_unpacker_(name) \
static inline bool locommon_msgpack_##name##_unpack_( \
name##_t* v, const msgpack_object* obj) { \
const char* str; \
size_t len; \
return \
mpkutil_get_str(obj, &str, &len) && \
name##_unstringify(v, str, len); \
}
enum_packer_(lobullet_type);
enum_unpacker_(lobullet_type);
enum_packer_(lochara_state);
enum_unpacker_(lochara_state);
enum_packer_(lochara_strategy);
enum_unpacker_(lochara_strategy);
enum_packer_(lochara_type);
enum_unpacker_(lochara_type);
enum_packer_(loeffect_id);
enum_unpacker_(loeffect_id);
enum_packer_(loground_type);
enum_unpacker_(loground_type);
enum_packer_(loparticle_type);
enum_unpacker_(loparticle_type);
enum_packer_(loresource_music_id);
enum_unpacker_(loresource_music_id);
enum_packer_(loresource_sound_id);
enum_unpacker_(loresource_sound_id);
#undef enum_packer_
#undef enum_unpacker_

94
core/locommon/screen.c Normal file
View File

@ -0,0 +1,94 @@
#include "./screen.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include "util/math/matrix.h"
#include "util/math/vector.h"
#define CM_PER_INCH_ 2.54f
#define CHUNK_INCH_ 16
#define MAX_SCALE_ (1/.5f)
bool locommon_screen_valid(const locommon_screen_t* screen) {
return
screen != NULL &&
vec2_valid(&screen->resolution) &&
vec2_valid(&screen->dpi) &&
screen->resolution.x > 0 &&
screen->resolution.y > 0 &&
screen->dpi.x > 0 &&
screen->dpi.y > 0;
}
void locommon_screen_calc_pixels_from_cm(
const locommon_screen_t* screen, vec2_t* pixels, const vec2_t* cm) {
assert(locommon_screen_valid(screen));
assert(pixels != NULL);
assert(vec2_valid(cm));
locommon_screen_calc_pixels_from_inch(
screen, pixels, &vec2(cm->x/CM_PER_INCH_, cm->y/CM_PER_INCH_));
}
void locommon_screen_calc_pixels_from_inch(
const locommon_screen_t* screen, vec2_t* pixels, const vec2_t* inch) {
assert(locommon_screen_valid(screen));
assert(pixels != NULL);
assert(vec2_valid(inch));
*pixels = *inch;
pixels->x *= screen->dpi.x;
pixels->y *= screen->dpi.y;
}
void locommon_screen_calc_winpos_from_cm(
const locommon_screen_t* screen, vec2_t* winpos, const vec2_t* cm) {
assert(locommon_screen_valid(screen));
assert(winpos != NULL);
assert(vec2_valid(cm));
locommon_screen_calc_winpos_from_inch(
screen, winpos, &vec2(cm->x/CM_PER_INCH_, cm->y/CM_PER_INCH_));
}
void locommon_screen_calc_winpos_from_inch(
const locommon_screen_t* screen, vec2_t* winpos, const vec2_t* inch) {
assert(locommon_screen_valid(screen));
assert(winpos != NULL);
assert(vec2_valid(inch));
locommon_screen_calc_pixels_from_inch(screen, winpos, inch);
winpos->x /= screen->resolution.x / 2;
winpos->y /= screen->resolution.y / 2;
}
void locommon_screen_calc_winpos_from_pixels(
const locommon_screen_t* screen, vec2_t* winpos, const vec2_t* pixels) {
assert(locommon_screen_valid(screen));
assert(winpos != NULL);
assert(vec2_valid(pixels));
*winpos = *pixels;
winpos->x /= screen->resolution.x / 2;
winpos->y /= screen->resolution.y / 2;
}
void locommon_screen_build_projection_matrix(
const locommon_screen_t* screen, mat4_t* proj) {
assert(screen != NULL);
assert(proj != NULL);
vec2_t scale;
locommon_screen_calc_winpos_from_inch(
screen, &scale, &vec2(CHUNK_INCH_, CHUNK_INCH_));
if (scale.x > MAX_SCALE_) {
scale.y *= MAX_SCALE_/scale.x;
scale.x = MAX_SCALE_;
}
*proj = mat4_scale(scale.x, scale.y, 1);
}

57
core/locommon/screen.h Normal file
View File

@ -0,0 +1,57 @@
#pragma once
#include <stdbool.h>
#include "util/math/matrix.h"
#include "util/math/vector.h"
typedef struct {
vec2_t resolution;
vec2_t dpi;
} locommon_screen_t;
bool
locommon_screen_valid(
const locommon_screen_t* screen
);
void
locommon_screen_calc_pixels_from_cm(
const locommon_screen_t* screen,
vec2_t* pixels,
const vec2_t* cm
);
void
locommon_screen_calc_pixels_from_inch(
const locommon_screen_t* screen,
vec2_t* pixels,
const vec2_t* inch
);
void
locommon_screen_calc_winpos_from_cm(
const locommon_screen_t* screen,
vec2_t* winpos,
const vec2_t* cm
);
void
locommon_screen_calc_winpos_from_inch(
const locommon_screen_t* screen,
vec2_t* winpos,
const vec2_t* inch
);
void
locommon_screen_calc_winpos_from_pixels(
const locommon_screen_t* screen,
vec2_t* winpos,
const vec2_t* pixels
);
void
locommon_screen_build_projection_matrix(
const locommon_screen_t* screen,
mat4_t* proj
);

View File

@ -22,9 +22,11 @@ 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 = time - ticker->time;
ticker->delta_f = ticker->delta*1.f / LOCOMMON_TICKER_UNIT;
ticker->time = time;
ticker->prev_time = ticker->time;
ticker->time = time;
}
void locommon_ticker_pack(

View File

@ -9,6 +9,7 @@
typedef struct {
uint64_t time;
uint64_t prev_time;
int64_t delta;
float delta_f;

View File

@ -2,7 +2,9 @@ add_library(loeffect
effect.c
generic.c
recipient.c
stance.c
)
target_crial_sources(loeffect
recipient.crial
)
target_link_libraries(loeffect
msgpackc

View File

@ -11,13 +11,6 @@
#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; \

View File

@ -11,17 +11,38 @@
#include "./generic.h"
typedef enum {
LOEFFECT_ID_IMMEDIATE_DAMAGE,
/* system effect */
LOEFFECT_ID_NONE,
LOEFFECT_ID_RESUSCITATE,
LOEFFECT_ID_LOST_DAMAGE,
LOEFFECT_ID_CURSE_TRIGGER,
LOEFFECT_ID_DAMAGE,
LOEFFECT_ID_HEAL,
LOEFFECT_ID_LOST,
LOEFFECT_ID_RETRIEVAL,
LOEFFECT_ID_FANATIC,
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;
#define LOEFFECT_ID_EACH_(PROC) do { \
PROC(NONE, "none", null); \
PROC(RESUSCITATE, "resuscitate", null); \
PROC(LOST_DAMAGE, "lost-damage", null); \
PROC(CURSE_TRIGGER, "curse-trigger", null); \
PROC(DAMAGE, "damage", imm); \
PROC(HEAL, "heal", imm); \
PROC(LOST, "lost", imm); \
PROC(RETRIEVAL, "retrieval", imm); \
PROC(FANATIC, "fanatic", lasting); \
PROC(CURSE, "curse", lasting); \
PROC(AMNESIA, "amnesia", lasting); \
} while (0)
typedef struct {
loeffect_id_t id;
union {
@ -31,36 +52,38 @@ typedef struct {
} data;
} loeffect_t;
#define loeffect_immediate_damage(a) \
#define loeffect_with_null_data_(ID) \
((loeffect_t) { \
.id = LOEFFECT_ID_IMMEDIATE_DAMAGE, \
.id = LOEFFECT_ID_##ID, \
} )
#define loeffect_with_imm_data_(ID, a) \
((loeffect_t) { \
.id = LOEFFECT_ID_##ID, \
.data = { .imm = { \
.amount = a, \
}, }, \
} )
#define loeffect_curse(b, dur) \
#define loeffect_with_lasting_data_(ID, d) \
((loeffect_t) { \
.id = LOEFFECT_ID_CURSE, \
.id = LOEFFECT_ID_##ID, \
.data = { .lasting = { \
.begin = b, \
.duration = dur, \
.duration = d, \
}, }, \
} )
#define loeffect_curse_trigger() \
((loeffect_t) { \
.id = LOEFFECT_ID_CURSE_TRIGGER, \
} )
#define loeffect_none() loeffect_with_null_data_(NONE)
#define loeffect_resuscitate() loeffect_with_null_data_(RESUSCITATE)
#define loeffect_lost_damage() loeffect_with_null_data_(LOST_DAMAGE)
#define loeffect_curse_trigger() loeffect_with_null_data_(CURSE_TRIGGER)
#define loeffect_amnesia(b, dur) \
((loeffect_t) { \
.id = LOEFFECT_ID_AMNESIA, \
.data = { .lasting = { \
.begin = b, \
.duration = dur, \
}, }, \
} )
#define loeffect_damage(a) loeffect_with_imm_data_(DAMAGE, a)
#define loeffect_heal(a) loeffect_with_imm_data_(HEAL, a)
#define loeffect_lost(a) loeffect_with_imm_data_(LOST, a)
#define loeffect_retrieval(a) loeffect_with_imm_data_(RETRIEVAL, a)
#define loeffect_curse(d) loeffect_with_lasting_data_(CURSE, d)
#define loeffect_fanatic(d) loeffect_with_lasting_data_(FANATIC, d)
#define loeffect_amnesia(d) loeffect_with_lasting_data_(AMNESIA, d)
const char*
loeffect_id_stringify(

View File

@ -45,8 +45,8 @@ void loeffect_generic_lasting_param_pack(
msgpack_pack_map(packer, 3);
mpkutil_pack_str(packer, "begin");
msgpack_pack_uint64(packer, param->begin);
mpkutil_pack_str(packer, "start");
msgpack_pack_uint64(packer, param->start);
mpkutil_pack_str(packer, "duration");
msgpack_pack_uint64(packer, param->duration);
@ -66,7 +66,7 @@ bool loeffect_generic_lasting_param_unpack(
# define item_(v) mpkutil_get_map_item_by_str(root, v)
if (!mpkutil_get_uint64(item_("begin"), &param->begin)) {
if (!mpkutil_get_uint64(item_("start"), &param->start)) {
return false;
}
if (!mpkutil_get_uint64(item_("duration"), &param->duration)) {

View File

@ -10,7 +10,7 @@ typedef struct {
} loeffect_generic_immediate_param_t;
typedef struct {
uint64_t begin;
uint64_t start;
uint64_t duration;
float amount;
} loeffect_generic_lasting_param_t;

View File

@ -16,20 +16,25 @@
#include "./generic.h"
#define LOEFFECT_RECIPIENT_EFFECT_PARAM_EACH_(PROC) do { \
PROC(curse); \
} while (0)
#define LOEFFECT_RECIPIENT_EFFECT_PARAM_COUNT 1
/* generated serializer */
#include "core/loeffect/crial/recipient.h"
#define LOST_DAMAGE_AMOUNT_ .03f
#define LOST_DAMAGE_PERIOD_ 1000
void loeffect_recipient_initialize(
loeffect_recipient_t* recipient, const locommon_ticker_t* ticker) {
loeffect_recipient_t* recipient,
const locommon_ticker_t* ticker,
const loeffect_recipient_status_t* status) {
assert(recipient != NULL);
assert(ticker != NULL);
*recipient = (typeof(*recipient)) {
.ticker = ticker,
.madness = 1,
.faith = 1,
};
loeffect_recipient_reset(recipient);
if (status != NULL) recipient->status = *status;
}
void loeffect_recipient_deinitialize(loeffect_recipient_t* recipient) {
@ -37,41 +42,72 @@ void loeffect_recipient_deinitialize(loeffect_recipient_t* recipient) {
}
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;
if (effect->id == LOEFFECT_ID_RESUSCITATE) {
recipient->madness = 1;
recipient->faith = 1;
recipient->effects = (typeof(recipient->effects)) {0};
recipient->last_resuscitate = recipient->ticker->time;
}
if (!loeffect_recipient_is_alive(recipient)) return;
const float faith_backup = recipient->faith;
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;
case LOEFFECT_ID_NONE:
case LOEFFECT_ID_RESUSCITATE:
break;
case LOEFFECT_ID_CURSE:
recipient->effects.curse = effect->data.lasting;
case LOEFFECT_ID_LOST_DAMAGE:
recipient->madness -= LOST_DAMAGE_AMOUNT_;
break;
case LOEFFECT_ID_CURSE_TRIGGER:
recipient->madness = 0;
recipient->last_damage = LOEFFECT_ID_CURSE;
break;
case LOEFFECT_ID_DAMAGE:
recipient->madness -=
effect->data.imm.amount * (1-recipient->status.defence);
recipient->last_damage = recipient->ticker->time;
break;
case LOEFFECT_ID_HEAL:
recipient->madness += effect->data.imm.amount;
recipient->last_heal = recipient->ticker->time;
break;
case LOEFFECT_ID_LOST:
recipient->faith -= effect->data.imm.amount;
recipient->last_lost = recipient->ticker->time;
break;
case LOEFFECT_ID_RETRIEVAL:
recipient->faith += effect->data.imm.amount;
recipient->last_retrieval = recipient->ticker->time;
break;
case LOEFFECT_ID_FANATIC:
recipient->effects.fanatic = effect->data.lasting;
recipient->effects.fanatic.start = recipient->ticker->time;
break;
case LOEFFECT_ID_CURSE:
recipient->effects.curse = effect->data.lasting;
recipient->effects.curse.start = recipient->ticker->time;
break;
case LOEFFECT_ID_AMNESIA:
recipient->effects.amnesia = effect->data.lasting;
recipient->effects.amnesia = effect->data.lasting;
recipient->effects.amnesia.start = recipient->ticker->time;
break;
default:
;
}
if (!loeffect_recipient_is_alive(recipient)) {
recipient->last_die = recipient->ticker->time;
recipient->last_die_reason = effect->id;
}
if (faith_backup > 0 && recipient->faith <= 0) {
recipient->lost_damage_since = recipient->ticker->time;
}
recipient->madness = MATH_CLAMP(recipient->madness, 0, 1);
recipient->faith = MATH_CLAMP(recipient->faith, 0, 1);
}
void loeffect_recipient_update(
@ -79,54 +115,57 @@ void loeffect_recipient_update(
assert(recipient != NULL);
assert(base != NULL);
const uint64_t t = recipient->ticker->time;
const uint64_t pt = recipient->ticker->prev_time;
recipient->status = *base;
if (recipient->madness > 0 && recipient->faith <= 0) {
recipient->madness -= recipient->ticker->delta_f / 30;
recipient->last_damage = LOEFFECT_ID_LOST;
const uint64_t since = recipient->lost_damage_since;
if (pt < since ||
(pt-since)/LOST_DAMAGE_PERIOD_ != (t-since)/LOST_DAMAGE_PERIOD_) {
loeffect_recipient_apply_effect(recipient, &loeffect_lost_damage());
}
}
recipient->madness = MATH_CLAMP(recipient->madness, 0, 1);
recipient->faith = MATH_CLAMP(recipient->faith, 0, 1);
const uint64_t fanatic_st = recipient->effects.fanatic.start;
const uint64_t fanatic_ed = fanatic_st + recipient->effects.fanatic.duration;
if (pt < fanatic_ed && fanatic_ed <= t && recipient->madness <= 0) {
recipient->last_die = recipient->ticker->time;
recipient->last_die_reason = LOEFFECT_ID_FANATIC;
}
}
void loeffect_recipient_effect_param_pack(
const loeffect_recipient_effect_param_t* param,
msgpack_packer* packer) {
assert(param != NULL);
assert(packer != NULL);
bool loeffect_recipient_is_alive(const loeffect_recipient_t* recipient) {
assert(recipient != NULL);
msgpack_pack_map(packer, LOEFFECT_RECIPIENT_EFFECT_PARAM_COUNT);
if (recipient->madness > 0) return true;
# define each_(name) do { \
mpkutil_pack_str(packer, #name); \
LOCOMMON_MSGPACK_PACK_ANY(packer, &param->name); \
} while (0)
const uint64_t t = recipient->ticker->time;
LOEFFECT_RECIPIENT_EFFECT_PARAM_EACH_(each_);
const uint64_t fanatic_st = recipient->effects.fanatic.start;
const uint64_t fanatic_ed = fanatic_st + recipient->effects.fanatic.duration;
if (fanatic_st <= t && t < fanatic_ed) return true;
# undef each_
return false;
}
bool loeffect_recipient_effect_param_unpack(
loeffect_recipient_effect_param_t* param, const msgpack_object* obj) {
assert(param != NULL);
void loeffect_recipient_pack(
const loeffect_recipient_t* recipient, msgpack_packer* packer) {
assert(recipient != NULL);
assert(packer != NULL);
if (obj == NULL) return false;
msgpack_pack_map(packer, CRIAL_PROPERTY_COUNT_);
CRIAL_SERIALIZER_;
}
bool loeffect_recipient_unpack(
loeffect_recipient_t* recipient, const msgpack_object* obj) {
assert(recipient != NULL);
const msgpack_object_map* root = mpkutil_get_map(obj);
if (root == NULL) return false;
# define item_(v) mpkutil_get_map_item_by_str(root, v)
# define each_(name) do { \
if (!LOCOMMON_MSGPACK_UNPACK_ANY(item_(#name), &param->name)) { \
param->name = (typeof(param->name)) {0}; \
} \
} while (0)
LOEFFECT_RECIPIENT_EFFECT_PARAM_EACH_(each_);
# undef each_
# undef item_
CRIAL_DESERIALIZER_;
return true;
}

View File

@ -0,0 +1,31 @@
/* CRIAL
SERIALIZER_BEGIN
mpkutil_pack_str(packer, "$name");
LOCOMMON_MSGPACK_PACK_ANY(packer, &recipient->$code);
END
DESERIALIZER_BEGIN
if (!LOCOMMON_MSGPACK_UNPACK_ANY(
mpkutil_get_map_item_by_str(root, "$name"), &recipient->$code)) {
return false;
}
END
PROPERTY attack = status.attack
PROPERTY defence = status.defence
PROPERTY speed = status.speed
PROPERTY jump = status.jump
PROPERTY faith
PROPERTY madness
PROPERTY last_die
PROPERTY last_die_reason
PROPERTY last_resuscitate
PROPERTY last_damage
PROPERTY last_heal
PROPERTY last_lost
PROPERTY last_retrieval
PROPERTY fanatic = effects.fanatic
PROPERTY curse = effects.curse
PROPERTY amnesia = effects.amnesia
*/

View File

@ -1,6 +1,7 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <msgpack.h>
@ -17,28 +18,37 @@ typedef struct {
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;
float madness;
float faith;
uint64_t last_die;
loeffect_id_t last_die_reason;
uint64_t lost_damage_since;
uint64_t last_resuscitate;
uint64_t last_damage;
uint64_t last_heal;
uint64_t last_lost;
uint64_t last_retrieval;
struct {
loeffect_generic_lasting_param_t fanatic;
loeffect_generic_lasting_param_t curse;
loeffect_generic_lasting_param_t amnesia;
} effects;
} loeffect_recipient_t;
void
loeffect_recipient_initialize(
loeffect_recipient_t* recipient,
const locommon_ticker_t* ticker
loeffect_recipient_t* recipient,
const locommon_ticker_t* ticker,
const loeffect_recipient_status_t* status /* NULLABLE */
);
void
@ -46,11 +56,6 @@ loeffect_recipient_deinitialize(
loeffect_recipient_t* recipient
);
void
loeffect_recipient_reset(
loeffect_recipient_t* recipient
);
void
loeffect_recipient_apply_effect(
loeffect_recipient_t* recipient,
@ -63,14 +68,19 @@ loeffect_recipient_update(
const loeffect_recipient_status_t* base
);
bool
loeffect_recipient_is_alive(
const loeffect_recipient_t* recipient
);
void
loeffect_recipient_effect_param_pack(
const loeffect_recipient_effect_param_t* recipient,
msgpack_packer* packer
loeffect_recipient_pack(
const loeffect_recipient_t* recipient,
msgpack_packer* packer
);
bool
loeffect_recipient_effect_param_unpack(
loeffect_recipient_effect_param_t* recipient,
const msgpack_object* obj /* NULLABLE */
loeffect_recipient_unpack(
loeffect_recipient_t* recipient,
const msgpack_object* obj
);

View File

@ -1,143 +0,0 @@
#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;
}

View File

@ -1,84 +0,0 @@
#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
);

View File

@ -13,4 +13,5 @@ target_link_libraries(loentity
memory
locommon
loeffect
)

View File

@ -25,6 +25,8 @@ struct loentity_character_t {
loentity_t super;
loentity_character_vtable_t vtable;
vec2_t velocity;
};
void

Some files were not shown because too many files have changed in this diff Show More