Flexible draw order; entity "base class" for all game objects
This commit is contained in:
parent
d3ffc7ff55
commit
8ef5ffd32c
39 changed files with 908 additions and 262 deletions
26
src/boss.c
26
src/boss.c
|
@ -12,13 +12,14 @@
|
|||
#include "global.h"
|
||||
#include "stage.h"
|
||||
#include "stagetext.h"
|
||||
#include "entity.h"
|
||||
|
||||
static void ent_draw_boss(EntityInterface *ent);
|
||||
|
||||
Boss* create_boss(char *name, char *ani, char *dialog, complex pos) {
|
||||
Boss *buf = malloc(sizeof(Boss));
|
||||
memset(buf, 0, sizeof(Boss));
|
||||
Boss *buf = calloc(1, sizeof(Boss));
|
||||
|
||||
buf->name = malloc(strlen(name) + 1);
|
||||
strcpy(buf->name, name);
|
||||
buf->name = strdup(name);
|
||||
buf->pos = pos;
|
||||
|
||||
char strbuf[strlen(ani) + sizeof("boss/")];
|
||||
|
@ -30,6 +31,11 @@ Boss* create_boss(char *name, char *ani, char *dialog, complex pos) {
|
|||
}
|
||||
|
||||
buf->birthtime = global.frames;
|
||||
|
||||
buf->ent.draw_layer = LAYER_BOSS;
|
||||
buf->ent.draw_func = ent_draw_boss;
|
||||
ent_register(&buf->ent, ENT_BOSS);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
@ -184,8 +190,6 @@ static int boss_glow(Projectile *p, int t) {
|
|||
|
||||
static Projectile* spawn_boss_glow(Boss *boss, Color clr, int timeout) {
|
||||
// XXX: memdup is required because the Sprite returned by animation_get_frame is only temporarily valid
|
||||
// XXX: this used to set size to 9000 to ensure it’s below everything but now that does not work anymore
|
||||
// maybe we can use a custom insertion rule?
|
||||
return PARTICLE(
|
||||
.sprite_ptr = memdup(aniplayer_get_frame(&boss->ani), sizeof(Sprite)),
|
||||
// this is in sync with the boss position oscillation
|
||||
|
@ -195,6 +199,7 @@ static Projectile* spawn_boss_glow(Boss *boss, Color clr, int timeout) {
|
|||
.draw_rule = BossGlow,
|
||||
.args = { timeout },
|
||||
.angle = M_PI * 2 * frand(),
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -244,7 +249,11 @@ void draw_boss_background(Boss *boss) {
|
|||
r_mat_pop();
|
||||
}
|
||||
|
||||
void draw_boss(Boss *boss) {
|
||||
static void ent_draw_boss(EntityInterface *ent) {
|
||||
// TODO: separate overlay from this
|
||||
|
||||
Boss *boss = ENT_CAST(ent, Boss);
|
||||
|
||||
float red = 0.5*exp(-0.5*(global.frames-boss->lastdamageframe));
|
||||
if(red > 1)
|
||||
red = 0;
|
||||
|
@ -370,7 +379,7 @@ void draw_boss(Boss *boss) {
|
|||
}
|
||||
}
|
||||
|
||||
r_color3(1,1,1);
|
||||
// r_color3(1,1,1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -771,6 +780,7 @@ static void free_attack(Attack *a) {
|
|||
|
||||
void free_boss(Boss *boss) {
|
||||
del_ref(boss);
|
||||
ent_unregister(&boss->ent);
|
||||
|
||||
for(int i = 0; i < boss->acount; i++)
|
||||
free_attack(&boss->attacks[i]);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "aniplayer.h"
|
||||
#include "color.h"
|
||||
#include "projectile.h"
|
||||
#include "entity.h"
|
||||
|
||||
enum {
|
||||
ATTACK_START_DELAY = 60,
|
||||
|
@ -99,8 +100,7 @@ typedef struct Attack {
|
|||
} Attack;
|
||||
|
||||
typedef struct Boss {
|
||||
// XXX: temporary hack for binary compatibility of pos with Enemy and Projectile structs
|
||||
OBJECT_INTERFACE(struct Boss);
|
||||
ENTITY_INTERFACE_NAMED(struct Boss, ent);
|
||||
complex pos;
|
||||
|
||||
Attack *attacks;
|
||||
|
@ -129,7 +129,6 @@ void free_boss(Boss *boss) attr_nonnull(1);
|
|||
void process_boss(Boss **boss) attr_nonnull(1);
|
||||
|
||||
void draw_extraspell_bg(Boss *boss, int time) attr_nonnull(1);
|
||||
void draw_boss(Boss *boss) attr_nonnull(1);
|
||||
void draw_boss_background(Boss *boss) attr_nonnull(1);
|
||||
|
||||
Attack* boss_add_attack(Boss *boss, AttackType type, char *name, float timeout, int hp, BossRule rule, BossRule draw_rule)
|
||||
|
|
14
src/drawlayers.inc.h
Normal file
14
src/drawlayers.inc.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
LAYER(PLAYER_SHOT)
|
||||
LAYER(PARTICLE_LOW)
|
||||
LAYER(ITEM)
|
||||
LAYER(PLAYER_SLAVE)
|
||||
LAYER(PLAYER)
|
||||
LAYER(LASER_LOW)
|
||||
LAYER(BULLET)
|
||||
LAYER(PARTICLE_HIGH)
|
||||
LAYER(ENEMY)
|
||||
LAYER(LASER_HIGH)
|
||||
LAYER(BOSS)
|
||||
LAYER(PLAYER_FOCUS)
|
||||
|
||||
#undef LAYER
|
45
src/enemy.c
45
src/enemy.c
|
@ -17,6 +17,7 @@
|
|||
#include "aniplayer.h"
|
||||
#include "stageobjects.h"
|
||||
#include "glm.h"
|
||||
#include "entity.h"
|
||||
|
||||
#ifdef create_enemy_p
|
||||
#undef create_enemy_p
|
||||
|
@ -30,6 +31,8 @@ Enemy* _enemy_attach_dbginfo(Enemy *e, DebugInfo *dbg) {
|
|||
}
|
||||
#endif
|
||||
|
||||
static void ent_draw_enemy(EntityInterface *ent);
|
||||
|
||||
Enemy *create_enemy_p(Enemy **enemies, complex pos, int hp, EnemyVisualRule visual_rule, EnemyLogicRule logic_rule,
|
||||
complex a1, complex a2, complex a3, complex a4) {
|
||||
if(IN_DRAW_CODE) {
|
||||
|
@ -56,6 +59,10 @@ Enemy *create_enemy_p(Enemy **enemies, complex pos, int hp, EnemyVisualRule visu
|
|||
e->args[2] = a3;
|
||||
e->args[3] = a4;
|
||||
|
||||
e->ent.draw_layer = LAYER_ENEMY;
|
||||
e->ent.draw_func = ent_draw_enemy;
|
||||
ent_register(&e->ent, ENT_ENEMY);
|
||||
|
||||
e->logic_rule(e, EVENT_BIRTH);
|
||||
return e;
|
||||
}
|
||||
|
@ -81,6 +88,7 @@ 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));
|
||||
|
||||
return NULL;
|
||||
|
@ -94,41 +102,32 @@ void delete_enemies(Enemy **enemies) {
|
|||
list_foreach(enemies, _delete_enemy, NULL);
|
||||
}
|
||||
|
||||
static void draw_enemy(Enemy *e) {
|
||||
static void ent_draw_enemy(EntityInterface *ent) {
|
||||
Enemy *e = ENT_CAST(ent, Enemy);
|
||||
|
||||
if(!e->visual_rule) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENEMY_DEBUG
|
||||
static Enemy prev_state;
|
||||
memcpy(&prev_state, e, sizeof(Enemy));
|
||||
#endif
|
||||
|
||||
if(e->alpha < 1) {
|
||||
r_color4(1, 1, 1, e->alpha);
|
||||
}
|
||||
|
||||
e->visual_rule(e, global.frames - e->birthtime, true);
|
||||
|
||||
#ifdef ENEMY_DEBUG
|
||||
if(memcmp(&prev_state, e, sizeof(Enemy))) {
|
||||
set_debug_info(&e->debug);
|
||||
log_fatal("Enemy modified its own state in draw rule");
|
||||
}
|
||||
#else
|
||||
e->visual_rule(e, global.frames - e->birthtime, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void draw_enemies(Enemy *enemies) {
|
||||
Enemy *e;
|
||||
bool reset = false;
|
||||
|
||||
for(e = enemies; e; e = e->next) {
|
||||
if(e->visual_rule) {
|
||||
if(e->alpha < 1) {
|
||||
r_color4(1,1,1,e->alpha);
|
||||
reset = true;
|
||||
}
|
||||
|
||||
draw_enemy(e);
|
||||
|
||||
if(reset) {
|
||||
r_color4(1,1,1,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void killall(Enemy *enemies) {
|
||||
Enemy *e;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "util.h"
|
||||
#include "projectile.h"
|
||||
#include "objectpool.h"
|
||||
#include "entity.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define ENEMY_DEBUG
|
||||
|
@ -27,7 +28,7 @@ enum {
|
|||
};
|
||||
|
||||
struct Enemy {
|
||||
OBJECT_INTERFACE(Enemy);
|
||||
ENTITY_INTERFACE_NAMED(Enemy, ent);
|
||||
|
||||
complex pos;
|
||||
complex pos0;
|
||||
|
@ -66,7 +67,6 @@ Enemy *create_enemy_p(
|
|||
#endif
|
||||
|
||||
void delete_enemy(Enemy **enemies, Enemy* enemy);
|
||||
void draw_enemies(Enemy *enemies);
|
||||
void delete_enemies(Enemy **enemies);
|
||||
|
||||
void process_enemies(Enemy **enemies);
|
||||
|
|
113
src/entity.c
Normal file
113
src/entity.c
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT-License
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
|
||||
*/
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "entity.h"
|
||||
#include "util.h"
|
||||
#include "renderer/api.h"
|
||||
|
||||
static struct {
|
||||
EntityInterface **array;
|
||||
uint num;
|
||||
uint capacity;
|
||||
uint32_t total_spawns;
|
||||
} entities;
|
||||
|
||||
#define FOR_EACH_ENT(ent) for(EntityInterface **_ent = entities.array, *ent = *entities.array; _ent < entities.array + entities.num; ent = *(++_ent))
|
||||
|
||||
void ent_init(void) {
|
||||
memset(&entities, 0, sizeof(entities));
|
||||
entities.capacity = 4096;
|
||||
entities.array = calloc(entities.capacity, sizeof(EntityInterface*));
|
||||
}
|
||||
|
||||
void ent_shutdown(void) {
|
||||
if(entities.num) {
|
||||
log_warn("%u entities were not properly unregistered", entities.num);
|
||||
}
|
||||
|
||||
FOR_EACH_ENT(ent) {
|
||||
ent_unregister(ent);
|
||||
}
|
||||
|
||||
free(entities.array);
|
||||
}
|
||||
|
||||
void ent_register(EntityInterface *ent, EntityType type) {
|
||||
assert(type > _ENT_TYPE_ENUM_BEGIN && type < _ENT_TYPE_ENUM_END);
|
||||
ent->type = type;
|
||||
ent->index = entities.num++;
|
||||
ent->spawn_id = ++entities.total_spawns;
|
||||
|
||||
if(ent->spawn_id == 0) {
|
||||
// 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");
|
||||
}
|
||||
|
||||
if(entities.capacity < entities.num) {
|
||||
entities.capacity *= 2;
|
||||
entities.array = realloc(entities.array, entities.capacity * sizeof(EntityInterface*));
|
||||
}
|
||||
|
||||
entities.array[ent->index] = ent;
|
||||
|
||||
assert(ent->index < entities.num);
|
||||
assert(entities.array[ent->index] == ent);
|
||||
}
|
||||
|
||||
void ent_unregister(EntityInterface *ent) {
|
||||
EntityInterface *sub = entities.array[--entities.num];
|
||||
assert(ent->index <= entities.num);
|
||||
assert(entities.array[ent->index] == ent);
|
||||
entities.array[sub->index = ent->index] = sub;
|
||||
}
|
||||
|
||||
static int ent_cmp(const void *ptr1, const void *ptr2) {
|
||||
const EntityInterface *ent1 = *(const EntityInterface**)ptr1;
|
||||
const EntityInterface *ent2 = *(const EntityInterface**)ptr2;
|
||||
|
||||
int r = (ent1->draw_layer > ent2->draw_layer) - (ent1->draw_layer < ent2->draw_layer);
|
||||
|
||||
if(r == 0) {
|
||||
// Same layer? Put whatever spawned later on top, then.
|
||||
r = (ent1->spawn_id > ent2->spawn_id) - (ent1->spawn_id < ent2->spawn_id);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void ent_draw(EntityPredicate predicate) {
|
||||
qsort(entities.array, entities.num, sizeof(EntityInterface*), ent_cmp);
|
||||
|
||||
if(predicate) {
|
||||
FOR_EACH_ENT(ent) {
|
||||
ent->index = _ent - entities.array;
|
||||
assert(entities.array[ent->index] == ent);
|
||||
|
||||
if(ent->draw_func && predicate(ent)) {
|
||||
r_state_push();
|
||||
ent->draw_func(ent);
|
||||
r_state_pop();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FOR_EACH_ENT(ent) {
|
||||
ent->index = _ent - entities.array;
|
||||
assert(entities.array[ent->index] == ent);
|
||||
|
||||
if(ent->draw_func) {
|
||||
r_state_push();
|
||||
ent->draw_func(ent);
|
||||
r_state_pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
112
src/entity.h
Normal file
112
src/entity.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT-License
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "taisei.h"
|
||||
|
||||
#include "objectpool.h"
|
||||
|
||||
#define LAYER_LOW_BITS 16
|
||||
#define LAYER_LOW_MASK ((1 << LAYER_LOW_BITS) - 1)
|
||||
typedef uint32_t drawlayer_t;
|
||||
typedef uint16_t drawlayer_low_t;
|
||||
|
||||
typedef enum DrawLayerID {
|
||||
LAYER_ID_NONE,
|
||||
#define LAYER(x) LAYER_ID_##x,
|
||||
#include "drawlayers.inc.h"
|
||||
} DrawLayerID;
|
||||
|
||||
typedef enum DrawLayer {
|
||||
#define LAYER(x) LAYER_##x = (LAYER_ID_##x << LAYER_LOW_BITS),
|
||||
#include "drawlayers.inc.h"
|
||||
} DrawLayer;
|
||||
|
||||
// NOTE: you can bit-or a drawlayer_low_t value with one of the LAYER_x constants
|
||||
// for sub-layer ordering.
|
||||
|
||||
#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) \
|
||||
|
||||
typedef enum EntityType {
|
||||
_ENT_TYPE_ENUM_BEGIN,
|
||||
#define ENT_TYPE(typename, id) id, _ENT_TYPEID_##typename = id,
|
||||
ENT_TYPES
|
||||
#undef ENT_TYPE
|
||||
_ENT_TYPE_ENUM_END,
|
||||
} EntityType;
|
||||
|
||||
typedef struct EntityInterface EntityInterface;
|
||||
typedef struct EntityListNode EntityListNode;
|
||||
typedef void (*EntityDrawFunc)(EntityInterface *ent);
|
||||
typedef bool (*EntityPredicate)(EntityInterface *ent);
|
||||
|
||||
#define ENTITY_INTERFACE_BASE(typename) struct { \
|
||||
OBJECT_INTERFACE(typename); \
|
||||
EntityType type; \
|
||||
EntityDrawFunc draw_func; \
|
||||
drawlayer_t draw_layer; \
|
||||
uint32_t spawn_id; \
|
||||
uint index; \
|
||||
}
|
||||
|
||||
#define ENTITY_INTERFACE(typename) union { \
|
||||
EntityInterface entity_interface; \
|
||||
ENTITY_INTERFACE_BASE(typename); \
|
||||
}
|
||||
|
||||
#define ENTITY_INTERFACE_NAMED(typename, name) union { \
|
||||
OBJECT_INTERFACE(typename); \
|
||||
EntityInterface entity_interface; \
|
||||
EntityInterface name; \
|
||||
}
|
||||
|
||||
struct EntityInterface {
|
||||
ENTITY_INTERFACE_BASE(EntityInterface);
|
||||
};
|
||||
|
||||
static inline attr_must_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";
|
||||
}
|
||||
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
#define ENT_TYPE_ID(typename) (_ENT_TYPEID_##typename)
|
||||
|
||||
#ifdef USE_GNU_EXTENSIONS
|
||||
#define ENT_CAST(ent, typename) (__extension__({ \
|
||||
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(ent->type != _ENT_TYPEID_##typename) { \
|
||||
log_fatal("Invalid entity cast from %s to " #typename, ent_type_name(ent->type)); \
|
||||
} \
|
||||
(typename *)(ent); \
|
||||
}))
|
||||
#else
|
||||
#define ENT_CAST(ent, typename) ((typename *)(ent))
|
||||
#endif
|
||||
|
||||
void ent_init(void);
|
||||
void ent_shutdown(void);
|
||||
void ent_register(EntityInterface *ent, EntityType type);
|
||||
void ent_unregister(EntityInterface *ent);
|
||||
void ent_draw(EntityPredicate predicate);
|
|
@ -424,6 +424,14 @@ void hashtable_copyfunc_ptr(void **dst, void *src) {
|
|||
*dst = src;
|
||||
}
|
||||
|
||||
hash_t hashtable_hashfunc_ptr(void *val) {
|
||||
hash_t x = (uintptr_t)val & ((1u << 31u) - 1u);
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = (x >> 16) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* Diagnostic functions
|
||||
*/
|
||||
|
|
|
@ -64,6 +64,7 @@ void hashtable_unset_deferred_string(Hashtable *ht, const char *key);
|
|||
void* hashtable_iter_free_data(void *key, void *data, void *arg);
|
||||
bool hashtable_cmpfunc_ptr(void *p1, void *p2);
|
||||
void hashtable_copyfunc_ptr(void **dst, void *src);
|
||||
hash_t hashtable_hashfunc_ptr(void *val);
|
||||
|
||||
int hashtable_test(void);
|
||||
|
||||
|
|
40
src/item.c
40
src/item.c
|
@ -30,6 +30,22 @@ static Sprite* item_sprite(ItemType type) {
|
|||
return get_sprite(map[type]);
|
||||
}
|
||||
|
||||
static void ent_draw_item(EntityInterface *ent) {
|
||||
Item *i = ENT_CAST(ent, Item);
|
||||
|
||||
Color c = rgba(1, 1, 1,
|
||||
i->type == BPoint && !i->auto_collect
|
||||
? clamp(2.0 - (global.frames - i->birthtime) / 60.0, 0.1, 1.0)
|
||||
: 1.0
|
||||
);
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite_ptr = item_sprite(i->type),
|
||||
.pos = { creal(i->pos), cimag(i->pos) },
|
||||
.color = c,
|
||||
});
|
||||
}
|
||||
|
||||
static int item_prio(List *litem) {
|
||||
Item *item = (Item*)litem;
|
||||
return item->type;
|
||||
|
@ -45,6 +61,9 @@ Item* create_item(complex pos, complex v, ItemType type) {
|
|||
// type = 1 + floor(Life * frand());
|
||||
|
||||
Item *i = (Item*)objpool_acquire(stage_object_pools.items);
|
||||
|
||||
// FIXME: use simpler/faster insertions when v1.2 compat is not needed
|
||||
// list_push(&global.items, i);
|
||||
list_insert_at_priority_tail(&global.items, i, type, item_prio);
|
||||
|
||||
i->pos = pos;
|
||||
|
@ -54,10 +73,15 @@ Item* create_item(complex pos, complex v, ItemType type) {
|
|||
i->auto_collect = 0;
|
||||
i->type = type;
|
||||
|
||||
i->ent.draw_layer = LAYER_ITEM | i->type;
|
||||
i->ent.draw_func = ent_draw_item;
|
||||
ent_register(&i->ent, ENT_ITEM);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void delete_item(Item *item) {
|
||||
ent_unregister(&item->ent);
|
||||
objpool_release(stage_object_pools.items, (ObjectInterface*)list_unlink(&global.items, item));
|
||||
}
|
||||
|
||||
|
@ -72,22 +96,6 @@ Item* create_bpoint(complex pos) {
|
|||
return i;
|
||||
}
|
||||
|
||||
void draw_items(void) {
|
||||
for(Item *i = global.items; i; i = i->next) {
|
||||
Color c = rgba(1, 1, 1,
|
||||
i->type == BPoint && !i->auto_collect
|
||||
? clamp(2.0 - (global.frames - i->birthtime) / 60.0, 0.1, 1.0)
|
||||
: 1.0
|
||||
);
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite_ptr = item_sprite(i->type),
|
||||
.pos = { creal(i->pos), cimag(i->pos) },
|
||||
.color = c,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void delete_items(void) {
|
||||
objpool_release_list(stage_object_pools.items, (List**)&global.items);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "util.h"
|
||||
#include "resource/texture.h"
|
||||
#include "objectpool.h"
|
||||
#include "entity.h"
|
||||
|
||||
typedef struct Item Item;
|
||||
|
||||
|
@ -28,7 +29,7 @@ typedef enum {
|
|||
} ItemType;
|
||||
|
||||
struct Item {
|
||||
OBJECT_INTERFACE(Item);
|
||||
ENTITY_INTERFACE_NAMED(Item, ent);
|
||||
|
||||
int birthtime;
|
||||
complex pos;
|
||||
|
@ -42,7 +43,6 @@ struct Item {
|
|||
|
||||
Item *create_item(complex pos, complex v, ItemType type);
|
||||
void delete_item(Item *item);
|
||||
void draw_items(void);
|
||||
void delete_items(void);
|
||||
|
||||
Item* create_bpoint(complex pos);
|
||||
|
|
48
src/laser.c
48
src/laser.c
|
@ -64,6 +64,8 @@ void lasers_free(void) {
|
|||
r_vertex_buffer_destroy(&lasers.vbuf);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
|
@ -87,10 +89,13 @@ Laser *create_laser(complex pos, float time, float deathtime, Color color, Laser
|
|||
l->width_exponent = 1.0;
|
||||
l->speed = 1;
|
||||
l->timeshift = 0;
|
||||
l->in_background = false;
|
||||
l->dead = false;
|
||||
l->unclearable = false;
|
||||
|
||||
l->ent.draw_layer = LAYER_LASER_HIGH;
|
||||
l->ent.draw_func = ent_draw_laser;
|
||||
ent_register(&l->ent, ENT_LASER);
|
||||
|
||||
if(l->lrule)
|
||||
l->lrule(l, EVENT_BIRTH);
|
||||
|
||||
|
@ -184,37 +189,31 @@ static void draw_laser_curve_generic(Laser *l) {
|
|||
r_draw(PRIM_TRIANGLE_FAN, 0, 4, NULL, instances, 0);
|
||||
}
|
||||
|
||||
void draw_lasers(int bgpass) {
|
||||
Laser *laser;
|
||||
VertexArray *varr_saved = r_vertex_array_current();
|
||||
ShaderProgram *prog_saved = r_shader_current();
|
||||
static void ent_draw_laser(EntityInterface *ent) {
|
||||
Laser *laser = ENT_CAST(ent, Laser);
|
||||
|
||||
r_texture(0, "part/lasercurve");
|
||||
r_blend(BLEND_ADD);
|
||||
|
||||
for(laser = global.lasers; laser; laser = laser->next) {
|
||||
if(bgpass != laser->in_background) {
|
||||
continue;
|
||||
}
|
||||
if(laser->shader) {
|
||||
// Specialized lasers work with either vertex array,
|
||||
// provided that the static models buffer is attached to it.
|
||||
// We'll only ever draw the first quad, and only care about
|
||||
// attributes 0 and 2 (vec3 position, vec2 uv)
|
||||
|
||||
if(laser->shader) {
|
||||
// Specialized lasers work with either vertex array,
|
||||
// provided that the static models buffer is attached to it.
|
||||
// We'll only ever draw the first quad, and only care about
|
||||
// attributes 0 and 2 (vec3 position, vec2 uv)
|
||||
r_shader_ptr(laser->shader);
|
||||
draw_laser_curve_specialized(laser);
|
||||
} else {
|
||||
VertexArray *va = r_vertex_array_current();
|
||||
|
||||
if(va != &lasers.varr && va != r_vertex_array_static_models()) {
|
||||
r_vertex_array(&lasers.varr);
|
||||
r_shader_ptr(lasers.shader_generic);
|
||||
draw_laser_curve_generic(laser);
|
||||
}
|
||||
}
|
||||
|
||||
r_blend(BLEND_ALPHA);
|
||||
r_shader_ptr(prog_saved);
|
||||
r_vertex_array(varr_saved);
|
||||
r_color4(1, 1, 1, 1);
|
||||
r_shader_ptr(laser->shader);
|
||||
draw_laser_curve_specialized(laser);
|
||||
} else {
|
||||
r_vertex_array(&lasers.varr);
|
||||
r_shader_ptr(lasers.shader_generic);
|
||||
draw_laser_curve_generic(laser);
|
||||
}
|
||||
}
|
||||
|
||||
void* _delete_laser(List **lasers, List *laser, void *arg) {
|
||||
|
@ -224,6 +223,7 @@ void* _delete_laser(List **lasers, List *laser, void *arg) {
|
|||
l->lrule(l, EVENT_DEATH);
|
||||
|
||||
del_ref(laser);
|
||||
ent_unregister(&l->ent);
|
||||
objpool_release(stage_object_pools.lasers, (ObjectInterface*)list_unlink(lasers, laser));
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "util.h"
|
||||
#include "projectile.h"
|
||||
#include "resource/shader_program.h"
|
||||
#include "entity.h"
|
||||
|
||||
typedef struct Laser Laser;
|
||||
|
||||
|
@ -19,7 +20,7 @@ typedef complex (*LaserPosRule)(Laser* l, float time);
|
|||
typedef void (*LaserLogicRule)(Laser* l, int time);
|
||||
|
||||
struct Laser {
|
||||
OBJECT_INTERFACE(Laser);
|
||||
ENTITY_INTERFACE_NAMED(Laser, ent);
|
||||
|
||||
complex pos;
|
||||
|
||||
|
@ -42,8 +43,6 @@ struct Laser {
|
|||
LaserPosRule prule;
|
||||
LaserLogicRule lrule;
|
||||
|
||||
int in_background;
|
||||
|
||||
complex args[4];
|
||||
|
||||
bool unclearable;
|
||||
|
@ -62,7 +61,6 @@ Laser *create_laserline(complex pos, complex dir, float charge, float dur, Color
|
|||
Laser *create_laserline_ab(complex a, complex b, float width, float charge, float dur, Color clr);
|
||||
|
||||
Laser *create_laser(complex pos, float time, float deathtime, Color color, LaserPosRule prule, LaserLogicRule lrule, complex a0, complex a1, complex a2, complex a3);
|
||||
void draw_lasers(int);
|
||||
void delete_lasers(void);
|
||||
void process_lasers(void);
|
||||
|
||||
|
|
|
@ -108,11 +108,11 @@ static List* list_insert_at_priority(List **list_head, List *elem, int prio, Lis
|
|||
return elem;
|
||||
}
|
||||
|
||||
List* list_insert_at_priority_head(List **dest, List *elem, int prio, ListPriorityFunc prio_func){
|
||||
List* list_insert_at_priority_head(List **dest, List *elem, int prio, ListPriorityFunc prio_func) {
|
||||
return list_insert_at_priority(dest, elem, prio, prio_func, true);
|
||||
}
|
||||
|
||||
List* list_insert_at_priority_tail(List **dest, List *elem, int prio, ListPriorityFunc prio_func){
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ taisei_src = files(
|
|||
'difficulty.c',
|
||||
'ending.c',
|
||||
'enemy.c',
|
||||
'entity.c',
|
||||
'events.c',
|
||||
'fbo.c',
|
||||
'framerate.c',
|
||||
|
|
99
src/player.c
99
src/player.c
|
@ -15,6 +15,7 @@
|
|||
#include "plrmodes.h"
|
||||
#include "stage.h"
|
||||
#include "stagetext.h"
|
||||
#include "entity.h"
|
||||
|
||||
void player_init(Player *plr) {
|
||||
memset(plr, 0, sizeof(Player));
|
||||
|
@ -35,6 +36,9 @@ void player_stage_pre_init(Player *plr) {
|
|||
plrmode_preload(plr->mode);
|
||||
}
|
||||
|
||||
static void ent_draw_player(EntityInterface *ent);
|
||||
static Enemy* player_spawn_focus_circle(void);
|
||||
|
||||
void player_stage_post_init(Player *plr) {
|
||||
assert(plr->mode != NULL);
|
||||
|
||||
|
@ -49,12 +53,21 @@ void player_stage_post_init(Player *plr) {
|
|||
}
|
||||
|
||||
aniplayer_create(&plr->ani, get_ani(plr->mode->character->player_sprite_name), "main");
|
||||
|
||||
plr->ent.draw_layer = LAYER_PLAYER;
|
||||
plr->ent.draw_func = ent_draw_player;
|
||||
ent_register(&plr->ent, ENT_PLAYER);
|
||||
|
||||
plr->focus_circle = player_spawn_focus_circle();
|
||||
}
|
||||
|
||||
void player_free(Player *plr) {
|
||||
if(plr->mode->procs.free) {
|
||||
plr->mode->procs.free(plr);
|
||||
}
|
||||
|
||||
ent_unregister(&plr->ent);
|
||||
delete_enemy(&plr->focus_circle, plr->focus_circle);
|
||||
}
|
||||
|
||||
static void player_full_power(Player *plr) {
|
||||
|
@ -107,44 +120,66 @@ void player_move(Player *plr, complex delta) {
|
|||
}
|
||||
}
|
||||
|
||||
void player_draw(Player* plr) {
|
||||
static void ent_draw_player(EntityInterface *ent) {
|
||||
Player *plr = ENT_CAST(ent, Player);
|
||||
|
||||
// FIXME: death animation?
|
||||
if(plr->deathtime > global.frames)
|
||||
return;
|
||||
|
||||
draw_enemies(plr->slaves);
|
||||
|
||||
r_mat_push();
|
||||
r_mat_translate(creal(plr->pos), cimag(plr->pos), 0);
|
||||
|
||||
if(plr->focus) {
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite = "fairy_circle",
|
||||
.rotation.angle = DEG2RAD * global.frames * 10,
|
||||
.color = rgba(1, 1, 1, 0.2 * (clamp(plr->focus, 0, 15) / 15.0)),
|
||||
});
|
||||
}
|
||||
|
||||
if(global.frames - abs(plr->recovery) < 0 && (global.frames/8)&1) {
|
||||
r_color4(0.4, 0.4, 1.0, 0.9);
|
||||
}
|
||||
|
||||
Sprite *spr = aniplayer_get_frame(&plr->ani);
|
||||
if(plr->focus) {
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite_ptr = spr,
|
||||
.pos = { 0, 0 },
|
||||
.sprite = "fairy_circle",
|
||||
.rotation.angle = DEG2RAD * global.frames * 10,
|
||||
.color = rgba(1, 1, 1, 0.2 * (clamp(plr->focus, 0, 15) / 15.0)),
|
||||
.pos = { creal(plr->pos), cimag(plr->pos) },
|
||||
});
|
||||
r_color3(1, 1, 1);
|
||||
}
|
||||
|
||||
if(plr->focus) {
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite = "focus",
|
||||
.rotation.angle = DEG2RAD * global.frames * -1,
|
||||
.color = rgba(1, 1, 1, plr->focus / 30.0),
|
||||
});
|
||||
}
|
||||
Color c;
|
||||
|
||||
r_mat_pop();
|
||||
if(global.frames - abs(plr->recovery) < 0 && (global.frames/8)&1) {
|
||||
c = rgba(0.4, 0.4, 1.0, 0.9);
|
||||
} else {
|
||||
c = rgba(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite_ptr = aniplayer_get_frame(&plr->ani),
|
||||
.pos = { creal(plr->pos), cimag(plr->pos) },
|
||||
.color = c,
|
||||
});
|
||||
}
|
||||
|
||||
static int player_focus_circle_logic(Enemy *e, int t) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void player_focus_circle_visual(Enemy *e, int t, bool render) {
|
||||
if(!render || !creal(e->args[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite = "focus",
|
||||
.rotation.angle = DEG2RAD * global.frames,
|
||||
.color = rgba(1, 1, 1, creal(e->args[0])),
|
||||
.pos = { creal(e->pos), cimag(e->pos) },
|
||||
});
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite = "focus",
|
||||
.rotation.angle = DEG2RAD * global.frames * -1,
|
||||
.color = rgba(1, 1, 1, creal(e->args[0])),
|
||||
.pos = { creal(e->pos), cimag(e->pos) },
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
f->ent.draw_layer = LAYER_PLAYER_FOCUS;
|
||||
return f;
|
||||
}
|
||||
|
||||
static void player_fail_spell(Player *plr) {
|
||||
|
@ -184,6 +219,7 @@ void player_logic(Player* plr) {
|
|||
|
||||
process_enemies(&plr->slaves);
|
||||
aniplayer_update(&plr->ani);
|
||||
|
||||
if(plr->deathtime < -1) {
|
||||
plr->deathtime++;
|
||||
plr->pos -= I;
|
||||
|
@ -193,6 +229,9 @@ 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->args[0] = plr->focus / 30.0;
|
||||
|
||||
if(plr->mode->procs.think) {
|
||||
plr->mode->procs.think(plr);
|
||||
}
|
||||
|
|
11
src/player.h
11
src/player.h
|
@ -18,6 +18,7 @@
|
|||
#include "gamepad.h"
|
||||
#include "aniplayer.h"
|
||||
#include "resource/animation.h"
|
||||
#include "entity.h"
|
||||
|
||||
enum {
|
||||
PLR_MAX_POWER = 400,
|
||||
|
@ -55,7 +56,11 @@ enum {
|
|||
INFLAGS_MOVE = INFLAG_UP | INFLAG_DOWN | INFLAG_LEFT | INFLAG_RIGHT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
typedef struct Player Player;
|
||||
|
||||
struct Player {
|
||||
ENTITY_INTERFACE_NAMED(Player, ent);
|
||||
|
||||
complex pos;
|
||||
complex deathpos;
|
||||
short focus;
|
||||
|
@ -80,6 +85,7 @@ typedef struct {
|
|||
struct PlayerMode *mode;
|
||||
AniPlayer ani;
|
||||
Enemy *slaves;
|
||||
Enemy *focus_circle;
|
||||
|
||||
int inputflags;
|
||||
bool gamepadmove;
|
||||
|
@ -93,7 +99,7 @@ typedef struct {
|
|||
#ifdef PLR_DPS_STATS
|
||||
int total_dmg;
|
||||
#endif
|
||||
} Player;
|
||||
};
|
||||
|
||||
// this is used by both player and replay code
|
||||
enum {
|
||||
|
@ -124,7 +130,6 @@ void player_stage_post_init(Player *plr);
|
|||
|
||||
void player_free(Player *plr);
|
||||
|
||||
void player_draw(Player*);
|
||||
void player_logic(Player*);
|
||||
bool player_should_shoot(Player *plr, bool extra);
|
||||
|
||||
|
|
|
@ -518,6 +518,7 @@ static void marisa_laser_respawn_slaves(Player *plr, short npow) {
|
|||
MarisaLaserData *ld = calloc(1, sizeof(MarisaLaserData));
|
||||
ld->prev_pos = e->pos + plr->pos;
|
||||
e->args[3] = add_ref(ld);
|
||||
e->ent.draw_layer = LAYER_PLAYER_SLAVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -532,6 +533,7 @@ 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;
|
||||
marisa_laser_respawn_slaves(plr, plr->power);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ static void marisa_star_trail_draw(Projectile *p, int t) {
|
|||
r_mat_rotate_deg(p->angle*180/M_PI+90, 0, 0, 1);
|
||||
r_mat_scale(s, s, 1);
|
||||
ProjDrawCore(p, clr);
|
||||
r_color4(1,1,1,1);
|
||||
r_mat_pop();
|
||||
}
|
||||
|
||||
|
@ -46,6 +45,7 @@ static int marisa_star_projectile(Projectile *p, int t) {
|
|||
.draw_rule = marisa_star_trail_draw,
|
||||
.angle = p->angle,
|
||||
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT,
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
|
||||
if(t == EVENT_DEATH) {
|
||||
|
@ -58,6 +58,7 @@ static int marisa_star_projectile(Projectile *p, int t) {
|
|||
.args = { 40, 2 },
|
||||
.angle = frand() * 2 * M_PI,
|
||||
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT,
|
||||
.layer = LAYER_PARTICLE_HIGH,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -211,9 +212,6 @@ static void marisa_star_orbit_visual(Enemy *e, int t, bool render) {
|
|||
r_mat_scale(0.6,0.6,1);
|
||||
draw_sprite_batched(0,0,"part/lightningball");
|
||||
r_mat_pop();
|
||||
r_blend(BLEND_ALPHA);
|
||||
|
||||
r_color4(1,1,1,1);
|
||||
}
|
||||
|
||||
|
||||
|
@ -284,6 +282,10 @@ 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);
|
||||
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) {
|
||||
e->ent.draw_layer = LAYER_PLAYER_SLAVE;
|
||||
}
|
||||
}
|
||||
|
||||
static void marisa_star_power(Player *plr, short npow) {
|
||||
|
|
|
@ -73,6 +73,7 @@ static void spawn_stardust(complex pos, Color clr, int timeout, complex v) {
|
|||
.args = { timeout, v, 0.2 + 0.1 * frand(), 1 },
|
||||
.angle = M_PI*2*frand(),
|
||||
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT,
|
||||
.layer = LAYER_PARTICLE_LOW | 1,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -117,6 +118,7 @@ static void myon_spawn_trail(Enemy *e, int t) {
|
|||
.draw_rule = Shrink,
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
|
||||
.angle = M_PI*2*frand(),
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
|
||||
spawn_stardust(pos, rgba(1, 1, 1, 0.1), 60, stardust_v);
|
||||
|
@ -338,7 +340,6 @@ static void youmu_mirror_shot(Player *plr) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static int youmu_split(Enemy *e, int t) {
|
||||
if(t < 0)
|
||||
return 1;
|
||||
|
@ -390,7 +391,8 @@ static void youmu_mirror_bomb(Player *plr) {
|
|||
}
|
||||
|
||||
static void youmu_mirror_init(Player *plr) {
|
||||
create_enemy_p(&plr->slaves, 40.0*I, ENEMY_IMMUNE, NULL, youmu_mirror_myon, 0, 0, 0, 0);
|
||||
Enemy *myon = create_enemy_p(&plr->slaves, 40.0*I, ENEMY_IMMUNE, NULL, youmu_mirror_myon, 0, 0, 0, 0);
|
||||
myon->ent.draw_layer = LAYER_PLAYER_SLAVE;
|
||||
}
|
||||
|
||||
static double youmu_mirror_speed_mod(Player *plr, double speed) {
|
||||
|
@ -429,12 +431,12 @@ PlayerMode plrmode_youmu_a = {
|
|||
.character = &character_youmu,
|
||||
.shot_mode = PLR_SHOT_YOUMU_MIRROR,
|
||||
.procs = {
|
||||
.bomb = youmu_mirror_bomb,
|
||||
.bomb = youmu_mirror_bomb,
|
||||
.speed_mod = youmu_mirror_speed_mod,
|
||||
.bomb_shader = youmu_mirror_shader,
|
||||
.bombbg = youmu_common_bombbg,
|
||||
.shot = youmu_mirror_shot,
|
||||
.init = youmu_mirror_init,
|
||||
.preload = youmu_mirror_preload,
|
||||
.shot = youmu_mirror_shot,
|
||||
.init = youmu_mirror_init,
|
||||
.preload = youmu_mirror_preload,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -81,6 +81,7 @@ static Projectile* youmu_homing_trail(Projectile *p, complex v, int to) {
|
|||
.args = { to, v },
|
||||
.flags = PFLAG_NOREFLECT,
|
||||
.shader_ptr = p->shader,
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -122,7 +123,11 @@ static Projectile* youmu_trap_trail(Projectile *p, complex v, int t) {
|
|||
|
||||
static int youmu_trap(Projectile *p, int t) {
|
||||
if(t == EVENT_DEATH) {
|
||||
PARTICLE("blast", p->pos, 0, blast_timeout, { 15 }, .draw_rule = Blast, .flags = PFLAG_REQUIREDPARTICLE);
|
||||
PARTICLE("blast", p->pos, 0, blast_timeout, { 15 },
|
||||
.draw_rule = Blast,
|
||||
.flags = PFLAG_REQUIREDPARTICLE,
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -140,8 +145,17 @@ static int youmu_trap(Projectile *p, int t) {
|
|||
p->shader_custom_param = charge;
|
||||
|
||||
if(!(global.plr.inputflags & INFLAG_FOCUS)) {
|
||||
PARTICLE("blast", p->pos, 0, blast_timeout, { 20 }, .draw_rule = Blast, .flags = PFLAG_REQUIREDPARTICLE);
|
||||
PARTICLE("blast", p->pos, 0, blast_timeout, { 23 }, .draw_rule = Blast, .flags = PFLAG_REQUIREDPARTICLE);
|
||||
PARTICLE("blast", p->pos, 0, blast_timeout, { 20 },
|
||||
.draw_rule = Blast,
|
||||
.flags = PFLAG_REQUIREDPARTICLE,
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
|
||||
PARTICLE("blast", p->pos, 0, blast_timeout, { 23 },
|
||||
.draw_rule = Blast,
|
||||
.flags = PFLAG_REQUIREDPARTICLE,
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
|
||||
int cnt = rint(creal(p->args[2]) * (0.25 + 0.75 * charge));
|
||||
int dmg = cimag(p->args[2]);
|
||||
|
|
120
src/projectile.c
120
src/projectile.c
|
@ -23,7 +23,7 @@ static ProjArgs defaults_proj = {
|
|||
.color = RGB(1, 1, 1),
|
||||
.blend = BLEND_ALPHA,
|
||||
.shader = "sprite_bullet",
|
||||
.insertion_rule = proj_insert_sizeprio,
|
||||
.layer = LAYER_BULLET,
|
||||
};
|
||||
|
||||
static ProjArgs defaults_part = {
|
||||
|
@ -34,8 +34,7 @@ static ProjArgs defaults_part = {
|
|||
.color = RGB(1, 1, 1),
|
||||
.blend = BLEND_ALPHA,
|
||||
.shader = "sprite_default",
|
||||
.insertion_rule = list_append,
|
||||
// .insertion_rule = proj_insert_sizeprio,
|
||||
.layer = LAYER_PARTICLE_HIGH,
|
||||
};
|
||||
|
||||
static void process_projectile_args(ProjArgs *args, ProjArgs *defaults) {
|
||||
|
@ -81,13 +80,17 @@ static void process_projectile_args(ProjArgs *args, ProjArgs *defaults) {
|
|||
args->color = defaults->color;
|
||||
}
|
||||
|
||||
if(!args->insertion_rule) {
|
||||
args->insertion_rule = defaults->insertion_rule;
|
||||
}
|
||||
|
||||
if(!args->max_viewport_dist && (args->type == Particle || args->type >= PlrProj)) {
|
||||
args->max_viewport_dist = 300;
|
||||
}
|
||||
|
||||
if(!args->layer) {
|
||||
if(args->type >= PlrProj) {
|
||||
args->layer = LAYER_PLAYER_SHOT;
|
||||
} else {
|
||||
args->layer = defaults->layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static double projectile_rect_area(Projectile *p) {
|
||||
|
@ -108,7 +111,10 @@ static void projectile_size(Projectile *p, double *w, double *h) {
|
|||
}
|
||||
}
|
||||
|
||||
static void ent_draw_projectile(EntityInterface *ent);
|
||||
|
||||
static int projectile_sizeprio_func(List *vproj) {
|
||||
// FIXME: remove this when v1.2 compat is not needed
|
||||
Projectile *proj = (Projectile*)vproj;
|
||||
|
||||
if(proj->priority_override) {
|
||||
|
@ -119,29 +125,10 @@ static int projectile_sizeprio_func(List *vproj) {
|
|||
}
|
||||
|
||||
List* proj_insert_sizeprio(List **dest, List *elem) {
|
||||
// FIXME: remove this when v1.2 compat is not needed
|
||||
return list_insert_at_priority_tail(dest, elem, projectile_sizeprio_func(elem), projectile_sizeprio_func);
|
||||
}
|
||||
|
||||
static int projectile_colorprio_func(List *vproj) {
|
||||
Projectile *p = (Projectile*)vproj;
|
||||
Color c = p->color;
|
||||
uint32_t c32 = 0;
|
||||
float r, g, b, a;
|
||||
|
||||
// convert color to 32bit
|
||||
parse_color(c, &r, &g, &b, &a);
|
||||
c32 |= (((c & CLRMASK_R) >> CLR_R) & 0xFF) << 0;
|
||||
c32 |= (((c & CLRMASK_G) >> CLR_G) & 0xFF) << 8;
|
||||
c32 |= (((c & CLRMASK_B) >> CLR_B) & 0xFF) << 16;
|
||||
|
||||
return (int)c32;
|
||||
}
|
||||
|
||||
List* proj_insert_colorprio(List **dest, List *elem) {
|
||||
return list_insert_at_priority_head(dest, elem, projectile_colorprio_func(elem), projectile_colorprio_func);
|
||||
// return list_push(dest, elem);
|
||||
}
|
||||
|
||||
static Projectile* _create_projectile(ProjArgs *args) {
|
||||
if(IN_DRAW_CODE) {
|
||||
log_fatal("Tried to spawn a projectile while in drawing code");
|
||||
|
@ -168,12 +155,49 @@ static Projectile* _create_projectile(ProjArgs *args) {
|
|||
|
||||
memcpy(p->args, args->args, sizeof(p->args));
|
||||
|
||||
p->ent.draw_layer = args->layer;
|
||||
|
||||
if(!(p->ent.draw_layer & LAYER_LOW_MASK)) {
|
||||
switch(p->type) {
|
||||
case EnemyProj:
|
||||
case FakeProj: {
|
||||
// 1. Large projectiles go below smaller ones.
|
||||
drawlayer_low_t sublayer = (LAYER_LOW_MASK - (drawlayer_low_t)projectile_rect_area(p));
|
||||
|
||||
// 2. Additive projectiles go below others.
|
||||
sublayer <<= 1;
|
||||
sublayer |= 1 * (p->blend == BLEND_ADD || p->flags & PFLAG_DRAWADD);
|
||||
|
||||
// If specific blending order is required, then set up the sublayer manually.
|
||||
p->ent.draw_layer |= sublayer;
|
||||
break;
|
||||
}
|
||||
|
||||
case Particle: {
|
||||
// 1. Additive particles go above others.
|
||||
drawlayer_low_t sublayer = (p->blend == BLEND_ADD || p->flags & PFLAG_DRAWADD) * PARTICLE_ADDITIVE_SUBLAYER;
|
||||
|
||||
// If specific blending order is required, then set up the sublayer manually.
|
||||
p->ent.draw_layer |= sublayer;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
p->ent.draw_func = ent_draw_projectile;
|
||||
ent_register(&p->ent, ENT_PROJECTILE);
|
||||
|
||||
// BUG: this currently breaks some projectiles
|
||||
// enable this when they're fixed
|
||||
// assert(rule != NULL);
|
||||
// rule(p, EVENT_BIRTH);
|
||||
|
||||
return (Projectile*)args->insertion_rule((List**)args->dest, (List*)p);
|
||||
// FIXME: use simpler/faster insertions when v1.2 compat is not needed
|
||||
// return (Projectile*)list_push(args->dest, p);
|
||||
return (Projectile*)proj_insert_sizeprio((List**)args->dest, (List*)p);
|
||||
}
|
||||
|
||||
Projectile* create_projectile(ProjArgs *args) {
|
||||
|
@ -200,6 +224,7 @@ static void* _delete_projectile(List **projs, List *proj, void *arg) {
|
|||
p->rule(p, EVENT_DEATH);
|
||||
|
||||
del_ref(proj);
|
||||
ent_unregister(&p->ent);
|
||||
objpool_release(stage_object_pools.projectiles, (ObjectInterface*)list_unlink(projs, proj));
|
||||
|
||||
return NULL;
|
||||
|