2018-04-13 21:13:48 +02:00
|
|
|
/*
|
2019-08-03 19:43:48 +02:00
|
|
|
* This software is licensed under the terms of the MIT License.
|
2018-04-13 21:13:48 +02:00
|
|
|
* See COPYING for further information.
|
|
|
|
* ---
|
2019-01-23 21:10:43 +01:00
|
|
|
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
2019-07-03 20:00:56 +02:00
|
|
|
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
2018-04-13 21:13:48 +02:00
|
|
|
*/
|
|
|
|
|
2019-01-23 21:10:43 +01:00
|
|
|
#ifndef IGUARD_entity_h
|
|
|
|
#define IGUARD_entity_h
|
|
|
|
|
2018-04-13 21:13:48 +02:00
|
|
|
#include "taisei.h"
|
|
|
|
|
|
|
|
#include "objectpool.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"
|
2020-04-17 09:18:53 +02:00
|
|
|
#include "known_entities.h"
|
2018-04-13 21:13:48 +02:00
|
|
|
|
|
|
|
#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.
|
|
|
|
|
2020-04-17 09:18:53 +02:00
|
|
|
#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
|
|
|
|
2020-04-17 09:18:53 +02:00
|
|
|
#define ENT_TYPE_ID(typename) ENT_TYPEID_##typename
|
2018-04-13 21:13:48 +02:00
|
|
|
|
|
|
|
typedef enum EntityType {
|
|
|
|
_ENT_TYPE_ENUM_BEGIN,
|
2020-04-17 09:18:53 +02:00
|
|
|
#define ENT_EMIT_ENUMS(typename, ...) ENT_TYPE_ID(typename),
|
|
|
|
ENTITIES(ENT_EMIT_ENUMS,)
|
|
|
|
#undef ENT_EMIT_ENUMS
|
2018-04-13 21:13:48 +02:00
|
|
|
_ENT_TYPE_ENUM_END,
|
|
|
|
} EntityType;
|
|
|
|
|
|
|
|
typedef struct EntityInterface EntityInterface;
|
|
|
|
typedef struct EntityListNode EntityListNode;
|
2019-07-16 07:07:30 +02:00
|
|
|
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,
|
2019-02-22 00:56:03 +01:00
|
|
|
DMG_PLAYER_DISCHARGE,
|
2018-07-30 09:04:09 +02:00
|
|
|
} DamageType;
|
|
|
|
|
2019-02-22 00:56:03 +01:00
|
|
|
#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;
|
|
|
|
|
2018-04-13 21:13:48 +02:00
|
|
|
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);
|
2019-01-04 23:59:39 +01:00
|
|
|
typedef void (*EntityDrawHookCallback)(EntityInterface *ent, void *arg);
|
2019-11-22 04:37:11 +01:00
|
|
|
typedef void (*EntityAreaDamageCallback)(EntityInterface *ent, cmplx ent_origin, void *arg);
|
2018-04-13 21:13:48 +02:00
|
|
|
|
|
|
|
#define ENTITY_INTERFACE_BASE(typename) struct { \
|
2019-04-12 10:36:40 +02:00
|
|
|
LIST_INTERFACE(typename); \
|
2018-04-13 21:13:48 +02:00
|
|
|
EntityDrawFunc draw_func; \
|
2018-07-30 09:04:09 +02:00
|
|
|
EntityDamageFunc damage_func; \
|
2018-04-13 21:13:48 +02:00
|
|
|
drawlayer_t draw_layer; \
|
|
|
|
uint32_t spawn_id; \
|
|
|
|
uint index; \
|
2020-04-17 09:18:53 +02:00
|
|
|
EntityType type; \
|
2018-04-13 21:13:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#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); \
|
2018-04-13 21:13:48 +02:00
|
|
|
EntityInterface entity_interface; \
|
|
|
|
EntityInterface name; \
|
|
|
|
}
|
|
|
|
|
|
|
|
struct EntityInterface {
|
|
|
|
ENTITY_INTERFACE_BASE(EntityInterface);
|
|
|
|
};
|
|
|
|
|
2020-04-17 09:18:53 +02:00
|
|
|
#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) {
|
2018-04-13 21:13:48 +02:00
|
|
|
switch(type) {
|
2020-04-17 09:18:53 +02:00
|
|
|
#define ENT_HANDLE_CASE(typename, ...) case ENT_TYPE_ID(typename): return #typename;
|
|
|
|
ENTITIES(ENT_HANDLE_CASE,)
|
|
|
|
#undef ENT_HANDLE_CASE
|
|
|
|
default: return "<INVALID>";
|
2018-04-13 21:13:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_GNU_EXTENSIONS
|
2020-04-17 09:18:53 +02:00
|
|
|
#define ENT_CAST(ent, typename) (__extension__ ({ \
|
2019-02-02 12:25:06 +01:00
|
|
|
__auto_type _ent = ent; \
|
2020-04-17 09:18:53 +02:00
|
|
|
assert(_ent->type == ENT_TYPE_ID(typename)); \
|
|
|
|
UNION_CAST(EntityInterface*, typename*, _ent); \
|
2018-04-13 21:13:48 +02:00
|
|
|
}))
|
|
|
|
#else
|
2020-04-17 09:18:53 +02:00
|
|
|
#define ENT_CAST(ent, typename) UNION_CAST(EntityInterface*, typename*, (ent));
|
2018-04-13 21:13:48 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
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);
|
2018-04-13 21:13:48 +02:00
|
|
|
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);
|
2019-11-22 04:37:11 +01:00
|
|
|
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);
|
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);
|
2019-01-23 21:10:43 +01:00
|
|
|
|
2019-07-24 19:40:19 +02:00
|
|
|
struct BoxedEntity {
|
2020-04-17 09:18:53 +02:00
|
|
|
EntityInterface *ent;
|
2019-07-24 19:40:19 +02:00
|
|
|
uint_fast32_t spawn_id;
|
|
|
|
};
|
|
|
|
|
2020-04-17 09:18:53 +02:00
|
|
|
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;
|
|
|
|
}
|
2019-07-16 07:07:30 +02:00
|
|
|
|
2020-04-17 09:18:53 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define ENT_EMIT_BOX_DEFS(typename, ...) \
|
2019-07-24 19:40:19 +02:00
|
|
|
typedef union Boxed##typename { \
|
|
|
|
BoxedEntity as_generic; \
|
|
|
|
struct { \
|
2020-04-17 09:18:53 +02:00
|
|
|
typename *ent; \
|
2019-07-24 19:40:19 +02:00
|
|
|
uint_fast32_t spawn_id; \
|
|
|
|
}; \
|
|
|
|
} Boxed##typename; \
|
2020-04-17 09:18:53 +02:00
|
|
|
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; }
|
2019-07-24 19:40:19 +02:00
|
|
|
|
2020-04-17 09:18:53 +02:00
|
|
|
ENTITIES(ENT_EMIT_BOX_DEFS,)
|
|
|
|
#undef ENT_EMIT_BOX_DEFS
|
2019-07-24 19:40:19 +02:00
|
|
|
|
2019-10-04 05:00:34 +02:00
|
|
|
INLINE BoxedEntity _ent_boxed_passthrough_helper_Entity(BoxedEntity box) { return box; }
|
2020-04-17 09:18:53 +02:00
|
|
|
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) \
|
2020-04-17 09:18:53 +02:00
|
|
|
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) \
|
2020-04-17 09:18:53 +02:00
|
|
|
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)
|
|
|
|
|
2020-04-17 09:18:53 +02:00
|
|
|
#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;
|
|
|
|
|
2020-04-17 09:18:53 +02:00
|
|
|
#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 void _ent_array_add_##typename(Boxed##typename box, Boxed##typename##Array *a) { \
|
|
|
|
assert(a->size < a->capacity); \
|
|
|
|
a->array[a->size++] = box; \
|
2020-09-29 19:11:36 +02:00
|
|
|
} \
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-09-29 19:11:36 +02:00
|
|
|
void _ent_array_compact_Entity(BoxedEntityArray *a);
|
|
|
|
|
2020-04-17 09:18:53 +02:00
|
|
|
ENTITIES(ENT_EMIT_ARRAY_DEFS,)
|
|
|
|
#undef ENT_EMIT_ARRAY_DEFS
|
2019-10-04 05:00:34 +02:00
|
|
|
|
|
|
|
INLINE void _ent_array_add_BoxedEntity(BoxedEntity box, BoxedEntityArray *a) {
|
|
|
|
assert(a->size < a->capacity);
|
|
|
|
a->array[a->size++] = box;
|
|
|
|
}
|
|
|
|
|
|
|
|
INLINE void _ent_array_add_Entity(struct EntityInterface *ent, BoxedEntityArray *a) {
|
|
|
|
_ent_array_add_BoxedEntity(ENT_BOX(ent), a);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define ENT_ARRAY_ADD(_array, _ent) ENT_BOXED_DISPATCH_FUNCTION(_ent_array_add_, ENT_BOX_OR_PASSTHROUGH(_ent), _array)
|
|
|
|
#define ENT_ARRAY_GET_BOXED(_array, _index) ((_array)->array[_index])
|
|
|
|
#define ENT_ARRAY_GET(_array, _index) ENT_UNBOX(ENT_ARRAY_GET_BOXED(_array, _index))
|
2020-09-29 19:11:36 +02:00
|
|
|
#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 })
|
|
|
|
|
2020-04-17 09:18:53 +02:00
|
|
|
#define _ent_array_iterator MACROHAX_ADDLINENUM(_ent_array_iterator)
|
|
|
|
#define _ent_array_temp MACROHAX_ADDLINENUM(_ent_array_temp)
|
|
|
|
|
2019-10-04 05:00:34 +02:00
|
|
|
#define ENT_ARRAY_FOREACH(_array, _var, _block) do { \
|
2020-04-17 09:18:53 +02:00
|
|
|
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; \
|
2019-10-04 05:00:34 +02:00
|
|
|
_block \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
} while(0)
|
2019-07-16 07:07:30 +02:00
|
|
|
|
2019-12-11 10:25:57 +01:00
|
|
|
#define ENT_ARRAY_FOREACH_COUNTER(_array, _cntr_var, _ent_var, _block) do { \
|
2020-04-17 09:18:53 +02:00
|
|
|
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; \
|
2019-12-11 10:25:57 +01:00
|
|
|
_block \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
} while(0)
|
|
|
|
|
2019-10-06 12:50:58 +02:00
|
|
|
#define ENT_ARRAY_CLEAR(_array) ((_array)->size = 0)
|
|
|
|
|
2019-01-23 21:10:43 +01:00
|
|
|
#endif // IGUARD_entity_h
|