taisei/src/entity.h

325 lines
10 KiB
C
Raw Normal View History

/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
2024-05-16 23:30:41 +02:00
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
*/
#pragma once
#include "taisei.h"
#include "known_entities.h"
#include "list.h"
2019-03-26 17:32:50 +01:00
#include "util/geometry.h"
2019-10-04 05:00:34 +02:00
#include "util/macrohax.h"
#define LAYER_LOW_BITS 16
#define LAYER_LOW_MASK ((1 << LAYER_LOW_BITS) - 1)
typedef uint32_t drawlayer_t;
typedef uint16_t drawlayer_low_t;
typedef enum DrawLayerID {
LAYER_ID_NONE,
#define LAYER(x) LAYER_ID_##x,
#include "drawlayers.inc.h"
} DrawLayerID;
typedef enum DrawLayer {
#define LAYER(x) LAYER_##x = (LAYER_ID_##x << LAYER_LOW_BITS),
#include "drawlayers.inc.h"
} DrawLayer;
// NOTE: you can bit-or a drawlayer_low_t value with one of the LAYER_x constants
// for sub-layer ordering.
#define ENT_EMIT_TYPEDEFS(typename, ...) typedef struct typename typename;
ENTITIES(ENT_EMIT_TYPEDEFS,)
#undef ENT_EMIT_TYPEDEFS
2020-03-25 07:52:18 +01:00
#define ENT_TYPE_ID(typename) ENT_TYPEID_##typename
typedef enum EntityType {
_ENT_TYPE_ENUM_BEGIN,
#define ENT_EMIT_ENUMS(typename, ...) ENT_TYPE_ID(typename),
ENTITIES(ENT_EMIT_ENUMS,)
#undef ENT_EMIT_ENUMS
_ENT_TYPE_ENUM_END,
} EntityType;
typedef struct EntityInterface EntityInterface;
typedef struct EntityListNode EntityListNode;
typedef struct BoxedEntity BoxedEntity;
2018-07-30 09:04:09 +02:00
typedef enum DamageType {
DMG_UNDEFINED,
DMG_ENEMY_SHOT,
DMG_ENEMY_COLLISION,
DMG_PLAYER_SHOT,
DMG_PLAYER_BOMB,
DMG_PLAYER_DISCHARGE,
2018-07-30 09:04:09 +02:00
} DamageType;
#define DAMAGETYPE_IS_PLAYER(dmg) (\
(dmg) == DMG_PLAYER_SHOT || \
(dmg) == DMG_PLAYER_BOMB || \
(dmg) == DMG_PLAYER_DISCHARGE \
)
2018-07-30 09:04:09 +02:00
typedef enum DamageResult {
DMG_RESULT_OK,
DMG_RESULT_IMMUNE,
DMG_RESULT_INAPPLICABLE,
} DamageResult;
typedef struct DamageInfo {
float amount;
DamageType type;
} DamageInfo;
typedef void (*EntityDrawFunc)(EntityInterface *ent);
typedef bool (*EntityPredicate)(EntityInterface *ent);
2018-07-30 09:04:09 +02:00
typedef DamageResult (*EntityDamageFunc)(EntityInterface *target, const DamageInfo *damage);
Lots of disorganized (mostly) visual overhaul (#156) * WIP some projectile effects * fix segfault * Laser smoothing and glow via post-processing blur magic TODO: make it optional * fix memory corruption * fix memory corruption for realsies now * fix color_get_hsl for out-of-range colors * some more bullet flare tweaks * some lame clear effect workarounds * spawn bullet flares after frame 0; looks better and fixes some problems * New baryon explosion; fix petal_explosion; leanify everything * Add missing bullet flare sprite, rebuild main atlas * improve batching efficiency with bullet spawn flares * forgot git add * Group projectiles/particles by shader where possible * Another take on baryon explosion; make fg framebuffers 16bit * WIP some settings for toasters * remove stupid debug log * microoptimization that probably does nothing anyway * somewhat more intuitive quality settings * Whitelist more particles (MarisaB is on hold) * Whitelist (and fix) some more stage6 particles (mostly ToE) * Add a spell name background * Experimental radial healthbar for bosses * healthbar tweaks * thiccer healthbars in response to feedback * remove healthbar survival timer; just fade out on survivals * Add linear healthbars option; WIP other boss HUD tweaks * Use the proper spell card name format * New font and some random garbage to go along with it * Generate static font outlines for use in text shaders * Use outlines in overlay text shader * Complete boss HUD/healthbar fading logic * fix boss timer limit * stage5 bombs explosion effect * split PFLAG_NOSPAWNZOOM into PFLAG_NOSPAWNFLARE and PFLAG_NOSPAWNFADE; introduce PFLAG_NOSPAWNEFFECTS which disables both (it's just the two values OR'd together) simplify vampiric vapor bullet spawning effect * Remove spawn fade-in from super-fast stage5 fairy projectiles (limiters) * lower particle density in v.vapor in minimal mode * graze effect tweaks * fix text shortening, tweak replay menu layout * stupid debug spam * revisit grazing effects again * dumb debug spam again * improve boss attack timer * overlay effect for boss deaths (similar to the player one) * spice up spellcard declaration (HUD) * don't spawn boss death overlay if fleed * modify Exo2 font to use tabular figures * adjust replay menu for the font change * draw timer & power with standard font (phasing out the numbers font) * WIP new HUD; random fixes/tweaks * hud: move difficulty indicator * hud: move debug stuff around * preloads, mostly * fix youmuA batching conflict * shitty workaround for the shitty screenshake shit * remove extraspell lag by stopping to draw stagebg sooner which is possible because extra spells have a different spellcard_intro timing. Fun fact of the day: the duration of spellcard_intro is always ATTACK_START_DELAY_EXTRA even for normal spells! * new stain particle * i disabled background rendering… * "batch" marisa_b masterspark draws * remove these once a new atlas is generated * make toe quick again * hopefully fix all occurences of changed stain and ScaleFade behavior * tweaking reimu_a and toe boson launch effects * make lhc fast again * softer involnerability effect * fix stage 1 snow on the water bug (and improve performance) translated the time to the future a bit because it only seemed to be an issue for small time values * remove unnecessary spawnflare from toe * tone down extra spell start effect * experimental ReimuB gap shader optimization * fix python3 shebangs * generate simple blur shaders w/ hardcoded kernels * New loading screen * lasers: fix incorrect draw hook registration * add webp support for atlas generator * Use ImageMagick for atlas composition (adds 16-bit support) * Atlas maintenance * make the vampiric vapor bullets less prone to invisibility * Revert a few particles to the quadratic fade curve * experimental baryon effect * improve baryon sprites * disable the baryon effect on minimal postprocessing setting
2019-01-04 23:59:39 +01:00
typedef void (*EntityDrawHookCallback)(EntityInterface *ent, void *arg);
typedef void (*EntityAreaDamageCallback)(EntityInterface *ent, cmplx ent_origin, void *arg);
#define ENTITY_INTERFACE_BASE(typename) struct { \
2019-04-12 10:36:40 +02:00
LIST_INTERFACE(typename); \
EntityDrawFunc draw_func; \
2018-07-30 09:04:09 +02:00
EntityDamageFunc damage_func; \
drawlayer_t draw_layer; \
uint32_t spawn_id; \
uint index; \
EntityType type; \
}
#define ENTITY_INTERFACE(typename) union { \
EntityInterface entity_interface; \
ENTITY_INTERFACE_BASE(typename); \
}
#define ENTITY_INTERFACE_NAMED(typename, name) union { \
2019-04-12 10:36:40 +02:00
LIST_INTERFACE(typename); \
EntityInterface entity_interface; \
EntityInterface name; \
}
struct EntityInterface {
ENTITY_INTERFACE_BASE(EntityInterface);
};
#define DEFINE_ENTITY_TYPE(typename, ...) \
struct typename { \
ENTITY_INTERFACE_NAMED(typename, ent); \
struct __VA_ARGS__; \
}
2020-03-25 07:52:18 +01:00
INLINE const char *ent_type_name(EntityType type) {
switch(type) {
#define ENT_HANDLE_CASE(typename, ...) case ENT_TYPE_ID(typename): return #typename;
ENTITIES(ENT_HANDLE_CASE,)
#undef ENT_HANDLE_CASE
default: return "<INVALID>";
}
}
#define ENT_CAST(ent, typename) ({ \
auto _ent = ent; \
assert(_ent->type == ENT_TYPE_ID(typename)); \
UNION_CAST(EntityInterface*, typename*, _ent); \
})
void ent_init(void);
void ent_shutdown(void);
2018-07-30 09:04:09 +02:00
void ent_register(EntityInterface *ent, EntityType type) attr_nonnull(1);
void ent_unregister(EntityInterface *ent) attr_nonnull(1);
void ent_draw(EntityPredicate predicate);
2018-07-30 09:04:09 +02:00
DamageResult ent_damage(EntityInterface *ent, const DamageInfo *damage) attr_nonnull(1, 2);
void ent_area_damage(cmplx origin, float radius, const DamageInfo *damage, EntityAreaDamageCallback callback, void *callback_arg) attr_nonnull(3);
2019-03-26 16:58:38 +01:00
void ent_area_damage_ellipse(Ellipse ellipse, const DamageInfo *damage, EntityAreaDamageCallback callback, void *callback_arg) attr_nonnull(2);
Lots of disorganized (mostly) visual overhaul (#156) * WIP some projectile effects * fix segfault * Laser smoothing and glow via post-processing blur magic TODO: make it optional * fix memory corruption * fix memory corruption for realsies now * fix color_get_hsl for out-of-range colors * some more bullet flare tweaks * some lame clear effect workarounds * spawn bullet flares after frame 0; looks better and fixes some problems * New baryon explosion; fix petal_explosion; leanify everything * Add missing bullet flare sprite, rebuild main atlas * improve batching efficiency with bullet spawn flares * forgot git add * Group projectiles/particles by shader where possible * Another take on baryon explosion; make fg framebuffers 16bit * WIP some settings for toasters * remove stupid debug log * microoptimization that probably does nothing anyway * somewhat more intuitive quality settings * Whitelist more particles (MarisaB is on hold) * Whitelist (and fix) some more stage6 particles (mostly ToE) * Add a spell name background * Experimental radial healthbar for bosses * healthbar tweaks * thiccer healthbars in response to feedback * remove healthbar survival timer; just fade out on survivals * Add linear healthbars option; WIP other boss HUD tweaks * Use the proper spell card name format * New font and some random garbage to go along with it * Generate static font outlines for use in text shaders * Use outlines in overlay text shader * Complete boss HUD/healthbar fading logic * fix boss timer limit * stage5 bombs explosion effect * split PFLAG_NOSPAWNZOOM into PFLAG_NOSPAWNFLARE and PFLAG_NOSPAWNFADE; introduce PFLAG_NOSPAWNEFFECTS which disables both (it's just the two values OR'd together) simplify vampiric vapor bullet spawning effect * Remove spawn fade-in from super-fast stage5 fairy projectiles (limiters) * lower particle density in v.vapor in minimal mode * graze effect tweaks * fix text shortening, tweak replay menu layout * stupid debug spam * revisit grazing effects again * dumb debug spam again * improve boss attack timer * overlay effect for boss deaths (similar to the player one) * spice up spellcard declaration (HUD) * don't spawn boss death overlay if fleed * modify Exo2 font to use tabular figures * adjust replay menu for the font change * draw timer & power with standard font (phasing out the numbers font) * WIP new HUD; random fixes/tweaks * hud: move difficulty indicator * hud: move debug stuff around * preloads, mostly * fix youmuA batching conflict * shitty workaround for the shitty screenshake shit * remove extraspell lag by stopping to draw stagebg sooner which is possible because extra spells have a different spellcard_intro timing. Fun fact of the day: the duration of spellcard_intro is always ATTACK_START_DELAY_EXTRA even for normal spells! * new stain particle * i disabled background rendering… * "batch" marisa_b masterspark draws * remove these once a new atlas is generated * make toe quick again * hopefully fix all occurences of changed stain and ScaleFade behavior * tweaking reimu_a and toe boson launch effects * make lhc fast again * softer involnerability effect * fix stage 1 snow on the water bug (and improve performance) translated the time to the future a bit because it only seemed to be an issue for small time values * remove unnecessary spawnflare from toe * tone down extra spell start effect * experimental ReimuB gap shader optimization * fix python3 shebangs * generate simple blur shaders w/ hardcoded kernels * New loading screen * lasers: fix incorrect draw hook registration * add webp support for atlas generator * Use ImageMagick for atlas composition (adds 16-bit support) * Atlas maintenance * make the vampiric vapor bullets less prone to invisibility * Revert a few particles to the quadratic fade curve * experimental baryon effect * improve baryon sprites * disable the baryon effect on minimal postprocessing setting
2019-01-04 23:59:39 +01:00
void ent_hook_pre_draw(EntityDrawHookCallback callback, void *arg);
void ent_unhook_pre_draw(EntityDrawHookCallback callback);
void ent_hook_post_draw(EntityDrawHookCallback callback, void *arg);
void ent_unhook_post_draw(EntityDrawHookCallback callback);
struct BoxedEntity {
EntityInterface *ent;
uint_fast32_t spawn_id;
};
attr_nonnull_all
INLINE BoxedEntity _ent_box_Entity(EntityInterface *ent) {
return (BoxedEntity) { .ent = ent, .spawn_id = ent->spawn_id } ;
}
INLINE EntityInterface *_ent_unbox_Entity(BoxedEntity box) {
EntityInterface *e = box.ent;
if(e && e->spawn_id == box.spawn_id) {
return e;
}
return NULL;
}
#define ENT_EMIT_BOX_DEFS(typename, ...) \
typedef union Boxed##typename { \
BoxedEntity as_generic; \
struct { \
typename *ent; \
uint_fast32_t spawn_id; \
}; \
} Boxed##typename; \
attr_nonnull_all INLINE Boxed##typename _ent_box_##typename(typename *ent) { \
EntityInterface *generic = UNION_CAST(typename*, EntityInterface*, ent); \
assert(generic->type == ENT_TYPE_ID(typename)); \
return (Boxed##typename) { .as_generic = _ent_box_Entity(generic) }; \
} \
INLINE typename *_ent_unbox_##typename(Boxed##typename box) { \
EntityInterface *generic = _ent_unbox_Entity(box.as_generic); \
if(!generic) { return NULL; } \
assert(generic->type == ENT_TYPE_ID(typename)); \
return UNION_CAST(EntityInterface*, typename*, generic); \
} \
INLINE Boxed##typename _ent_boxed_passthrough_helper_##typename(Boxed##typename box) { return box; } \
INLINE typename *_ent_unboxed_passthrough_helper_##typename(typename *ent) { return ent; }
ENTITIES(ENT_EMIT_BOX_DEFS,)
#undef ENT_EMIT_BOX_DEFS
2019-10-04 05:00:34 +02:00
INLINE BoxedEntity _ent_boxed_passthrough_helper_Entity(BoxedEntity box) { return box; }
INLINE EntityInterface *_ent_unboxed_passthrough_helper_Entity(EntityInterface *ent) { return ent; }
#define ENT_HANDLE_UNBOXED_DISPATCH(typename, func_prefix) \
typename*: func_prefix##typename,
2019-10-04 05:00:34 +02:00
#define ENT_UNBOXED_DISPATCH_TABLE(func_prefix) \
ENTITIES(ENT_HANDLE_UNBOXED_DISPATCH, func_prefix) \
EntityInterface*: func_prefix##Entity
#define ENT_HANDLE_BOXED_DISPATCH(typename, func_prefix) \
Boxed##typename: func_prefix##typename,
2019-10-04 05:00:34 +02:00
#define ENT_BOXED_DISPATCH_TABLE(func_prefix) \
ENTITIES(ENT_HANDLE_BOXED_DISPATCH, func_prefix) \
BoxedEntity: func_prefix##Entity
2019-10-04 05:00:34 +02:00
#define ENT_UNBOXED_DISPATCH_FUNCTION(func_prefix, ...) \
_Generic((MACROHAX_FIRST(__VA_ARGS__)), \
ENT_UNBOXED_DISPATCH_TABLE(func_prefix) \
)(MACROHAX_EXPAND(__VA_ARGS__))
#define ENT_BOXED_DISPATCH_FUNCTION(func_prefix, ...) \
_Generic((MACROHAX_FIRST(__VA_ARGS__)), \
ENT_BOXED_DISPATCH_TABLE(func_prefix) \
)(MACROHAX_EXPAND(__VA_ARGS__))
#define ENT_MIXED_DISPATCH_FUNCTION(func_prefix_unboxed, func_prefix_boxed, ...) \
_Generic((MACROHAX_FIRST(__VA_ARGS__)), \
ENT_UNBOXED_DISPATCH_TABLE(func_prefix_unboxed), \
ENT_BOXED_DISPATCH_TABLE(func_prefix_boxed) \
)(MACROHAX_EXPAND(__VA_ARGS__))
#define ENT_BOX(ent) ENT_UNBOXED_DISPATCH_FUNCTION(_ent_box_, ent)
#define ENT_BOX_OR_PASSTHROUGH(ent) ENT_MIXED_DISPATCH_FUNCTION(_ent_box_, _ent_boxed_passthrough_helper_, ent)
#define ENT_UNBOX(box) ENT_BOXED_DISPATCH_FUNCTION(_ent_unbox_, box)
#define ENT_UNBOX_OR_PASSTHROUGH(ent) ENT_MIXED_DISPATCH_FUNCTION(_ent_unboxed_passthrough_helper_, _ent_unbox_, ent)
2020-04-01 22:08:40 +02:00
2019-10-04 05:00:34 +02:00
typedef struct BoxedEntityArray {
BoxedEntity *array;
uint capacity;
uint size;
} BoxedEntityArray;
#define ENT_EMIT_ARRAY_DEFS(typename, ...) \
2019-10-04 05:00:34 +02:00
typedef union Boxed##typename##Array { \
BoxedEntityArray as_generic_UNSAFE; \
struct { \
Boxed##typename *array; \
uint capacity; \
uint size; \
}; \
} Boxed##typename##Array; \
INLINE int _ent_array_add_##typename(Boxed##typename box, Boxed##typename##Array *a) { \
return _ent_array_add_BoxedEntity(box.as_generic, &a->as_generic_UNSAFE); \
} \
INLINE int _ent_array_add_firstfree_##typename(Boxed##typename box, Boxed##typename##Array *a) { \
return _ent_array_add_firstfree_BoxedEntity(box.as_generic, &a->as_generic_UNSAFE); \
} \
INLINE void _ent_array_compact_##typename(Boxed##typename##Array *a) { \
_ent_array_compact_Entity(&a->as_generic_UNSAFE); \
2019-10-04 05:00:34 +02:00
}
void _ent_array_compact_Entity(BoxedEntityArray *a);
INLINE int _ent_array_add_BoxedEntity(BoxedEntity box, BoxedEntityArray *a) {
2019-10-04 05:00:34 +02:00
assert(a->size < a->capacity);
int i = a->size++;
a->array[i] = box;
return i;
}
INLINE int _ent_array_add_Entity(struct EntityInterface *ent, BoxedEntityArray *a) {
return _ent_array_add_BoxedEntity(ENT_BOX(ent), a);
2019-10-04 05:00:34 +02:00
}
int _ent_array_add_firstfree_BoxedEntity(BoxedEntity box, BoxedEntityArray *a);
INLINE int _ent_array_add_firstfree_Entity(
struct EntityInterface *ent, BoxedEntityArray *a
) {
return _ent_array_add_firstfree_BoxedEntity(ENT_BOX(ent), a);
2019-10-04 05:00:34 +02:00
}
ENTITIES(ENT_EMIT_ARRAY_DEFS,)
#undef ENT_EMIT_ARRAY_DEFS
#define ENT_ARRAY_ADD(_array, _ent) \
ENT_BOXED_DISPATCH_FUNCTION(_ent_array_add_, ENT_BOX_OR_PASSTHROUGH(_ent), _array)
#define ENT_ARRAY_ADD_FIRSTFREE(_array, _ent) \
ENT_BOXED_DISPATCH_FUNCTION(_ent_array_add_firstfree_, ENT_BOX_OR_PASSTHROUGH(_ent), _array)
2019-10-04 05:00:34 +02:00
#define ENT_ARRAY_GET_BOXED(_array, _index) ((_array)->array[_index])
#define ENT_ARRAY_GET(_array, _index) ENT_UNBOX(ENT_ARRAY_GET_BOXED(_array, _index))
#define ENT_ARRAY_COMPACT(_array) \
_Generic((_array)->array[0], \
ENT_BOXED_DISPATCH_TABLE(_ent_array_compact_) \
)(_array)
2019-10-04 05:00:34 +02:00
#define DECLARE_ENT_ARRAY(_ent_type, _name, _capacity) \
Boxed##_ent_type##Array _name; \
_name.size = 0; \
_name.capacity = _capacity; \
Boxed##_ent_type _ent_array_data##_name[_name.capacity]; \
_name.array = _ent_array_data##_name
#define ENT_ARRAY(_typename, _capacity) \
((Boxed##_typename##Array) { .array = (Boxed##_typename[_capacity]) { 0 }, .capacity = (_capacity), .size = 0 })
#define _ent_array_iterator MACROHAX_ADDLINENUM(_ent_array_iterator)
#define _ent_array_temp MACROHAX_ADDLINENUM(_ent_array_temp)
2024-04-06 02:57:15 +02:00
#define ENT_ARRAY_FOREACH(_array, _var, ...) do { \
for(uint _ent_array_iterator = 0; _ent_array_iterator < (_array)->size; ++_ent_array_iterator) { \
void *_ent_array_temp = ENT_ARRAY_GET((_array), _ent_array_iterator); \
if(_ent_array_temp != NULL) { \
_var = _ent_array_temp; \
2024-04-06 02:57:15 +02:00
__VA_ARGS__ \
2019-10-04 05:00:34 +02:00
} \
} \
} while(0)
2024-04-06 02:57:15 +02:00
#define ENT_ARRAY_FOREACH_COUNTER(_array, _cntr_var, _ent_var, ...) do { \
for(uint _ent_array_iterator = 0; _ent_array_iterator < (_array)->size; ++_ent_array_iterator) { \
void *_ent_array_temp = ENT_ARRAY_GET((_array), _ent_array_iterator); \
if(_ent_array_temp != NULL) { \
_cntr_var = _ent_array_iterator; \
_ent_var = _ent_array_temp; \
2024-04-06 02:57:15 +02:00
__VA_ARGS__ \
} \
} \
} while(0)
2019-10-06 12:50:58 +02:00
#define ENT_ARRAY_CLEAR(_array) ((_array)->size = 0)