use "anchored" lists for most game objects

these have a pointer to the last element, thus appending is fast
This commit is contained in:
Andrei Alexeyev 2018-06-01 21:40:18 +03:00
parent 1c588f1a41
commit 83a8961df6
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
33 changed files with 522 additions and 214 deletions

View file

@ -22,13 +22,13 @@ void aniplayer_create(AniPlayer *plr, Animation *ani, const char *startsequence)
}
Sprite *aniplayer_get_frame(AniPlayer *plr) {
assert(plr->queue != NULL);
return animation_get_frame(plr->ani, plr->queue->sequence, plr->queue->clock);
assert(plr->queue.first != NULL);
return animation_get_frame(plr->ani, plr->queue.first->sequence, plr->queue.first->clock);
}
void aniplayer_free(AniPlayer *plr) {
plr->queuesize = 0;
list_free_all(&plr->queue);
alist_free_all(&plr->queue);
}
// Deletes the queue. If hard is set, even the last element is removed leaving the player in an invalid state.
@ -36,18 +36,19 @@ static void aniplayer_reset(AniPlayer *plr, bool hard) {
if(plr->queuesize == 0)
return;
if(hard) {
list_free_all(&plr->queue);
alist_free_all(&plr->queue);
plr->queuesize = 0;
return;
}
list_free_all(&plr->queue->next);
list_free_all(&plr->queue.first->next);
plr->queue.last = plr->queue.first;
plr->queuesize = 1;
}
AniQueueEntry *aniplayer_queue(AniPlayer *plr, const char *seqname, int loops) {
AniQueueEntry *s = calloc(1, sizeof(AniQueueEntry));
list_append(&plr->queue, s);
alist_append(&plr->queue, s);
plr->queuesize++;
if(loops < 0)
@ -76,13 +77,13 @@ AniQueueEntry *aniplayer_hard_switch(AniPlayer *plr, const char *seqname, int lo
}
void aniplayer_update(AniPlayer *plr) {
assert(plr->queue); // The queue should never be empty.
AniQueueEntry *s = plr->queue;
assert(plr->queue.first != NULL); // The queue should never be empty.
AniQueueEntry *s = plr->queue.first;
s->clock++;
// The last condition assures that animations only switch at their end points
if(s->clock >= s->duration && plr->queuesize > 1 && s->clock%s->sequence->length == 0) {
free(list_pop(&plr->queue));
free(alist_pop(&plr->queue));
plr->queuesize--;
}
}

View file

@ -82,7 +82,7 @@ typedef struct AniPlayer AniPlayer;
struct AniPlayer{
Animation *ani;
AniQueueEntry *queue;
LIST_ANCHOR(AniQueueEntry) queue;
int queuesize;
};

View file

@ -33,14 +33,14 @@ Enemy* _enemy_attach_dbginfo(Enemy *e, DebugInfo *dbg) {
static void ent_draw_enemy(EntityInterface *ent);
Enemy *create_enemy_p(Enemy **enemies, complex pos, int hp, EnemyVisualRule visual_rule, EnemyLogicRule logic_rule,
Enemy *create_enemy_p(EnemyList *enemies, complex pos, int hp, EnemyVisualRule visual_rule, EnemyLogicRule logic_rule,
complex a1, complex a2, complex a3, complex a4) {
if(IN_DRAW_CODE) {
log_fatal("Tried to spawn an enemy while in drawing code");
}
// XXX: some code relies on the insertion logic
Enemy *e = (Enemy*)list_insert(enemies, objpool_acquire(stage_object_pools.enemies));
Enemy *e = (Enemy*)alist_insert(enemies, enemies->first, objpool_acquire(stage_object_pools.enemies));
e->moving = false;
e->dir = 0;
@ -67,7 +67,7 @@ Enemy *create_enemy_p(Enemy **enemies, complex pos, int hp, EnemyVisualRule visu
return e;
}
void* _delete_enemy(List **enemies, List* enemy, void *arg) {
void* _delete_enemy(ListAnchor *enemies, List* enemy, void *arg) {
Enemy *e = (Enemy*)enemy;
if(e->hp <= 0 && e->hp > ENEMY_IMMUNE) {
@ -94,17 +94,17 @@ void* _delete_enemy(List **enemies, List* enemy, void *arg) {
e->logic_rule(e, EVENT_DEATH);
del_ref(enemy);
ent_unregister(&e->ent);
objpool_release(stage_object_pools.enemies, (ObjectInterface*)list_unlink(enemies, enemy));
objpool_release(stage_object_pools.enemies, (ObjectInterface*)alist_unlink(enemies, enemy));
return NULL;
}
void delete_enemy(Enemy **enemies, Enemy* enemy) {
_delete_enemy((List**)enemies, (List*)enemy, NULL);
void delete_enemy(EnemyList *enemies, Enemy* enemy) {
_delete_enemy((ListAnchor*)enemies, (List*)enemy, NULL);
}
void delete_enemies(Enemy **enemies) {
list_foreach(enemies, _delete_enemy, NULL);
void delete_enemies(EnemyList *enemies) {
alist_foreach(enemies, _delete_enemy, NULL);
}
static void ent_draw_enemy(EntityInterface *ent) {
@ -133,11 +133,10 @@ static void ent_draw_enemy(EntityInterface *ent) {
#endif
}
void killall(Enemy *enemies) {
Enemy *e;
for(e = enemies; e; e = e->next)
void killall(EnemyList *enemies) {
for(Enemy *e = enemies->first; e; e = e->next) {
e->hp = 0;
}
}
int enemy_flare(Projectile *p, int t) { // a[0] velocity, a[1] ref to enemy
@ -242,8 +241,8 @@ void Swirl(Enemy *e, int t, bool render) {
});
}
void process_enemies(Enemy **enemies) {
Enemy *enemy = *enemies, *del = NULL;
void process_enemies(EnemyList *enemies) {
Enemy *enemy = enemies->first, *del = NULL;
while(enemy != NULL) {
int action = enemy->logic_rule(enemy, global.frames - enemy->birthtime);

View file

@ -19,6 +19,7 @@
#endif
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);
@ -57,7 +58,7 @@ struct Enemy {
#define create_enemy1c(p,h,d,l,a1) create_enemy_p(&global.enemies,p,h,d,l,a1,0,0,0)
Enemy *create_enemy_p(
Enemy **enemies, complex pos, int hp, EnemyVisualRule draw_rule, EnemyLogicRule logic_rule,
EnemyList *enemies, complex pos, int hp, EnemyVisualRule draw_rule, EnemyLogicRule logic_rule,
complex a1, complex a2, complex a3, complex a4
);
@ -66,12 +67,12 @@ Enemy *create_enemy_p(
#define create_enemy_p(...) _enemy_attach_dbginfo(create_enemy_p(__VA_ARGS__), _DEBUG_INFO_PTR_)
#endif
void delete_enemy(Enemy **enemies, Enemy* enemy);
void delete_enemies(Enemy **enemies);
void delete_enemy(EnemyList *enemies, Enemy* enemy);
void delete_enemies(EnemyList *enemies);
void process_enemies(Enemy **enemies);
void process_enemies(EnemyList *enemies);
void killall(Enemy *enemies);
void killall(EnemyList *enemies);
void Fairy(Enemy*, int t, bool render);
void Swirl(Enemy*, int t, bool render);

View file

@ -14,8 +14,15 @@
#include "video.h"
#include "gamepad.h"
typedef struct EventHandlerContainer {
LIST_INTERFACE(struct EventHandlerContainer);
EventHandler *handler;
} EventHandlerContainer;
typedef LIST_ANCHOR(EventHandlerContainer) EventHandlerList;
static hrtime_t keyrepeat_paused_until;
static ListContainer *global_handlers;
static EventHandlerList global_handlers;
uint32_t sdl_first_user_event;
@ -49,7 +56,7 @@ void events_shutdown(void) {
events_unregister_default_handlers();
#ifdef DEBUG
if(global_handlers) {
if(global_handlers.first) {
log_warn(
"Someone didn't unregister their handler. "
"Clean up after yourself, I'm not your personal maid. "
@ -72,7 +79,7 @@ static bool events_invoke_handler(SDL_Event *event, EventHandler *handler) {
}
static int handler_container_prio_func(List *h) {
return ((EventHandler*)((ListContainer*)h)->data)->priority;
return ((EventHandlerContainer*)h)->handler->priority;
}
static EventPriority real_priority(EventPriority prio) {
@ -84,7 +91,13 @@ static EventPriority real_priority(EventPriority prio) {
return prio;
}
static bool events_invoke_handlers(SDL_Event *event, ListContainer *h_list, EventHandler *h_array) {
static EventHandlerContainer* ehandler_wrap_container(EventHandler *handler) {
EventHandlerContainer *c = calloc(1, sizeof(ListContainer));
c->handler = handler;
return c;
}
static bool events_invoke_handlers(SDL_Event *event, EventHandlerContainer *h_list, EventHandler *h_array) {
// invoke handlers from two sources (a list and an array) in the correct order according to priority
// list items take precedence
//
@ -97,8 +110,8 @@ static bool events_invoke_handlers(SDL_Event *event, ListContainer *h_list, Even
if(h_list && !h_array) {
// case 1 (simplest): we have a list and no custom handlers
for(ListContainer *c = h_list; c; c = c->next) {
if((result = events_invoke_handler(event, (EventHandler*)c->data))) {
for(EventHandlerContainer *c = h_list; c; c = c->next) {
if((result = events_invoke_handler(event, c->handler))) {
break;
}
}
@ -110,44 +123,31 @@ static bool events_invoke_handlers(SDL_Event *event, ListContainer *h_list, Even
// case 2 (suboptimal): we have both a list and a disordered array; need to do some actual work
// if you want to optimize this be my guest
ListContainer *merged_list = NULL;
ListContainer *prevc = NULL;
EventHandlerList merged_list = { .first = NULL };
// copy the list
for(ListContainer *c = h_list; c; c = c->next) {
ListContainer *newc = calloc(1, sizeof(ListContainer));
newc->data = c->data;
newc->prev = prevc;
if(prevc) {
prevc->next = newc;
}
if(!merged_list) {
merged_list = newc;
}
prevc = newc;
for(EventHandlerContainer *c = h_list; c; c = c->next) {
alist_append(&merged_list, ehandler_wrap_container(c->handler));
}
// merge the array into the list copy, respecting priority
for(EventHandler *h = h_array; h->proc; ++h) {
list_insert_at_priority_tail(
alist_insert_at_priority_tail(
&merged_list,
list_wrap_container(h),
ehandler_wrap_container(h),
real_priority(h->priority),
handler_container_prio_func
);
}
// iterate over the merged list
for(ListContainer *c = merged_list; c; c = c->next) {
if((result = events_invoke_handler(event, (EventHandler*)c->data))) {
for(EventHandlerContainer *c = merged_list.first; c; c = c->next) {
if((result = events_invoke_handler(event, c->handler))) {
break;
}
}
list_free_all(&merged_list);
alist_free_all(&merged_list);
return result;
}
@ -177,9 +177,9 @@ void events_register_handler(EventHandler *handler) {
}
assert(handler_alloc->priority > EPRIO_DEFAULT);
list_insert_at_priority_tail(
alist_insert_at_priority_tail(
&global_handlers,
list_wrap_container(handler_alloc),
ehandler_wrap_container(handler_alloc),
handler_alloc->priority,
handler_container_prio_func
);
@ -188,14 +188,13 @@ void events_register_handler(EventHandler *handler) {
}
void events_unregister_handler(EventHandlerProc proc) {
ListContainer *next;
for(ListContainer *c = global_handlers; c; c = next) {
EventHandler *h = c->data;
for(EventHandlerContainer *c = global_handlers.first, *next; c; c = next) {
EventHandler *h = c->handler;
next = c->next;
if(h->proc == proc) {
free(c->data);
free(list_unlink(&global_handlers, c));
free(c->handler);
free(alist_unlink(&global_handlers, c));
return;
}
}
@ -229,7 +228,7 @@ void events_poll(EventHandler *handlers, EventFlags flags) {
events_emit(TE_FRAME, 0, NULL, NULL);
while(SDL_PollEvent(&event)) {
events_invoke_handlers(&event, global_handlers, handlers);
events_invoke_handlers(&event, global_handlers.first, handlers);
}
}

View file

@ -87,12 +87,11 @@ typedef struct {
int8_t diff; // this holds values of type Difficulty, but should be signed to prevent obscure overflow errors
Player plr;
Projectile *projs;
Enemy *enemies;
Item *items;
Laser *lasers;
Projectile *particles;
ProjectileList projs;
ProjectileList particles;
EnemyList enemies;
ItemList items;
LaserList lasers;
int frames; // stage global timer
int timer; // stage event timer (freezes on bosses, dialogs, etc.)

View file

@ -56,7 +56,7 @@ Item* create_item(complex pos, complex v, ItemType type) {
// type = 1 + floor(Life * frand());
Item *i = (Item*)objpool_acquire(stage_object_pools.items);
list_append(&global.items, i);
alist_append(&global.items, i);
i->pos = pos;
i->pos0 = pos;
@ -74,7 +74,7 @@ Item* create_item(complex pos, complex v, ItemType type) {
void delete_item(Item *item) {
ent_unregister(&item->ent);
objpool_release(stage_object_pools.items, (ObjectInterface*)list_unlink(&global.items, item));
objpool_release(stage_object_pools.items, &alist_unlink(&global.items, item)->object_interface);
}
Item* create_bpoint(complex pos) {
@ -93,7 +93,7 @@ Item* create_bpoint(complex pos) {
}
void delete_items(void) {
objpool_release_list(stage_object_pools.items, (List**)&global.items);
objpool_release_alist(stage_object_pools.items, &global.items);
}
complex move_item(Item *i) {
@ -138,7 +138,7 @@ static bool item_out_of_bounds(Item *item) {
}
void process_items(void) {
Item *item = global.items, *del = NULL;
Item *item = global.items.first, *del = NULL;
float r = player_property(&global.plr, PLR_PROP_COLLECT_RADIUS);
bool plr_alive = global.plr.deathtime <= global.frames && global.plr.deathtime == -1;
bool plr_bombing = global.frames - global.plr.recovery < 0;;

View file

@ -15,6 +15,7 @@
#include "entity.h"
typedef struct Item Item;
typedef LIST_ANCHOR(Item) ItemList;
typedef enum {
// from least important to most important

View file

@ -67,7 +67,7 @@ void lasers_free(void) {
static void ent_draw_laser(EntityInterface *ent);
Laser *create_laser(complex pos, float time, float deathtime, Color color, LaserPosRule prule, LaserLogicRule lrule, complex a0, complex a1, complex a2, complex a3) {
Laser *l = (Laser*)list_push(&global.lasers, objpool_acquire(stage_object_pools.lasers));
Laser *l = (Laser*)alist_push(&global.lasers, objpool_acquire(stage_object_pools.lasers));
l->birthtime = global.frames;
l->timespan = time;
@ -216,7 +216,7 @@ static void ent_draw_laser(EntityInterface *ent) {
}
}
void* _delete_laser(List **lasers, List *laser, void *arg) {
void* _delete_laser(ListAnchor *lasers, List *laser, void *arg) {
Laser *l = (Laser*)laser;
if(l->lrule)
@ -224,19 +224,19 @@ void* _delete_laser(List **lasers, List *laser, void *arg) {
del_ref(laser);
ent_unregister(&l->ent);
objpool_release(stage_object_pools.lasers, (ObjectInterface*)list_unlink(lasers, laser));
objpool_release(stage_object_pools.lasers, (ObjectInterface*)alist_unlink(lasers, laser));
return NULL;
}
void delete_laser(Laser **lasers, Laser *laser) {
_delete_laser((List**)lasers, (List*)laser, NULL);
void delete_laser(LaserList *lasers, Laser *laser) {
_delete_laser((ListAnchor*)lasers, (List*)laser, NULL);
}
void delete_lasers(void) {
list_foreach(&global.lasers, _delete_laser, NULL);
alist_foreach(&global.lasers, _delete_laser, NULL);
}
bool clear_laser(Laser **laserlist, Laser *l, bool force, bool now) {
bool clear_laser(LaserList *laserlist, Laser *l, bool force, bool now) {
if(!force && l->unclearable) {
return false;
}
@ -249,7 +249,7 @@ bool clear_laser(Laser **laserlist, Laser *l, bool force, bool now) {
static bool collision_laser_curve(Laser *l);
void process_lasers(void) {
Laser *laser = global.lasers, *del = NULL;
Laser *laser = global.lasers.first, *del = NULL;
while(laser != NULL) {
if(laser->dead) {

View file

@ -15,6 +15,7 @@
#include "entity.h"
typedef struct Laser Laser;
typedef LIST_ANCHOR(Laser) LaserList;
typedef complex (*LaserPosRule)(Laser* l, float time);
typedef void (*LaserLogicRule)(Laser* l, int time);
@ -64,7 +65,7 @@ Laser *create_laser(complex pos, float time, float deathtime, Color color, Laser
void delete_lasers(void);
void process_lasers(void);
bool clear_laser(Laser **laserlist, Laser *l, bool force, bool now);
bool clear_laser(LaserList *laserlist, Laser *l, bool force, bool now);
complex las_linear(Laser *l, float t);
complex las_accel(Laser *l, float t);

View file

@ -35,6 +35,35 @@ List* list_insert(List **dest, List *elem) {
return elem;
}
List* alist_insert(ListAnchor *list, List *ref, List *elem) {
elem->prev = ref;
if(ref != NULL) {
elem->next = ref->next;
if(elem->next != NULL) {
elem->next->prev = elem;
}
if(ref == list->last) {
list->last = elem;
}
if(list->first == NULL) {
// list->first = elem;
}
ref->next = elem;
} else {
assert(list->first == NULL);
assert(list->last == NULL);
elem->next = elem->prev = NULL;
list->first = list->last = elem;
}
return elem;
}
List* list_push(List **dest, List *elem) {
if(*dest) {
(*dest)->prev = elem;
@ -47,6 +76,22 @@ List* list_push(List **dest, List *elem) {
return elem;
}
List* alist_push(ListAnchor *list, List *elem) {
elem->next = list->first;
elem->prev = NULL;
if(list->last == NULL) {
assert(list->first == NULL);
list->last = elem;
} else {
assert(list->first != NULL);
list->first->prev = elem;
}
list->first = elem;
return elem;
}
List* list_append(List **dest, List *elem) {
if(*dest == NULL) {
return list_insert(dest, elem);
@ -60,6 +105,22 @@ List* list_append(List **dest, List *elem) {
return list_insert(&end, elem);
}
List* alist_append(ListAnchor *list, List *elem) {
elem->next = NULL;
elem->prev = list->last;
if(list->last == NULL) {
assert(list->first == NULL);
list->first = elem;
} else {
assert(list->first != NULL);
list->last->next = elem;
}
list->last = elem;
return elem;
}
attr_hot
static List* list_insert_at_priority(List **list_head, List *elem, int prio, ListPriorityFunc prio_func, bool head) {
if(!*list_head) {
@ -112,10 +173,101 @@ List* list_insert_at_priority_head(List **dest, List *elem, int prio, ListPriori
return list_insert_at_priority(dest, elem, prio, prio_func, true);
}
List* alist_insert_at_priority_head(ListAnchor *list, List *elem, int prio, ListPriorityFunc prio_func) {
if(list->first == NULL) {
assert(list->last == NULL);
elem->prev = elem->next = NULL;
list->first = list->last = elem;
return elem;
}
List *dest = list->first;
int dest_prio = prio_func(dest);
int candidate_prio = dest_prio;
for(List *e = dest->next; e && (candidate_prio = prio_func(e)) < prio; e = e->next) {
dest = e;
dest_prio = candidate_prio;
}
if(dest == list->first && dest_prio > prio) {
elem->next = dest;
elem->prev = dest->prev;
if(elem->prev) {
elem->prev->next = elem;
}
dest->prev = elem;
list->first = elem;
} else {
elem->prev = dest;
elem->next = dest->next;
if(dest->next) {
dest->next->prev = elem;
} else {
assert(dest == list->last);
list->last = elem;
}
dest->next = elem;
}
return elem;
}
List* list_insert_at_priority_tail(List **dest, List *elem, int prio, ListPriorityFunc prio_func) {
return list_insert_at_priority(dest, elem, prio, prio_func, false);
}
List* alist_insert_at_priority_tail(ListAnchor *list, List *elem, int prio, ListPriorityFunc prio_func) {
if(list->last == NULL) {
assert(list->first == NULL);
}
if(list->first == NULL) {
assert(list->last == NULL);
elem->prev = elem->next = NULL;
list->first = list->last = elem;
return elem;
}
List *dest = list->last;
int dest_prio = prio_func(dest);
for(List *e = dest->prev; e && dest_prio > prio; e = e->prev) {
dest = e;
dest_prio = prio_func(e);
}
if(dest == list->first && dest_prio > prio) {
elem->next = dest;
elem->prev = dest->prev;
if(elem->prev) {
elem->prev->next = elem;
}
dest->prev = elem;
list->first = elem;
} else {
elem->prev = dest;
elem->next = dest->next;
if(dest->next) {
dest->next->prev = elem;
} else {
assert(dest == list->last);
list->last = elem;
}
dest->next = elem;
}
return elem;
}
List* list_unlink(List **dest, List *elem) {
if(elem->prev != NULL) {
elem->prev->next = elem->next;
@ -132,6 +284,14 @@ List* list_unlink(List **dest, List *elem) {
return elem;
}
List* alist_unlink(ListAnchor *list, List *elem) {
if(list->last == elem) {
list->last = list->last->prev;
}
return list_unlink(&list->first, elem);
}
List* list_pop(List **dest) {
if(*dest == NULL) {
return NULL;
@ -140,20 +300,40 @@ List* list_pop(List **dest) {
return list_unlink(dest, *dest);
}
List* alist_pop(ListAnchor *list) {
if(list->first == NULL) {
return NULL;
}
return alist_unlink(list, list->first);
}
void* list_foreach(List **dest, ListForeachCallback callback, void *arg) {
List *e = *dest;
void *ret = NULL;
while(e != 0) {
void *ret;
List *tmp = e;
e = e->next;
for(List *e = *dest, *next; e; e = next) {
next = e->next;
if((ret = callback(dest, tmp, arg)) != NULL) {
if((ret = callback(dest, e, arg)) != NULL) {
return ret;
}
}
return NULL;
return ret;
}
void* alist_foreach(ListAnchor *list, ListAnchorForeachCallback callback, void *arg) {
void *ret = NULL;
for(List *e = list->first, *next; e; e = next) {
next = e->next;
if((ret = callback(list, e, arg)) != NULL) {
return ret;
}
}
return ret;
}
void* list_callback_free_element(List **dest, List *elem, void *arg) {
@ -162,10 +342,20 @@ void* list_callback_free_element(List **dest, List *elem, void *arg) {
return NULL;
}
void* alist_callback_free_element(ListAnchor *list, List *elem, void *arg) {
alist_unlink(list, elem);
free(elem);
return NULL;
}
void list_free_all(List **dest) {
list_foreach(dest, list_callback_free_element, NULL);
}
void alist_free_all(ListAnchor *list) {
alist_foreach(list, alist_callback_free_element, NULL);
}
ListContainer* list_wrap_container(void *data) {
ListContainer *c = calloc(1, sizeof(ListContainer));
c->data = data;

View file

@ -11,6 +11,8 @@
typedef struct ListInterface ListInterface;
typedef struct List List;
typedef struct ListAnchorInterface ListAnchorInterface;
typedef struct ListAnchor ListAnchor;
typedef struct ListContainer ListContainer;
#define LIST_INTERFACE_BASE(typename) struct { \
@ -31,14 +33,38 @@ struct List {
LIST_INTERFACE(List);
};
#define LIST_ANCHOR_INTERFACE_BASE(typename) struct { \
typename *first; \
typename *last; \
}
#define LIST_ANCHOR_INTERFACE(typename) union { \
ListAnchorInterface list_anchor_interface; \
LIST_ANCHOR_INTERFACE_BASE(typename); \
}
#define LIST_ANCHOR(typename) struct { \
LIST_ANCHOR_INTERFACE(typename); \
}
struct ListAnchorInterface {
LIST_ANCHOR_INTERFACE_BASE(ListInterface);
};
struct ListAnchor {
LIST_ANCHOR_INTERFACE(List);
};
struct ListContainer {
LIST_INTERFACE(ListContainer);
void *data;
};
typedef void* (*ListForeachCallback)(List **head, List *elem, void *arg);
typedef int (*ListPriorityFunc)(List *elem);
typedef void* (*ListAnchorForeachCallback)(ListAnchor *list, List *elem, void *arg);
typedef List* (*ListInsertionRule)(List **dest, List *elem);
typedef List* (*ListAnchorInsertionRule)(ListAnchor *dest, List *elem);
typedef int (*ListPriorityFunc)(List *elem);
List* list_insert(List **dest, List *elem) attr_nonnull(1, 2);
List* list_push(List **dest, List *elem) attr_nonnull(1, 2);
@ -50,6 +76,18 @@ List* list_unlink(List **dest, List *elem) attr_nonnull(1, 2);
void* list_foreach(List **dest, ListForeachCallback callback, void *arg) attr_nonnull(1, 2);
void* list_callback_free_element(List **dest, List *elem, void *arg);
void list_free_all(List **dest) attr_nonnull(1);
List* alist_insert(ListAnchor *list, List *ref, List *elem) attr_nonnull(1, 3);
List* alist_push(ListAnchor *list, List *elem) attr_nonnull(1, 2);
List* alist_append(ListAnchor *list, List *elem) attr_nonnull(1, 2);
List* alist_insert_at_priority_head(ListAnchor *list, List *elem, int prio, ListPriorityFunc prio_func) attr_hot attr_nonnull(1, 2, 4);
List* alist_insert_at_priority_tail(ListAnchor *list, List *elem, int prio, ListPriorityFunc prio_func) attr_hot attr_nonnull(1, 2, 4);
List* alist_pop(ListAnchor *list) attr_nonnull(1);
List* alist_unlink(ListAnchor *list, List *elem) attr_nonnull(1, 2);
void* alist_foreach(ListAnchor *list, ListAnchorForeachCallback callback, void *arg) attr_nonnull(1, 2);
void* alist_callback_free_element(ListAnchor *list, List *elem, void *arg);
void alist_free_all(ListAnchor *list) attr_nonnull(1);
ListContainer* list_wrap_container(void *data) attr_nodiscard;
// type-generic macros
@ -58,6 +96,7 @@ ListContainer* list_wrap_container(void *data) attr_nodiscard;
#ifdef USE_GNU_EXTENSIONS
// thorough safeguard
#define LIST_CAST(expr,ptrlevel) (__extension__({ \
static_assert(__builtin_types_compatible_p(ListInterface, __typeof__((ptrlevel (expr)).list_interface)), \
"struct must implement ListInterface (use the LIST_INTERFACE macro)"); \
@ -65,10 +104,20 @@ ListContainer* list_wrap_container(void *data) attr_nodiscard;
"list_interface must be the first member in struct"); \
(List ptrlevel)(expr); \
}))
#define LIST_ANCHOR_CAST(expr,ptrlevel) (__extension__({ \
static_assert(__builtin_types_compatible_p(ListAnchorInterface, __typeof__((ptrlevel (expr)).list_anchor_interface)), \
"struct must implement ListAnchorInterface (use the LIST_ANCHOR_INTERFACE macro)"); \
static_assert(__builtin_offsetof(__typeof__(ptrlevel (expr)), list_anchor_interface) == 0, \
"list_anchor_interface must be the first member in struct"); \
(ListAnchor ptrlevel)(expr); \
}))
#define LIST_CAST_RETURN(expr) (__typeof__(expr))
#else
// basic safeguard
#define LIST_CAST(expr,ptrlevel) ((void)sizeof((ptrlevel (expr)).list_interface), (List ptrlevel)(expr))
#define LIST_ANCHOR_CAST(expr,ptrlevel) ((void)sizeof((ptrlevel (expr)).list_anchor_interface), (ListAnchor ptrlevel)(expr))
// don't even think about adding a void* cast here
#define LIST_CAST_RETURN(expr)
#endif
@ -76,28 +125,55 @@ ListContainer* list_wrap_container(void *data) attr_nodiscard;
#define list_insert(dest,elem) \
(LIST_CAST_RETURN(elem) list_insert(LIST_CAST(dest, **), LIST_CAST(elem, *)))
#define alist_insert(dest,ref,elem) \
(LIST_CAST_RETURN(elem) alist_insert(LIST_ANCHOR_CAST(dest, *), LIST_CAST(ref, *), LIST_CAST(elem, *)))
#define list_push(dest,elem) \
(LIST_CAST_RETURN(elem) list_push(LIST_CAST(dest, **), LIST_CAST(elem, *)))
#define alist_push(dest,elem) \
(LIST_CAST_RETURN(elem) alist_push(LIST_ANCHOR_CAST(dest, *), LIST_CAST(elem, *)))
#define list_append(dest,elem) \
(LIST_CAST_RETURN(elem) list_append(LIST_CAST(dest, **), LIST_CAST(elem, *)))
#define alist_append(dest,elem) \
(LIST_CAST_RETURN(elem) alist_append(LIST_ANCHOR_CAST(dest, *), LIST_CAST(elem, *)))
#define list_insert_at_priority_head(dest,elem,prio,prio_func) \
(LIST_CAST_RETURN(elem) list_insert_at_priority_head(LIST_CAST(dest, **), LIST_CAST(elem, *), prio, prio_func))
#define alist_insert_at_priority_head(dest,elem,prio,prio_func) \
(LIST_CAST_RETURN(elem) alist_insert_at_priority_head(LIST_ANCHOR_CAST(dest, *), LIST_CAST(elem, *), prio, prio_func))
#define list_insert_at_priority_tail(dest,elem,prio,prio_func) \
(LIST_CAST_RETURN(elem) list_insert_at_priority_tail(LIST_CAST(dest, **), LIST_CAST(elem, *), prio, prio_func))
#define alist_insert_at_priority_tail(dest,elem,prio,prio_func) \
(LIST_CAST_RETURN(elem) alist_insert_at_priority_tail(LIST_ANCHOR_CAST(dest, *), LIST_CAST(elem, *), prio, prio_func))
#define list_pop(dest) \
(LIST_CAST_RETURN(*(dest)) list_pop(LIST_CAST(dest, **)))
#define alist_pop(dest) \
(LIST_CAST_RETURN((dest)->first) alist_pop(LIST_ANCHOR_CAST(dest, *)))
#define list_unlink(dest,elem) \
(LIST_CAST_RETURN(elem) list_unlink(LIST_CAST(dest, **), LIST_CAST(elem, *)))
#define alist_unlink(dest,elem) \
(LIST_CAST_RETURN(elem) alist_unlink(LIST_ANCHOR_CAST(dest, *), LIST_CAST(elem, *)))
#define list_foreach(dest,callback,arg) \
list_foreach(LIST_CAST(dest, **), callback, arg)
#define alist_foreach(dest,callback,arg) \
alist_foreach(LIST_ANCHOR_CAST(dest, *), callback, arg)
#define list_free_all(dest) \
list_free_all(LIST_CAST(dest, **))
#define alist_free_all(dest) \
alist_free_all(LIST_ANCHOR_CAST(dest, *))
#endif // LIST_NO_MACROS

View file

@ -17,10 +17,22 @@ static void* objpool_release_list_callback(List **dest, List *elem, void *vpool)
return NULL;
}
static void* objpool_release_alist_callback(ListAnchor *list, List *elem, void *vpool) {
alist_unlink(list, elem);
objpool_release((ObjectPool*)vpool, (ObjectInterface*)elem);
return NULL;
}
#undef objpool_release_list
void objpool_release_list(ObjectPool *pool, List **dest) {
list_foreach(dest, objpool_release_list_callback, pool);
}
#undef objpool_release_alist
void objpool_release_alist(ObjectPool *pool, ListAnchor *list) {
alist_foreach(list, objpool_release_alist_callback, pool);
}
bool objpool_is_full(ObjectPool *pool) {
ObjectPoolStats stats;
objpool_get_stats(pool, &stats);

View file

@ -12,6 +12,13 @@
#include "objectpool.h"
void objpool_release_list(ObjectPool *pool, List **dest);
void objpool_release_alist(ObjectPool *pool, ListAnchor *list);
bool objpool_is_full(ObjectPool *pool);
size_t objpool_object_contents_size(ObjectPool *pool);
void *objpool_object_contents(ObjectPool *pool, ObjectInterface *obj, size_t *out_size);
#define objpool_release_list(pool,dest) \
objpool_release_list(pool, LIST_CAST(dest, **))
#define objpool_release_alist(pool,list) \
objpool_release_alist(pool, LIST_ANCHOR_CAST(list, *))

View file

@ -41,7 +41,7 @@ double player_property(Player *plr, PlrProperty prop) {
}
static void ent_draw_player(EntityInterface *ent);
static Enemy* player_spawn_focus_circle(void);
static void player_spawn_focus_circle(Player *plr);
void player_stage_post_init(Player *plr) {
assert(plr->mode != NULL);
@ -63,7 +63,7 @@ void player_stage_post_init(Player *plr) {
plr->ent.draw_func = ent_draw_player;
ent_register(&plr->ent, ENT_PLAYER);
plr->focus_circle = player_spawn_focus_circle();
player_spawn_focus_circle(plr);
}
void player_free(Player *plr) {
@ -73,7 +73,7 @@ void player_free(Player *plr) {
aniplayer_free(&plr->ani);
ent_unregister(&plr->ent);
delete_enemy(&plr->focus_circle, plr->focus_circle);
delete_enemy(&plr->focus_circle, plr->focus_circle.first);
}
static void player_full_power(Player *plr) {
@ -193,11 +193,9 @@ static void player_focus_circle_visual(Enemy *e, int t, bool render) {
});
}
static Enemy* player_spawn_focus_circle(void) {
Enemy *f = NULL;
create_enemy_p(&f, 0, ENEMY_IMMUNE, player_focus_circle_visual, player_focus_circle_logic, 0, 0, 0, 0);
static void player_spawn_focus_circle(Player *plr) {
Enemy *f = create_enemy_p(&plr->focus_circle, 0, ENEMY_IMMUNE, player_focus_circle_visual, player_focus_circle_logic, 0, 0, 0, 0);
f->ent.draw_layer = LAYER_PLAYER_FOCUS;
return f;
}
static void player_fail_spell(Player *plr) {
@ -246,7 +244,7 @@ void player_logic(Player* plr) {
}
plr->focus = approach(plr->focus, (plr->inputflags & INFLAG_FOCUS) ? 30 : 0, 1);
plr->focus_circle->pos = plr->pos;
plr->focus_circle.first->pos = plr->pos;
process_enemies(&plr->focus_circle);
if(plr->mode->procs.think) {
@ -277,7 +275,7 @@ void player_logic(Player* plr) {
const int damage = 100;
for(Enemy *en = global.enemies; en; en = en->next) {
for(Enemy *en = global.enemies.first; en; en = en->next) {
if(en->hp > ENEMY_IMMUNE) {
en->hp -= damage;
}
@ -556,7 +554,7 @@ bool player_updateinputflags(Player *plr, PlrInputFlag flags) {
}
if((flags & INFLAG_FOCUS) && !(plr->inputflags & INFLAG_FOCUS)) {
plr->focus_circle->birthtime = global.frames;
plr->focus_circle.first->birthtime = global.frames;
}
plr->inputflags = flags;

View file

@ -86,8 +86,8 @@ struct Player {
struct PlayerMode *mode;
AniPlayer ani;
Enemy *slaves;
Enemy *focus_circle;
EnemyList slaves;
EnemyList focus_circle;
int inputflags;
bool gamepadmove;

View file

@ -15,6 +15,7 @@
// args are pain
static float global_magicstar_alpha;
static Enemy *laser_renderer;
typedef struct MarisaLaserData {
struct {
@ -54,7 +55,7 @@ static void draw_laser_beam(complex src, complex dst, double size, double step,
static void trace_laser(Enemy *e, complex vel, int damage) {
ProjCollisionResult col;
Projectile *lproj = NULL;
ProjectileList lproj = { .first = NULL };
MarisaLaserData *ld = REF(e->args[3]);
@ -77,8 +78,8 @@ static void trace_laser(Enemy *e, complex vel, int damage) {
int original_hp;
} *prev_collisions = NULL;
while(lproj) {
timeofs = trace_projectile(lproj, &col, col_types | PCOL_VOID, timeofs);
while(lproj.first) {
timeofs = trace_projectile(lproj.first, &col, col_types | PCOL_VOID, timeofs);
struct enemy_col *c = NULL;
if(!first_found) {
@ -109,7 +110,7 @@ static void trace_laser(Enemy *e, complex vel, int damage) {
col.fatal = false;
}
apply_projectile_collision(&lproj, lproj, &col);
apply_projectile_collision(&lproj, lproj.first, &col);
if(col.type == PCOL_BOSS) {
assert(!col.fatal);
@ -181,8 +182,8 @@ static void marisa_laser_slave_visual(Enemy *e, int t, bool render) {
return;
}
float laser_alpha = global.plr.slaves->args[0];
float star_alpha = global.plr.slaves->args[1] * global_magicstar_alpha;
float laser_alpha = laser_renderer->args[0];
float star_alpha = laser_renderer->args[1] * global_magicstar_alpha;
draw_magic_star(e->pos, 0.75 * star_alpha,
rgb(1.0, 0.1, 0.1),
@ -215,7 +216,7 @@ static float get_laser_alpha(Enemy *e, float a) {
return min(a, min(1, (global.frames - e->birthtime) * 0.1));
}
#define FOR_EACH_SLAVE(e) for(Enemy *e = global.plr.slaves; e; e = e->next) if(e != renderer)
#define FOR_EACH_SLAVE(e) for(Enemy *e = global.plr.slaves.first; e; e = e->next) if(e != renderer)
#define FOR_EACH_REAL_SLAVE(e) FOR_EACH_SLAVE(e) if(e->visual_rule == marisa_laser_slave_visual)
static void marisa_laser_renderer_visual(Enemy *renderer, int t, bool render) {
@ -343,8 +344,8 @@ static int marisa_laser_slave(Enemy *e, int t) {
return 1;
}
if(t == EVENT_DEATH && !global.game_over && global.plr.slaves && creal(global.plr.slaves->args[0])) {
spawn_laser_fader(e, global.plr.slaves->args[0]);
if(t == EVENT_DEATH && !global.game_over && creal(laser_renderer->args[0])) {
spawn_laser_fader(e, laser_renderer->args[0]);
MarisaLaserData *ld = REF(e->args[3]);
free(ld);
@ -488,7 +489,7 @@ static void marisa_laser_bomb(Player *plr) {
}
static void marisa_laser_respawn_slaves(Player *plr, short npow) {
Enemy *e = plr->slaves, *tmp;
Enemy *e = plr->slaves.first, *tmp;
double dmg = 8;
while(e != 0) {
@ -517,7 +518,7 @@ static void marisa_laser_respawn_slaves(Player *plr, short npow) {
create_enemy_p(&plr->slaves, -17-30.0*I, ENEMY_IMMUNE, marisa_laser_slave_visual, marisa_laser_slave, -4-45.0*I, dmg, -M_PI/60, 0);
}
for(e = plr->slaves; e; e = e->next) {
for(e = plr->slaves.first; e; e = e->next) {
if(e->logic_rule == marisa_laser_slave) {
MarisaLaserData *ld = calloc(1, sizeof(MarisaLaserData));
ld->prev_pos = e->pos + plr->pos;
@ -536,20 +537,19 @@ static void marisa_laser_power(Player *plr, short npow) {
}
static void marisa_laser_init(Player *plr) {
create_enemy_p(&plr->slaves, 0, ENEMY_IMMUNE, marisa_laser_renderer_visual, marisa_laser_renderer, 0, 0, 0, 0);
plr->slaves->ent.draw_layer = LAYER_PLAYER_SHOT;
laser_renderer = create_enemy_p(&plr->slaves, 0, ENEMY_IMMUNE, marisa_laser_renderer_visual, marisa_laser_renderer, 0, 0, 0, 0);
laser_renderer->ent.draw_layer = LAYER_PLAYER_SHOT;
marisa_laser_respawn_slaves(plr, plr->power);
}
static void marisa_laser_think(Player *plr) {
Enemy *laser_renderer = plr->slaves;
assert(laser_renderer != NULL);
assert(laser_renderer->logic_rule == marisa_laser_renderer);
if(creal(laser_renderer->args[0]) > 0) {
bool found = false;
for(Projectile *p = global.projs; p && !found; p = p->next) {
for(Projectile *p = global.projs.first; p && !found; p = p->next) {
if(p->type != EnemyProj) {
continue;
}

View file

@ -252,7 +252,7 @@ static void marisa_star_bombbg(Player *plr) {
}
static void marisa_star_respawn_slaves(Player *plr, short npow) {
Enemy *e = plr->slaves, *tmp;
Enemy *e = plr->slaves.first, *tmp;
double dmg = 56;
while(e != 0) {
@ -280,7 +280,7 @@ static void marisa_star_respawn_slaves(Player *plr, short npow) {
create_enemy_p(&plr->slaves, -30, ENEMY_IMMUNE, marisa_common_slave_visual, marisa_star_slave, -25+30.0*I, 0.6-2.0*I, -2-0.1*I, dmg);
}
for(Enemy *e = plr->slaves; e; e = e->next) {
for(Enemy *e = plr->slaves.first; e; e = e->next) {
e->ent.draw_layer = LAYER_PLAYER_SLAVE;
}
}

View file

@ -13,6 +13,8 @@
#include "youmu.h"
#include "renderer/api.h"
#define MYON (global.plr.slaves.first)
static Color myon_color(float f, float a) {
// return rgba(0.8+0.2*f, 0.9-0.4*sqrt(f), 1.0-0.2*f*f, a);
// return rgba(0.8+0.2*f, 0.9-0.4*sqrt(f), 1.0-0.4*f*f, a);
@ -32,7 +34,7 @@ static int myon_particle_rule(Projectile *p, int t) {
}
static complex myon_tail_dir(void) {
double angle = carg(global.plr.slaves->args[0]);
double angle = carg(MYON->args[0]);
complex dir = cexp(I*(0.1 * sin(global.frames * 0.05) + angle));
float f = abs(global.plr.focus) / 30.0;
return f * f * dir;
@ -44,7 +46,7 @@ static int myon_flare_particle_rule(Projectile *p, int t) {
}
// wiggle wiggle
p->pos += 0.05 * (global.plr.slaves->pos - p->pos) * cexp(I * sin((t - global.frames * 2) * 0.1) * M_PI/8);
p->pos += 0.05 * (MYON->pos - p->pos) * cexp(I * sin((t - global.frames * 2) * 0.1) * M_PI/8);
p->args[0] = 3 * myon_tail_dir();
p->color = derive_color(p->color, CLRMASK_A, rgba(1, 1, 1, pow(1 - min(1, t / (double)p->timeout), 2)));

View file

@ -23,7 +23,7 @@ static complex youmu_homing_target(complex org, complex fallback) {
mindst = cabs(target - org);
}
for(Enemy *e = global.enemies; e; e = e->next) {
for(Enemy *e = global.enemies.first; e; e = e->next) {
if(e->hp == ENEMY_IMMUNE){
continue;
}

View file

@ -266,7 +266,7 @@ static Projectile* _create_projectile(ProjArgs *args) {
// But in that case, code that uses this function's return value must be careful to not dereference a NULL pointer.
proj_call_rule(p, EVENT_BIRTH);
return list_append(args->dest, p);
return alist_append(args->dest, p);