Refactor entity system (#214)

* Smarter generic entity macros

The list of "core" entities is now defined in one macro, and hardcoded
_Generic dispatch tables are eliminated

* Get rid of "custom" entities

All entities are now "first-class". The list of known entity types has
been moved to known_entities.h. The system no longer needs to know the
definition of all entity structs.

* Refactor guts of ENT_BOX/ENT_UNBOX

Made the functions inline, Box::ent is now a proper pointer type (but
please don't use it directly), ENT_UNBOX returns NULL if the box is
"empty" (references NULL entity)

* Merge TASK_BIND_UNBOXED with TASK_BIND

* s/YoumuMyon/YoumuAMyon for consistency
This commit is contained in:
Andrei Alexeyev 2020-04-17 10:18:53 +03:00 committed by GitHub
parent b3ce65d5ac
commit e16e2184a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 473 additions and 369 deletions

View file

@ -47,7 +47,7 @@ Boss* create_boss(char *name, char *ani, cmplx pos) {
boss->ent.draw_layer = LAYER_BOSS;
boss->ent.draw_func = ent_draw_boss;
boss->ent.damage_func = ent_damage_boss;
ent_register(&boss->ent, ENT_BOSS);
ent_register(&boss->ent, ENT_TYPE_ID(Boss));
// This is not necessary because the default will be set at the start of every attack.
// But who knows. Maybe this will be triggered somewhen. If bosses without attacks start

View file

@ -34,11 +34,10 @@ enum {
BOSS_DEATH_DELAY = 120,
};
typedef struct Boss Boss;
typedef struct Attack Attack;
typedef struct AttackInfo AttackInfo;
typedef void (*BossRule)(struct Boss*, int time) attr_nonnull(1);
typedef void (*BossRule)(Boss*, int time) attr_nonnull(1);
typedef enum AttackType {
AT_Normal,
@ -125,8 +124,7 @@ struct Attack {
AttackInfo *info; // NULL for attacks created directly through boss_add_attack
};
struct Boss {
ENTITY_INTERFACE_NAMED(Boss, ent);
DEFINE_ENTITY_TYPE(Boss, {
cmplx pos;
Attack attacks[BOSS_MAX_ATTACKS];
@ -173,7 +171,7 @@ struct Boss {
} hud;
COEVENTS_ARRAY(defeated) events;
};
});
Boss* create_boss(char *name, char *ani, cmplx pos) attr_nonnull(1, 2) attr_returns_nonnull;
void free_boss(Boss *boss) attr_nonnull(1);

View file

@ -1030,18 +1030,3 @@ DEFINE_EXTERN_TASK(_cancel_task_helper) {
cotask_cancel(task);
}
}
#include <projectile.h>
#include <laser.h>
#include <item.h>
#include <enemy.h>
#include <boss.h>
#include <player.h>
#define ENT_TYPE(typename, id) \
typename *_cotask_bind_to_entity_##typename(CoTask *task, typename *ent) { \
return ENT_CAST((cotask_bind_to_entity)(task, ent ? &ent->entity_interface : NULL), typename); \
}
ENT_TYPES
#undef ENT_TYPE

View file

@ -398,8 +398,8 @@ DECLARE_EXTERN_TASK(_cancel_task_helper, { BoxedTask task; });
#define TASK_EVENTS(task) cotask_get_events(cotask_unbox(task))
#define TASK_MALLOC(size) cotask_malloc(cotask_active(), size)
#define TASK_HOST_CUSTOM_ENT(ent_struct_type) \
ENT_CAST_CUSTOM(cotask_host_entity(cotask_active(), sizeof(ent_struct_type), ENT_CUSTOM), ent_struct_type)
#define TASK_HOST_ENT(ent_struct_type) \
ENT_CAST(cotask_host_entity(cotask_active(), sizeof(ent_struct_type), ENT_TYPE_ID(ent_struct_type)), ent_struct_type)
#define TASK_HOST_EVENTS(events_array) \
cotask_host_events(cotask_active(), sizeof(events_array)/sizeof(CoEvent), &((events_array)._first_event_))
@ -410,27 +410,22 @@ DECLARE_EXTERN_TASK(_cancel_task_helper, { BoxedTask task; });
#define WAIT_EVENT_OR_DIE(e) cotask_wait_event_or_die((e), NULL)
#define STALL cotask_wait(INT_MAX)
#define ENT_TYPE(typename, id) \
struct typename; \
struct typename *_cotask_bind_to_entity_##typename(CoTask *task, struct typename *ent) attr_returns_nonnull attr_returns_max_aligned;
// first arg of the generated function needs to be the ent, because ENT_UNBOXED_DISPATCH_FUNCTION dispatches on first arg.
#define _cotask_emit_bindfunc(typename, ...) \
INLINE typename *_cotask_bind_to_entity_##typename(typename *ent, CoTask *task) { \
return ENT_CAST((cotask_bind_to_entity)(task, ent ? UNION_CAST(typename*, EntityInterface*, ent) : NULL), typename); \
}
ENT_TYPES
#undef ENT_TYPE
ENTITIES(_cotask_emit_bindfunc,)
#undef _cotask_emit_bindfunc
#define cotask_bind_to_entity(task, ent) (_Generic((ent), \
struct Projectile*: _cotask_bind_to_entity_Projectile, \
struct Laser*: _cotask_bind_to_entity_Laser, \
struct Enemy*: _cotask_bind_to_entity_Enemy, \
struct Boss*: _cotask_bind_to_entity_Boss, \
struct Player*: _cotask_bind_to_entity_Player, \
struct Item*: _cotask_bind_to_entity_Item, \
EntityInterface*: cotask_bind_to_entity \
)(task, ent))
INLINE EntityInterface *_cotask_bind_to_entity_Entity(EntityInterface *ent, CoTask *task) {
return (cotask_bind_to_entity)(task, ent);
}
#define TASK_BIND(box) cotask_bind_to_entity(cotask_active(), ENT_UNBOX(box))
#define TASK_BIND_UNBOXED(ent) cotask_bind_to_entity(cotask_active(), ent)
#define cotask_bind_to_entity(task, ent) \
ENT_UNBOXED_DISPATCH_FUNCTION(_cotask_bind_to_entity_, ent, task)
#define TASK_BIND_CUSTOM(box, type) ENT_CAST_CUSTOM((cotask_bind_to_entity)(cotask_active(), ENT_UNBOX(box)), type)
#define TASK_BIND_UNBOXED_CUSTOM(ent, type) ENT_CAST_CUSTOM((cotask_bind_to_entity)(cotask_active(), &(ent)->entity_interface), type)
#define TASK_BIND(ent_or_box) cotask_bind_to_entity(cotask_active(), ENT_UNBOX_OR_PASSTHROUGH(ent_or_box))
#endif // IGUARD_coroutine_h

