"Boxed" entities; facility to bind tasks to entity lifetime
This commit is contained in:
parent
6a8ed64adb
commit
1579082e30
5 changed files with 66 additions and 13 deletions
|
@ -16,6 +16,7 @@
|
|||
struct CoTask {
|
||||
LIST_INTERFACE(CoTask);
|
||||
koishi_coroutine_t ko;
|
||||
BoxedEntity bound_ent;
|
||||
};
|
||||
|
||||
struct CoSched {
|
||||
|
@ -44,10 +45,20 @@ CoTask *cotask_new(CoTaskFunc func) {
|
|||
}
|
||||
|
||||
void cotask_free(CoTask *task) {
|
||||
memset(&task->bound_ent, 0, sizeof(task->bound_ent));
|
||||
alist_push(&task_pool, task);
|
||||
}
|
||||
|
||||
CoTask *cotask_active(void) {
|
||||
return CASTPTR_ASSUME_ALIGNED((char*)koishi_active() - offsetof(CoTask, ko), CoTask);
|
||||
}
|
||||
|
||||
void *cotask_resume(CoTask *task, void *arg) {
|
||||
if(task->bound_ent.ent && !ent_unbox(task->bound_ent)) {
|
||||
koishi_kill(&task->ko);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return koishi_resume(&task->ko, arg);
|
||||
}
|
||||
|
||||
|
@ -59,6 +70,11 @@ CoStatus cotask_status(CoTask *task) {
|
|||
return koishi_state(&task->ko);
|
||||
}
|
||||
|
||||
void cotask_bind_to_entity(CoTask *task, EntityInterface *ent) {
|
||||
assert(task->bound_ent.ent == 0);
|
||||
task->bound_ent = ent_box(ent);
|
||||
}
|
||||
|
||||
CoSched *cosched_new(void) {
|
||||
CoSched *sched = calloc(1, sizeof(*sched));
|
||||
return sched;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "entity.h"
|
||||
#include <koishi.h>
|
||||
|
||||
typedef struct CoTask CoTask;
|
||||
|
@ -34,6 +35,8 @@ void cotask_free(CoTask *task);
|
|||
void *cotask_resume(CoTask *task, void *arg);
|
||||
void *cotask_yield(void *arg);
|
||||
CoStatus cotask_status(CoTask *task);
|
||||
CoTask *cotask_active(void);
|
||||
void cotask_bind_to_entity(CoTask *task, EntityInterface *ent);
|
||||
|
||||
CoSched *cosched_new(void);
|
||||
void *cosched_new_task(CoSched *sched, CoTaskFunc func, void *arg); // creates and runs the task, returns whatever it yields, schedules it for resume on cosched_run_tasks if it's still alive
|
||||
|
@ -64,4 +67,6 @@ static inline attr_must_inline void cosched_set_invoke_target(CoSched *sched) {
|
|||
#define BYIELD do { YIELD; CHECK_BREAK; } while(0)
|
||||
#define BWAIT(frames) do { WAIT(frames); CHECK_BREAK; } while(0)
|
||||
|
||||
#define TASK_BIND(ent) cotask_bind_to_entity(cotask_active(), &ent->entity_interface)
|
||||
|
||||
#endif // IGUARD_coroutine_h
|
||||
|
|
20
src/entity.c
20
src/entity.c
|
@ -92,7 +92,7 @@ void ent_register(EntityInterface *ent, EntityType type) {
|
|||
// This is not really an error, but it may result in weird draw order
|
||||
// in some corner cases. If this overflow ever actually occurs, though,
|
||||
// then you've probably got much bigger problems.
|
||||
log_debug("spawn_id just overflowed. You might be spawning stuff waaaay too often");
|
||||
log_warn("spawn_id just overflowed. You might be spawning stuff waaaay too often");
|
||||
}
|
||||
|
||||
if(entities.capacity < entities.num) {
|
||||
|
@ -107,6 +107,7 @@ void ent_register(EntityInterface *ent, EntityType type) {
|
|||
}
|
||||
|
||||
void ent_unregister(EntityInterface *ent) {
|
||||
ent->spawn_id = 0;
|
||||
EntityInterface *sub = entities.array[--entities.num];
|
||||
assert(ent->index <= entities.num);
|
||||
assert(entities.array[ent->index] == ent);
|
||||
|
@ -242,3 +243,20 @@ 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(EntityInterface *ent) {
|
||||
BoxedEntity h;
|
||||
h.ent = (uintptr_t)ent;
|
||||
h.spawn_id = ent->spawn_id;
|
||||
return h;
|
||||
}
|
||||
|
||||
EntityInterface *ent_unbox(BoxedEntity box) {
|
||||
EntityInterface *e = (EntityInterface*)box.ent;
|
||||
|
||||
if(e->spawn_id == box.spawn_id) {
|
||||
return e;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
20
src/entity.h
20
src/entity.h
|
@ -51,6 +51,7 @@ typedef enum EntityType {
|
|||
|
||||
typedef struct EntityInterface EntityInterface;
|
||||
typedef struct EntityListNode EntityListNode;
|
||||
typedef struct BoxedEntity BoxedEntity;
|
||||
|
||||
typedef enum DamageType {
|
||||
DMG_UNDEFINED,
|
||||
|
@ -109,6 +110,11 @@ struct EntityInterface {
|
|||
ENTITY_INTERFACE_BASE(EntityInterface);
|
||||
};
|
||||
|
||||
struct BoxedEntity {
|
||||
uintptr_t ent; // not an actual pointer to avert the temptation to use it directly.
|
||||
uint_fast32_t spawn_id;
|
||||
};
|
||||
|
||||
INLINE const char* ent_type_name(EntityType type) {
|
||||
switch(type) {
|
||||
#define ENT_TYPE(typename, id) case id: return #id;
|
||||
|
@ -152,4 +158,18 @@ void ent_unhook_pre_draw(EntityDrawHookCallback callback);
|
|||
void ent_hook_post_draw(EntityDrawHookCallback callback, void *arg);
|
||||
void ent_unhook_post_draw(EntityDrawHookCallback callback);
|
||||
|
||||
BoxedEntity ent_box(EntityInterface *ent);
|
||||
EntityInterface *ent_unbox(BoxedEntity box);
|
||||
|
||||
#define ENT_BOX(ent) ent_box(&(ent)->entity_interface)
|
||||
|
||||
#if defined(USE_GNU_EXTENSIONS) && defined(DEBUG)
|
||||
#define ENT_UNBOX(h, typename) (__extension__ ({ \
|
||||
EntityInterface *_unboxed_ent = ent_unbox(h); \
|
||||
_unboxed_ent ? ENT_CAST(_unboxed_ent, typename) : NULL; \
|
||||
}))
|
||||
#else
|
||||
#define ENT_UNBOX(h, typename) CASTPTR_ASSUME_ALIGNED(ent_unbox(h), typename)
|
||||
#endif
|
||||
|
||||
#endif // IGUARD_entity_h
|
||||
|
|
|
@ -18,21 +18,19 @@ static void cotest_stub_proc(void) { }
|
|||
|
||||
TASK(test_enemy, { double hp; complex pos; complex dir; }) {
|
||||
Enemy *e = create_enemy1c(ARGS.pos, ARGS.hp, BigFairy, NULL, 0);
|
||||
int eref = add_ref(e);
|
||||
TASK_BIND(e);
|
||||
|
||||
#define BREAK_CONDITION !UPDATE_REF(eref, e)
|
||||
|
||||
BYIELD;
|
||||
YIELD;
|
||||
|
||||
while(true) {
|
||||
// wander around for a bit...
|
||||
for(int i = 0; i < 20; ++i) {
|
||||
e->pos += ARGS.dir;
|
||||
BYIELD;
|
||||
YIELD;
|
||||
}
|
||||
|
||||
// stop and...
|
||||
BWAIT(10);
|
||||
WAIT(10);
|
||||
|
||||
// pew pew!!!
|
||||
int pcount = 10 + 10 * frand();
|
||||
|
@ -48,17 +46,13 @@ TASK(test_enemy, { double hp; complex pos; complex dir; }) {
|
|||
.args = { aim },
|
||||
);
|
||||
|
||||
BWAIT(3);
|
||||
WAIT(3);
|
||||
}
|
||||
|
||||
// keep wandering, randomly
|
||||
ARGS.dir *= cexp(I*M_PI*nfrand());
|
||||
BYIELD;
|
||||
YIELD;
|
||||
}
|
||||
|
||||
BREAK:
|
||||
log_debug("enemy died!");
|
||||
free_ref(eref);
|
||||
}
|
||||
|
||||
TASK(stage_main, { int ignored; }) {
|
||||
|
|
Loading…
Reference in a new issue