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

View file

@ -42,35 +42,31 @@
#define NUM_GAPS 4
typedef struct ReimuGap ReimuGap;
typedef struct ReimuSlave ReimuSlave;
typedef struct ReimuBController ReimuBController;
typedef struct ReimuBGap ReimuBGap;
struct ReimuGap {
ReimuGap *link; // bullets entering this gap will exit from the linked gap
struct ReimuBGap {
ReimuBGap *link; // bullets entering this gap will exit from the linked gap
cmplx pos; // position of the gap's center in viewport space
cmplx orientation; // normalized, points to the side the gap is 'attached' to
cmplx parallel_axis; // normalized, parallel the gap, perpendicular to orientation
};
struct ReimuSlave {
ENTITY_INTERFACE_NAMED(ReimuSlave, ent);
DEFINE_ENTITY_TYPE(ReimuBSlave, {
Sprite *sprite;
ShaderProgram *shader;
cmplx pos;
uint alive;
};
});
struct ReimuBController {
ENTITY_INTERFACE_NAMED(ReimuBController, gap_renderer);
DEFINE_ENTITY_TYPE(ReimuBController, {
Player *plr;
ShaderProgram *yinyang_shader;
Sprite *yinyang_sprite;
union {
ReimuGap array[NUM_GAPS];
ReimuBGap array[NUM_GAPS];
struct {
ReimuGap left, top, right, bottom;
ReimuBGap left, top, right, bottom;
};
} gaps;
@ -79,7 +75,7 @@ struct ReimuBController {
) events;
real bomb_alpha;
};
});
static void reimu_dream_gap_bomb_projectile_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
SpriteParamsBuffer spbuf;
@ -116,7 +112,7 @@ TASK(reimu_dream_gap_bomb_projectile, {
Sprite *sprite;
Sprite *impact_sprite;
}) {
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *p = TASK_BIND(PROJECTILE(
.angle = rng_angle(),
.color = ARGS.color,
.damage = 75,
@ -165,7 +161,7 @@ TASK(reimu_dream_bomb_barrage, { ReimuBController *ctrl; }) {
Color *pcolor = HSLA(t/30.0, 0.5, 0.5, 0.5);
for(int i = 0; i < NUM_GAPS; ++i) {
ReimuGap *gap = ctrl->gaps.array + i;
ReimuBGap *gap = ctrl->gaps.array + i;
INVOKE_TASK(reimu_dream_gap_bomb_projectile,
.pos = gap->pos + gap->parallel_axis * GAP_LENGTH * 0.25 * rng_sreal(),
.vel = -20 * gap->orientation,
@ -216,7 +212,7 @@ static void reimu_dream_draw_gap_lights(ReimuBController *ctrl, int time, real s
real len = GAP_LENGTH * 3 * sqrt(log1p(strength + 1) / 0.693f);
for(int i = 0; i < NUM_GAPS; ++i) {
ReimuGap *gap = ctrl->gaps.array + i;
ReimuBGap *gap = ctrl->gaps.array + i;
cmplx center = gap->pos - gap->orientation * (len * 0.5 - GAP_WIDTH * 0.6);
r_mat_mv_push();
@ -229,14 +225,14 @@ static void reimu_dream_draw_gap_lights(ReimuBController *ctrl, int time, real s
}
static void reimu_dream_draw_gaps(EntityInterface *gap_renderer_ent) {
ReimuBController *ctrl = ENT_CAST_CUSTOM(gap_renderer_ent, ReimuBController);
ReimuBController *ctrl = ENT_CAST(gap_renderer_ent, ReimuBController);
float gaps[NUM_GAPS][2];
float angles[NUM_GAPS];
int links[NUM_GAPS];
for(int i = 0; i < NUM_GAPS; ++i) {
ReimuGap *gap = ctrl->gaps.array + i;
ReimuBGap *gap = ctrl->gaps.array + i;
gaps[i][0] = creal(gap->pos);
gaps[i][1] = cimag(gap->pos);
angles[i] = -carg(gap->orientation);
@ -284,7 +280,7 @@ static void reimu_dream_draw_gaps(EntityInterface *gap_renderer_ent) {
};
for(int i = 0; i < NUM_GAPS; ++i) {
ReimuGap *gap = ctrl->gaps.array + i;
ReimuBGap *gap = ctrl->gaps.array + i;
yinyang.pos.as_cmplx = gap->pos + gap->parallel_axis * -0.5 * GAP_LENGTH;
r_draw_sprite(&yinyang);
yinyang.pos.as_cmplx = gap->pos + gap->parallel_axis * +0.5 * GAP_LENGTH;
@ -327,7 +323,7 @@ static void reimu_dream_bullet_warp(ReimuBController *ctrl, Projectile *p, int *
Rect p_bbox = { p->pos - p_long_side * half, p->pos + p_long_side * half };
for(int i = 0; i < NUM_GAPS; ++i) {
ReimuGap *gap = ctrl->gaps.array + i;
ReimuBGap *gap = ctrl->gaps.array + i;
real a = (carg(-gap->orientation) - carg(p->move.velocity));
if(fabs(a) < M_TAU/3) {
@ -351,7 +347,7 @@ static void reimu_dream_bullet_warp(ReimuBController *ctrl, Projectile *p, int *
fract = 1 - cimag(o - gap_bbox.top_left) / cimag(gap_size);
}
ReimuGap *ngap = gap->link;
ReimuBGap *ngap = gap->link;
o = ngap->pos + ngap->parallel_axis * GAP_LENGTH * (1 - fract - 0.5);
reimu_dream_spawn_warp_effect(gap->pos + gap->parallel_axis * GAP_LENGTH * (fract - 0.5), false);
@ -370,7 +366,7 @@ static void reimu_dream_bullet_warp(ReimuBController *ctrl, Projectile *p, int *
}
static void reimu_dream_draw_slave(EntityInterface *ent) {
ReimuSlave *slave = ENT_CAST_CUSTOM(ent, ReimuSlave);
ReimuBSlave *slave = ENT_CAST(ent, ReimuBSlave);
r_draw_sprite(&(SpriteParams) {
.sprite_ptr = slave->sprite,
.shader_ptr = slave->shader,
@ -388,7 +384,7 @@ TASK(reimu_dream_needle, {
ShaderProgram *shader;
}) {
ReimuBController *ctrl = ARGS.ctrl;
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *p = TASK_BIND(PROJECTILE(
.proto = pp_needle2,
.pos = ARGS.pos,
.color = RGBA_MUL_ALPHA(1, 1, 1, 0.35),
@ -421,12 +417,12 @@ TASK(reimu_dream_needle, {
TASK(reimu_dream_slave_shot, {
ReimuBController *ctrl;
BoxedEntity slave;
BoxedReimuBSlave slave;
cmplx vel;
}) {
ReimuBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
ReimuSlave *slave = TASK_BIND_CUSTOM(ARGS.slave, ReimuSlave);
ReimuBSlave *slave = TASK_BIND(ARGS.slave);
cmplx vel = ARGS.vel;
ShaderProgram *shader = r_shader_get("sprite_particle");
@ -451,7 +447,7 @@ TASK(reimu_dream_slave, {
}) {
ReimuBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
ReimuSlave *slave = TASK_HOST_CUSTOM_ENT(ReimuSlave);
ReimuBSlave *slave = TASK_HOST_ENT(ReimuBSlave);
slave->ent.draw_layer = LAYER_PLAYER_SLAVE;
slave->ent.draw_func = reimu_dream_draw_slave;
slave->sprite = get_sprite("yinyang"),
@ -469,7 +465,7 @@ TASK(reimu_dream_slave, {
INVOKE_SUBTASK(reimu_dream_slave_shot,
.ctrl = ctrl,
.slave = ENT_BOX_CUSTOM(slave),
.slave = ENT_BOX(slave),
.vel = 20 * ARGS.shot_dir
);
@ -523,7 +519,7 @@ TASK(reimu_dream_process_gaps, { ReimuBController *ctrl; }) {
cmplx ofs_factors = 1 + I;
for(int i = 0; i < NUM_GAPS; ++i) {
ReimuGap *gap = ctrl->gaps.array + i;
ReimuBGap *gap = ctrl->gaps.array + i;
gap->pos = reimu_dream_gap_pos(vp, ref_pos, gap->orientation, gap->parallel_axis, ofs_factors);
gap->pos += (2 * GAP_WIDTH + GAP_OFFSET) * gap->orientation;
}
@ -537,7 +533,7 @@ TASK(reimu_dream_process_gaps, { ReimuBController *ctrl; }) {
ofs_factors = 1 + I * !is_focused; // no extra clamping of vertical gaps if focused
for(int i = 0; i < NUM_GAPS; ++i) {
ReimuGap *gap = ctrl->gaps.array + i;
ReimuBGap *gap = ctrl->gaps.array + i;
cmplx target_pos = reimu_dream_gap_pos(vp, ref_pos, gap->orientation, gap->parallel_axis, ofs_factors);
capproach_asymptotic_p(&gap->pos, target_pos, pos_approach_rate, 1e-5);
}
@ -546,7 +542,7 @@ TASK(reimu_dream_process_gaps, { ReimuBController *ctrl; }) {
TASK(reimu_dream_ofuda, { ReimuBController *ctrl; cmplx pos; cmplx vel; ShaderProgram *shader; }) {
ReimuBController *ctrl = ARGS.ctrl;
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),
@ -626,39 +622,36 @@ TASK(reimu_dream_controller_tick, { ReimuBController *ctrl; }) {
}
TASK(reimu_dream_controller, { BoxedPlayer plr; }) {
ReimuBController ctrl = { 0 };
ctrl.plr = TASK_BIND(ARGS.plr);
COEVENT_INIT_ARRAY(ctrl.events);
ReimuBController *ctrl = TASK_HOST_ENT(ReimuBController);
ctrl->plr = TASK_BIND(ARGS.plr);
TASK_HOST_EVENTS(ctrl->events);
ctrl.yinyang_shader = r_shader_get("sprite_yinyang");
ctrl.yinyang_sprite = get_sprite("yinyang");
ctrl->yinyang_shader = r_shader_get("sprite_yinyang");
ctrl->yinyang_sprite = get_sprite("yinyang");
ctrl.gap_renderer.draw_func = reimu_dream_draw_gaps;
ctrl.gap_renderer.draw_layer = LAYER_PLAYER_FOCUS;
ent_register(&ctrl.gap_renderer, ENT_CUSTOM);
ctrl->ent.draw_func = reimu_dream_draw_gaps;
ctrl->ent.draw_layer = LAYER_PLAYER_FOCUS;
ctrl.gaps.left.link = &ctrl.gaps.top;
ctrl.gaps.top.link = &ctrl.gaps.left;
ctrl.gaps.right.link = &ctrl.gaps.bottom;
ctrl.gaps.bottom.link = &ctrl.gaps.right;
ctrl->gaps.left.link = &ctrl->gaps.top;
ctrl->gaps.top.link = &ctrl->gaps.left;
ctrl->gaps.right.link = &ctrl->gaps.bottom;
ctrl->gaps.bottom.link = &ctrl->gaps.right;
ctrl.gaps.left.orientation = -1;
ctrl.gaps.right.orientation = 1;
ctrl.gaps.top.orientation = -I;
ctrl.gaps.bottom.orientation = I;
ctrl->gaps.left.orientation = -1;
ctrl->gaps.right.orientation = 1;
ctrl->gaps.top.orientation = -I;
ctrl->gaps.bottom.orientation = I;
ctrl.gaps.left.parallel_axis = ctrl.gaps.right.parallel_axis = I;
ctrl.gaps.top.parallel_axis = ctrl.gaps.bottom.parallel_axis = 1;
ctrl->gaps.left.parallel_axis = ctrl->gaps.right.parallel_axis = I;
ctrl->gaps.top.parallel_axis = ctrl->gaps.bottom.parallel_axis = 1;
INVOKE_SUBTASK(reimu_dream_controller_tick, &ctrl);
INVOKE_SUBTASK(reimu_dream_process_gaps, &ctrl);
INVOKE_SUBTASK(reimu_dream_shot_forward, &ctrl);
INVOKE_SUBTASK(reimu_dream_power_handler, &ctrl);
INVOKE_SUBTASK(reimu_dream_bomb_handler, &ctrl);
INVOKE_SUBTASK(reimu_dream_controller_tick, ctrl);
INVOKE_SUBTASK(reimu_dream_process_gaps, ctrl);
INVOKE_SUBTASK(reimu_dream_shot_forward, ctrl);
INVOKE_SUBTASK(reimu_dream_power_handler, ctrl);
INVOKE_SUBTASK(reimu_dream_bomb_handler, ctrl);
WAIT_EVENT(&TASK_EVENTS(THIS_TASK)->finished);
COEVENT_CANCEL_ARRAY(ctrl.events);
ent_unregister(&ctrl.gap_renderer);
STALL;
}
static void reimu_dream_init(Player *plr) {

View file

@ -0,0 +1,18 @@
/*
* 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_b_entities_h
#define IGUARD_plrmodes_reimu_b_entities_h
#include "taisei.h"
#define ENTITIES_ReimuB(X, ...) \
X(ReimuBController, __VA_ARGS__) \
X(ReimuBSlave, __VA_ARGS__) \
#endif // IGUARD_plrmodes_reimu_b_entities_h

View file

@ -21,9 +21,9 @@
#define SHOT_MYON_DAMAGE 30
typedef struct YoumuAController YoumuAController;
typedef struct YoumuMyon YoumuMyon;
typedef struct YoumuAMyon YoumuAMyon;
struct YoumuMyon {
struct YoumuAMyon {
struct {
Sprite *trail;
Sprite *smoke;
@ -44,7 +44,7 @@ struct YoumuAController {
} sprites;
Player *plr;
YoumuMyon myon;
YoumuAMyon myon;
};
static Color *myon_color(Color *c, float f, float opacity, float alpha) {
@ -53,7 +53,7 @@ static Color *myon_color(Color *c, float f, float opacity, float alpha) {
return c;
}
static cmplx myon_tail_dir(YoumuMyon *myon) {
static cmplx myon_tail_dir(YoumuAMyon *myon) {
cmplx dir = myon->dir * cdir(0.1 * sin(global.frames * 0.05));
real f = myon->focus_factor;
return f * f * dir;
@ -83,10 +83,10 @@ static ProjDrawRule myon_draw_trail(float focus_factor, float opacity) {
};
}
TASK(youmu_mirror_myon_trail, { YoumuMyon *myon; cmplx pos; }) {
YoumuMyon *myon = ARGS.myon;
TASK(youmu_mirror_myon_trail, { YoumuAMyon *myon; cmplx pos; }) {
YoumuAMyon *myon = ARGS.myon;
Projectile *p = TASK_BIND_UNBOXED(PARTICLE(
Projectile *p = TASK_BIND(PARTICLE(
.angle = rng_angle(),
.draw_rule = pdraw_timeout_scale(2, 0.01),
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE | PFLAG_MANUALANGLE | PFLAG_PLRSPECIALPARTICLE,
@ -108,7 +108,7 @@ TASK(youmu_mirror_myon_trail, { YoumuMyon *myon; cmplx pos; }) {
}
}
static void myon_spawn_trail(YoumuMyon *myon, int t) {
static void myon_spawn_trail(YoumuAMyon *myon, int t) {
cmplx pos = myon->pos + 3 * cdir(global.frames * 0.07);
cmplx stardust_v = 3 * myon_tail_dir(myon) * cdir(M_PI/16*sin(1.33*t));
real f = myon->focus_factor;
@ -160,7 +160,7 @@ static void myon_draw_proj_trail(Projectile *p, int t, ProjDrawRuleArgs args) {
}
TASK(youmu_mirror_myon_proj, { cmplx pos; cmplx vel; real dmg; const Color *clr; ShaderProgram *shader; }) {
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *p = TASK_BIND(PROJECTILE(
.color = ARGS.clr,
.damage = ARGS.dmg,
.layer = LAYER_PLAYER_SHOT | 0x10,
@ -229,7 +229,7 @@ static void myon_proj_color(Color *clr, real focus_factor) {
TASK(youmu_mirror_myon_shot, { YoumuAController *ctrl; }) {
YoumuAController *ctrl = ARGS.ctrl;
YoumuMyon *myon = &ctrl->myon;
YoumuAMyon *myon = &ctrl->myon;
Player *plr = ctrl->plr;
ShaderProgram *shader = r_shader_get("sprite_youmu_myon_shot");
@ -287,7 +287,7 @@ TASK(youmu_mirror_myon_shot, { YoumuAController *ctrl; }) {
TASK(youmu_mirror_myon, { YoumuAController *ctrl; }) {
YoumuAController *ctrl = ARGS.ctrl;
YoumuMyon *myon = &ctrl->myon;
YoumuAMyon *myon = &ctrl->myon;
Player *plr = ctrl->plr;
myon->sprites.trail = get_sprite("part/myon");
@ -455,7 +455,7 @@ static void youmu_mirror_draw_speed_trail(Projectile *p, int t, ProjDrawRuleArgs
TASK(youmu_mirror_bomb_controller, { YoumuAController *ctrl; }) {
YoumuAController *ctrl = ARGS.ctrl;
YoumuMyon *myon = &ctrl->myon;
YoumuAMyon *myon = &ctrl->myon;
Player *plr = ctrl->plr;
cmplx vel = -30 * myon->dir;
@ -506,7 +506,7 @@ TASK(youmu_mirror_bomb_controller, { YoumuAController *ctrl; }) {
TASK(youmu_mirror_bomb_postprocess, { YoumuAController *ctrl; }) {
YoumuAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
YoumuMyon *myon = &ctrl->myon;
YoumuAMyon *myon = &ctrl->myon;
CoEvent *pp_event = &stage_get_draw_events()->postprocess_before_overlay;
ShaderProgram *shader = r_shader_get("youmua_bomb");

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_youmu_a_entities_h
#define IGUARD_plrmodes_youmu_a_entities_h
#include "taisei.h"
#define ENTITIES_YoumuA(X, ...) \
#endif // IGUARD_plrmodes_youmu_a_entities_h

View file

@ -208,7 +208,7 @@ static void youmu_haunting_power_shot(Player *plr, int p) {
}
TASK(youmu_homing_shot, { BoxedPlayer plr; }) {
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *p = TASK_BIND(PROJECTILE(
.proto = pp_hghost,
.pos = ENT_UNBOX(ARGS.plr)->pos,
.color = RGB(0.75, 0.9, 1),
@ -263,7 +263,7 @@ TASK(youmu_orb_homing_spirit, { cmplx pos; cmplx velocity; cmplx target; real ch
return;
}
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *p = TASK_BIND(PROJECTILE(
.proto = pp_hghost,
.pos = ARGS.pos,
.color = color_mul_scalar(RGB(0.75, 0.9, 1), 0.5),
@ -414,7 +414,7 @@ TASK(youmu_orb_shot, { BoxedPlayer plr; }) {
Player *plr = ENT_UNBOX(ARGS.plr);
int pwr = plr->power / 100;
Projectile *orb = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *orb = TASK_BIND(PROJECTILE(
.proto = pp_youhoming,
.pos = plr->pos,
.color = RGB(1, 1, 1),

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_youmu_b_entities_h
#define IGUARD_plrmodes_youmu_b_entities_h
#include "taisei.h"
#define ENTITIES_YoumuB(X, ...) \
#endif // IGUARD_plrmodes_youmu_b_entities_h

View file

@ -295,7 +295,7 @@ static Projectile* _create_projectile(ProjArgs *args) {
projectile_set_layer(p, args->layer);
COEVENT_INIT_ARRAY(p->events);
ent_register(&p->ent, ENT_PROJECTILE);
ent_register(&p->ent, ENT_TYPE_ID(Projectile));
// TODO: Maybe allow ACTION_DESTROY here?
// But in that case, code that uses this function's return value must be careful to not dereference a NULL pointer.

View file

@ -25,11 +25,16 @@
#define PROJ_DEBUG
#endif
#ifdef PROJ_DEBUG
#define IF_PROJ_DEBUG(...) __VA_ARGS__
#else
#define IF_PROJ_DEBUG(...)
#endif
enum {
RULE_ARGC = 4
};
typedef struct Projectile Projectile;
typedef LIST_ANCHOR(Projectile) ProjectileList;
typedef LIST_INTERFACE(Projectile) ProjectileListInterface;
@ -84,9 +89,7 @@ typedef enum ProjFlags {
// FIXME: prototype stuff awkwardly shoved in this header because of dependency cycles.
typedef struct ProjPrototype ProjPrototype;
struct Projectile {
ENTITY_INTERFACE_NAMED(Projectile, ent);
DEFINE_ENTITY_TYPE(Projectile, {
cmplx pos;
cmplx pos0;
cmplx prevpos; // used to lerp trajectory for collision detection; set this to pos if you intend to "teleport" the projectile in the rule!
@ -127,10 +130,10 @@ struct Projectile {
int graze_cooldown;
short graze_counter;
#ifdef PROJ_DEBUG
DebugInfo debug;
#endif
};
IF_PROJ_DEBUG(
DebugInfo debug;
)
});
typedef struct ProjArgs {
ProjPrototype *proto;

View file

@ -572,12 +572,12 @@ static bool proximity_predicate(EntityInterface *ent, void *varg) {
Circle *area = varg;
switch(ent->type) {
case ENT_PROJECTILE: {
case ENT_TYPE_ID(Projectile): {
Projectile *p = ENT_CAST(ent, Projectile);
return cabs(p->pos - area->origin) < area->radius;
}
case ENT_LASER: {
case ENT_TYPE_ID(Laser): {
Laser *l = ENT_CAST(ent, Laser);
return laser_intersects_circle(l, *area);
}
@ -590,12 +590,12 @@ static bool ellipse_predicate(EntityInterface *ent, void *varg) {
Ellipse *e = varg;
switch(ent->type) {
case ENT_PROJECTILE: {
case ENT_TYPE_ID(Projectile): {
Projectile *p = ENT_CAST(ent, Projectile);
return point_in_ellipse(p->pos, *e);
}
case ENT_LASER: {
case ENT_TYPE_ID(Laser): {
Laser *l = ENT_CAST(ent, Laser);
return laser_intersects_ellipse(l, *e);
}

View file

@ -595,13 +595,13 @@ static bool powersurge_draw_predicate(EntityInterface *ent) {
return true;
}
if(ent->type == ENT_PROJECTILE) {
if(ent->type == ENT_TYPE_ID(Projectile)) {
Projectile *p = ENT_CAST(ent, Projectile);
return p->flags & PFLAG_PLRSPECIALPARTICLE;
}
}
if(ent->type == ENT_ITEM) {
if(ent->type == ENT_TYPE_ID(Item)) {
Item *i = ENT_CAST(ent, Item);
return i->type == ITEM_VOLTAGE;
}
@ -841,7 +841,7 @@ bool stage_should_draw_particle(Projectile *p) {
}
static bool stage_draw_predicate(EntityInterface *ent) {
if(ent->type == ENT_PROJECTILE) {
if(ent->type == ENT_TYPE_ID(Projectile)) {
Projectile *p = ENT_CAST(ent, Projectile);
if(p->type == PROJ_PARTICLE) {

View file

@ -79,7 +79,7 @@ TASK(wait_event_test, { BoxedEnemy e; int rounds; int delay; int cnt; int cnt_in
TASK(test_enemy, {
double hp; cmplx pos; cmplx dir;
}) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, ARGS.hp, BigFairy, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, ARGS.hp, BigFairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.life_fragment = 1,
@ -150,7 +150,7 @@ TASK(subtask_test_init, NO_ARGS) {
}
TASK(punching_bag, NO_ARGS) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(0.5*(VIEWPORT_W+VIEWPORT_H*I), 1000, BigFairy, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(0.5*(VIEWPORT_W+VIEWPORT_H*I), 1000, BigFairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.life_fragment = 10,

View file

@ -66,7 +66,7 @@ TASK(glider_bullet, {
TASK(glider_fairy, {
double hp; cmplx pos; cmplx dir;
}) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(VIEWPORT_W/2-10*I, ARGS.hp, BigFairy, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(VIEWPORT_W/2-10*I, ARGS.hp, BigFairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.power = 3,

View file

@ -91,8 +91,8 @@ static bool stage1_draw_predicate(EntityInterface *ent) {
}
switch(ent->type) {
case ENT_BOSS: return true;
case ENT_ENEMY: {
case ENT_TYPE_ID(Boss): return true;
case ENT_TYPE_ID(Enemy): {
Enemy *e = ENT_CAST(ent, Enemy);
if(e->hp == ENEMY_BOMB) {
@ -101,7 +101,7 @@ static bool stage1_draw_predicate(EntityInterface *ent) {
return true;
}
case ENT_PROJECTILE: {
case ENT_TYPE_ID(Projectile): {
Projectile *p = ENT_CAST(ent, Projectile);
if(p->type == PROJ_PARTICLE) {

View file

@ -424,7 +424,7 @@ TASK(halation_orb, {
cmplx pos[4];
int activation_time;
}) {
Projectile *orb = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *orb = TASK_BIND(PROJECTILE(
.proto = pp_plainball,
.pos = ARGS.pos[0],
.max_viewport_dist = 200,
@ -567,7 +567,7 @@ DEFINE_EXTERN_TASK(stage1_spell_snow_halation) {
}
TASK(cirno_icicle, { cmplx pos; cmplx vel; }) {
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *p = TASK_BIND(PROJECTILE(
.proto = pp_crystal,
.pos = ARGS.pos,
.color = RGB(0.3, 0.3, 0.9),
@ -649,7 +649,7 @@ TASK(cirno_frostbolt_trail, { BoxedProjectile proj; }) {
}
TASK(cirno_frostbolt, { cmplx pos; cmplx vel; }) {
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
Projectile *p = TASK_BIND(PROJECTILE(
.proto = pp_wave,
.pos = ARGS.pos,
.color = RGBA(0.2, 0.2, 0.4, 0.0),
@ -756,7 +756,7 @@ void cirno_benchmark(Boss* b, int t) {
}
TASK(burst_fairy, { cmplx pos; cmplx dir; }) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, 700, Fairy, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 700, Fairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 1,
@ -840,7 +840,7 @@ TASK(circletoss_shoot_toss, { BoxedEnemy e; int times; int duration; int period;
}
TASK(circletoss_fairy, { cmplx pos; cmplx velocity; cmplx exit_accel; int exit_time; }) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, 1500, BigFairy, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 1500, BigFairy, NULL, 0));
e->move = move_linear(ARGS.velocity);
@ -880,7 +880,7 @@ TASK(sinepass_swirl_move, { BoxedEnemy e; cmplx v; cmplx sv; }) {
}
TASK(sinepass_swirl, { cmplx pos; cmplx vel; cmplx svel; }) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, 100, Swirl, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 100, Swirl, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 1,
@ -910,7 +910,7 @@ TASK(sinepass_swirl, { cmplx pos; cmplx vel; cmplx svel; }) {
}
TASK(circle_fairy, { cmplx pos; cmplx target_pos; }) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, 1400, BigFairy, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 1400, BigFairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 3,
@ -959,7 +959,7 @@ TASK(circle_fairy, { cmplx pos; cmplx target_pos; }) {
}
TASK(drop_swirl, { cmplx pos; cmplx vel; cmplx accel; }) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, 100, Swirl, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 100, Swirl, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 2,
@ -988,7 +988,7 @@ TASK(drop_swirl, { cmplx pos; cmplx vel; cmplx accel; }) {
}
TASK(multiburst_fairy, { cmplx pos; cmplx target_pos; cmplx exit_accel; }) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, 1000, Fairy, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 1000, Fairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 3,
@ -1046,7 +1046,7 @@ TASK(instantcircle_fairy_shoot, { BoxedEnemy e; int cnt; double speed; double bo
}
TASK(instantcircle_fairy, { cmplx pos; cmplx target_pos; cmplx exit_accel; }) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, 1200, Fairy, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 1200, Fairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 2,
@ -1094,7 +1094,7 @@ TASK(waveshot, { cmplx pos; real angle; real spread; real freq; int shots; int i
}
TASK(waveshot_fairy, { cmplx pos; cmplx target_pos; cmplx exit_accel; }) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, 4200, BigFairy, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 4200, BigFairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 4,
@ -1125,7 +1125,7 @@ TASK(waveshot_fairy, { cmplx pos; cmplx target_pos; cmplx exit_accel; }) {
}
TASK(explosion_fairy, { cmplx pos; cmplx target_pos; cmplx exit_accel; }) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, 6000, BigFairy, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 6000, BigFairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 8,
@ -1579,7 +1579,7 @@ TASK(spawn_midboss, NO_ARGS) {
}
TASK(tritoss_fairy, { cmplx pos; cmplx velocity; cmplx end_velocity; }) {
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, 16000, BigFairy, NULL, 0));
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 16000, BigFairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 5,

View file

@ -297,6 +297,10 @@ typedef cmplx64 cmplx;
#define ASSUME_ALIGNED(expr, alignment) (expr)
#endif
#define UNION_CAST(_from_type, _to_type, _expr) \
((union { _from_type f; _to_type t; }) { .f = (_expr) }).t
// #define CASTPTR_ASSUME_ALIGNED(expr, type) UNION_CAST(void*, type*, ASSUME_ALIGNED(expr, alignof(type)))
#define CASTPTR_ASSUME_ALIGNED(expr, type) ((type*)ASSUME_ALIGNED((expr), alignof(type)))
#ifdef USE_GNU_EXTENSIONS