1 /*
2  * This software is licensed under the terms of the MIT License.
3  * See COPYING for further information.
4  * ---
5  * Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
6  * Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
7  */
8 
9 #ifndef IGUARD_projectile_h
10 #define IGUARD_projectile_h
11 
12 #include "taisei.h"
13 
14 #include "util.h"
15 #include "resource/sprite.h"
16 #include "resource/shader_program.h"
17 #include "color.h"
18 #include "objectpool.h"
19 #include "renderer/api.h"
20 #include "entity.h"
21 
22 #ifdef DEBUG
23 	#define PROJ_DEBUG
24 #endif
25 
26 enum {
27 	RULE_ARGC = 4
28 };
29 
30 typedef struct Projectile Projectile;
31 typedef LIST_ANCHOR(Projectile) ProjectileList;
32 typedef LIST_INTERFACE(Projectile) ProjectileListInterface;
33 
34 typedef int (*ProjRule)(Projectile *p, int t);
35 typedef void (*ProjDrawRule)(Projectile *p, int t);
36 typedef bool (*ProjPredicate)(Projectile *p);
37 
38 typedef enum {
39 	PROJ_INVALID,
40 
41 	PROJ_ENEMY,    // hazard, collides with player
42 	PROJ_DEAD,     // no collision, will be cleared shortly
43 	PROJ_PARTICLE, // no collision, not a hazard
44 	PROJ_PLAYER,   // collides with enemies and bosses
45 } ProjType;
46 
47 typedef enum ProjFlags {
48 	// NOTE: Many of these flags are only meaningful for a specific type(s) of projectile.
49 	// The relevant ProjType enum(s) are specified in [square brackets], or [ALL] if all apply.
50 
51 	PFLAG_NOSPAWNFLARE = (1 << 0),          // [PROJ_ENEMY] Don't spawn the standard particle effect when spawned.
52 	PFLAG_NOSPAWNFADE = (1 << 1),           // [PROJ_ENEMY] Don't fade in when spawned; assume 100% opacity immediately.
53 	PFLAG_NOGRAZE = (1 << 3),               // [PROJ_ENEMY] Can't be grazed.
54 	PFLAG_NOCLEAR = (1 << 4),               // [PROJ_ENEMY] Can't be cleared (unless forced).
55 	PFLAG_NOCLEAREFFECT = (1 << 5),         // [PROJ_ENEMY, PROJ_DEAD] Don't spawn the standard particle effect when cleared.
56 	PFLAG_NOCOLLISIONEFFECT = (1 << 6),     // [PROJ_ENEMY, PROJ_DEAD, PROJ_PLAYER] Don't spawn the standard particle effect on collision.
57 	PFLAG_NOCLEARBONUS = (1 << 7),          // [PROJ_ENEMY, PROJ_DEAD] Don't spawn any bonus items on clear.
58 	// PFLAG_RESERVED = (1 << 8),
59 	PFLAG_NOREFLECT = (1 << 9),             // [ALL] Don't render a "reflection" of this on the Stage 1 water surface.
60 	PFLAG_REQUIREDPARTICLE = (1 << 10),     // [PROJ_PARTICLE] Visible at "minimal" particles setting.
61 	PFLAG_PLRSPECIALPARTICLE = (1 << 11),   // [PROJ_PARTICLE] Apply Power Surge effect to this particle, as if it was a PROJ_PLAYER.
62 	PFLAG_NOCOLLISION = (1 << 12),          // [PROJ_ENEMY, PROJ_PLAYER] Disable collision detection.
63 
64 	PFLAG_NOSPAWNEFFECTS = PFLAG_NOSPAWNFADE | PFLAG_NOSPAWNFLARE,
65 } ProjFlags;
66 
67 // FIXME: prototype stuff awkwardly shoved in this header because of dependency cycles.
68 typedef struct ProjPrototype ProjPrototype;
69 
70 struct Projectile {
71 	ENTITY_INTERFACE_NAMED(Projectile, ent);
72 
73 	cmplx pos;
74 	cmplx pos0;
75 	cmplx prevpos; // used to lerp trajectory for collision detection; set this to pos if you intend to "teleport" the projectile in the rule!
76 	cmplx size; // affects out-of-viewport culling and grazing
77 	cmplx collision_size; // affects collision with player (TODO: make this work for player projectiles too?)
78 	cmplx args[RULE_ARGC];
79 	ProjRule rule;
80 	ProjDrawRule draw_rule;
81 	ShaderProgram *shader;
82 	Sprite *sprite;
83 	ProjPrototype *proto;
84 	Color color;
85 	ShaderCustomParams shader_params;
86 	BlendMode blend;
87 	int birthtime;
88 	float damage;
89 	float angle;
90 	ProjType type;
91 	DamageType damage_type;
92 	int max_viewport_dist;
93 	ProjFlags flags;
94 	uint clear_flags;
95 
96 	// XXX: this is in frames of course, but needs to be float
97 	// to avoid subtle truncation and integer division gotchas.
98 	float timeout;
99 
100 	int graze_counter_reset_timer;
101 	int graze_cooldown;
102 	short graze_counter;
103 
104 #ifdef PROJ_DEBUG
105 	DebugInfo debug;
106 #endif
107 };
108 
109 typedef struct ProjArgs {
110 	ProjPrototype *proto;
111 	const Color *color;
112 	const char *sprite;
113 	Sprite *sprite_ptr;
114 	const char *shader;
115 	ShaderProgram *shader_ptr;
116 	const ShaderCustomParams *shader_params;
117 	ProjectileList *dest;
118 	ProjRule rule;
119 	cmplx args[RULE_ARGC];
120 	ProjDrawRule draw_rule;
121 	cmplx pos;
122 	cmplx size; // affects default draw order, out-of-viewport culling, and grazing
123 	cmplx collision_size; // affects collision with player (TODO: make this work for player projectiles too?)
124 	ProjType type;
125 	ProjFlags flags;
126 	BlendMode blend;
127 	float angle;
128 	float damage;
129 	DamageType damage_type;
130 	int max_viewport_dist;
131 	drawlayer_t layer;
132 
133 	// XXX: this is in frames of course, but needs to be float
134 	// to avoid subtle truncation and integer division gotchas.
135 	float timeout;
136 } attr_designated_init ProjArgs;
137 
138 struct ProjPrototype {
139 	void (*preload)(ProjPrototype *proto);
140 	void (*process_args)(ProjPrototype *proto, ProjArgs *args);
141 	void (*init_projectile)(ProjPrototype *proto, Projectile *p);
142 	void (*deinit_projectile)(ProjPrototype *proto, Projectile *p);
143 	void *private;
144 };
145 
146 #define PP(name) \
147 	extern ProjPrototype _pp_##name; \
148 	extern ProjPrototype *pp_##name; \
149 
150 #include "projectile_prototypes/all.inc.h"
151 
152 #define PARTICLE_ADDITIVE_SUBLAYER (1 << 3)
153 
154 typedef enum ProjCollisionType {
155 	PCOL_NONE                = 0,
156 	PCOL_ENTITY              = (1 << 0),
157 	PCOL_PLAYER_GRAZE        = (1 << 1),
158 	PCOL_VOID                = (1 << 2),
159 } ProjCollisionType;
160 
161 typedef struct ProjCollisionResult {
162 	ProjCollisionType type;
163 	bool fatal; // for the projectile
164 	cmplx location;
165 	DamageInfo damage;
166 	EntityInterface *entity;
167 } ProjCollisionResult;
168 
169 Projectile* create_projectile(ProjArgs *args);
170 Projectile* create_particle(ProjArgs *args);
171 
172 #ifdef PROJ_DEBUG
173 	Projectile* _proj_attach_dbginfo(Projectile *p, DebugInfo *dbg, const char *callsite_str);
174 	#define _PROJ_WRAP_SPAWN(p) _proj_attach_dbginfo((p), _DEBUG_INFO_PTR_, #p)
175 #else
176 	#define _PROJ_WRAP_SPAWN(p) (p)
177 #endif
178 
179 #define _PROJ_GENERIC_SPAWN(constructor, ...) _PROJ_WRAP_SPAWN((constructor)((&(ProjArgs) { __VA_ARGS__ })))
180 
181 #define PROJECTILE(...) _PROJ_GENERIC_SPAWN(create_projectile, __VA_ARGS__)
182 #define PARTICLE(...) _PROJ_GENERIC_SPAWN(create_particle, __VA_ARGS__)
183 
184 void delete_projectile(ProjectileList *projlist, Projectile *proj);
185 void delete_projectiles(ProjectileList *projlist);
186 
187 void calc_projectile_collision(Projectile *p, ProjCollisionResult *out_col);
188 void apply_projectile_collision(ProjectileList *projlist, Projectile *p, ProjCollisionResult *col);
189 int trace_projectile(Projectile *p, ProjCollisionResult *out_col, ProjCollisionType stopflags, int timeofs);
190 bool projectile_in_viewport(Projectile *proj);
191 void process_projectiles(ProjectileList *projlist, bool collision);
192 bool projectile_is_clearable(Projectile *p);
193 
194 Projectile* spawn_projectile_collision_effect(Projectile *proj);
195 Projectile* spawn_projectile_clear_effect(Projectile *proj);
196 Projectile* spawn_projectile_highlight_effect(Projectile *proj);
197 
198 void projectile_set_prototype(Projectile *p, ProjPrototype *proto);
199 
200 bool clear_projectile(Projectile *proj, uint flags);
201 
202 int linear(Projectile *p, int t);
203 int accelerated(Projectile *p, int t);
204 int asymptotic(Projectile *p, int t);
205 
206 void ProjDrawCore(Projectile *proj, const Color *c);
207 void ProjDraw(Projectile *p, int t);
208 void ProjNoDraw(Projectile *proj, int t);
209 
210 void Shrink(Projectile *p, int t);
211 void DeathShrink(Projectile *p, int t);
212 void Fade(Projectile *p, int t);
213 void GrowFade(Projectile *p, int t);
214 void ScaleFade(Projectile *p, int t);
215 void ScaleSquaredFade(Projectile *p, int t);
216 
217 void Petal(Projectile *p, int t);
218 void petal_explosion(int n, cmplx pos);
219 
220 void Blast(Projectile *p, int t);
221 
222 void projectiles_preload(void);
223 void projectiles_free(void);
224 
225 cmplx projectile_graze_size(Projectile *p);
226 
227 #endif // IGUARD_projectile_h
228