effect.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. /** Particle and effect engine for all sorts of glitzy effects. :)
  2. **/
  3. #include "eruta.h"
  4. #include "monolog.h"
  5. /**
  6. * An effect is a localized graphical effect
  7. */
  8. struct Effect_ {
  9. Point center;
  10. };
  11. /**
  12. * A particle is a single graphical element (that may be a point or whatever)
  13. * which effects use to visualize themselves.
  14. */
  15. struct Particle_ {
  16. Point position;
  17. };
  18. #define ERUTA_EFFECTS 64
  19. #define ERUTA_EFFECT_PARTICLES 64
  20. #define ERUTA_PARTICLES (ERUTA_EFFECTS * ERUTA_EFFECT_PARTICLES)
  21. /**
  22. * Effects holds all the data for the visual effects engine.
  23. */
  24. struct Effects_ {
  25. struct Effect_ effects [ERUTA_EFFECTS];
  26. struct Particle_ particles[ERUTA_PARTICLES];
  27. };
  28. // XXX: todo, implement this when we're ready to do so.
  29. #ifdef COMMENT_
  30. struct GyDropClass_;
  31. typedef struct GyDropClass_ GyDropClass;
  32. struct GyDropClass_ {
  33. GyDropInitFunction * init;
  34. GyDropDrawFunction * draw;
  35. GyDropUpdateFunction * update;
  36. };
  37. GyDropClass * gydropclass_init(GyDropClass * klass,
  38. GyDropUpdateFunction * update,
  39. GyDropDrawFunction * draw,
  40. GyDropInitFunction * init
  41. ){
  42. if (!klass) return NULL;
  43. klass->update = update;
  44. klass->draw = draw;
  45. klass->init = init;
  46. return klass;
  47. }
  48. /**
  49. * A Drop is a
  50. */
  51. struct GyDrop_ {
  52. GyFlow * flow;
  53. GyColor color;
  54. GyImage * image;
  55. char * text;
  56. GyDropDrawFunction * draw;
  57. GyDropUpdateFunction * update;
  58. int kind;
  59. int lifetime;
  60. GyNumber x, y;
  61. GyNumber vx, vy;
  62. GyNumber ax, ay;
  63. };
  64. /** Returns TRUE if this drop is idle(unused) or NULL, FALSE if not. */
  65. int gydrop_idle(GyDrop * drop) {
  66. if (!drop) return TRUE;
  67. if (drop->lifetime < 1) return TRUE;
  68. return FALSE;
  69. }
  70. /** Returns TRUE if this drop is still active, FALSE if not. */
  71. int gydrop_active(GyDrop * drop) {
  72. return !gydrop_idle(drop);
  73. }
  74. /** Deactivates a drop. */
  75. void gydrop_disable(GyDrop * drop) {
  76. if(!drop) return;
  77. drop->lifetime = 0;
  78. }
  79. /** Deactivates a drop if it is of the given kind. */
  80. void gydrop_disablekind(GyDrop * drop, int kind) {
  81. if (!drop) return;
  82. if (drop->kind != kind) return;
  83. gydrop_disable(drop);
  84. }
  85. GyDrop * gydrop_init(GyDrop * drop, GyFlow * flow) {
  86. if(!drop) return NULL ;
  87. drop->flow = flow;
  88. drop->lifetime = 0;
  89. drop->vx = 0;
  90. drop->vy = 0;
  91. drop->ax = 0;
  92. drop->ay = 0;
  93. drop->text = NULL;
  94. drop->image = NULL;
  95. drop->update = NULL;
  96. drop->draw = NULL;
  97. return drop;
  98. }
  99. GyDrop * gydrop_update(GyDrop * drop, int time) {
  100. if (gydrop_idle(drop)) return NULL;
  101. // automatically reduce lifetime
  102. drop->lifetime -= time;
  103. if (!drop->update) {
  104. LOG_WARNING("cannot update drop!\n");
  105. return NULL;
  106. }
  107. // skip drops that are not active or not set up correctly.
  108. drop->update(drop, time);
  109. return drop;
  110. }
  111. // Draws the drop as a small filled rectangle
  112. GyDrop * gydrop_drawslab(GyDrop * drop, GyImage * im) {
  113. gyimage_slab(im, drop->x, drop->y, 5, 5, drop->color);
  114. return drop;
  115. }
  116. // Draws the drop as a small filled circle
  117. GyDrop * gydrop_drawdisk(GyDrop * drop, GyImage * im) {
  118. gyimage_slab(im, drop->x, drop->y, 3, 3, drop->color);
  119. return drop;
  120. }
  121. // Draws the drop as snow
  122. GyDrop * gydrop_drawsnow(GyDrop * drop, GyImage * im) {
  123. gyimage_disk(im, drop->x, drop->y, 3, drop->color);
  124. // gyimage_slab(im, drop->x, drop->y, 3, 3, drop->color);
  125. return drop;
  126. }
  127. // Draws a generic drop.
  128. GyDrop * gydrop_draw(GyDrop * drop, GyImage * im) {
  129. // Skip drops that are not active or not set up correctly.
  130. if (gydrop_idle(drop)) return NULL;
  131. // Draw images for drops that have an image
  132. if (drop->image) {
  133. gyimage_blit(im, drop->x, drop->y, drop->image);
  134. return drop;
  135. }
  136. // Use the draw function for drops that have one
  137. if (drop->draw) {
  138. drop->draw(drop, im);
  139. return drop;
  140. }
  141. // Otherwise just draw a filled rectangle.
  142. gydrop_drawslab(drop, im);
  143. return drop;
  144. }
  145. /* GyWell is a a point location source where particles are generated.
  146. It's in other words a kind of initial configuration for particles. */
  147. struct GyWell_ {
  148. int kind;
  149. GyColor color;
  150. GyNumber x, y;
  151. GyImage * image;
  152. char * text;
  153. GyDropDrawFunction * draw;
  154. GyDropUpdateFunction * update;
  155. GyDropInitFunction * init;
  156. };
  157. /* GyFlow models a particle engine. */
  158. struct GyFlow_ {
  159. GyCamera * camera;
  160. size_t size;
  161. GyDrop * particles;
  162. size_t active;
  163. int screenw, screenh;
  164. };
  165. GyFlow * gyflow_init(GyFlow * flow, size_t size) {
  166. int index;
  167. if(!flow) return NULL;
  168. flow->size = size;
  169. flow->active = 0;
  170. flow->screenw = 640;
  171. flow->screenh = 480;
  172. flow->particles = GY_MALLOC(sizeof(GyDrop) * flow->size);
  173. if(!flow->particles) {
  174. flow->size = 0;
  175. return NULL;
  176. }
  177. for(index = 0; index < flow->size; index++) {
  178. gydrop_init(flow->particles+index, flow);
  179. }
  180. return flow;
  181. }
  182. GyFlow * gyflow_done(GyFlow * flow) {
  183. if(!flow) { return NULL; }
  184. flow->size = 0;
  185. flow->active = 0;
  186. GY_FREE(flow->particles);
  187. flow->particles = NULL;
  188. return flow;
  189. }
  190. GyFlow * gyflow_make(size_t size) {
  191. GyFlow * flow = GY_ALLOCATE(GyFlow);
  192. return gyflow_init(flow, size);
  193. }
  194. void gyflow_free(GyFlow * flow) {
  195. gyflow_done(flow);
  196. GY_FREE(flow);
  197. }
  198. void gyflow_update(GyFlow * flow, int time) {
  199. int index;
  200. // XXX: should perhaps be limited to active particles only?
  201. for(index = 0; index < flow->size ; index++) {
  202. gydrop_update(flow->particles+index, time);
  203. }
  204. }
  205. void gyflow_draw(GyFlow * flow, GyImage * im) {
  206. int index;
  207. for(index = 0; index < flow->size; index++) {
  208. gydrop_draw(flow->particles+index, im);
  209. }
  210. }
  211. // Automatically integrate motion based upon speed
  212. // To be called in your custom callback, etc.
  213. GyDrop * gydrop_updatespeed(GyDrop * drop, int time) {
  214. drop->x += GY_DROP_DIVIDE(drop->vx * time);
  215. drop->y += GY_DROP_DIVIDE(drop->vy * time);
  216. return drop;
  217. }
  218. // Automatically update speed based on accelleration
  219. // To be called in your custom callback, etc.
  220. GyDrop * gydrop_updateaccell(GyDrop * drop, int time) {
  221. drop->vx += GY_DROP_DIVIDE(drop->ax * time);
  222. drop->vy += GY_DROP_DIVIDE(drop->ay * time);
  223. return drop;
  224. }
  225. GyDrop * gydrop_initsnow(GyDrop * drop, GyWell * well) {
  226. drop->x = gyrandom(0, drop->flow->screenw);
  227. drop->y = gyrandom(0, drop->flow->screenh);
  228. drop->ax = 0;
  229. drop->ay = GY_DROP_MULTIPLY(100);
  230. drop->vx = 0;
  231. drop->vy = gyrandom(GY_DROP_MULTIPLY(1), GY_DROP_MULTIPLY(3));
  232. drop->lifetime = gyrandom(10, 100);
  233. return drop;
  234. }
  235. GyDrop * gydrop_updatesnow(GyDrop * drop, int time) {
  236. if (!(drop->lifetime % 5)) {
  237. drop->vx = GY_DROP_MULTIPLY(gyrandom(-1, 1));
  238. } else {
  239. drop->vx = 0;
  240. }
  241. gydrop_updateaccell(drop, time);
  242. return gydrop_updatespeed(drop, time);
  243. }
  244. /*
  245. # Draws a single particle.
  246. # Override this in child classes.
  247. def render_particle(x, y, surface, particle, ci)
  248. x2 = x
  249. y2 = y + rand(5) + 2
  250. l = 7 + rand(3)
  251. w = 1 # + rand(2)
  252. # surface.draw_line(x, y, x2, y2, ci)
  253. surface.draw_rect(x, y, w, l, ci, true)
  254. end
  255. end
  256. class Blood < Stream
  257. BLOOD_G = DUST_TIME + 1
  258. def initialize(color = [255, 0, 0], amount = 15, max = 100)
  259. super
  260. end
  261. # Sets up an individual particle. Overide this in child clases.
  262. def init_particle()
  263. x = 0.0
  264. y = 0.0
  265. vx = (rand(1001)- 500) / 1000.0
  266. vy = 0
  267. g = 0.00000001
  268. # Speeds set randomly for this kind of stream
  269. time = 0 # rand(100)
  270. # Randomly shorten their lifetime
  271. return Stardust.make_particle(x, y, vx, vy, time, g)
  272. end
  273. def update_particle(particle, time)
  274. time = particle[DUST_TIME]
  275. if time < @timemax * 0.1
  276. particle[DUST_Y] -= (rand(100) + 1) / 25.0
  277. elsif time < @timemax * 0.2
  278. particle[DUST_Y] += (rand(100) + 1) / 25.0
  279. particle[DUST_VX] /= 2
  280. else
  281. particle[DUST_VY] = 0
  282. particle[DUST_VX] = 0
  283. end
  284. # particle[BLOOD_G]
  285. # particle[DUST_VY] = 2 if particle[DUST_VY] > -2
  286. if time > @timemax
  287. return false
  288. end
  289. return true
  290. end
  291. # Draws a single particle.
  292. # Override this in child classes.
  293. def render_particle(x, y, surface, particle, ci)
  294. surface.draw_circle(x, y, 2, ci, true, 0.5)
  295. end
  296. end
  297. class Slash < Stream
  298. SLASH_SX = DUST_TIME + 1
  299. SLASH_SY = DUST_TIME + 2
  300. SLASH_W = DUST_TIME + 3
  301. def initialize(color = [255, 0, 0], amount = 1, max = 30)
  302. super
  303. end
  304. # Sets up an individual particle. Overide this in child clases.
  305. def init_particle()
  306. x = 0.0
  307. y = 0.0
  308. vx = 0.5
  309. vy = 1
  310. g = 0.00000001
  311. # Speeds set randomly for this kind of stream
  312. time = 0 # rand(100)
  313. # Randomly shorten their lifetime
  314. return Stardust.make_particle(x, y, vx, vy, time, 0.0, 0.0, 1)
  315. end
  316. def update_particle(particle, time)
  317. time = particle[DUST_TIME]
  318. # particle[BLOOD_G]
  319. # particle[DUST_VY] = 2 if particle[DUST_VY] > -2
  320. if time > @timemax
  321. return false
  322. end
  323. return true
  324. end
  325. # Draws a single particle.
  326. # Override this in child classes.
  327. def render_particle(x, y, surface, particle, ci)
  328. dx = x - particle[DUST_X]
  329. dy = y - particle[DUST_Y]
  330. sx = dx + particle[SLASH_SX]
  331. sy = dy + particle[SLASH_SY]
  332. sw = particle[SLASH_W]
  333. surface.draw_line(x, y, sx, sy, ci, true)
  334. surface.draw_line(x, y+sw, sx, sy+sw, ci, true)
  335. surface.draw_line(x, y-sw, sx, sy-sw, ci, true)
  336. end
  337. end
  338. */
  339. GyWell * gywell_init(GyWell * well, int kind,
  340. int x, int y, GyColor color,
  341. GyImage * im, char * text) {
  342. if(!well) return NULL;
  343. well->x = x;
  344. well->y = y;
  345. well->color = color;
  346. well->kind = kind;
  347. well->text = text;
  348. well->image = im;
  349. well->draw = gydrop_drawslab;
  350. well->update = gydrop_updatespeed;
  351. well->init = gydrop_initsnow;
  352. switch(well->kind) {
  353. case GyFlowSnow:
  354. well->draw = gydrop_drawsnow;
  355. well->update = gydrop_updatesnow;
  356. return well;
  357. default:
  358. LOG_WARNING("Unknown effect type %d:", well->kind);
  359. return well;
  360. }
  361. return well;
  362. }
  363. /** Initializes a drop with info from a Well */
  364. GyDrop * gydrop_initwell(GyDrop * drop, GyFlow * flow, GyWell *well)
  365. {
  366. if(!gydrop_init(drop, flow)) return NULL;
  367. if(!well) return NULL;
  368. drop->x = well->x;
  369. drop->y = well->y;
  370. drop->color = well->color;
  371. drop->image = well->image;
  372. drop->text = well->text;
  373. drop->kind = well->kind;
  374. drop->update = well->update;
  375. drop->draw = well->draw;
  376. if(well->init) {
  377. well->init(drop, well);
  378. }
  379. return drop;
  380. }
  381. /**
  382. * Gets the next idle (unused) drop from the particle engine.
  383. * Returns NULL if no idle drop is available.
  384. */
  385. GyDrop * gyflow_idle(GyFlow * flow) {
  386. int index;
  387. if(!flow) return NULL;
  388. for (index = 0; index < flow->size; index++) {
  389. GyDrop * drop = flow->particles + index;
  390. if(gydrop_idle(drop)) return drop;
  391. }
  392. return NULL;
  393. }
  394. /**
  395. * Activates amount Drops (particles) in the GyFlow flow.
  396. * The particle will be of kind kind, and will be placed, depending on the
  397. * kind, at x a,d y, and displayed using given color, image or text
  398. */
  399. GyFlow * gyflow_activate(GyFlow * flow,
  400. int amount, int kind, int x, int y, GyColor color,
  401. GyImage * im, char * text) {
  402. int index;
  403. GyWell well;
  404. if(!gywell_init(&well, kind, x, y, color, im, text)) {
  405. return NULL;
  406. }
  407. for(index = 0; index < amount; index++) {
  408. GyDrop * drop;
  409. drop = gyflow_idle(flow);
  410. if(!drop) {
  411. LOG_WARNING("No idle drops: %d!\n", amount );
  412. return NULL;
  413. }
  414. gydrop_initwell(drop, flow, &well);
  415. }
  416. return flow;
  417. }
  418. /**
  419. * Deactivates all Drops of the given kind of effect.
  420. */
  421. GyFlow * gyflow_disablekind(GyFlow * flow, int kind) {
  422. int index;
  423. for (index = 0; index < flow->size; index++) {
  424. GyDrop * drop = flow->particles + index;
  425. gydrop_disablekind(drop, kind);
  426. }
  427. return flow;
  428. }
  429. #endif