]> icculus.org git repositories - btb/d2x.git/blob - main/cntrlcen.c
Moved arch/sdl_* to arch/sdl
[btb/d2x.git] / main / cntrlcen.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 #ifdef HAVE_CONFIG_H
15 #include <conf.h>
16 #endif
17
18 #ifdef RCS
19 static char rcsid[] = "$Id: cntrlcen.c,v 1.2 2001-01-31 15:17:49 bradleyb Exp $";
20 #endif
21
22 #ifdef WINDOWS
23 #include "desw.h"
24 #endif
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29
30 #include "pstypes.h"
31 #include "error.h"
32 #include "mono.h"
33
34 #include "inferno.h"
35 #include "cntrlcen.h"
36 #include "game.h"
37 #include "laser.h"
38 #include "gameseq.h"
39 #include "ai.h"
40 #include "multi.h"
41 #include "wall.h"
42 #include "object.h"
43 #include "robot.h"
44 #include "vclip.h"
45 #include "fireball.h"
46 #include "endlevel.h"
47
48 //@@vms_vector controlcen_gun_points[MAX_CONTROLCEN_GUNS];
49 //@@vms_vector controlcen_gun_dirs[MAX_CONTROLCEN_GUNS];
50
51 reactor Reactors[MAX_REACTORS];
52 int Num_reactors=0;
53
54 control_center_triggers ControlCenterTriggers;
55
56 int     N_controlcen_guns;
57 int     Control_center_been_hit;
58 int     Control_center_player_been_seen;
59 int     Control_center_next_fire_time;
60 int     Control_center_present;
61
62 vms_vector      Gun_pos[MAX_CONTROLCEN_GUNS], Gun_dir[MAX_CONTROLCEN_GUNS];
63
64 void do_countdown_frame();
65
66 //      -----------------------------------------------------------------------------
67 //return the position & orientation of a gun on the control center object 
68 void calc_controlcen_gun_point(vms_vector *gun_point,vms_vector *gun_dir,object *obj,int gun_num)
69 {
70         reactor *reactor;
71         vms_matrix m;
72
73         Assert(obj->type == OBJ_CNTRLCEN);
74         Assert(obj->render_type==RT_POLYOBJ);
75
76         reactor = &Reactors[obj->id];
77
78         Assert(gun_num < reactor->n_guns);
79
80         //instance gun position & orientation
81
82         vm_copy_transpose_matrix(&m,&obj->orient);
83
84         vm_vec_rotate(gun_point,&reactor->gun_points[gun_num],&m);
85         vm_vec_add2(gun_point,&obj->pos);
86         vm_vec_rotate(gun_dir,&reactor->gun_dirs[gun_num],&m);
87 }
88
89 //      -----------------------------------------------------------------------------
90 //      Look at control center guns, find best one to fire at *objp.
91 //      Return best gun number (one whose direction dotted with vector to player is largest).
92 //      If best gun has negative dot, return -1, meaning no gun is good.
93 int calc_best_gun(int num_guns, vms_vector *gun_pos, vms_vector *gun_dir, vms_vector *objpos)
94 {
95         int     i;
96         fix     best_dot;
97         int     best_gun;
98
99         best_dot = -F1_0*2;
100         best_gun = -1;
101
102         for (i=0; i<num_guns; i++) {
103                 fix                     dot;
104                 vms_vector      gun_vec;
105
106                 vm_vec_sub(&gun_vec, objpos, &gun_pos[i]);
107                 vm_vec_normalize_quick(&gun_vec);
108                 dot = vm_vec_dot(&gun_dir[i], &gun_vec);
109
110                 if (dot > best_dot) {
111                         best_dot = dot;
112                         best_gun = i;
113                 }
114         }
115
116         Assert(best_gun != -1);         // Contact Mike.  This is impossible.  Or maybe you're getting an unnormalized vector somewhere.
117
118         if (best_dot < 0)
119                 return -1;
120         else
121                 return best_gun;
122
123 }
124
125 extern fix Player_time_of_death;                //      object.c
126
127 int     Dead_controlcen_object_num=-1;
128
129 //how long to blow up on insane
130 int Base_control_center_explosion_time=DEFAULT_CONTROL_CENTER_EXPLOSION_TIME;
131
132 int Control_center_destroyed = 0;
133 fix Countdown_timer=0;
134 int Countdown_seconds_left=0, Total_countdown_time=0;           //in whole seconds
135
136 int     Alan_pavlish_reactor_times[NDL] = {90, 60, 45, 35, 30};
137
138 //      -----------------------------------------------------------------------------
139 //      Called every frame.  If control center been destroyed, then actually do something.
140 void do_controlcen_dead_frame(void)
141 {
142         if ((Dead_controlcen_object_num != -1) && (Countdown_seconds_left > 0))
143                 if (d_rand() < FrameTime*4)
144                         create_small_fireball_on_object(&Objects[Dead_controlcen_object_num], F1_0, 1);
145
146         if (Control_center_destroyed && !Endlevel_sequence)
147                 do_countdown_frame();
148 }
149
150 #define COUNTDOWN_VOICE_TIME fl2f(12.75)
151
152 void do_countdown_frame()
153 {
154         fix     old_time;
155         int     fc, div_scale;
156
157         if (!Control_center_destroyed)  return;
158
159         #if !defined(D2_OEM) && !defined(SHAREWARE)     // get countdown in OEM and SHAREWARE only
160         //      On last level, we don't want a countdown.
161         if ((Current_mission_num == 0) && (Current_level_num == Last_level))
162     {           
163      if (!(Game_mode & GM_MULTI))
164            return;
165           if (Game_mode & GM_MULTI_ROBOTS)
166                 return;
167     }    
168         #endif
169    
170         //      Control center destroyed, rock the player's ship.
171         fc = Countdown_seconds_left;
172         if (fc > 16)
173                 fc = 16;
174
175         //      At Trainee, decrease rocking of ship by 4x.
176         div_scale = 1;
177         if (Difficulty_level == 0)
178                 div_scale = 4;
179
180         ConsoleObject->mtype.phys_info.rotvel.x += (fixmul(d_rand() - 16384, 3*F1_0/16 + (F1_0*(16-fc))/32))/div_scale;
181         ConsoleObject->mtype.phys_info.rotvel.z += (fixmul(d_rand() - 16384, 3*F1_0/16 + (F1_0*(16-fc))/32))/div_scale;
182         //      Hook in the rumble sound effect here.
183
184         old_time = Countdown_timer;
185         Countdown_timer -= RealFrameTime;
186         Countdown_seconds_left = f2i(Countdown_timer + F1_0*7/8);
187
188         if ( (old_time > COUNTDOWN_VOICE_TIME ) && (Countdown_timer <= COUNTDOWN_VOICE_TIME) )  {
189                 digi_play_sample( SOUND_COUNTDOWN_13_SECS, F3_0 );
190         }
191         if ( f2i(old_time + F1_0*7/8) != Countdown_seconds_left )       {
192                 if ( (Countdown_seconds_left>=0) && (Countdown_seconds_left<10) ) 
193                         digi_play_sample( SOUND_COUNTDOWN_0_SECS+Countdown_seconds_left, F3_0 );
194                 if ( Countdown_seconds_left==Total_countdown_time-1)
195                         digi_play_sample( SOUND_COUNTDOWN_29_SECS, F3_0 );
196         }                                               
197
198         if (Countdown_timer > 0) {
199                 fix size,old_size;
200                 size = (i2f(Total_countdown_time)-Countdown_timer) / fl2f(0.65);
201                 old_size = (i2f(Total_countdown_time)-old_time) / fl2f(0.65);
202                 if (size != old_size && (Countdown_seconds_left < (Total_countdown_time-5) ))           {                       // Every 2 seconds!
203                         //@@if (Dead_controlcen_object_num != -1) {
204                         //@@    vms_vector vp;  //,v,c;
205                         //@@    compute_segment_center(&vp, &Segments[Objects[Dead_controlcen_object_num].segnum]);
206                         //@@    object_create_explosion( Objects[Dead_controlcen_object_num].segnum, &vp, size*10, VCLIP_SMALL_EXPLOSION);
207                         //@@}
208
209                         digi_play_sample( SOUND_CONTROL_CENTER_WARNING_SIREN, F3_0 );
210                 }
211         }  else {
212                 int flash_value;
213
214                 if (old_time > 0)
215                         digi_play_sample( SOUND_MINE_BLEW_UP, F1_0 );
216
217                 flash_value = f2i(-Countdown_timer * (64 / 4)); // 4 seconds to total whiteness
218                 PALETTE_FLASH_SET(flash_value,flash_value,flash_value);
219
220                 if (PaletteBlueAdd > 64 )       {
221                 WINDOS(
222                         dd_gr_set_current_canvas(NULL),
223                         gr_set_current_canvas( NULL )
224                 );
225                 WINDOS(
226                         dd_gr_clear_canvas(BM_XRGB(31,31,31)),
227                         gr_clear_canvas(BM_XRGB(31,31,31))
228                 );                                                                                                              //make screen all white to match palette effect
229                         reset_cockpit();                                                                //force cockpit redraw next time
230                         reset_palette_add();                                                    //restore palette for death message
231                         //controlcen->MaxCapacity = Fuelcen_max_amount;
232                         //gauge_message( "Control Center Reset" );
233                         DoPlayerDead();         //kill_player();
234                 }                                                                                                                                                               
235         }
236 }
237
238 //      -----------------------------------------------------------------------------
239 //      Called when control center gets destroyed.
240 //      This code is common to whether control center is implicitly imbedded in a boss,
241 //      or is an object of its own.
242 //      if objp == NULL that means the boss was the control center and don't set Dead_controlcen_object_num
243 void do_controlcen_destroyed_stuff(object *objp)
244 {
245         int     i;
246
247    if ((Game_mode & GM_MULTI_ROBOTS) && Control_center_destroyed) 
248     return; // Don't allow resetting if control center and boss on same level
249
250         // Must toggle walls whether it is a boss or control center.
251         for (i=0;i<ControlCenterTriggers.num_links;i++)
252                 wall_toggle(&Segments[ControlCenterTriggers.seg[i]], ControlCenterTriggers.side[i]); 
253
254         // And start the countdown stuff.
255         Control_center_destroyed = 1;
256
257         //      If a secret level, delete secret.sgc to indicate that we can't return to our secret level.
258         if (Current_level_num < 0) {
259                 int     rval;
260                 #ifndef MACINTOSH
261                 rval = unlink("secret.sgc");
262                 #else
263                 rval = unlink(":Players:secret.sgc");
264                 #endif
265                 mprintf((0, "Deleting secret.sgc, return value = %i\n", rval));
266         }
267
268         if (Base_control_center_explosion_time != DEFAULT_CONTROL_CENTER_EXPLOSION_TIME)
269                 Total_countdown_time = Base_control_center_explosion_time + Base_control_center_explosion_time * (NDL-Difficulty_level-1)/2;
270         else
271                 Total_countdown_time = Alan_pavlish_reactor_times[Difficulty_level];
272
273         Countdown_timer = i2f(Total_countdown_time);
274
275         if (!Control_center_present || objp==NULL) {
276                 //Assert(objp == NULL);
277                 return;
278         }
279
280         //Assert(objp != NULL);
281
282         Dead_controlcen_object_num = objp-Objects;
283 }
284
285 int     Last_time_cc_vis_check = 0;
286
287 //      -----------------------------------------------------------------------------
288 //do whatever this thing does in a frame
289 void do_controlcen_frame(object *obj)
290 {
291         int                     best_gun_num;
292
293         //      If a boss level, then Control_center_present will be 0.
294         if (!Control_center_present)
295                 return;
296
297 #ifndef NDEBUG
298         if (!Robot_firing_enabled || (Game_suspended & SUSP_ROBOTS))
299                 return;
300 #else
301         if (!Robot_firing_enabled)
302                 return;
303 #endif
304
305         if (!(Control_center_been_hit || Control_center_player_been_seen)) {
306                 if (!(FrameCount % 8)) {                //      Do every so often...
307                         vms_vector      vec_to_player;
308                         fix                     dist_to_player;
309                         int                     i;
310                         segment         *segp = &Segments[obj->segnum];
311
312                         // This is a hack.  Since the control center is not processed by
313                         // ai_do_frame, it doesn't know to deal with cloaked dudes.  It
314                         // seems to work in single-player mode because it is actually using
315                         // the value of Believed_player_position that was set by the last
316                         // person to go through ai_do_frame.  But since a no-robots game
317                         // never goes through ai_do_frame, I'm making it so the control
318                         // center can spot cloaked dudes.  
319
320                         if (Game_mode & GM_MULTI)
321                                 Believed_player_pos = Objects[Players[Player_num].objnum].pos;
322
323                         //      Hack for special control centers which are isolated and not reachable because the
324                         //      real control center is inside the boss.
325                         for (i=0; i<MAX_SIDES_PER_SEGMENT; i++)
326                                 if (IS_CHILD(segp->children[i]))
327                                         break;
328                         if (i == MAX_SIDES_PER_SEGMENT)
329                                 return;
330
331                         vm_vec_sub(&vec_to_player, &ConsoleObject->pos, &obj->pos);
332                         dist_to_player = vm_vec_normalize_quick(&vec_to_player);
333                         if (dist_to_player < F1_0*200) {
334                                 Control_center_player_been_seen = player_is_visible_from_object(obj, &obj->pos, 0, &vec_to_player);
335                                 Control_center_next_fire_time = 0;
336                         }
337                 }                       
338
339                 return;
340         }
341
342         //      Periodically, make the reactor fall asleep if player not visible.
343         if (Control_center_been_hit || Control_center_player_been_seen) {
344                 if ((Last_time_cc_vis_check + F1_0*5 < GameTime) || (Last_time_cc_vis_check > GameTime)) {
345                         vms_vector      vec_to_player;
346                         fix                     dist_to_player;
347
348                         vm_vec_sub(&vec_to_player, &ConsoleObject->pos, &obj->pos);
349                         dist_to_player = vm_vec_normalize_quick(&vec_to_player);
350                         Last_time_cc_vis_check = GameTime;
351                         if (dist_to_player < F1_0*120) {
352                                 Control_center_player_been_seen = player_is_visible_from_object(obj, &obj->pos, 0, &vec_to_player);
353                                 if (!Control_center_player_been_seen)
354                                         Control_center_been_hit = 0;
355                         }
356                 }
357
358         }
359
360         if ((Control_center_next_fire_time < 0) && !(Player_is_dead && (GameTime > Player_time_of_death+F1_0*2))) {
361                 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
362                         best_gun_num = calc_best_gun(N_controlcen_guns, Gun_pos, Gun_dir, &Believed_player_pos);
363                 else
364                         best_gun_num = calc_best_gun(N_controlcen_guns, Gun_pos, Gun_dir, &ConsoleObject->pos);
365
366                 if (best_gun_num != -1) {
367                         int                     rand_prob, count;
368                         vms_vector      vec_to_goal;
369                         fix                     dist_to_player;
370                         fix                     delta_fire_time;
371
372                         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
373                                 vm_vec_sub(&vec_to_goal, &Believed_player_pos, &Gun_pos[best_gun_num]);
374                                 dist_to_player = vm_vec_normalize_quick(&vec_to_goal);
375                         } else {
376                                 vm_vec_sub(&vec_to_goal, &ConsoleObject->pos, &Gun_pos[best_gun_num]);
377                                 dist_to_player = vm_vec_normalize_quick(&vec_to_goal);
378                         }
379
380                         if (dist_to_player > F1_0*300)
381                         {
382                                 Control_center_been_hit = 0;
383                                 Control_center_player_been_seen = 0;
384                                 return;
385                         }
386         
387                         #ifdef NETWORK
388                         if (Game_mode & GM_MULTI)
389                                 multi_send_controlcen_fire(&vec_to_goal, best_gun_num, obj-Objects);    
390                         #endif
391                         Laser_create_new_easy( &vec_to_goal, &Gun_pos[best_gun_num], obj-Objects, CONTROLCEN_WEAPON_NUM, 1);
392
393                         //      some of time, based on level, fire another thing, not directly at player, so it might hit him if he's constantly moving.
394                         rand_prob = F1_0/(abs(Current_level_num)/4+2);
395                         count = 0;
396                         while ((d_rand() > rand_prob) && (count < 4)) {
397                                 vms_vector      randvec;
398
399                                 make_random_vector(&randvec);
400                                 vm_vec_scale_add2(&vec_to_goal, &randvec, F1_0/6);
401                                 vm_vec_normalize_quick(&vec_to_goal);
402                                 #ifdef NETWORK
403                                 if (Game_mode & GM_MULTI)
404                                         multi_send_controlcen_fire(&vec_to_goal, best_gun_num, obj-Objects);
405                                 #endif
406                                 Laser_create_new_easy( &vec_to_goal, &Gun_pos[best_gun_num], obj-Objects, CONTROLCEN_WEAPON_NUM, 0);
407                                 count++;
408                         }
409
410                         delta_fire_time = (NDL - Difficulty_level) * F1_0/4;
411                         if (Difficulty_level == 0)
412                                 delta_fire_time += F1_0/2;
413
414                         if (Game_mode & GM_MULTI) // slow down rate of fire in multi player
415                                 delta_fire_time *= 2;
416
417                         Control_center_next_fire_time = delta_fire_time;
418
419                 }
420         } else
421                 Control_center_next_fire_time -= FrameTime;
422
423 }
424
425 int Reactor_strength=-1;                //-1 mean not set by designer
426
427 //      -----------------------------------------------------------------------------
428 //      This must be called at the start of each level.
429 //      If this level contains a boss and mode != multiplayer, don't do control center stuff.  (Ghost out control center object.)
430 //      If this level contains a boss and mode == multiplayer, do control center stuff.
431 void init_controlcen_for_level(void)
432 {
433         int             i;
434         object  *objp;
435         int             cntrlcen_objnum=-1, boss_objnum=-1;
436
437         for (i=0; i<=Highest_object_index; i++) {
438                 objp = &Objects[i];
439                 if (objp->type == OBJ_CNTRLCEN)
440                 {
441                         if (cntrlcen_objnum != -1)
442                                 mprintf((1, "Warning: Two or more control centers including %i and %i\n", i, cntrlcen_objnum));
443                         else
444                                 cntrlcen_objnum = i;
445                 }
446
447                 if ((objp->type == OBJ_ROBOT) && (Robot_info[objp->id].boss_flag)) {
448 //                      mprintf((0, "Found boss robot %d.\n", objp->id));
449                         if (boss_objnum != -1)
450                                 mprintf((1, "Warning: Two or more bosses including %i and %i\n", i, boss_objnum));
451                         else
452                                 boss_objnum = i;
453                 }
454         }
455
456 #ifndef NDEBUG
457         if (cntrlcen_objnum == -1) {
458                 mprintf((1, "Warning: No control center.\n"));
459                 return;
460         }
461 #endif
462
463         if ( (boss_objnum != -1) && !((Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_ROBOTS)) ) {
464                 if (cntrlcen_objnum != -1) {
465 //                      mprintf((0, "Ghosting control center\n"));
466                         Objects[cntrlcen_objnum].type = OBJ_GHOST;
467                         Objects[cntrlcen_objnum].render_type = RT_NONE;
468                         Control_center_present = 0;
469                 }
470         } else {
471                 //      Compute all gun positions.
472                 objp = &Objects[cntrlcen_objnum];
473                 N_controlcen_guns = Reactors[objp->id].n_guns;
474                 for (i=0; i<N_controlcen_guns; i++)
475                         calc_controlcen_gun_point(&Gun_pos[i], &Gun_dir[i], objp, i);
476                 Control_center_present = 1;
477
478                 if (Reactor_strength == -1) {           //use old defaults
479                         //      Boost control center strength at higher levels.
480                         if (Current_level_num >= 0)
481                                 objp->shields = F1_0*200 + (F1_0*200/4) * Current_level_num;
482                         else
483                                 objp->shields = F1_0*200 - Current_level_num*F1_0*150;
484                 }
485                 else {
486                         objp->shields = i2f(Reactor_strength);
487                 }
488
489         }
490
491         //      Say the control center has not yet been hit.
492         Control_center_been_hit = 0;
493         Control_center_player_been_seen = 0;
494         Control_center_next_fire_time = 0;
495         
496         Dead_controlcen_object_num = -1;
497 }
498
499 void special_reactor_stuff(void)
500 {
501         mprintf((0, "Mucking with reactor countdown time.\n"));
502         if (Control_center_destroyed) {
503                 Countdown_timer += i2f(Base_control_center_explosion_time + (NDL-1-Difficulty_level)*Base_control_center_explosion_time/(NDL-1));
504                 Total_countdown_time = f2i(Countdown_timer)+2;  //      Will prevent "Self destruct sequence activated" message from replaying.
505         }
506 }
507