View file

@ -109,7 +109,7 @@ Enemy *create_enemy_p(EnemyList *enemies, cmplx pos, float hp, EnemyVisualRule v
coevent_init(&e->events.killed);
fix_pos0_visual(e);
ent_register(&e->ent, ENT_ENEMY);
ent_register(&e->ent, ENT_TYPE_ID(Enemy));
enemy_call_logic_rule(e, EVENT_BIRTH);
return e;

View file

@ -22,12 +22,17 @@
#define ENEMY_DEBUG
#endif
#ifdef ENEMY_DEBUG
#define IF_ENEMY_DEBUG(...) __VA_ARGS__
#else
#define IF_ENEMY_DEBUG(...)
#endif
#define ENEMY_HURT_RADIUS 7
typedef struct Enemy Enemy;
typedef LIST_ANCHOR(Enemy) EnemyList;
typedef int (*EnemyLogicRule)(struct Enemy*, int t);
typedef void (*EnemyVisualRule)(struct Enemy*, int t, bool render);
typedef int (*EnemyLogicRule)(Enemy*, int t);
typedef void (*EnemyVisualRule)(Enemy*, int t, bool render);
enum {
ENEMY_IMMUNE = -9000,
@ -35,9 +40,7 @@ enum {
ENEMY_KILLED = -9002,
};
struct Enemy {
ENTITY_INTERFACE_NAMED(Enemy, ent);
DEFINE_ENTITY_TYPE(Enemy, {
cmplx pos;
cmplx pos0;
cmplx pos0_visual;
@ -64,10 +67,10 @@ struct Enemy {
bool moving;
#ifdef ENEMY_DEBUG
DebugInfo debug;
#endif
};
IF_ENEMY_DEBUG(
DebugInfo debug;
)
});
#define create_enemy4c(p,h,d,l,a1,a2,a3,a4) create_enemy_p(&global.enemies,p,h,d,l,a1,a2,a3,a4)
#define create_enemy3c(p,h,d,l,a1,a2,a3) create_enemy_p(&global.enemies,p,h,d,l,a1,a2,a3,0)

View file

@ -37,6 +37,7 @@ static void add_hook(EntityDrawHookList *list, EntityDrawHookCallback cb, void *
EntityDrawHook *hook = calloc(1, sizeof(*hook));
hook->callback = cb;
hook->arg = arg;
alist_append(list, hook);
}
@ -227,32 +228,3 @@ void ent_hook_post_draw(EntityDrawHookCallback callback, void *arg) {
void ent_unhook_post_draw(EntityDrawHookCallback callback) {
remove_hook(&entities.hooks.post_draw, callback);
}
BoxedEntity _ent_box_Entity(EntityInterface *ent) {
BoxedEntity h;
h.ent = (uintptr_t)ent;
h.spawn_id = ent->spawn_id;
return h;
}
EntityInterface *_ent_unbox_Entity(BoxedEntity box) {
EntityInterface *e = (EntityInterface*)box.ent;
if(e->spawn_id == box.spawn_id) {
return e;
}
return NULL;
}
#define ENT_TYPE(typename, id) \
Boxed##typename _ent_box_##typename(struct typename *ent) { \
return (Boxed##typename) { .as_generic = _ent_box_Entity(&ent->entity_interface) };\
} \
struct typename *_ent_unbox_##typename(Boxed##typename box) { \
EntityInterface *e = _ent_unbox_Entity(box.as_generic); \
return e ? ENT_CAST(e, typename) : NULL; \
}
ENT_TYPES
#undef ENT_TYPE

View file

@ -14,6 +14,7 @@
#include "objectpool.h"
#include "util/geometry.h"
#include "util/macrohax.h"
#include "known_entities.h"
#define LAYER_LOW_BITS 16
#define LAYER_LOW_MASK ((1 << LAYER_LOW_BITS) - 1)
@ -34,22 +35,17 @@ typedef enum DrawLayer {
// NOTE: you can bit-or a drawlayer_low_t value with one of the LAYER_x constants
// for sub-layer ordering.
typedef struct CustomEntity CustomEntity;
#define ENT_EMIT_TYPEDEFS(typename, ...) typedef struct typename typename;
ENTITIES(ENT_EMIT_TYPEDEFS,)
#undef ENT_EMIT_TYPEDEFS
#define ENT_TYPES \
ENT_TYPE(Projectile, ENT_PROJECTILE) \
ENT_TYPE(Laser, ENT_LASER) \
ENT_TYPE(Enemy, ENT_ENEMY) \
ENT_TYPE(Boss, ENT_BOSS) \
ENT_TYPE(Player, ENT_PLAYER) \
ENT_TYPE(Item, ENT_ITEM) \
ENT_TYPE(CustomEntity, ENT_CUSTOM) \
#define ENT_TYPE_ID(typename) ENT_TYPEID_##typename
typedef enum EntityType {
_ENT_TYPE_ENUM_BEGIN,
#define ENT_TYPE(typename, id) id, _ENT_TYPEID_##typename = id,
ENT_TYPES
#undef ENT_TYPE
#define ENT_EMIT_ENUMS(typename, ...) ENT_TYPE_ID(typename),
ENTITIES(ENT_EMIT_ENUMS,)
#undef ENT_EMIT_ENUMS
_ENT_TYPE_ENUM_END,
} EntityType;
@ -91,12 +87,12 @@ typedef void (*EntityAreaDamageCallback)(EntityInterface *ent, cmplx ent_origin,
#define ENTITY_INTERFACE_BASE(typename) struct { \
LIST_INTERFACE(typename); \
EntityType type; \
EntityDrawFunc draw_func; \
EntityDamageFunc damage_func; \
drawlayer_t draw_layer; \
uint32_t spawn_id; \
uint index; \
EntityType type; \
}
#define ENTITY_INTERFACE(typename) union { \
@ -114,42 +110,31 @@ struct EntityInterface {
ENTITY_INTERFACE_BASE(EntityInterface);
};
struct CustomEntity {
ENTITY_INTERFACE(CustomEntity);
};
#define DEFINE_ENTITY_TYPE(typename, ...) \
struct typename { \
ENTITY_INTERFACE_NAMED(typename, ent); \
struct __VA_ARGS__; \
}
INLINE const char *ent_type_name(EntityType type) {
switch(type) {
#define ENT_TYPE(typename, id) case id: return #id;
ENT_TYPES
#undef ENT_TYPE
default: return "ENT_INVALID";
#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_TYPE_ID(typename) (_ENT_TYPEID_##typename)
#ifdef USE_GNU_EXTENSIONS
#define _internal_ENT_CAST(ent, typename, ent_type_id) (__extension__ ({ \
#define ENT_CAST(ent, typename) (__extension__ ({ \
__auto_type _ent = ent; \
static_assert(__builtin_types_compatible_p(EntityInterface, __typeof__(*(_ent))), \
"Expression is not an EntityInterface pointer"); \
static_assert(__builtin_types_compatible_p(EntityInterface, __typeof__(((typename){}).entity_interface)), \
#typename " doesn't implement EntityInterface"); \
static_assert(__builtin_offsetof(typename, entity_interface) == 0, \
"entity_interface has non-zero offset in " #typename); \
IF_DEBUG(if(_ent && _ent->type != ent_type_id) { \
log_fatal("Invalid entity cast from %s to " #typename, ent_type_name(_ent->type)); \
}); \
CASTPTR_ASSUME_ALIGNED(_ent, typename); \
assert(_ent->type == ENT_TYPE_ID(typename)); \
UNION_CAST(EntityInterface*, typename*, _ent); \
}))
#else
#define _internal_ENT_CAST(ent, typename, ent_type_id) CASTPTR_ASSUME_ALIGNED(ent, typename)
#define ENT_CAST(ent, typename) UNION_CAST(EntityInterface*, typename*, (ent));
#endif
#define ENT_CAST(ent, typename) _internal_ENT_CAST(ent, typename, ENT_TYPE_ID(typename))
#define ENT_CAST_CUSTOM(ent, typename) _internal_ENT_CAST(ent, typename, _ENT_TYPEID_CustomEntity)
void ent_init(void);
void ent_shutdown(void);
void ent_register(EntityInterface *ent, EntityType type) attr_nonnull(1);
@ -165,48 +150,66 @@ void ent_hook_post_draw(EntityDrawHookCallback callback, void *arg);
void ent_unhook_post_draw(EntityDrawHookCallback callback);
struct BoxedEntity {
alignas(alignof(void*)) uintptr_t ent; // not an actual pointer to avert the temptation to use it directly.
EntityInterface *ent;
uint_fast32_t spawn_id;
};
BoxedEntity _ent_box_Entity(EntityInterface *ent);
EntityInterface *_ent_unbox_Entity(BoxedEntity box);
attr_nonnull_all
INLINE BoxedEntity _ent_box_Entity(EntityInterface *ent) {
return (BoxedEntity) { .ent = ent, .spawn_id = ent->spawn_id } ;
}
#define ENT_TYPE(typename, 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 { \
uintptr_t ent; \
typename *ent; \
uint_fast32_t spawn_id; \
}; \
} Boxed##typename; \
struct typename; \
Boxed##typename _ent_box_##typename(struct typename *ent); \
struct typename *_ent_unbox_##typename(Boxed##typename box); \
INLINE Boxed##typename _ent_boxed_passthrough_helper_##typename(Boxed##typename box) { return box; }
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; }
ENT_TYPES
#undef ENT_TYPE
ENTITIES(ENT_EMIT_BOX_DEFS,)
#undef ENT_EMIT_BOX_DEFS
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,
#define ENT_UNBOXED_DISPATCH_TABLE(func_prefix) \
struct Projectile*: func_prefix##Projectile, \
struct Laser*: func_prefix##Laser, \
struct Enemy*: func_prefix##Enemy, \
struct Boss*: func_prefix##Boss, \
struct Player*: func_prefix##Player, \
struct Item*: func_prefix##Item, \
EntityInterface*: func_prefix##Entity \
ENTITIES(ENT_HANDLE_UNBOXED_DISPATCH, func_prefix) \
EntityInterface*: func_prefix##Entity
#define ENT_HANDLE_BOXED_DISPATCH(typename, func_prefix) \
Boxed##typename: func_prefix##typename,
#define ENT_BOXED_DISPATCH_TABLE(func_prefix) \
BoxedProjectile: func_prefix##Projectile, \
BoxedLaser: func_prefix##Laser, \
BoxedEnemy: func_prefix##Enemy, \
BoxedBoss: func_prefix##Boss, \
BoxedPlayer: func_prefix##Player, \
BoxedItem: func_prefix##Item, \
BoxedEntity: func_prefix##Entity \
ENTITIES(ENT_HANDLE_BOXED_DISPATCH, func_prefix) \
BoxedEntity: func_prefix##Entity
#define ENT_UNBOXED_DISPATCH_FUNCTION(func_prefix, ...) \
_Generic((MACROHAX_FIRST(__VA_ARGS__)), \
@ -225,11 +228,10 @@ INLINE BoxedEntity _ent_boxed_passthrough_helper_Entity(BoxedEntity box) { retur
)(MACROHAX_EXPAND(__VA_ARGS__))
#define ENT_BOX(ent) ENT_UNBOXED_DISPATCH_FUNCTION(_ent_box_, ent)
#define ENT_UNBOX(box) ENT_BOXED_DISPATCH_FUNCTION(_ent_unbox_, box)
#define ENT_BOX_OR_PASSTHROUGH(ent) ENT_MIXED_DISPATCH_FUNCTION(_ent_box_, _ent_boxed_passthrough_helper_, ent)
#define ENT_BOX_CUSTOM(ent) _ent_box_Entity(&(ent)->entity_interface)
#define ENT_UNBOX_CUSTOM(box, type) ENT_CAST_CUSTOM(_ent_unbox_Entity(box), type)
#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)
typedef struct BoxedEntityArray {
BoxedEntity *array;
@ -237,7 +239,7 @@ typedef struct BoxedEntityArray {
uint size;
} BoxedEntityArray;
#define ENT_TYPE(typename, id) \
#define ENT_EMIT_ARRAY_DEFS(typename, ...) \
typedef union Boxed##typename##Array { \
BoxedEntityArray as_generic_UNSAFE; \
struct { \
@ -251,8 +253,8 @@ typedef struct BoxedEntityArray {
a->array[a->size++] = box; \
}
ENT_TYPES
#undef ENT_TYPE
ENTITIES(ENT_EMIT_ARRAY_DEFS,)
#undef ENT_EMIT_ARRAY_DEFS
INLINE void _ent_array_add_BoxedEntity(BoxedEntity box, BoxedEntityArray *a) {
assert(a->size < a->capacity);
@ -277,22 +279,25 @@ INLINE void _ent_array_add_Entity(struct EntityInterface *ent, BoxedEntityArray
#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)
#define ENT_ARRAY_FOREACH(_array, _var, _block) do { \
for(uint MACROHAX_ADDLINENUM(_ent_array_iterator) = 0; MACROHAX_ADDLINENUM(_ent_array_iterator) < (_array)->size; ++MACROHAX_ADDLINENUM(_ent_array_iterator)) { \
void *MACROHAX_ADDLINENUM(_ent_array_temp) = ENT_ARRAY_GET((_array), MACROHAX_ADDLINENUM(_ent_array_iterator)); \
if(MACROHAX_ADDLINENUM(_ent_array_temp) != NULL) { \
_var = MACROHAX_ADDLINENUM(_ent_array_temp); \
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; \
_block \
} \
} \
} while(0)
#define ENT_ARRAY_FOREACH_COUNTER(_array, _cntr_var, _ent_var, _block) do { \
for(uint MACROHAX_ADDLINENUM(_ent_array_iterator) = 0; MACROHAX_ADDLINENUM(_ent_array_iterator) < (_array)->size; ++MACROHAX_ADDLINENUM(_ent_array_iterator)) { \
void *MACROHAX_ADDLINENUM(_ent_array_temp) = ENT_ARRAY_GET((_array), MACROHAX_ADDLINENUM(_ent_array_iterator)); \
if(MACROHAX_ADDLINENUM(_ent_array_temp) != NULL) { \
_cntr_var = MACROHAX_ADDLINENUM(_ent_array_iterator); \
_ent_var = MACROHAX_ADDLINENUM(_ent_array_temp); \
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; \
_block \
} \
} \

View file

@ -125,7 +125,7 @@ Item* create_item(cmplx pos, cmplx v, ItemType type) {
i->ent.draw_layer = LAYER_ITEM | i->type;
i->ent.draw_func = ent_draw_item;
ent_register(&i->ent, ENT_ITEM);
ent_register(&i->ent, ENT_TYPE_ID(Item));
return i;
}

View file

@ -16,7 +16,6 @@
#include "objectpool.h"
#include "entity.h"
typedef struct Item Item;
typedef LIST_ANCHOR(Item) ItemList;
typedef enum {
@ -54,9 +53,7 @@ typedef union ItemCounts {
int as_array[ITEM_LAST - ITEM_FIRST];
} ItemCounts;
struct Item {
ENTITY_INTERFACE_NAMED(Item, ent);
DEFINE_ENTITY_TYPE(Item, {
int birthtime;
int collecttime;
cmplx pos;
@ -67,7 +64,7 @@ struct Item {
float pickup_value;
cmplx v;
};
});
Item *create_item(cmplx pos, cmplx v, ItemType type);
void delete_item(Item *item);

28
src/known_entities.h Normal file
View file

@ -0,0 +1,28 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_known_entities_h
#define IGUARD_known_entities_h
#include "taisei.h"
#define ENTITIES_CORE(X, ...) \
X(Boss, __VA_ARGS__) \
X(Enemy, __VA_ARGS__) \
X(Item, __VA_ARGS__) \
X(Laser, __VA_ARGS__) \
X(Player, __VA_ARGS__) \
X(Projectile, __VA_ARGS__) \
#include "plrmodes/entities.h"
#define ENTITIES(X, ...) \
ENTITIES_CORE(X, __VA_ARGS__) \
ENTITIES_PLAYERMODES(X, __VA_ARGS__) \
#endif // IGUARD_known_entities_h

View file

@ -169,7 +169,7 @@ Laser *create_laser(cmplx pos, float time, float deathtime, const Color *color,
l->ent.draw_layer = LAYER_LASER_HIGH;
l->ent.draw_func = ent_draw_laser;
ent_register(&l->ent, ENT_LASER);
ent_register(&l->ent, ENT_TYPE_ID(Laser));
if(l->lrule)
l->lrule(l, EVENT_BIRTH);
@ -318,7 +318,7 @@ static void lasers_ent_predraw_hook(EntityInterface *ent, void *arg) {
return;
}
if(ent && ent->type == ENT_LASER) {
if(ent && ent->type == ENT_TYPE_ID(Laser)) {
if(lasers.saved_fb != NULL) {
return;
}

View file

@ -16,15 +16,12 @@
#include "resource/shader_program.h"
#include "entity.h"
typedef struct Laser Laser;
typedef LIST_ANCHOR(Laser) LaserList;
typedef cmplx (*LaserPosRule)(Laser* l, float time);
typedef void (*LaserLogicRule)(Laser* l, int time);
struct Laser {
ENTITY_INTERFACE_NAMED(Laser, ent);
DEFINE_ENTITY_TYPE(Laser, {
cmplx pos;
cmplx args[4];
@ -47,7 +44,7 @@ struct Laser {
uchar unclearable : 1;
uchar collision_active : 1;
};
});
#define create_lasercurve1c(p, time, deathtime, clr, rule, a0) create_laser(p, time, deathtime, clr, rule, 0, a0, 0, 0, 0)
#define create_lasercurve2c(p, time, deathtime, clr, rule, a0, a1) create_laser(p, time, deathtime, clr, rule, 0, a0, a1, 0, 0)

View file

@ -67,7 +67,7 @@ void player_stage_post_init(Player *plr) {
plr->ent.draw_layer = LAYER_PLAYER;
plr->ent.draw_func = ent_draw_player;
plr->ent.damage_func = ent_damage_player;
ent_register(&plr->ent, ENT_PLAYER);
ent_register(&plr->ent, ENT_TYPE_ID(Player));
COEVENT_INIT_ARRAY(plr->events);
@ -1623,13 +1623,13 @@ void player_register_damage(Player *plr, EntityInterface *target, const DamageIn
if(target != NULL) {
switch(target->type) {
case ENT_ENEMY: {
case ENT_TYPE_ID(Enemy): {
pos = ENT_CAST(target, Enemy)->pos;
player_add_points(&global.plr, damage->amount * 0.5, pos);
break;
}
case ENT_BOSS: {
case ENT_TYPE_ID(Boss): {
pos = ENT_CAST(target, Boss)->pos;
player_add_points(&global.plr, damage->amount * 0.2, pos);
break;
@ -1640,7 +1640,7 @@ void player_register_damage(Player *plr, EntityInterface *target, const DamageIn
}
if(!isnan(creal(pos)) && damage->type == DMG_PLAYER_DISCHARGE) {
double rate = target->type == ENT_BOSS ? 110 : 256;
double rate = target->type == ENT_TYPE_ID(Boss) ? 110 : 256;
spawn_and_collect_items(pos, 1, ITEM_VOLTAGE, (int)(damage->amount / rate));
}

View file

@ -15,6 +15,12 @@
#define PLR_DPS_STATS
#endif
#ifdef PLR_DPS_STATS
#define IF_PLR_DPS_STATS(...) __VA_ARGS__
#else
#define IF_PLR_DPS_STATS(...)
#endif
#include "util.h"
#include "enemy.h"
#include "gamepad.h"
@ -78,8 +84,6 @@ enum {
INFLAGS_MOVE = INFLAG_UP | INFLAG_DOWN | INFLAG_LEFT | INFLAG_RIGHT
};
typedef struct Player Player;
typedef struct PowerSurgeBonus {
uint baseline;
uint score;
@ -89,9 +93,7 @@ typedef struct PowerSurgeBonus {
float discharge_damage;
} PowerSurgeBonus;
struct Player {
ENTITY_INTERFACE_NAMED(Player, ent);
DEFINE_ENTITY_TYPE(Player, {
cmplx pos;
cmplx velocity;
cmplx deathpos;
@ -156,11 +158,11 @@ struct Player {
bool gamepadmove;
bool iddqd;
#ifdef PLR_DPS_STATS
int dmglogframe;
int dmglog[240];
#endif
};
IF_PLR_DPS_STATS(
int dmglogframe;
int dmglog[240];
)
});
// this is used by both player and replay code
enum {

31
src/plrmodes/entities.h Normal file
View file

@ -0,0 +1,31 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_plrmodes_entities_h
#define IGUARD_plrmodes_entities_h
#include "taisei.h"
#include "reimu_a_entities.h"
#include "reimu_b_entities.h"
#include "marisa_a_entities.h"
#include "marisa_b_entities.h"
#include "youmu_a_entities.h"
#include "youmu_b_entities.h"
#define ENTITIES_PLAYERMODES(X, ...) \
ENTITIES_ReimuA(X, __VA_ARGS__) \
ENTITIES_ReimuB(X, __VA_ARGS__) \
ENTITIES_MarisaA(X, __VA_ARGS__) \
ENTITIES_MarisaB(X, __VA_ARGS__) \
ENTITIES_YoumuA(X, __VA_ARGS__) \
ENTITIES_YoumuB(X, __VA_ARGS__) \
#endif // IGUARD_plrmodes_entities_h

View file

@ -20,13 +20,9 @@
#define HAKKERO_RETRACT_TIME 4
typedef struct MarisaAController MarisaAController;
typedef struct MarisaLaser MarisaLaser;
typedef struct MarisaSlave MarisaSlave;
typedef struct MasterSpark MasterSpark;
typedef struct MarisaALaser MarisaALaser;
struct MarisaSlave {
ENTITY_INTERFACE_NAMED(MarisaAController, ent);
DEFINE_ENTITY_TYPE(MarisaASlave, {
Sprite *sprite;
ShaderProgram *shader;
cmplx pos;
@ -34,10 +30,16 @@ struct MarisaSlave {
real shot_angle;
real flare_alpha;
bool alive;
};
});
struct MarisaLaser {
LIST_INTERFACE(MarisaLaser);
DEFINE_ENTITY_TYPE(MarisaAMasterSpark, {
cmplx pos;
cmplx dir;
real alpha;
});
struct MarisaALaser {
LIST_INTERFACE(MarisaALaser);
cmplx pos;
struct {
cmplx first;
@ -46,25 +48,16 @@ struct MarisaLaser {
real alpha;
};
struct MasterSpark {
ENTITY_INTERFACE_NAMED(MarisaAController, ent);
cmplx pos;
cmplx dir;
real alpha;
};
struct MarisaAController {
ENTITY_INTERFACE_NAMED(MarisaAController, laser_renderer);
DEFINE_ENTITY_TYPE(MarisaAController, {
Player *plr;
LIST_ANCHOR(MarisaLaser) lasers;
LIST_ANCHOR(MarisaALaser) lasers;
COEVENTS_ARRAY(
slaves_expired
) events;
};
});
static void trace_laser(MarisaLaser *laser, cmplx vel, real damage) {
static void trace_laser(MarisaALaser *laser, cmplx vel, real damage) {
ProjCollisionResult col;
ProjectileList lproj = { .first = NULL };
@ -111,7 +104,7 @@ static void trace_laser(MarisaLaser *laser, cmplx vel, real damage) {
.layer = LAYER_PARTICLE_HIGH,
);
if(col.type == PCOL_ENTITY && col.entity->type == ENT_ENEMY) {
if(col.type == PCOL_ENTITY && col.entity->type == ENT_TYPE_ID(Enemy)) {
assert(num_enemy_collissions < ARRAY_SIZE(enemy_collisions));
if(num_enemy_collissions < ARRAY_SIZE(enemy_collisions)) {
c = enemy_collisions + num_enemy_collissions++;
@ -154,7 +147,7 @@ static float set_alpha_dimmed(Uniform *u_alpha, float a) {
}
static void marisa_laser_draw_slave(EntityInterface *ent) {
MarisaSlave *slave = ENT_CAST_CUSTOM(ent, MarisaSlave);
MarisaASlave *slave = ENT_CAST(ent, MarisaASlave);
ShaderCustomParams shader_params;
shader_params.color = *RGBA(0.2, 0.4, 0.5, slave->flare_alpha * 0.75);
@ -193,7 +186,7 @@ static void draw_laser_beam(cmplx src, cmplx dst, real size, real step, real t,
}
static void marisa_laser_draw_lasers(EntityInterface *ent) {
MarisaAController *ctrl = ENT_CAST_CUSTOM(ent, MarisaAController);
MarisaAController *ctrl = ENT_CAST(ent, MarisaAController);
real t = global.frames;
ShaderProgram *shader = r_shader_get("marisa_laser");
@ -222,7 +215,7 @@ static void marisa_laser_draw_lasers(EntityInterface *ent) {
BLENDFACTOR_SRC_COLOR, BLENDFACTOR_ONE, BLENDOP_MAX
));
for(MarisaLaser *laser = ctrl->lasers.first; laser; laser = laser->next) {
for(MarisaALaser *laser = ctrl->lasers.first; laser; laser = laser->next) {
if(set_alpha(u_alpha, laser->alpha)) {
draw_laser_beam(laser->pos, laser->trace_hit.last, 32, 128, -0.02 * t, tex1, u_length);
}
@ -250,7 +243,7 @@ static void marisa_laser_draw_lasers(EntityInterface *ent) {
r_uniform_vec4(u_clr0, 0.5, 0.0, 0.0, 0.0);
r_uniform_vec4(u_clr1, 1.0, 0.0, 0.0, 0.0);
for(MarisaLaser *laser = ctrl->lasers.first; laser; laser = laser->next) {
for(MarisaALaser *laser = ctrl->lasers.first; laser; laser = laser->next) {
if(set_alpha_dimmed(u_alpha, laser->alpha)) {
draw_laser_beam(laser->pos, laser->trace_hit.first, 40, 128, t * -0.12, tex0, u_length);
}
@ -259,7 +252,7 @@ static void marisa_laser_draw_lasers(EntityInterface *ent) {
r_uniform_vec4(u_clr0, 2.0, 1.0, 2.0, 0.0);
r_uniform_vec4(u_clr1, 0.1, 0.1, 1.0, 0.0);
for(MarisaLaser *laser = ctrl->lasers.first; laser; laser = laser->next) {
for(MarisaALaser *laser = ctrl->lasers.first; laser; laser = laser->next) {
if(set_alpha_dimmed(u_alpha, laser->alpha)) {
draw_laser_beam(laser->pos, laser->trace_hit.first, 42, 200, t * -0.03, tex0, u_length);
}
@ -270,7 +263,7 @@ static void marisa_laser_draw_lasers(EntityInterface *ent) {
.shader_ptr = r_shader_get("sprite_default"),
};
for(MarisaLaser *laser = ctrl->lasers.first; laser; laser = laser->next) {
for(MarisaALaser *laser = ctrl->lasers.first; laser; laser = laser->next) {
float a = laser->alpha * 0.8f;
cmplx spread;
@ -297,25 +290,25 @@ static void marisa_laser_flash_draw(Projectile *p, int t, ProjDrawRuleArgs args)
r_draw_sprite(&sp);
}
TASK(marisa_laser_slave_expire, { BoxedEntity slave; }) {
MarisaSlave *slave = TASK_BIND_CUSTOM(ARGS.slave, MarisaSlave);
TASK(marisa_laser_slave_expire, { BoxedMarisaASlave slave; }) {
MarisaASlave *slave = TASK_BIND(ARGS.slave);
slave->alive = false;
}
static void marisa_laser_show_laser(MarisaAController *ctrl, MarisaLaser *laser) {
static void marisa_laser_show_laser(MarisaAController *ctrl, MarisaALaser *laser) {
alist_append(&ctrl->lasers, laser);
}
static void marisa_laser_hide_laser(MarisaAController *ctrl, MarisaLaser *laser) {
static void marisa_laser_hide_laser(MarisaAController *ctrl, MarisaALaser *laser) {
alist_unlink(&ctrl->lasers, laser);
}
TASK(marisa_laser_fader, {
MarisaAController *ctrl;
const MarisaLaser *ref_laser;
const MarisaALaser *ref_laser;
}) {
MarisaAController *ctrl = ARGS.ctrl;
MarisaLaser fader = *ARGS.ref_laser;
MarisaALaser fader = *ARGS.ref_laser;
marisa_laser_show_laser(ctrl, &fader);
@ -327,16 +320,16 @@ TASK(marisa_laser_fader, {
marisa_laser_hide_laser(ctrl, &fader);
}
static void marisa_laser_fade_laser(MarisaAController *ctrl, MarisaLaser *laser) {
static void marisa_laser_fade_laser(MarisaAController *ctrl, MarisaALaser *laser) {
marisa_laser_hide_laser(ctrl, laser);
INVOKE_TASK(marisa_laser_fader, ctrl, laser);
}
TASK(marisa_laser_slave_shot_cleanup, {
MarisaAController *ctrl;
MarisaLaser **active_laser;
MarisaALaser **active_laser;
}) {
MarisaLaser *active_laser = *ARGS.active_laser;
MarisaALaser *active_laser = *ARGS.active_laser;
if(active_laser) {
marisa_laser_fade_laser(ARGS.ctrl, active_laser);
}
@ -344,16 +337,16 @@ TASK(marisa_laser_slave_shot_cleanup, {
TASK(marisa_laser_slave_shot, {
MarisaAController *ctrl;
BoxedEntity slave;
BoxedMarisaASlave slave;
}) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
MarisaSlave *slave = TASK_BIND_CUSTOM(ARGS.slave, MarisaSlave);
MarisaASlave *slave = TASK_BIND(ARGS.slave);
Animation *fire_anim = get_ani("fire");
AniSequence *fire_anim_seq = get_ani_sequence(fire_anim, "main");
MarisaLaser *active_laser = NULL;
MarisaALaser *active_laser = NULL;
INVOKE_TASK_AFTER(&TASK_EVENTS(THIS_TASK)->finished, marisa_laser_slave_shot_cleanup, ctrl, &active_laser);
for(;;) {
@ -362,7 +355,7 @@ TASK(marisa_laser_slave_shot, {
// We started shooting - register a laser for rendering
MarisaLaser laser = { 0 };
MarisaALaser laser = { 0 };
marisa_laser_show_laser(ctrl, &laser);
active_laser = &laser;
@ -408,7 +401,7 @@ TASK(marisa_laser_slave, {
}) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
MarisaSlave *slave = TASK_HOST_CUSTOM_ENT(MarisaSlave);
MarisaASlave *slave = TASK_HOST_ENT(MarisaASlave);
slave->alive = true;
slave->ent.draw_func = marisa_laser_draw_slave;
slave->ent.draw_layer = LAYER_PLAYER_SLAVE;
@ -416,11 +409,11 @@ TASK(marisa_laser_slave, {
slave->shader = r_shader_get("sprite_hakkero");
slave->sprite = get_sprite("hakkero");
INVOKE_TASK_WHEN(&ctrl->events.slaves_expired, marisa_laser_slave_expire, ENT_BOX_CUSTOM(slave));
INVOKE_TASK_WHEN(&ctrl->events.slaves_expired, marisa_laser_slave_expire, ENT_BOX(slave));
BoxedTask shot_task = cotask_box(INVOKE_SUBTASK(marisa_laser_slave_shot,
.ctrl = ctrl,
.slave = ENT_BOX_CUSTOM(slave)
.slave = ENT_BOX(slave)
));
/* unfocused focused */
@ -476,7 +469,7 @@ static real marisa_laser_masterspark_width(real progress) {
}
static void marisa_laser_draw_masterspark(EntityInterface *ent) {
MasterSpark *ms = ENT_CAST_CUSTOM(ent, MasterSpark);
MarisaAMasterSpark *ms = ENT_CAST(ent, MarisaAMasterSpark);
marisa_common_masterspark_draw(1, &(MarisaBeamInfo) {
ms->pos,
800 + I * VIEWPORT_H * 1.25,
@ -485,7 +478,7 @@ static void marisa_laser_draw_masterspark(EntityInterface *ent) {
}, ms->alpha);
}
static void marisa_laser_masterspark_damage(MasterSpark *ms) {
static void marisa_laser_masterspark_damage(MarisaAMasterSpark *ms) {
// lazy inefficient approximation of the beam parabola
float r = 96 * ms->alpha;
@ -521,7 +514,7 @@ TASK(marisa_laser_masterspark, { MarisaAController *ctrl; }) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
MasterSpark *ms = TASK_HOST_CUSTOM_ENT(MasterSpark);
MarisaAMasterSpark *ms = TASK_HOST_ENT(MarisaAMasterSpark);
ms->dir = 1;
ms->ent.draw_func = marisa_laser_draw_masterspark;
ms->ent.draw_layer = LAYER_PLAYER_FOCUS - 1;
@ -696,12 +689,12 @@ TASK(marisa_laser_shot_forward, { MarisaAController *ctrl; }) {
}
TASK(marisa_laser_controller, { BoxedPlayer plr; }) {
MarisaAController *ctrl = TASK_HOST_CUSTOM_ENT(MarisaAController);
MarisaAController *ctrl = TASK_HOST_ENT(MarisaAController);
ctrl->plr = TASK_BIND(ARGS.plr);
TASK_HOST_EVENTS(ctrl->events);
ctrl->laser_renderer.draw_func = marisa_laser_draw_lasers;
ctrl->laser_renderer.draw_layer = LAYER_PLAYER_FOCUS;
ctrl->ent.draw_func = marisa_laser_draw_lasers;
ctrl->ent.draw_layer = LAYER_PLAYER_FOCUS;
INVOKE_SUBTASK(marisa_laser_power_handler, ctrl);
INVOKE_SUBTASK(marisa_laser_bomb_handler, ctrl);

View file

@ -0,0 +1,19 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_plrmodes_marisa_a_entities_h
#define IGUARD_plrmodes_marisa_a_entities_h
#include "taisei.h"
#define ENTITIES_MarisaA(X, ...) \
X(MarisaAController, __VA_ARGS__) \
X(MarisaAMasterSpark, __VA_ARGS__) \
X(MarisaASlave, __VA_ARGS__) \
#endif // IGUARD_plrmodes_marisa_a_entities_h

View file

@ -0,0 +1,16 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_plrmodes_marisa_b_entities_h
#define IGUARD_plrmodes_marisa_b_entities_h
#include "taisei.h"
#define ENTITIES_MarisaB(X, ...) \
#endif // IGUARD_plrmodes_marisa_b_entities_h

View file

@ -32,15 +32,13 @@ typedef struct ReimuAController {
) events;
} ReimuAController;
typedef struct ReimuSlave ReimuSlave;
struct ReimuSlave {
ENTITY_INTERFACE_NAMED(ReimuSlave, ent);
DEFINE_ENTITY_TYPE(ReimuASlave, {
Sprite *sprite;
ShaderProgram *shader;
cmplx pos;
Color color;
uint alive;
};
});
static void reimu_spirit_preload(void) {
const int flags = RESF_DEFAULT;
@ -72,7 +70,7 @@ static void reimu_spirit_preload(void) {
}
TASK(reimu_spirit_needle, { cmplx pos; cmplx vel; real damage; ShaderProgram *shader; }) {
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *p = TASK_BIND(PROJECTILE(
.proto = pp_needle,
.pos = ARGS.pos,
.color = RGBA(0.5, 0.5, 0.5, 0.5),
@ -131,7 +129,7 @@ static Projectile *reimu_spirit_spawn_ofuda_particle(Projectile *p, int t, real
TASK(reimu_spirit_homing_impact, { BoxedProjectile p; }) {
Projectile *ref = NOT_NULL(ENT_UNBOX(ARGS.p));
Projectile *p = TASK_BIND_UNBOXED(PARTICLE(
Projectile *p = TASK_BIND(PARTICLE(
.proto = ref->proto,
.color = &ref->color,
.timeout = 32,
@ -159,7 +157,7 @@ static inline real reimu_spirit_homing_aimfactor(real t, real maxt) {
}
TASK(reimu_spirit_homing, { cmplx pos; cmplx vel; real damage; ShaderProgram *shader; }) {
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *p = TASK_BIND(PROJECTILE(
.proto = pp_ofuda,
.pos = ARGS.pos,
.color = RGBA(0.7, 0.63, 0.665, 0.7),
@ -329,7 +327,7 @@ TASK(reimu_spirit_bomb_orb, { BoxedPlayer plr; int index; real angle; }) {
int index = ARGS.index;
Player *plr = ENT_UNBOX(ARGS.plr);
Projectile *orb = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *orb = TASK_BIND(PROJECTILE(
.pos = plr->pos,
.timeout = 200 + 20 * index,
.type = PROJ_PLAYER,
@ -442,7 +440,7 @@ TASK(reimu_spirit_bomb_handler, { ReimuAController *ctrl; }) {
}
TASK(reimu_spirit_ofuda, { cmplx pos; cmplx vel; real damage; ShaderProgram *shader; }) {
Projectile *ofuda = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *ofuda = TASK_BIND(PROJECTILE(
.proto = pp_ofuda,
.pos = ARGS.pos,
.color = RGBA_MUL_ALPHA(1, 1, 1, 0.5),
@ -459,7 +457,7 @@ TASK(reimu_spirit_ofuda, { cmplx pos; cmplx vel; real damage; ShaderProgram *sha
}
static void reimu_spirit_draw_slave(EntityInterface *ent) {
ReimuSlave *slave = ENT_CAST_CUSTOM(ent, ReimuSlave);
ReimuASlave *slave = ENT_CAST(ent, ReimuASlave);
r_draw_sprite(&(SpriteParams) {
.color = &slave->color,
.pos.as_cmplx = slave->pos,
@ -469,11 +467,9 @@ static void reimu_spirit_draw_slave(EntityInterface *ent) {
});
}
static ReimuSlave *reimu_spirit_create_slave(ReimuAController *ctrl, const Color *color) {
static ReimuASlave *reimu_spirit_create_slave(ReimuAController *ctrl, const Color *color) {
Player *plr = ctrl->plr;
ReimuSlave *slave = TASK_HOST_CUSTOM_ENT(ReimuSlave);
ReimuASlave *slave = TASK_HOST_ENT(ReimuASlave);
slave->sprite = get_sprite("yinyang");
slave->shader = r_shader_get("sprite_yinyang");
slave->pos = plr->pos;
@ -491,14 +487,14 @@ static ReimuSlave *reimu_spirit_create_slave(ReimuAController *ctrl, const Color
}
TASK(reimu_slave_shot_particles, {
BoxedEntity slave;
BoxedReimuASlave slave;
int num;
cmplx dir;
Color *color0;
Color *color1;
Sprite *sprite;
}) {
ReimuSlave *slave = TASK_BIND_CUSTOM(ARGS.slave, ReimuSlave);
ReimuASlave *slave = TASK_BIND(ARGS.slave);
Color color0 = *ARGS.color0;
Color color1 = *ARGS.color1;
int num = ARGS.num;
@ -527,10 +523,10 @@ TASK(reimu_slave_shot_particles, {
TASK(reimu_spirit_slave_needle_shot, {
ReimuAController *ctrl;
BoxedEntity e;
BoxedReimuASlave slave;
}) {
Player *plr = ARGS.ctrl->plr;
ReimuSlave *slave = TASK_BIND_CUSTOM(ARGS.e, ReimuSlave);
ReimuASlave *slave = TASK_BIND(ARGS.slave);
ShaderProgram *shader = r_shader_get("sprite_particle");
Sprite *particle_spr = get_sprite("part/stain");
@ -542,7 +538,7 @@ TASK(reimu_spirit_slave_needle_shot, {
WAIT_EVENT_OR_DIE(&plr->events.shoot);
INVOKE_SUBTASK(reimu_slave_shot_particles,
.slave = ENT_BOX_CUSTOM(slave),
.slave = ENT_BOX(slave),
.color0 = RGBA(0.5, 0, 0, 0),
.color1 = RGBA(0.5, 0.25, 0, 0),
.num = delay,
@ -569,9 +565,9 @@ TASK(reimu_spirit_slave_needle, {
}) {
ReimuAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
ReimuSlave *slave = reimu_spirit_create_slave(ctrl, RGB(1.0, 0.8, 0.8));
ReimuASlave *slave = reimu_spirit_create_slave(ctrl, RGB(1.0, 0.8, 0.8));
INVOKE_SUBTASK(reimu_spirit_slave_needle_shot, ctrl, ENT_BOX_CUSTOM(slave));
INVOKE_SUBTASK(reimu_spirit_slave_needle_shot, ctrl, ENT_BOX(slave));
real dist = ARGS.distance;
cmplx offset = dist * cdir(ARGS.rotation_offset);
@ -593,11 +589,11 @@ TASK(reimu_spirit_slave_needle, {
TASK(reimu_spirit_slave_homing_shot, {
ReimuAController *ctrl;
BoxedEntity slave;
BoxedReimuASlave slave;
cmplx vel;
}) {
Player *plr = ARGS.ctrl->plr;
ReimuSlave *slave = TASK_BIND_CUSTOM(ARGS.slave, ReimuSlave);
ReimuASlave *slave = TASK_BIND(ARGS.slave);
ShaderProgram *shader = r_shader_get("sprite_particle");
Sprite *particle_spr = get_sprite("part/stain");
@ -611,7 +607,7 @@ TASK(reimu_spirit_slave_homing_shot, {
WAIT_EVENT_OR_DIE(&plr->events.shoot);
INVOKE_SUBTASK(reimu_slave_shot_particles,
.slave = ENT_BOX_CUSTOM(slave),
.slave = ENT_BOX(slave),
.color0 = RGBA(0.5, 0.125, 0, 0),
.color1 = RGBA(0.5, 0.125, 0.25, 0),
.num = delay,
@ -637,9 +633,9 @@ TASK(reimu_spirit_slave_homing, {
}) {
ReimuAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
ReimuSlave *slave = reimu_spirit_create_slave(ctrl, RGB(0.95, 0.75, 1.0));
ReimuASlave *slave = reimu_spirit_create_slave(ctrl, RGB(0.95, 0.75, 1.0));
INVOKE_SUBTASK(reimu_spirit_slave_homing_shot, ctrl, ENT_BOX_CUSTOM(slave), ARGS.shot_vel);
INVOKE_SUBTASK(reimu_spirit_slave_homing_shot, ctrl, ENT_BOX(slave), ARGS.shot_vel);
cmplx offset = ARGS.offset;
real target_speed = 0.005 * cabs(offset);

View file

@ -0,0 +1,17 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_plrmodes_reimu_a_entities_h
#define IGUARD_plrmodes_reimu_a_entities_h
#include "taisei.h"
#define ENTITIES_ReimuA(X, ...) \
X(ReimuASlave, __VA_ARGS__) \
#endif // IGUARD_plrmodes_reimu_a_entities_h