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.
16 * Code for controlling player movement
53 //look at keyboard, mouse, joystick, CyberMan, whatever, and set
54 //physics vars rotvel, velocity
56 fix Afterburner_charge=f1_0;
58 #define AFTERBURNER_USE_SECS 3 //use up in 3 seconds
59 #define DROP_DELTA_TIME (f1_0/15) //drop 3 per second
61 extern int Drop_afterburner_blob_flag; //ugly hack
63 extern fix Seismic_tremor_magnitude;
65 void read_flying_controls( object * obj )
67 fix forward_thrust_time;
69 Assert(FrameTime > 0); //Get MATT if hit this!
71 // this section commented and moved to the bottom by WraithX
72 // if (Player_is_dead) {
73 // vm_vec_zero(&obj->mtype.phys_info.rotthrust);
74 // vm_vec_zero(&obj->mtype.phys_info.thrust);
77 // end of section to be moved.
79 if ((obj->type!=OBJ_PLAYER) || (obj->id!=Player_num)) return; //references to player_ship require that this obj be the player
81 if (Guided_missile[Player_num] && Guided_missile[Player_num]->signature==Guided_missile_sig[Player_num]) {
83 vms_matrix rotmat,tempm;
86 //this is a horrible hack. guided missile stuff should not be
87 //handled in the middle of a routine that is dealing with the player
89 vm_vec_zero(&obj->mtype.phys_info.rotthrust);
91 rotangs.p = Controls.pitch_time / 2 + Seismic_tremor_magnitude/64;
92 rotangs.b = Controls.bank_time / 2 + Seismic_tremor_magnitude/16;
93 rotangs.h = Controls.heading_time / 2 + Seismic_tremor_magnitude/64;
95 vm_angles_2_matrix(&rotmat,&rotangs);
97 vm_matrix_x_matrix(&tempm,&Guided_missile[Player_num]->orient,&rotmat);
99 Guided_missile[Player_num]->orient = tempm;
101 speed = Weapon_info[Guided_missile[Player_num]->id].speed[Difficulty_level];
103 vm_vec_copy_scale(&Guided_missile[Player_num]->mtype.phys_info.velocity,&Guided_missile[Player_num]->orient.fvec,speed);
105 if (Game_mode & GM_MULTI)
106 multi_send_guided_info (Guided_missile[Player_num],0);
111 obj->mtype.phys_info.rotthrust.x = Controls.pitch_time;
112 obj->mtype.phys_info.rotthrust.y = Controls.heading_time;
113 obj->mtype.phys_info.rotthrust.z = Controls.bank_time;
116 // mprintf( (0, "Rot thrust = %.3f,%.3f,%.3f\n", f2fl(obj->mtype.phys_info.rotthrust.x),f2fl(obj->mtype.phys_info.rotthrust.y), f2fl(obj->mtype.phys_info.rotthrust.z) ));
118 forward_thrust_time = Controls.forward_thrust_time;
120 if (Players[Player_num].flags & PLAYER_FLAGS_AFTERBURNER)
122 if (Controls.state[afterburner]) { // player has key down
123 //if (forward_thrust_time >= 0) { //..and isn't moving backward
125 fix afterburner_scale;
126 int old_count,new_count;
128 //add in value from 0..1
129 afterburner_scale = f1_0 + min(f1_0/2,Afterburner_charge) * 2;
131 forward_thrust_time = fixmul(FrameTime,afterburner_scale); //based on full thrust
133 old_count = (Afterburner_charge / (DROP_DELTA_TIME/AFTERBURNER_USE_SECS));
135 Afterburner_charge -= FrameTime/AFTERBURNER_USE_SECS;
137 if (Afterburner_charge < 0)
138 Afterburner_charge = 0;
140 new_count = (Afterburner_charge / (DROP_DELTA_TIME/AFTERBURNER_USE_SECS));
142 if (old_count != new_count)
143 Drop_afterburner_blob_flag = 1; //drop blob (after physics called)
147 fix cur_energy,charge_up;
150 charge_up = min(FrameTime/8,f1_0 - Afterburner_charge); //recharge over 8 seconds
152 cur_energy = max(Players[Player_num].energy-i2f(10),0); //don't drop below 10
154 //maybe limit charge up by energy
155 charge_up = min(charge_up,cur_energy/10);
157 Afterburner_charge += charge_up;
159 Players[Player_num].energy -= charge_up * 100 / 10; //full charge uses 10% of energy
163 // Set object's thrust vector for forward/backward
164 vm_vec_copy_scale(&obj->mtype.phys_info.thrust,&obj->orient.fvec, forward_thrust_time );
167 vm_vec_scale_add2(&obj->mtype.phys_info.thrust,&obj->orient.rvec, Controls.sideways_thrust_time );
170 vm_vec_scale_add2(&obj->mtype.phys_info.thrust,&obj->orient.uvec, Controls.vertical_thrust_time );
172 if (obj->mtype.phys_info.flags & PF_WIGGLE)
175 fix_fastsincos(GameTime, &swiggle, NULL);
176 if (FrameTime < F1_0) // Only scale wiggle if getting at least 1 FPS, to avoid causing the opposite problem.
177 swiggle = fixmul(swiggle*20, FrameTime); //make wiggle fps-independent (based on pre-scaled amount of wiggle at 20 FPS)
178 vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&obj->orient.uvec,fixmul(swiggle,Player_ship->wiggle));
181 // As of now, obj->mtype.phys_info.thrust & obj->mtype.phys_info.rotthrust are
182 // in units of time... In other words, if thrust==FrameTime, that
183 // means that the user was holding down the Max_thrust key for the
184 // whole frame. So we just scale them up by the max, and divide by
185 // FrameTime to make them independant of framerate
187 // Prevent divide overflows on high frame rates.
188 // In a signed divide, you get an overflow if num >= div<<15
192 // Note, you must check for ft < F1_0/2, else you can get an overflow on the << 15.
193 if ((ft < F1_0/2) && (ft << 15 <= Player_ship->max_thrust)) {
194 mprintf((0, "Preventing divide overflow in controls.c for Max_thrust!\n"));
195 ft = (Player_ship->max_thrust >> 15) + 1;
198 vm_vec_scale( &obj->mtype.phys_info.thrust, fixdiv(Player_ship->max_thrust,ft) );
200 if ((ft < F1_0/2) && (ft << 15 <= Player_ship->max_rotthrust)) {
201 mprintf((0, "Preventing divide overflow in controls.c for max_rotthrust!\n"));
202 ft = (Player_ship->max_thrust >> 15) + 1;
205 vm_vec_scale( &obj->mtype.phys_info.rotthrust, fixdiv(Player_ship->max_rotthrust,ft) );
208 // moved here by WraithX
211 //vm_vec_zero(&obj->mtype.phys_info.rotthrust); // let dead players rotate, changed by WraithX
212 vm_vec_zero(&obj->mtype.phys_info.thrust); // don't let dead players move, changed by WraithX
219 extern void transfer_energy_to_shield(fix);
221 control_info Controls;
223 fix Cruise_speed = 0;
228 #define JOYSTICK_READ_TIME (F1_0 / 40) // Read joystick at 40 Hz.
230 #define JOYSTICK_READ_TIME (F1_0 / 10) // Read joystick at 10 Hz.
233 fix LastReadTime = 0;
235 fix joy_axis[JOY_MAX_AXES];
238 void reset_cruise(void)
244 static inline void button_down(control_button button)
246 Controls.count[button]++;
247 Controls.state[button] = 1;
248 Controls.time_went_down[button] = timer_get_fixed_seconds();
252 static inline void button_up(control_button button)
254 Controls.state[button] = 0;
255 Controls.time_held_down[button] += timer_get_fixed_seconds() - Controls.time_went_down[button];
259 void controls_cmd_strafe_on(int argc, char **argv) { button_down(slide_on); }
260 void controls_cmd_strafe_off(int argc, char **argv) { button_up(slide_on); }
261 void controls_cmd_bank_on(int argc, char **argv) { button_down(bank_on); }
262 void controls_cmd_bank_off(int argc, char **argv) { button_up(bank_on); }
263 void controls_cmd_attack_on(int argc, char **argv) { button_down(fire_primary); }
264 void controls_cmd_attack_off(int argc, char **argv) { button_up(fire_primary); }
265 void controls_cmd_attack2_on(int argc, char **argv) { button_down(fire_secondary); }
266 void controls_cmd_attack2_off(int argc, char **argv) { button_up(fire_secondary); }
267 void controls_cmd_rearview_on(int argc, char **argv) { button_down(rear_view); }
268 void controls_cmd_rearview_off(int argc, char **argv) { button_up(rear_view); }
269 void controls_cmd_automap_on(int argc, char **argv) { button_down(automap); }
270 void controls_cmd_automap_off(int argc, char **argv) { button_up(automap); }
271 void controls_cmd_afterburn_on(int argc, char **argv) { button_down(afterburner); }
272 void controls_cmd_afterburn_off(int argc, char **argv) { button_up(afterburner); }
273 void controls_cmd_flare(int argc, char **argv) { button_down(fire_flare); }
274 void controls_cmd_bomb(int argc, char **argv) { button_up(drop_bomb); }
275 void controls_cmd_cycle(int argc, char **argv) { button_down(cycle_primary); }
276 void controls_cmd_cycle2(int argc, char **argv) { button_up(cycle_secondary); }
277 void controls_cmd_headlight(int argc, char **argv) { button_down(headlight); }
281 void controls_init(void)
283 cmd_addcommand("+strafe", controls_cmd_strafe_on);
284 cmd_addcommand("-strafe", controls_cmd_strafe_off);
285 cmd_addcommand("+bank", controls_cmd_bank_on);
286 cmd_addcommand("-bank", controls_cmd_bank_off);
287 cmd_addcommand("+attack", controls_cmd_attack_on);
288 cmd_addcommand("-attack", controls_cmd_attack_off);
289 cmd_addcommand("+attack2", controls_cmd_attack2_on);
290 cmd_addcommand("-attack2", controls_cmd_attack2_off);
291 cmd_addcommand("+rearview", controls_cmd_rearview_on);
292 cmd_addcommand("-rearview", controls_cmd_rearview_off);
293 cmd_addcommand("+automap", controls_cmd_automap_on);
294 cmd_addcommand("-automap", controls_cmd_automap_off);
295 cmd_addcommand("+afterburner", controls_cmd_afterburn_on);
296 cmd_addcommand("-afterburner", controls_cmd_afterburn_off);
297 cmd_addcommand("flare", controls_cmd_flare);
298 cmd_addcommand("bomb", controls_cmd_bomb);
299 cmd_addcommand("cycle", controls_cmd_cycle);
300 cmd_addcommand("cycle2", controls_cmd_cycle2);
301 cmd_addcommand("headlight", controls_cmd_headlight);
305 /* Preserves pitch, heading, and states */
306 void controls_reset(void)
308 Controls.forward_thrust_time = 0;
309 Controls.sideways_thrust_time = 0;
310 Controls.vertical_thrust_time = 0;
311 Controls.bank_time = 0;
312 memset(Controls.count, 0, CONTROL_NUM_BUTTONS);
316 void controls_read_all()
319 int slide_on, bank_on;
322 int raw_joy_axis[JOY_MAX_AXES];
325 fix analog_control[7]; // indexed on control_analog
327 memset(analog_control, 0, sizeof(analog_control));
336 ctime = timer_get_fixed_seconds();
338 //--------- Read Joystick -----------
339 if ( (LastReadTime + JOYSTICK_READ_TIME > ctime) ) {
341 if ((ctime < 0) && (LastReadTime >= 0))
343 if ((ctime < 0) && (LastReadTime > 0))
345 LastReadTime = ctime;
346 } else if (Config_control_joystick.intval) {
347 LastReadTime = ctime;
348 channel_masks = joystick_read_raw_axis( JOY_ALL_AXIS, raw_joy_axis );
350 Assert(joy_num_axes <= 6); // don't have cvar mapping above 6 yet
351 for (i = 0; i < joy_num_axes; i++) {
353 if (channel_masks&(1<<i)) {
355 int joy_null_value = f2i(Config_joystick_deadzone[joy_advaxes[i].intval - 1].intval * 128);
357 raw_joy_axis[i] = joy_get_scaled_reading( raw_joy_axis[i], i );
359 if (raw_joy_axis[i] > joy_null_value)
360 raw_joy_axis[i] = ((raw_joy_axis[i] - joy_null_value) * 128) / (128 - joy_null_value);
361 else if (raw_joy_axis[i] < -joy_null_value)
362 raw_joy_axis[i] = ((raw_joy_axis[i] + joy_null_value) * 128) / (128 - joy_null_value);
365 joy_axis[i] = (raw_joy_axis[i] * FrameTime) / 128;
373 for (i = 0; i < joy_num_axes; i++)
377 if (Config_control_joystick.intval)
378 for (i = 0; i < 6; i++)
379 analog_control[joy_advaxes[i].intval] += joy_axis[i] * (joy_invert[i].intval ? -1 : 1) * Config_joystick_sensitivity[joy_advaxes[i].intval-1].value;
381 if (Config_control_mouse.intval) {
382 //--------- Read Mouse -----------
383 mouse_get_delta( &dx, &dy, &dz );
385 analog_control[mouse_axes[0].intval] += dx * FrameTime / 35 * (mouse_invert[0].intval ? -1 : 1) * Config_mouse_sensitivity[mouse_axes[0].intval-1].value;
386 analog_control[mouse_axes[1].intval] += dy * FrameTime / 25 * (mouse_invert[1].intval ? -1 : 1) * Config_mouse_sensitivity[mouse_axes[1].intval-1].value;
387 analog_control[mouse_axes[2].intval] += dz * FrameTime * (mouse_invert[2].intval ? -1 : 1) * Config_mouse_sensitivity[mouse_axes[2].intval-1].value;
390 //------------ Read pitch -----------
391 if ( !Controls.state[slide_on] ) {
392 // mprintf((0, "pitch: %7.3f %7.3f: %7.3f\n", f2fl(k4), f2fl(k6), f2fl(Controls.heading_time)));
395 kp += console_control_down_time(CONCNTL_LOOKDOWN) / (PH_SCALE * 2);
396 kp -= console_control_down_time(CONCNTL_LOOKUP) / (PH_SCALE * 2);
399 Controls.pitch_time = 0;
401 if (Controls.pitch_time < 0)
402 Controls.pitch_time = 0;
404 if (Controls.pitch_time > 0)
405 Controls.pitch_time = 0;
406 Controls.pitch_time += kp;
408 Controls.pitch_time -= analog_control[AXIS_PITCH];
411 Controls.pitch_time = 0;
413 if (!Player_is_dead) {
415 //----------- Read vertical_thrust -----------------
417 if ( Controls.state[slide_on] ) {
418 Controls.vertical_thrust_time += console_control_down_time(CONCNTL_LOOKDOWN);
419 Controls.vertical_thrust_time -= console_control_down_time(CONCNTL_LOOKUP);
420 Controls.vertical_thrust_time += analog_control[AXIS_PITCH];
423 Controls.vertical_thrust_time += console_control_down_time(CONCNTL_MOVEUP);
424 Controls.vertical_thrust_time -= console_control_down_time(CONCNTL_MOVEDOWN);
425 Controls.vertical_thrust_time += analog_control[AXIS_UPDOWN];
429 //---------- Read heading -----------
431 if ( !Controls.state[slide_on] && !Controls.state[bank_on] ) {
432 //mprintf((0, "heading: %7.3f %7.3f: %7.3f\n", f2fl(k4), f2fl(k6), f2fl(Controls.heading_time)));
435 kh -= console_control_down_time(CONCNTL_LEFT) / PH_SCALE;
436 kh += console_control_down_time(CONCNTL_RIGHT) / PH_SCALE;
439 Controls.heading_time = 0;
441 if (Controls.heading_time < 0)
442 Controls.heading_time = 0;
444 if (Controls.heading_time > 0)
445 Controls.heading_time = 0;
446 Controls.heading_time += kh;
448 Controls.heading_time += analog_control[AXIS_TURN];
451 Controls.heading_time = 0;
453 if (!Player_is_dead) {
455 //----------- Read sideways_thrust -----------------
458 Controls.sideways_thrust_time -= console_control_down_time(CONCNTL_LEFT);
459 Controls.sideways_thrust_time += console_control_down_time(CONCNTL_RIGHT);
460 Controls.sideways_thrust_time += analog_control[AXIS_TURN];
463 Controls.sideways_thrust_time -= console_control_down_time(CONCNTL_MOVELEFT);
464 Controls.sideways_thrust_time += console_control_down_time(CONCNTL_MOVERIGHT);
465 Controls.sideways_thrust_time += analog_control[AXIS_LEFTRIGHT];
469 //----------- Read bank -----------------
471 if ( Controls.state[bank_on] ) {
472 Controls.bank_time += console_control_down_time(CONCNTL_LEFT);
473 Controls.bank_time -= console_control_down_time(CONCNTL_RIGHT);
474 Controls.bank_time -= analog_control[AXIS_TURN];
477 Controls.bank_time += console_control_down_time(CONCNTL_BANKLEFT);
478 Controls.bank_time -= console_control_down_time(CONCNTL_BANKRIGHT);
479 Controls.bank_time -= analog_control[AXIS_BANK];
481 // the following "if" added by WraithX, 4/14/00
482 // done so that dead players can't move
483 if (!Player_is_dead) {
485 //----------- Read forward_thrust -------------
487 Controls.forward_thrust_time += console_control_down_time(CONCNTL_FORWARD);
488 Controls.forward_thrust_time -= console_control_down_time(CONCNTL_BACK);
489 Controls.forward_thrust_time -= analog_control[AXIS_THROTTLE];
491 //---------Read Energy->Shield key----------
493 if ((Players[Player_num].flags & PLAYER_FLAGS_CONVERTER) && console_control_state(CONCNTL_NRGSHIELD))
494 transfer_energy_to_shield(console_control_down_time(CONCNTL_NRGSHIELD));
498 //----------- Read stupid-cruise-control-type of throttle.
500 Cruise_speed += console_control_down_time(CONCNTL_CRUISEUP);
501 Cruise_speed -= console_control_down_time(CONCNTL_CRUISEDOWN);
503 if (console_control_down_count(CONCNTL_CRUISEOFF))
506 if (Cruise_speed > i2f(100))
507 Cruise_speed = i2f(100);
508 if (Cruise_speed < 0)
511 if (Controls.forward_thrust_time == 0)
512 Controls.forward_thrust_time = fixmul(Cruise_speed,FrameTime) / 100;
518 //----------- Clamp values between -FrameTime and FrameTime
519 if (FrameTime > F1_0 )
520 mprintf( (1, "Bogus frame time of %.2f seconds\n", f2fl(FrameTime) ));
522 if (Controls.pitch_time > FrameTime/2 ) Controls.pitch_time = FrameTime/2;
523 if (Controls.vertical_thrust_time > FrameTime ) Controls.vertical_thrust_time = FrameTime;
524 if (Controls.heading_time > FrameTime ) Controls.heading_time = FrameTime;
525 if (Controls.sideways_thrust_time > FrameTime ) Controls.sideways_thrust_time = FrameTime;
526 if (Controls.bank_time > FrameTime ) Controls.bank_time = FrameTime;
527 if (Controls.forward_thrust_time > FrameTime ) Controls.forward_thrust_time = FrameTime;
528 //if (Controls.afterburner_time > FrameTime ) Controls.afterburner_time = FrameTime;
530 if (Controls.pitch_time < -FrameTime/2 ) Controls.pitch_time = -FrameTime/2;
531 if (Controls.vertical_thrust_time < -FrameTime ) Controls.vertical_thrust_time = -FrameTime;
532 if (Controls.heading_time < -FrameTime ) Controls.heading_time = -FrameTime;
533 if (Controls.sideways_thrust_time < -FrameTime ) Controls.sideways_thrust_time = -FrameTime;
534 if (Controls.bank_time < -FrameTime ) Controls.bank_time = -FrameTime;
535 if (Controls.forward_thrust_time < -FrameTime ) Controls.forward_thrust_time = -FrameTime;
536 //if (Controls.afterburner_time < -FrameTime ) Controls.afterburner_time = -FrameTime;
538 //--------- Don't do anything if in debug mode
540 if ( keyd_pressed[KEY_DELETE] ) {
541 memset( &Controls, 0, sizeof(control_info) );