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