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