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