1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
#include <stdlib.h>
#include "fb.h"
#include "helpers.h"
#include "particle.h"
#include "particles.h"
/* a "rocket" particle type */
#define ROCKET_MAX_DECAY_RATE 20
#define ROCKET_MIN_DECAY_RATE 2
#define ROCKET_MAX_LIFETIME 500
#define ROCKET_MIN_LIFETIME 300
#define ROCKETS_MAX 20
#define ROCKETS_XPLODE_MIN_SIZE 2000
#define ROCKETS_XPLODE_MAX_SIZE 8000
extern particle_ops_t burst_ops;
extern particle_ops_t spark_ops;
extern particle_ops_t xplode_ops;
static unsigned rockets_cnt;
typedef struct rocket_ctxt_t {
int decay_rate;
int longevity;
v3f_t wander;
float last_velocity; /* cache velocity to sense violent accelerations and explode when they happen */
} rocket_ctxt_t;
static int rocket_init(particles_t *particles, const particles_conf_t *conf, particle_t *p)
{
rocket_ctxt_t *ctxt = p->ctxt;
if (rockets_cnt >= ROCKETS_MAX) {
return 0;
}
rockets_cnt++;
ctxt->decay_rate = rand_within_range(ROCKET_MIN_DECAY_RATE, ROCKET_MAX_DECAY_RATE);
ctxt->longevity = rand_within_range(ROCKET_MIN_LIFETIME, ROCKET_MAX_LIFETIME);
ctxt->wander.x = (float)(rand_within_range(0, 628) - 314) / 10000.0f;
ctxt->wander.y = (float)(rand_within_range(0, 628) - 314) / 10000.0f;
ctxt->wander.z = (float)(rand_within_range(0, 628) - 314) / 10000.0f;
ctxt->wander = v3f_normalize(&ctxt->wander);
ctxt->last_velocity = p->props->velocity;
p->props->drag = 0.4;
p->props->mass = 0.8;
p->props->virtual = 0;
return 1;
}
static particle_status_t rocket_sim(particles_t *particles, const particles_conf_t *conf, particle_t *p, fb_fragment_t *f)
{
rocket_ctxt_t *ctxt = p->ctxt;
int i, n_sparks;
if (!ctxt->longevity ||
(ctxt->longevity -= ctxt->decay_rate) <= 0 ||
p->props->velocity - ctxt->last_velocity > p->props->velocity * .05) { /* explode if accelerated too hard (burst) */
int n_xplode;
/* on death we explode */
ctxt->longevity = 0;
/* add a burst shockwave particle at our location
* TODO: need way to supply particle-type-specific parameters at spawn (burst size should derive from n_xplode)
*/
particles_spawn_particle(particles, p, NULL, &burst_ops);
/* add a bunch of new explosion particles */
/* TODO: also particle-type-specific parameters, colors! rocket bursts should be able to vary the color. */
n_xplode = rand_within_range(ROCKETS_XPLODE_MIN_SIZE, ROCKETS_XPLODE_MAX_SIZE);
for (i = 0; i < n_xplode; i++) {
particle_props_t props = *p->props;
particle_ops_t *ops = &xplode_ops;
props.direction.x = ((float)(rand_within_range(0, 314159 * 2) - 314159) / 100000.0);
props.direction.y = ((float)(rand_within_range(0, 314159 * 2) - 314159) / 100000.0);
props.direction.z = ((float)(rand_within_range(0, 314159 * 2) - 314159) / 100000.0);
props.direction = v3f_normalize(&props.direction);
//props->velocity = ((float)rand_within_range(100, 200) / 100000.0);
props.velocity = ((float)rand_within_range(100, 300) / 100000.0);
particles_spawn_particle(particles, p, &props, ops);
}
return PARTICLE_DEAD;
}
#if 1
/* FIXME: this isn't behaving as intended */
p->props->direction = v3f_add(&p->props->direction, &ctxt->wander);
p->props->direction = v3f_normalize(&p->props->direction);
#endif
p->props->velocity += .00003;
/* spray some sparks behind the rocket */
n_sparks = rand_within_range(10, 40);
for (i = 0; i < n_sparks; i++) {
particle_props_t props = *p->props;
props.direction = v3f_negate(&props.direction);
props.direction.x += (float)(rand_within_range(0, 40) - 20) / 100.0;
props.direction.y += (float)(rand_within_range(0, 40) - 20) / 100.0;
props.direction.z += (float)(rand_within_range(0, 40) - 20) / 100.0;
props.direction = v3f_normalize(&props.direction);
props.velocity = (float)rand_within_range(10, 50) / 100000.0;
particles_spawn_particle(particles, p, &props, &spark_ops);
}
ctxt->last_velocity = p->props->velocity;
return PARTICLE_ALIVE;
}
static void rocket_draw(particles_t *particles, const particles_conf_t *conf, particle_t *p, int x, int y, fb_fragment_t *f)
{
rocket_ctxt_t *ctxt = p->ctxt;
if (!should_draw_expire_if_oob(particles, p, x, y, f, &ctxt->longevity))
/* kill off parts that wander off screen */
return;
fb_fragment_put_pixel_unchecked(f, x, y, 0xff0000);
}
static void rocket_cleanup(particles_t *particles, const particles_conf_t *conf, particle_t *p)
{
rockets_cnt--;
}
particle_ops_t rocket_ops = {
.context_size = sizeof(rocket_ctxt_t),
.sim = rocket_sim,
.init = rocket_init,
.draw = rocket_draw,
.cleanup = rocket_cleanup,
};
|