taisei/src/enemy.h
2024-05-17 14:11:48 +02:00

145 lines
4.1 KiB
C

/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
*/
#pragma once
#include "taisei.h"
#include "coroutine/coevent.h"
#include "entity.h"
#include "move.h"
#include "resource/resource.h"
#include "util.h"
#ifdef DEBUG
#define ENEMY_DEBUG
#endif
#ifdef ENEMY_DEBUG
#define IF_ENEMY_DEBUG(...) __VA_ARGS__
#else
#define IF_ENEMY_DEBUG(...)
#endif
typedef LIST_ANCHOR(Enemy) EnemyList;
typedef enum EnemyFlag {
EFLAG_KILLED = (1 << 0), // is dead, pending removal (internal)
EFLAG_NO_HIT = (1 << 1), // can't be hit by player
EFLAG_NO_HURT = (1 << 2), // can't hurt player
EFLAG_NO_AUTOKILL = (1 << 3), // no autokill when out of bounds
EFLAG_NO_VISUAL_CORRECTION = (1 << 4), // disable the slide-in hack for enemies spawning at screen edges
EFLAG_NO_DEATH_EXPLOSION = (1 << 5), // don't explode on death; currently also disables bonus voltage on kill
EFLAG_INVULNERABLE = (1 << 6), // can't be damaged by player (but can be hit)
EFLAG_IMPENETRABLE = (1 << 7), // penetrating shots can't pass through (e.g. Marisa's laser)
EFLAGS_GHOST =
EFLAG_NO_HIT |
EFLAG_NO_HURT |
EFLAG_NO_AUTOKILL |
EFLAG_NO_VISUAL_CORRECTION |
EFLAG_NO_DEATH_EXPLOSION |
EFLAG_INVULNERABLE |
0,
} EnemyFlag;
typedef struct EnemyDrawParams {
cmplx pos; // NOTE: subject to slide-in correction at screen edges
int time;
} EnemyDrawParams;
typedef void (*EnemyDrawFunc)(Enemy*, EnemyDrawParams);
typedef struct EnemyVisual {
EnemyDrawFunc draw;
void *drawdata;
} EnemyVisual;
#define ENEMY_NOVISUAL ((EnemyVisual) {})
DEFINE_ENTITY_TYPE(Enemy, {
cmplx pos;
cmplx pos0;
cmplx pos0_visual;
MoveParams move;
EnemyVisual visual;
COEVENTS_ARRAY(
predamage,
damaged,
killed
) events;
/*
* This field is usually NULL except during handling of "predamage", "damaged", and "killed"
* events.
*
* "Predamage" events are run any time something attempts to damage the enemy, before the
* damage is applied. The event handler may modify the DamageInfo struct to affect the outcome.
*
* "Damaged" events are run after the damage has been applied. Immunities are not considered
* successful applications. `damage_info` describes the damage that has been applied.
*
* If after the "damaged" event health drops to 0 or lower, the "killed" event is signaled and
* the enemy is marked for removal. `damage_info` describes the cause of death.
*
* The "damaged" event is also signaled when an enemy is killed through non-damage means, such
* as calling `enemy_kill()` directly. In that case, `damage_info` is NULL. This does NOT happen
* if the enemy is auto-removed due to going out of bounds - if you want to catch that case,
* watch for a cancelled "killed" event.
*
* `damage_info` is also NULL when handling a cancelled event of any type.
*
* The pointer becomes invalid as soon as the event handler yields.
*/
DamageInfo *damage_info;
EnemyFlag flags;
int birthtime;
int dir;
float spawn_hp;
float hp;
float hit_radius;
float hurt_radius;
float max_viewport_dist;
bool moving;
IF_ENEMY_DEBUG(
DebugInfo debug;
)
});
Enemy *create_enemy_p(EnemyList *enemies, cmplx pos, float hp, EnemyVisual visual);
#ifdef ENEMY_DEBUG
Enemy *_enemy_attach_dbginfo(Enemy *p, DebugInfo *dbg);
#define create_enemy_p(...) _enemy_attach_dbginfo(create_enemy_p(__VA_ARGS__), _DEBUG_INFO_PTR_)
#endif
#define create_enemy(pos, hp, visual) \
create_enemy_p(&global.enemies, pos, hp, visual)
void delete_enemy(EnemyList *enemies, Enemy* enemy);
void delete_enemies(EnemyList *enemies);
void process_enemies(EnemyList *enemies);
bool enemy_is_vulnerable(Enemy *enemy);
bool enemy_is_targetable(Enemy *enemy);
bool enemy_in_viewport(Enemy *enemy);
float enemy_get_hurt_radius(Enemy *enemy);
cmplx enemy_visual_pos(Enemy *enemy);
void enemy_kill(Enemy *enemy);
void enemy_kill_all(EnemyList *enemies);
void enemies_preload(ResourceGroup *rg);