]> icculus.org git repositories - taylor/freespace2.git/blob - src/starfield/supernova.cpp
Initial revision
[taylor/freespace2.git] / src / starfield / supernova.cpp
1 /*
2  * $Logfile: /Freespace2/code/Starfield/Supernova.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Include file for nebula stuff
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:10  root
11  * Initial revision
12  *
13  * 
14  * 5     9/09/99 11:40p Dave
15  * Handle an Assert() in beam code. Added supernova sounds. Play the right
16  * 2 end movies properly, based upon what the player did in the mission.
17  * 
18  * 4     9/03/99 1:32a Dave
19  * CD checking by act. Added support to play 2 cutscenes in a row
20  * seamlessly. Fixed super low level cfile bug related to files in the
21  * root directory of a CD. Added cheat code to set campaign mission # in
22  * main hall.
23  * 
24  * 3     7/31/99 4:15p Dave
25  * Fixed supernova particle velocities. Handle OBJ_NONE in target
26  * monitoring view. Properly use objectives notify gauge colors.
27  * 
28  * 2     7/21/99 8:10p Dave
29  * First run of supernova effect.
30  *  
31  *
32  * $NoKeywords: $
33  */
34
35 #include "freespace.h"
36 #include "vecmat.h"
37 #include "object.h"
38 #include "lighting.h"
39 #include "starfield.h"
40 #include "supernova.h"
41 #include "particle.h"
42 #include "ship.h"
43 #include "timer.h"
44 #include "model.h"
45 #include "popupdead.h"
46 #include "key.h"
47 #include "missioncampaign.h"
48 #include "gamesequence.h"
49 #include "gamesnd.h"
50 #include "sound.h"
51
52
53 // --------------------------------------------------------------------------------------------------------------------------
54 // SUPERNOVA DEFINES/VARS
55 //
56
57 // supernova time 1
58 #define SUPERNOVA_SOUND_1_TIME                                  15.0f
59 #define SUPERNOVA_SOUND_2_TIME                                  5.0f
60 int Supernova_sound_1_played = 0;
61 int Supernova_sound_2_played = 0;
62
63 // countdown for supernova
64 float Supernova_time_total = -1.0f;
65 float Supernova_time = -1.0f;
66 int Supernova_finished = 0;
67 int Supernova_popup = 0;
68 float Supernova_fade_to_white = 0.0f;
69 int Supernova_particle_stamp = -1;
70
71 // supernova camera pos
72 vector Supernova_camera_pos;
73 matrix Supernova_camera_orient;
74
75 int Supernova_status = SUPERNOVA_NONE;
76
77 // --------------------------------------------------------------------------------------------------------------------------
78 // SUPERNOVA FUNCTIONS
79 //
80
81 // level init
82 void supernova_level_init()
83 {
84         Supernova_time_total = -1.0f;
85         Supernova_time = -1.0f; 
86         Supernova_finished = 0;
87         Supernova_fade_to_white = 0.0f;
88         Supernova_popup = 0;
89         Supernova_particle_stamp = -1;
90
91         Supernova_sound_1_played = 0;
92         Supernova_sound_2_played = 0;
93
94         Supernova_status = SUPERNOVA_NONE;
95 }
96
97 // start a supernova
98 void supernova_start(int seconds)
99 {
100         // bogus time
101         if((float)seconds < SUPERNOVA_CUT_TIME){
102                 return;
103         }
104
105         // no supernova in multiplayer
106         if(Game_mode & GM_MULTIPLAYER){
107                 return;
108         }
109
110         // only good if we have one sun
111         if(Num_suns != 1){
112                 return;
113         }
114         
115         Supernova_time_total = (float)seconds;
116         Supernova_time = (float)seconds;
117         Supernova_finished = 0;
118         Supernova_fade_to_white = 0.0f;
119         Supernova_popup = 0;
120         Supernova_particle_stamp = -1;
121
122         Supernova_status = SUPERNOVA_STARTED;   
123 }
124
125 int sn_particles = 100;
126 DCF(sn_part, "")
127 {
128         dc_get_arg(ARG_INT);
129         sn_particles = Dc_arg_int;
130 }
131 void supernova_do_particles()
132 {       
133         int idx;
134         vector a, b, ta, tb;
135         vector norm, sun_temp;
136
137         // no player ship
138         if((Player_obj == NULL) || (Player_ship == NULL)){
139                 return;
140         }
141
142         // timestamp
143         if((Supernova_particle_stamp == -1) || timestamp_elapsed(Supernova_particle_stamp)){
144                 Supernova_particle_stamp = timestamp(sn_particles);
145
146                 // get particle norm            
147                 stars_get_sun_pos(0, &sun_temp);
148                 vm_vec_add2(&sun_temp, &Player_obj->pos);
149                 vm_vec_sub(&norm, &Player_obj->pos, &sun_temp);
150                 vm_vec_normalize(&norm);
151
152                 particle_emitter whee;
153                 whee.max_life = 1.0f;
154                 whee.min_life = 0.6f;
155                 whee.max_vel = 50.0f;
156                 whee.min_vel = 25.0f;
157                 whee.normal_variance = 0.75f;
158                 whee.num_high = 5;
159                 whee.num_low = 2;
160                 whee.min_rad = 0.5f;
161                 whee.max_rad = 1.25f;           
162
163                 // emit
164                 for(idx=0; idx<10; idx++){                      
165                         submodel_get_two_random_points(Player_ship->modelnum, 0, &ta, &tb);
166
167                         // rotate into world space
168                         vm_vec_unrotate(&a, &ta, &Player_obj->orient);                  
169                         vm_vec_add2(&a, &Player_obj->pos);                      
170                         whee.pos = a;
171                         whee.vel = norm;
172                         vm_vec_scale(&whee.vel, 30.0f);                                         
173                         vm_vec_add2(&whee.vel, &Player_obj->phys_info.vel);                     
174                         whee.normal = norm;                     
175                         particle_emit(&whee, PARTICLE_FIRE, (uint)-1);
176
177                         vm_vec_unrotate(&b, &tb, &Player_obj->orient);
178                         vm_vec_add2(&b, &Player_obj->pos);
179                         whee.pos = b;                   
180                         particle_emit(&whee, PARTICLE_FIRE, (uint)-1);
181                 }
182         }
183 }
184
185 // call once per frame
186 float sn_shudder = 0.45f;
187 DCF(sn_shud, "")
188 {
189         dc_get_arg(ARG_FLOAT);
190         sn_shudder = Dc_arg_float;
191 }
192
193 void supernova_process()
194 {       
195         int sn_stage;   
196
197         // if the supernova is running
198         sn_stage = supernova_active();
199         if(sn_stage){
200                 Supernova_time -= flFrametime;
201
202                 // sound stuff
203                 if((Supernova_time <= SUPERNOVA_SOUND_1_TIME) && !Supernova_sound_1_played){
204                         Supernova_sound_1_played = 1;
205                         snd_play(&Snds[SND_SUPERNOVA_1], 0.0f, 1.0f, SND_PRIORITY_MUST_PLAY);
206                 }
207                 if((Supernova_time <= SUPERNOVA_SOUND_2_TIME) && !Supernova_sound_2_played){
208                         Supernova_sound_2_played = 1;
209                         snd_play(&Snds[SND_SUPERNOVA_2], 0.0f, 1.0f, SND_PRIORITY_MUST_PLAY);
210                 }
211
212                 // if we've crossed from stage 1 to stage 2 kill all particles and stick a bunch on the player ship
213                 if((sn_stage == 1) && (supernova_active() == 2)){
214                         // first kill all active particles so we have a bunch of free ones
215                         particle_kill_all();                            
216                 }               
217
218                 // if we're in stage 2, emit particles
219                 if((sn_stage >= 2) && (sn_stage != 5)){
220                         supernova_do_particles();
221                 }
222
223                 // if we've got negative. the supernova is done
224                 if(Supernova_time < 0.0f){                                                      
225                         Supernova_finished = 1;
226                         Supernova_fade_to_white += flFrametime;
227
228                         // start the dead popup
229                         if(Supernova_fade_to_white >= SUPERNOVA_FADE_TO_WHITE_TIME){
230                                 if(!Supernova_popup){
231                                         // main freespace 2 campaign? if so - end it now
232                                         if((Game_mode & GM_CAMPAIGN_MODE) && !stricmp(Campaign.filename, "freespace2") && Campaign_ended_in_mission){
233                                                 gameseq_post_event(GS_EVENT_END_CAMPAIGN);
234                                         } else {
235                                                 popupdead_start();
236                                         }
237                                         Supernova_popup = 1;
238                                 }
239                                 Supernova_finished = 2;
240                         }
241                 }
242         }                       
243 }
244
245 // is there a supernova active
246 int supernova_active()
247 {
248         // if the supernova has "finished". fade to white and dead popup
249         if(Supernova_finished == 1){
250                 Supernova_status = SUPERNOVA_HIT;
251                 return 4;
252         }
253         if(Supernova_finished == 2){
254                 Supernova_status = SUPERNOVA_HIT;
255                 return 5;
256         }
257
258         // no supernova
259         if((Supernova_time_total <= 0.0f) || (Supernova_time <= 0.0f)){
260                 return 0;
261         }       
262
263         // final stage, 
264         if(Supernova_time < (SUPERNOVA_CUT_TIME - SUPERNOVA_CAMERA_MOVE_TIME)){         
265                 Supernova_status = SUPERNOVA_HIT;
266                 return 3;
267         }       
268
269         // 2nd stage
270         if(Supernova_time < SUPERNOVA_CUT_TIME){
271                 Supernova_status = SUPERNOVA_HIT;
272                 return 2;
273         }
274
275         // first stage
276         return 1;
277 }
278
279 // time left before the supernova hits
280 float supernova_time_left()
281 {
282         return Supernova_time;
283 }
284
285 // pct complete the supernova (0.0 to 1.0)
286 float supernova_pct_complete()
287 {
288         // bogus
289         if(!supernova_active()){
290                 return -1.0f;
291         }
292
293         return (Supernova_time_total - Supernova_time) / Supernova_time_total;
294 }
295
296 // if the camera should cut to the "you-are-toast" cam
297 int supernova_camera_cut()
298 {
299         // if we're not in a supernova
300         if(!supernova_active()){
301                 return 0;
302         }
303
304         // if we're past the critical time
305         if(Supernova_time <= SUPERNOVA_CUT_TIME){
306                 return 1;
307         }
308
309         // no cut yet
310         return 0;
311 }
312
313 // apply a shake to the orient matrix
314 void supernova_apply_shake(matrix *eye_orient, float intensity)
315 {       
316         angles  tangles;
317
318         tangles.p = 0.0f;
319         tangles.h = 0.0f;
320         tangles.b = 0.0f;       
321
322         // Make eye shake due to engine wash            
323         int r1 = myrand();
324         int r2 = myrand();
325         tangles.p += 0.07f * intensity * (float) (r1-RAND_MAX/2)/RAND_MAX;
326         tangles.h += 0.07f * intensity * (float) (r2-RAND_MAX/2)/RAND_MAX;                      
327
328         matrix  tm, tm2;
329         vm_angles_2_matrix(&tm, &tangles);
330         Assert(vm_vec_mag(&tm.fvec) > 0.0f);
331         Assert(vm_vec_mag(&tm.rvec) > 0.0f);
332         Assert(vm_vec_mag(&tm.uvec) > 0.0f);
333         vm_matrix_x_matrix(&tm2, eye_orient, &tm);
334         *eye_orient = tm2;      
335 }
336
337 // get view params from supernova
338 float sn_distance = 300.0f;                             // shockwave moving at 1000/ms ?
339 float sn_cam_distance = 25.0f;
340 DCF(sn_dist, "")
341 {
342         dc_get_arg(ARG_FLOAT);
343         sn_distance = Dc_arg_float;
344 }
345 DCF(sn_cam_dist, "")
346 {
347         dc_get_arg(ARG_FLOAT);
348         sn_cam_distance = Dc_arg_float;
349 }
350 void supernova_set_view(vector *eye_pos, matrix *eye_orient)
351 {
352         vector at;
353         vector sun_temp, sun;
354         vector move;
355         vector view;
356         float cut_pct = 1.0f - (Supernova_time / SUPERNOVA_CUT_TIME);           
357         
358         // set the controls for the heart of the sun    
359         stars_get_sun_pos(0, &sun_temp);
360         vm_vec_add2(&sun_temp, &Player_obj->pos);
361         vm_vec_sub(&sun, &sun_temp, &Player_obj->pos);
362         vm_vec_normalize(&sun);
363
364         // always set the camera pos
365         matrix whee;
366         vm_vector_2_matrix(&whee, &move, NULL, NULL);
367         vm_vec_scale_add(&Supernova_camera_pos, &Player_obj->pos, &whee.rvec, sn_cam_distance);
368         vm_vec_scale_add2(&Supernova_camera_pos, &whee.uvec, 30.0f);
369         *eye_pos = Supernova_camera_pos;
370
371         // if we're no longer moving the camera
372         if(Supernova_time < (SUPERNOVA_CUT_TIME - SUPERNOVA_CAMERA_MOVE_TIME)){
373                 // *eye_pos = Supernova_camera_pos;
374                 *eye_orient = Supernova_camera_orient;          
375
376                 // shake the eye                
377                 supernova_apply_shake(eye_orient, cut_pct * sn_shudder);
378
379                 return;
380         } 
381         // otherwise move it
382         else {
383                 // get a vector somewhere between the supernova shockwave and the player ship   
384                 at = Player_obj->pos;
385                 vm_vec_scale_add2(&at, &sun, sn_distance);
386                 vm_vec_sub(&move, &Player_obj->pos, &at);
387                 vm_vec_normalize(&move);
388                                 
389                 // linearly move towards the player pos
390                 float pct = ((SUPERNOVA_CUT_TIME - Supernova_time) / SUPERNOVA_CAMERA_MOVE_TIME);
391                 vm_vec_scale_add2(&at, &move, sn_distance * pct);       
392
393                 *eye_pos = Supernova_camera_pos;
394                 vm_vec_sub(&view, &at, eye_pos);
395                 vm_vec_normalize(&view);
396                 vm_vector_2_matrix(&Supernova_camera_orient, &view, NULL, NULL);
397                 *eye_orient = Supernova_camera_orient;
398         }       
399
400         // shake the eye
401         supernova_apply_shake(eye_orient, cut_pct * sn_shudder);
402 }