FS2_Open
Open source remastering of the Freespace 2 engine
modelanim.cpp
Go to the documentation of this file.
1 /*
2  * Created by Mike "Bobboau" Abegg for the FreeSpace2 Source Code Project.
3  * You may not sell or otherwise commercially exploit the source or things you
4  * create based on the source.
5  */
6 
7 
8 
9 
10 #include "globalincs/linklist.h"
11 #include "io/timer.h"
12 #include "model/model.h"
13 #include "model/modelanim.h"
14 #include "network/multi.h"
15 #include "ship/ship.h"
16 
17 
18 extern float flFrametime;
19 
21 
23 {
24  "initial",
25  "docking-stage-1",
26  "docking-stage-2",
27  "docking-stage-3",
28  "docked",
29  "primary-bank",
30  "secondary-bank",
31  "fighterbay",
32  "afterburner",
33  "turret-firing",
34  "scripted",
35  "turret-fired"
36 };
37 
39 {
40  int i;
41 
42  // standard match
43  for (i = 0; i < MAX_TRIGGER_ANIMATION_TYPES; i++) {
44  if ( !strnicmp(p, Animation_type_names[i], strlen(Animation_type_names[i])) )
45  return i;
46  }
47 
48  // Goober5000 - misspelling
49  if ( !strnicmp(p, "inital", 6) || !strnicmp(p, "\"inital\"", 8) ) {
50  Warning(LOCATION, "Spelling error in table file. Please change \"inital\" to \"initial\".");
51  return TRIGGER_TYPE_INITIAL;
52  }
53 
54  // Goober5000 - deprecation
55  if ( !strnicmp(p, "docking", 7) || !strnicmp(p, "\"docking\"", 9) ) {
56  mprintf(("The \"docking\" animation type name is deprecated. Specify \"%s\" instead.\n", Animation_type_names[TRIGGER_TYPE_DOCKING_STAGE_2]));
58  } else if ( !strnicmp(p, "primary_bank", 12) || !strnicmp(p, "\"primary_bank\"", 14) ) {
59  mprintf(( "The \"primary_bank\" animation type name is deprecated. Specify \"%s\" instead.\n", Animation_type_names[TRIGGER_TYPE_PRIMARY_BANK]));
61  } else if ( !strnicmp(p, "secondary_bank", 14) || !strnicmp(p, "\"secondary_bank\"", 16) ) {
62  mprintf(("The \"secondary_bank\" animation type name is deprecated. Specify \"%s\" instead.\n", Animation_type_names[TRIGGER_TYPE_SECONDARY_BANK]));
64  } else if ( !strnicmp(p, "door", 4) || !strnicmp(p, "\"door\"", 6) ) {
65  mprintf(("The \"door\" animation type name is deprecated. Specify \"%s\" instead.\n", Animation_type_names[TRIGGER_TYPE_DOCK_BAY_DOOR]));
67  } else if ( !strnicmp(p, "turret firing", 13) || !strnicmp(p, "\"turret firing\"", 15) ) {
68  mprintf(("The \"turret firing\" animation type name is deprecated. Specify \"%s\" instead.\n", Animation_type_names[TRIGGER_TYPE_TURRET_FIRING]));
70  }
71 
72  // Goober5000 - with quotes
73  for (i = 0; i < MAX_TRIGGER_ANIMATION_TYPES; i++) {
74  char quoted_name[NAME_LENGTH + 2];
75  strcpy(quoted_name, "\"");
76  strcat(quoted_name, Animation_type_names[i]);
77  strcat(quoted_name, "\"");
78 
79  if ( !strnicmp(p, quoted_name, strlen(quoted_name)) ) {
80  mprintf(( "Old usage warning: Please remove quotes from animation type %s.\n", quoted_name));
81  return i;
82  }
83  }
84 
85  return TRIGGER_TYPE_NONE;
86 }
87 
88 
95 {
96  instance = q->instance;
97 
98  nprintf(("ModelAnim", "Starting animation type %i at %i ...\n", q->type, timestamp()));
99 
100  current_snd = -2;
101  current_snd_index = start_sound = q->start_sound;
102  loop_sound = q->loop_sound;
103  end_sound = q->end_sound;
104  snd_rad = q->snd_rad;
105 
106  for (int axis = 0; axis < 3; axis++) {
107  direction.a1d[axis] = (end_angle.a1d[axis] + q->angle.a1d[axis]) - current_ang.a1d[axis];
108 
109  if (direction.a1d[axis])
110  direction.a1d[axis] /= fabs(direction.a1d[axis]);
111 
112  if (!q->absolute) {
113  end_angle.a1d[axis] = q->angle.a1d[axis] + end_angle.a1d[axis];
114  }
115 
116  rot_vel.a1d[axis] = q->vel.a1d[axis] * direction.a1d[axis];
117  rot_accel.a1d[axis] = q->accel.a1d[axis] * direction.a1d[axis];
118 
119  if (q->accel.a1d[axis] == 0.0f)
120  slow_angle.a1d[axis] = end_angle.a1d[axis];
121  else
122  slow_angle.a1d[axis] = end_angle.a1d[axis] + (((q->vel.a1d[axis] * q->vel.a1d[axis]) / (2.0f * q->accel.a1d[axis])) * direction.a1d[axis]);
123  }
124 
125  nprintf(("ModelAnim", "Dir=[%f, %f, %f], End=[%f, %f, %f], Vel=[%f, %f, %f], Accel=[%f, %f, %f], Slow=[%f, %f, %f]\n", direction.a1d[0], direction.a1d[1], direction.a1d[2],
127  slow_angle.a1d[0], slow_angle.a1d[1], slow_angle.a1d[2]));
128 
129  has_started = true;
130  end_time = q->end_time;
131 }
132 
134 {
135  // Vasudan Admiral - And now actually APPLY the angle data to the subobjects themselves!
136  submodel_angles->p = current_ang.xyz.x;
137  submodel_angles->h = current_ang.xyz.y;
138  submodel_angles->b = current_ang.xyz.z;
139 
140  if (submodel_angles->p >= PI2)
141  submodel_angles->p -= PI2;
142  else if (submodel_angles->p < 0.0f)
143  submodel_angles->p += PI2;
144 
145  if (submodel_angles->h >= PI2)
146  submodel_angles->h -= PI2;
147  else if (submodel_angles->h < 0.0f)
148  submodel_angles->h += PI2;
149 
150  if (submodel_angles->b >= PI2)
151  submodel_angles->b -= PI2;
152  else if (submodel_angles->b < 0.0f)
153  submodel_angles->b += PI2;
154 }
155 
157 {
158  for (int axis = 0; axis < 3; axis++)
159  {
160  current_ang.a1d[axis] = q->angle.a1d[axis];
161  }
162 }
163 
165 {
166  for (int axis = 0; axis < 3; axis++)
167  {
168  current_ang.a1d[axis] = q->angle.a1d[axis];
169  end_angle.a1d[axis] = current_ang.a1d[axis];
170  }
171 }
172 
174 {
175  clear();
176 }
177 
179 {
180  for (int i = 0; i < MAX_TRIGGERED_ANIMATIONS; i++)
181  {
182  queued_animation_init(&queue[i]);
183  queued_animation_init(&queue_tmp[i]);
184  }
185 
191  start_time = 0;
192  end_time = 0;
194  n_queue = 0;
195  instance = -1;
196  has_started = false;
197  start_sound = -1;
198  loop_sound = -1;
199  end_sound = -1;
200  current_snd = -1;
201  current_snd_index = -1;
202  snd_rad = 0.0;
203  obj_num = -1;
204 }
205 
207 {
208 }
209 
211 {
212  int i;
213  queued_animation new_queue;
214  memcpy( &new_queue, the_queue, sizeof(queued_animation) );
215 
216  if (dir == -1) {
217  new_queue.start = new_queue.reverse_start;
218  vm_vec_negate( &new_queue.angle );
219  }
220 
221  memcpy(queue_tmp, queue, sizeof(queued_animation) * MAX_TRIGGERED_ANIMATIONS);
222 
223  if (n_queue > 0) {
224  //remove any items on the queue that are the opposite of what we are thinking about doing
225  // if we are reversing an animation see if the forward animation is on the queue already, and remove it
226  for (i = 0; i < n_queue && (i < MAX_TRIGGERED_ANIMATIONS); i++) {
227  if ( (new_queue.type == queue_tmp[i].type) && (new_queue.subtype == queue_tmp[i].subtype) ) {
228  // same type, if they have the same values (direction reversed) then replace it
229  if (new_queue.instance == queue_tmp[i].instance)
230  break;
231  }
232  }
233 
234  if (i != n_queue) {
235  // replace if it's not the last item on the list
236  if ( i < (MAX_TRIGGERED_ANIMATIONS-1) )
237  memmove( &queue_tmp[i], &queue_tmp[i+1], sizeof(queued_animation) * (MAX_TRIGGERED_ANIMATIONS-(i+1)) );
238 
239  // ok these two animations cancelled each other out, so he doesn't get on the queue
240  n_queue--;
241 
242  return;
243  }
244  }
245 
246  if (new_queue.start == 0) {
247  new_queue.start_time = timestamp();
248  new_queue.end_time = timestamp( new_queue.end );
249 
250  // if there is no delay don't bother with the queue, just start the thing
251  start( &new_queue );
252 
253  return;
254  }
255 
256  if (new_queue.instance == instance) {
257  // same animation is playing that we are about to think about playing some point in the future
258  if ( (direction.xyz.x * rot_vel.xyz.x) == new_queue.vel.xyz.x &&
259  (direction.xyz.y * rot_vel.xyz.y) == new_queue.vel.xyz.y &&
260  (direction.xyz.z * rot_vel.xyz.z) == new_queue.vel.xyz.z)
261  {
262  // they're going in opposite directions, one of them is a reversal!
263  // so this means that there is some sort of delay that's getting fubared becase of other queue items getting removed due to reversal
264  // this animation needs to be started now!
265  new_queue.start_time = timestamp();
266  new_queue.end_time = timestamp( new_queue.end );
267 
268  // if there is no delay don't bother with the queue, just start the thing
269  start( &new_queue );
270 
271  return;
272  }
273  }
274 
275  Assert( new_queue.start >= 0 );
276 
277  // starts that many milliseconds from now
278  new_queue.start_time = timestamp( new_queue.start );
279 
280  // runs for that long
281  new_queue.end_time = timestamp( new_queue.start + new_queue.end );
282 
283  if (n_queue > 0) {
284  // if we already have something in the queue find the first item on the
285  // queue that is going to start after the new item,
286  for (i = 0; (i < n_queue) && (i < MAX_TRIGGERED_ANIMATIONS) && (new_queue.start_time > queue_tmp[i].start_time); i++);
287 
288  if (i >= MAX_TRIGGERED_ANIMATIONS)
289  return;
290 
291  // then insert the new item before that item
292  // from the begining of the queue to the item on the queue that is just before the new item
293  if (i)
294  memcpy( queue, queue_tmp, sizeof(queued_animation) * i );
295 
296  // if there are any items after, copy them from the original queue
297  if ( (n_queue >= (i+1)) && (i < (MAX_TRIGGERED_ANIMATIONS - 1)) ) {
298  if (n_queue >= MAX_TRIGGERED_ANIMATIONS) {
299  // if the queue is full, we don't want to copy past the end of it
300  memcpy(&queue[i + 1], &queue_tmp[i], sizeof(queued_animation) * (MAX_TRIGGERED_ANIMATIONS - i - 1));
301  } else {
302  memcpy( &queue[i+1], &queue_tmp[i], sizeof(queued_animation) * (n_queue - i) );
303  }
304  }
305 
306  // add the new item
307  queue[i] = new_queue;
308  } else {
309  queue[0] = new_queue;
310  }
311 
312  n_queue++;
313 }
314 
320 {
321  int i;
322 
323  // if there is nothing to process then bail
324  if ( !n_queue )
325  return;
326 
327  // all items on the queue are in chronological order (or at least they should be)
328  // so execute all items who's starting timestamps are less than the current time
329  for (i = 0; (i < n_queue) && timestamp_elapsed(queue[i].start_time); i++)
330  start( &queue[i] );
331 
332  // if no items were processed then bail
333  if ( !i )
334  return;
335 
336  // all the triggered animations associated with this object
337  memcpy( queue_tmp, queue, sizeof(queued_animation) * MAX_TRIGGERED_ANIMATIONS );
338 
339  // if there are more items on the queue than we just executed reallocate the queue.
340  // copy all the items after the last one we executed
341  memcpy( queue, &queue_tmp[i], sizeof(queued_animation) * (n_queue-i) );
342 
343  // then erase the old queue
344  n_queue -= i;
345 
346  queue[n_queue].start_time = 0;
347  queue[n_queue].end_time = 0;
348 }
349 
351 {
352  qa->angle = vmd_zero_vector;
353  qa->vel = vmd_zero_vector;
354  qa->accel = vmd_zero_vector;
355 
356  qa->start = 0;
357  qa->end = 0;
358  qa->type = TRIGGER_TYPE_NONE;
360 
361  qa->absolute = false;
362  qa->reverse_start = -1;
363  qa->instance = -1;
364  qa->real_end_time = 0;
365 
366  qa->start_sound = -1;
367  qa->loop_sound = -1;
368  qa->end_sound = -1;
369  qa->snd_rad = 0.0f;
370 }
371 
373 {
374  for (int i = 0; i < 3; i++) {
375  if ( qa->accel.a1d[i] == 0.0f )
376  continue;
377 
378  if ( ((qa->vel.a1d[i] * qa->vel.a1d[i]) / qa->accel.a1d[i]) > fabs(qa->angle.a1d[i]) )
379  qa->vel.a1d[i] = fl_sqrt( fabs(qa->accel.a1d[i] * qa->angle.a1d[i]) );
380  }
381 }
382 
383 
384 
385 //************************************//
386 //*** triggered submodel animation ***//
387 //************************************//
388 
389 
390 /*
391 ok a triggered animation works like this, at some point a subobject will be triggered to rotate
392 when this happens the following phases of rotation will happen
393 1) it will accelerate at a constant rate until it reaches a
394 quasi-arbitrary (there are limitations on what it can be) velocity
395 2) it will maintain a constant rotational velocity untill it reaches the angle at which it
396 needs to start slowing down in order to stop at the right end angle
397 3)it will slow down at the same rate it has sped up earlier, when the rotational velocity
398 starts going in the wrong direction it'll be locked at 0 and the angle of the submodel will
399 be locked at the angle it's suposed to end at
400 */
401 //-Bobboau
402 
404 {
405  Assert( psub != NULL );
406  Assert( ss != NULL );
407  Assert( psub->flags & MSS_FLAG_TRIGGERED );
408  Assert( ss->triggered_rotation_index >= 0 );
409 
410  triggered_rotation *trigger = &Triggered_rotations[ss->triggered_rotation_index];
412  int not_moving_count = 0;
413 
414  if ( psub->model_num < 0 ) {
415  Int3();
416  return;
417  }
418 
419  if ( psub->subobj_num < 0 ) {
420  Int3();
421  return;
422  }
423 
424  if ( !trigger->has_started )
425  return;
426 
427  // save last angles
428  sii->prev_angs = sii->angs;
429 
430  // process velocity and position
431  // first you accelerate, then you maintain a speed, then you slow down, then you stay put
432  for (int i = 0; i < 3; i++) {
433  // are we moving?
434  if ( (trigger->current_vel.a1d[i] != 0.0f) || ((trigger->current_ang.a1d[i] * trigger->direction.a1d[i]) <= (trigger->slow_angle.a1d[i] * trigger->direction.a1d[i])) ) {
435  // yep...
436 
437  // our velocity is something other than 0 or we are in the acceleration phase (where velocity starts out at 0)
438 
439  // while you are not slowing down...
440  if ( (trigger->current_ang.a1d[i] * trigger->direction.a1d[i]) <= (trigger->slow_angle.a1d[i] * trigger->direction.a1d[i]) ) {
441  // speeding up
442  if ( (trigger->current_vel.a1d[i] * trigger->direction.a1d[i]) < (trigger->rot_vel.a1d[i] * trigger->direction.a1d[i]) && (trigger->rot_accel.a1d[i] != 0.0f) ) {
443  trigger->current_vel.a1d[i] += (trigger->rot_accel.a1d[i] * flFrametime);
444  }
445  // have reached target speed
446  else {
447  trigger->current_vel.a1d[i] = trigger->rot_vel.a1d[i];
448  }
449  }
450  // we are slowing down...
451  else {
452  // velocity matches desired heading
453  if ( ((trigger->current_vel.a1d[i] * trigger->direction.a1d[i]) > 0) && (trigger->rot_accel.a1d[i] != 0.0f) ) {
454  trigger->current_vel.a1d[i] -= (trigger->rot_accel.a1d[i] * flFrametime);
455  }
456  // velocity does not match desired heading
457  else {
458  // this can happen if we have decelerated too long or if an animation was reversed quickly
459  // the way to tell the difference between these two cases is the acceleration
460 
461  // if the curent velocity is in the opposite direction as the acceleration then it was interupted
462  if ( (trigger->current_vel.a1d[i] / fabs(trigger->current_vel.a1d[i])) != (trigger->rot_accel.a1d[i] / fabs(trigger->rot_accel.a1d[i])) ) {
463  // this is gona be some messy stuff in here to figure out when it should start to slow down again
464  // it'll have to make a new slow angle I guess
465  // with an initial v in the opposite direction the time it will take for it to stop
466  // will be v/a, to get back up to the same speed again we will need twice that
467  // it should be back to where it was in terms of both speed and position then
468  // so...
469  trigger->slow_angle.a1d[i] = trigger->current_ang.a1d[i];
470  trigger->rot_vel.a1d[i] = -(trigger->current_vel.a1d[i]);
471 
472  // I guess that wasn't so messy after all :D
473 
474  // it might hit exactly 0 every now and then, but it will be before the slow angle so it will be fine
475  // this assumes that the reversed animation is the same exact animation only played in reverse,
476  // if the speeds or accelerations are diferent then it might not work
477  } else {
478  // our velocity has gone in the opposite direction because we decelerated too long
479  trigger->current_vel.a1d[i] = 0.0f;
480  }
481  }
482  }
483 
484  // if we've over shot the angle, this shouldn't happen but it might if odd values are given
485  if ( (trigger->current_ang.a1d[i] * trigger->direction.a1d[i]) >= (trigger->end_angle.a1d[i] * trigger->direction.a1d[i]) ) {
486  trigger->current_ang.a1d[i] = trigger->end_angle.a1d[i];
487  trigger->current_vel.a1d[i] = 0.0f;
488  not_moving_count++;
489  } else {
490  trigger->current_ang.a1d[i] += (trigger->current_vel.a1d[i] * flFrametime);
491  }
492  } else {
493  // not moving
494  trigger->current_ang.a1d[i] = trigger->end_angle.a1d[i];
495  not_moving_count++;
496  }
497  }
498 
499  if (not_moving_count == 3) {
500  trigger->has_started = false;
501  trigger->instance = -1;
502  }
503 
504  // objects can be animated along several axes at the same time
505  // I'm prety sure using the magnitude of the vectors is at least pretty close for any code that might be using it
506  sii->cur_turn_rate = vm_vec_mag(&trigger->current_vel);
507  sii->desired_turn_rate = vm_vec_mag(&trigger->rot_vel);
508  sii->turn_accel = vm_vec_mag(&trigger->rot_accel);
509 
510  // the extra math here is/was useless, it just returns the exact same value (or really just 0 in the old code)
511  sii->angs.p = trigger->current_ang.xyz.x; //- (2.0f * PI2 * (trigger->current_ang.xyz.x / (2.0f * PI2)));
512  sii->angs.h = trigger->current_ang.xyz.y; //- (2.0f * PI2 * (trigger->current_ang.xyz.y / (2.0f * PI2)));
513  sii->angs.b = trigger->current_ang.xyz.z; //- (2.0f * PI2 * (trigger->current_ang.xyz.z / (2.0f * PI2)));
514 
515  if (sii->angs.p >= PI2)
516  sii->angs.p -= PI2;
517  else if (sii->angs.p < 0.0f)
518  sii->angs.p += PI2;
519 
520  if (sii->angs.h >= PI2)
521  sii->angs.h -= PI2;
522  else if (sii->angs.h < 0.0f)
523  sii->angs.h += PI2;
524 
525  if (sii->angs.b >= PI2)
526  sii->angs.b -= PI2;
527  else if (sii->angs.b < 0.0f)
528  sii->angs.b += PI2;
529 }
530 
531 //************************************//
532 //*** ship related animation stuff ***//
533 //************************************//
534 
535 // Checks if the given subtype matches a particular animation
537 {
538  Assert( psub != NULL );
539  Assert( anim_q != NULL );
540 
541  if ( (subtype == ANIMATION_SUBTYPE_ALL) || (anim_q->subtype == ANIMATION_SUBTYPE_ALL) )
542  return true;
543 
544  // Fighterbay door animations can have negative subtypes, so handle those
545  // separately here
546  if (anim_q->type == TRIGGER_TYPE_DOCK_BAY_DOOR) {
547  int anim_subtype = anim_q->subtype -1; // in the tables, bay door +sub_types are 1-based so change to 0-based
548 
549  if (anim_subtype < 0) {
550  if (abs(anim_subtype) != subtype) {
551  return true;
552  }
553  } else {
554  if (anim_subtype == subtype) {
555  return true;
556  }
557  }
558  } else {
559  if ( anim_q->subtype == subtype )
560  return true;
561  }
562 
563  return false;
564 }
565 
566 bool model_anim_start_type(ship_subsys *pss, int animation_type, int subtype, int direction, bool instant)
567 {
568  Assert( pss != NULL );
569 
570  if (pss->max_hits > 0 && pss->current_hits <= 0.0f) //allow subobjects with hitpoints disabled -nuke
571  return false;
572 
573  model_subsystem *psub = pss->system_info;
574  bool retval = false;
575 
576  if ( !(psub->flags & MSS_FLAG_TRIGGERED) )
577  return false;
578  Assert(pss->triggered_rotation_index >= 0);
579  triggered_rotation *trigger = &Triggered_rotations[pss->triggered_rotation_index];
580 
581  for (int i = 0; i < psub->n_triggers; i++) {
582  if ( (psub->triggers[i].type == animation_type) && subtype_check(psub, &psub->triggers[i], subtype) ) {
583  // rotate instantly; don't use the queue
584  if (instant) {
585  trigger->set_to_final(&psub->triggers[i]);
586  trigger->apply_trigger_angles(&pss->submodel_info_1.angs);
587 
588  retval = true;
589  }
590  // rotate normally
591  else {
592  psub->triggers[i].instance = i;
593  trigger->add_queue(&psub->triggers[i], direction);
594 
595  retval = true;
596  }
597  }
598  }
599 
600  return retval;
601 }
602 
603 bool model_anim_start_type(ship *shipp, int animation_type, int subtype, int direction, bool instant)
604 {
605  // this makes the logic for docking triggers a bit cleaner
606  if (shipp == NULL)
607  return false;
608 
609  ship_subsys *pss;
610  bool retval = false;
611 
612  for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
613  bool rc = model_anim_start_type(pss, animation_type, subtype, direction, instant);
614 
615  if (rc)
616  retval = rc;
617  }
618 
619  return retval;
620 }
621 
634 {
635  int ret = 0;
636  int temp = 0;
637 
638  for (int a = 0; a < 3; a++) {
639  temp = fl2i( ((3.0f * properties->vel.a1d[a] * properties->vel.a1d[a]) + (2.0f * properties->accel.a1d[a] * fabs(properties->angle.a1d[a])))
640  / (2.0f * properties->accel.a1d[a] * properties->vel.a1d[a]) * 1000.0f )
641  + properties->start;
642 
643  if (temp > ret)
644  ret = temp;
645  }
646 
647  return ret;
648 }
649 
650 int model_anim_get_actual_time_type(ship *shipp, int animation_type, int subtype)
651 {
652  ship_subsys *pss;
653  model_subsystem *psub;
654  int ret = 0;
655  int temp_ret = 0;
656  int i;
657 
658  for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
659  psub = pss->system_info;
660 
661  // Don't process destroyed objects (but allow subobjects with hitpoints disabled -nuke)
662  if ( pss->max_hits > 0 && pss->current_hits <= 0.0f )
663  continue;
664 
665  // not a triggered animation, skip it
666  if ( !(psub->flags & MSS_FLAG_TRIGGERED) )
667  continue;
668 
669  for (i = 0; i < psub->n_triggers; i++) {
670  if ( (psub->triggers[i].type == animation_type) && subtype_check(psub, &psub->triggers[i], subtype) ) {
671  temp_ret = model_anim_instance_get_actual_time(&psub->triggers[i]);
672 
673  if (temp_ret > ret)
674  ret = temp_ret;
675  }
676  }
677  }
678 
679  return ret;
680 }
681 
682 int model_anim_get_actual_time_type(ship_info *sip, int animation_type, int subtype)
683 {
684  model_subsystem *psub;
685  int ret = 0;
686  int temp_ret = 0;
687  int n, i;
688 
689  for (n = 0; n < sip->n_subsystems; n++) {
690  psub = &sip->subsystems[n];
691 
692  for (i = 0; i < psub->n_triggers; i++) {
693  if ( (psub->triggers[i].type == animation_type) && subtype_check(psub, &psub->triggers[i], subtype) ) {
694  temp_ret = model_anim_instance_get_actual_time(&psub->triggers[i]);
695 
696  if (temp_ret > ret)
697  ret = temp_ret;
698  }
699  }
700  }
701 
702  return ret;
703 }
704 
706 {
707  model_subsystem *psub;
708  int i, j;
709  int ani_time = 0;
710  int type = 0;
711 
712  for (type = 0; type < MAX_TRIGGER_ANIMATION_TYPES; type++) {
713  // figure out how long it's going to take for the animation to complete
715 
716  for (i = 0; i < sip->n_subsystems; i++) {
717  psub = &sip->subsystems[i];
718 
719  for (j = 0; j < psub->n_triggers; j++) {
720  if (psub->triggers[j].type == type) {
721  // if there isn't a user defined overide already present
722  if (psub->triggers[j].reverse_start == -1)
723  psub->triggers[j].reverse_start = ani_time - model_anim_instance_get_actual_time(&psub->triggers[j]);
724 
725  if (psub->triggers[j].reverse_start < 0) {
726  mprintf(("WARNING: Animation trigger #%i on subsystem '%s', for ship '%s', has a negative reverse_start value! Capping it at 0!\n", j, psub->subobj_name, sip->name));
727  psub->triggers[j].reverse_start = 0;
728  }
729 
731  }
732  }
733  }
734  }
735 }
736 
740 int model_anim_get_time_type(ship_subsys *pss, int animation_type, int subtype)
741 {
742  Assert( pss != NULL );
743 
744  if (pss->max_hits > 0 && pss->current_hits <= 0.0f) //allow subobjects with hitpoints disabled -nuke
745  return timestamp();
746 
747  model_subsystem *psub = pss->system_info;
748  int i, ret = 0;
749 
750  if ( !(psub->flags & MSS_FLAG_TRIGGERED) )
751  return timestamp();
752  Assert(pss->triggered_rotation_index >= 0);
753  triggered_rotation *tr = &Triggered_rotations[pss->triggered_rotation_index];
754 
755  for (i = 0; i < psub->n_triggers; i++) {
756  if ( (psub->triggers[i].type == animation_type) &&
757  ((psub->triggers[i].subtype == ANIMATION_SUBTYPE_ALL) || (psub->triggers[i].subtype == subtype)) )
758  {
759  int ani_time = 0;
760 
761  if ( (tr->current_vel.a1d[0] != 0.0f) || (tr->current_vel.a1d[1] != 0.0f) || (tr->current_vel.a1d[2] != 0.0f)) {
762  // if the subobject is moving then things get really complicated
763  int a_time = 0;
764  int real_time = model_anim_instance_get_actual_time(&psub->triggers[i]);
765  int pad = real_time - psub->triggers[i].end;
766 
767  for (int a = 0; a < 3; a++) {
768  float end_angle = (tr->current_ang.a1d[a] + (((tr->rot_vel.a1d[a]*tr->rot_vel.a1d[a]) - (tr->current_vel.a1d[a]*tr->current_vel.a1d[a])) / (2*tr->rot_accel.a1d[a])));
769 
770  if (end_angle > tr->slow_angle.a1d[a]) {
771  //T(total) = (2V(maximum) - V(initial))/a + (S(turnpoint) - S(initial) + (V(initial)^2 - V(maximum)^2)/2a) / V(maximum)
772  a_time = fl2i(((((2*tr->rot_vel.a1d[a]) - tr->current_ang.a1d[a])/tr->rot_accel.a1d[a]) + tr->slow_angle.a1d[a] - tr->current_ang.a1d[a] + (((tr->current_vel.a1d[a]*tr->current_vel.a1d[a]) - (tr->rot_vel.a1d[a]*tr->rot_vel.a1d[a])) / (2*tr->rot_accel.a1d[a])))*1000.0f);
773  if (ani_time < a_time)
774  ani_time = a_time;
775  } else {
776  //T(total) = 2 * sqrt((S(final) - S(initial))/a - ( (V(initial)/a)^2 ) / 2 ) - V(initial)/a
777  a_time = fl2i((2 * fl_sqrt(((tr->end_angle.a1d[a] - tr->current_ang.a1d[a])/tr->rot_accel.a1d[a]) - ((tr->current_vel.a1d[a] * tr->current_vel.a1d[a]) / (tr->rot_accel.a1d[a] * tr->rot_accel.a1d[a])) / 2) - (tr->current_vel.a1d[a] / tr->rot_accel.a1d[a]))*1000.0f);
778  if (ani_time < a_time)
779  ani_time = a_time;
780  }
781  }
782 
783  if (ani_time)
784  ani_time += pad;
785  } else {
786  // if it isn't moving then it's trivial.
787  // no currently playing animation
788  ani_time = psub->triggers[i].end + psub->triggers[i].start;
789  }
790 
791  if (ret < ani_time)
792  ret = ani_time;
793  }
794  }
795 
796  return timestamp(ret);
797 }
798 
807 int model_anim_get_time_type(ship *shipp, int animation_type, int subtype)
808 {
809  ship_subsys *pss;
810  int ani_time = 0, ret = 0;
811 
812 
813  for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
814  ani_time = model_anim_get_time_type(pss, animation_type, subtype);
815 
816  if (ret < ani_time)
817  ret = ani_time;
818  }
819 
820  return ret;
821 }
822 
824 {
825  ship_weapon *swp = &shipp->weapons;
826  ship_subsys *pss;
827  model_subsystem *psub;
828  int i;
829 
830  for (i = 0; i < MAX_SHIP_PRIMARY_BANKS; i++)
831  swp->primary_animation_done_time[i] = 0;
832 
833  for (i = 0; i < MAX_SHIP_SECONDARY_BANKS; i++)
834  swp->secondary_animation_done_time[i] = 0;
835 
836  ship_primary_changed(shipp);
837  ship_secondary_changed(shipp);
838 
839  for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
840  psub = pss->system_info;
841 
842  for (i = 0; i < psub->n_triggers; i++) {
843  if (psub->triggers[i].type == TRIGGER_TYPE_INITIAL) {
844  if (psub->type == SUBSYSTEM_TURRET) {
845  // special case for turrets
846  pss->submodel_info_2.angs.p = psub->triggers[i].angle.xyz.x;
847  pss->submodel_info_1.angs.h = psub->triggers[i].angle.xyz.y;
848  } else {
849  Assertion(pss->triggered_rotation_index >= 0, "Unable to find triggered rotation for ship %s.", shipp->ship_name);
850  triggered_rotation *tr = &Triggered_rotations[pss->triggered_rotation_index];
851 
852  tr->set_to_initial(&psub->triggers[i]);
854  }
855  }
856  }
857  }
858 }
859 
864 {
865  Assert( shipp != NULL );
866 
867  ship_subsys *pss;
868  model_subsystem *psub;
869 
870  if ( !(Game_mode & GM_MULTIPLAYER) ) {
871  Int3();
872  return;
873  }
874 
876  return;
877 
878  for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
879  psub = pss->system_info;
880 
881  // Don't process destroyed objects (but allow subobjects with hitpoints disabled -nuke)
882  if ( pss->max_hits > 0 && pss->current_hits <= 0.0f )
883  continue;
884 
885  // not a triggered animation, skip it
886  if ( !(psub->flags & MSS_FLAG_TRIGGERED) )
887  continue;
888  Assert(pss->triggered_rotation_index >= 0);
889 
890  for (int i = 0; i < psub->n_triggers; i++) {
891  switch (psub->triggers[i].type)
892  {
896  {
897  Triggered_rotations[pss->triggered_rotation_index].process_queue();
899 
900  break;
901  }
902 
903  default:
904  break;
905  }
906  }
907  }
908 }
909 
910 // Goober5000 - stack based animation for reversing a sequence of animations
911 
913 
914 bool model_anim_push_and_start_type(int stack_unique_id, ship *shipp, int animation_type, int subtype, int direction, bool instant)
915 {
916  stack_item item;
917  item.shipp = shipp;
918  item.animation_type = animation_type;
919  item.subtype = subtype;
920  item.direction = direction;
921  item.instant = instant;
922 
923  Animation_map[stack_unique_id].push_back(item);
924 
925  return model_anim_start_type(shipp, animation_type, subtype, direction, instant);
926 }
927 
928 bool model_anim_pop_and_start_type(int stack_unique_id)
929 {
930  animation_stack stack = Animation_map[stack_unique_id];
931 
932  if (stack.empty())
933  return false;
934 
935  stack_item item = stack.back();
936  stack.pop_back();
937 
938  return model_anim_start_type(item.shipp, item.animation_type, item.subtype, item.direction * -1, item.instant);
939 }
#define TRIGGER_TYPE_NONE
Definition: modelanim.h:26
bool model_anim_start_type(ship_subsys *pss, int animation_type, int subtype, int direction, bool instant)
Definition: modelanim.cpp:566
int timestamp(int delta_ms)
Definition: timer.cpp:226
int i
Definition: multi_pxo.cpp:466
#define TRIGGER_TYPE_DOCKING_STAGE_2
Definition: modelanim.h:29
float p
Definition: pstypes.h:111
model_subsystem * system_info
Definition: ship.h:314
#define MAX_SHIP_PRIMARY_BANKS
Definition: globals.h:62
int Game_mode
Definition: systemvars.cpp:24
ship_weapon weapons
Definition: ship.h:658
net_player * Net_player
Definition: multi.cpp:94
float vm_vec_mag(const vec3d *v)
Definition: vecmat.cpp:325
void apply_trigger_angles(angles *submodel_angles)
Definition: modelanim.cpp:133
submodel_instance_info submodel_info_1
Definition: ship.h:368
void set_to_final(queued_animation *q)
Definition: modelanim.cpp:164
float max_hits
Definition: ship.h:320
void _cdecl void void _cdecl void _cdecl Warning(char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
Assert(pm!=NULL)
submodel_instance_info submodel_info_2
Definition: ship.h:369
#define mprintf(args)
Definition: pstypes.h:238
SCP_vector< triggered_rotation > Triggered_rotations
Definition: modelanim.cpp:20
#define MSS_FLAG_TRIGGERED
Definition: model.h:113
struct vec3d::@225::@227 xyz
#define MAX_TRIGGER_ANIMATION_TYPES
Definition: modelanim.h:40
GLclampf f
Definition: Glext.h:7097
int model_anim_get_actual_time_type(ship *shipp, int animation_type, int subtype)
Definition: modelanim.cpp:650
#define Assertion(expr, msg,...)
Definition: clang.h:41
model_subsystem * subsystems
Definition: ship.h:1271
int flags
Definition: multi.h:463
#define Int3()
Definition: pstypes.h:292
ship * shipp
Definition: lua.cpp:9162
int subtype
Definition: modelanim.h:171
int subobj_num
Definition: model.h:175
GLenum type
Definition: Gl.h:1492
bool instant
Definition: modelanim.h:173
ship_subsys subsys_list
Definition: ship.h:630
#define MAX_SHIP_SECONDARY_BANKS
Definition: globals.h:63
float a1d[3]
Definition: pstypes.h:93
const float PI2
Definition: pstypes.h:305
void queued_animation_init(queued_animation *qa)
Definition: modelanim.cpp:350
char * Animation_type_names[MAX_TRIGGER_ANIMATION_TYPES]
Definition: modelanim.cpp:22
#define ANIMATION_SUBTYPE_ALL
Definition: modelanim.h:52
#define NETINFO_FLAG_AM_MASTER
Definition: multi.h:599
void model_anim_set_initial_states(ship *shipp)
Definition: modelanim.cpp:823
void model_anim_fix_reverse_times(ship_info *sip)
Definition: modelanim.cpp:705
int subtype
Definition: lua.cpp:9763
uint flags
Definition: model.h:169
#define nprintf(args)
Definition: pstypes.h:239
#define GM_MULTIPLAYER
Definition: systemvars.h:18
GLboolean GLboolean GLboolean GLboolean a
Definition: Glext.h:5781
#define MAX_TRIGGERED_ANIMATIONS
Definition: modelanim.h:17
#define strnicmp(s1, s2, n)
Definition: config.h:272
#define TRIGGER_TYPE_DOCK_BAY_DOOR
Definition: modelanim.h:34
int primary_animation_done_time[MAX_SHIP_PRIMARY_BANKS]
Definition: ship.h:153
void set_to_initial(queued_animation *q)
Definition: modelanim.cpp:156
int n_subsystems
Definition: ship.h:1270
#define vm_vec_negate(v)
Definition: vecmat.h:70
#define SUBSYSTEM_TURRET
Definition: model.h:54
int model_num
Definition: model.h:176
queued_animation * triggers
Definition: model.h:225
void model_anim_handle_multiplayer(ship *shipp)
Definition: modelanim.cpp:863
int model_anim_get_time_type(ship_subsys *pss, int animation_type, int subtype)
Definition: modelanim.cpp:740
void ship_primary_changed(ship *sp)
Definition: ship.cpp:15996
float current_hits
Definition: ship.h:319
Definition: ship.h:534
int model_anim_instance_get_actual_time(queued_animation *properties)
Finds the actual amount of time that motion of an animation type will take to stop, not for gameplay purposes but for stuff that is involved in coordinating the animation itself.
Definition: modelanim.cpp:633
int n_triggers
Definition: model.h:224
char name[NAME_LENGTH]
Definition: ship.h:1163
for(int idx=0;idx< i;idx++)
Definition: multi_pxo.cpp:472
GLclampd n
Definition: Glext.h:7286
bool model_anim_push_and_start_type(int stack_unique_id, ship *shipp, int animation_type, int subtype, int direction, bool instant)
Definition: modelanim.cpp:914
bool subtype_check(model_subsystem *psub, queued_animation *anim_q, int subtype)
Definition: modelanim.cpp:536
if(aifft_max_checks<=0)
Definition: aiturret.cpp:1581
GLdouble GLdouble GLdouble GLdouble q
Definition: Glext.h:5345
float desired_turn_rate
Definition: model.h:81
#define NAME_LENGTH
Definition: globals.h:15
#define fl2i(fl)
Definition: floating.h:33
int direction
Definition: modelanim.h:172
char subobj_name[MAX_NAME_LEN]
Definition: model.h:172
ship * shipp
Definition: modelanim.h:169
#define fl_sqrt(fl)
Definition: floating.h:29
void ship_secondary_changed(ship *sp)
Definition: ship.cpp:16053
GLfloat GLfloat p
Definition: Glext.h:8373
void queued_animation_correct(queued_animation *qa)
Definition: modelanim.cpp:372
#define LOCATION
Definition: pstypes.h:245
int secondary_animation_done_time[MAX_SHIP_SECONDARY_BANKS]
Definition: ship.h:154
int model_anim_match_type(char *p)
Definition: modelanim.cpp:38
#define timestamp_elapsed(stamp)
Definition: timer.h:102
float flFrametime
Definition: fredstubs.cpp:22
#define TRIGGER_TYPE_TURRET_FIRING
Definition: modelanim.h:36
#define TRIGGER_TYPE_AFTERBURNER
Definition: modelanim.h:35
int triggered_rotation_index
Definition: ship.h:376
int temp
Definition: lua.cpp:4996
void add_queue(queued_animation *new_queue, int dir)
Definition: modelanim.cpp:210
#define TRIGGER_TYPE_PRIMARY_BANK
Definition: modelanim.h:32
int animation_type
Definition: modelanim.h:170
#define TRIGGER_TYPE_SECONDARY_BANK
Definition: modelanim.h:33
void model_anim_submodel_trigger_rotate(model_subsystem *psub, ship_subsys *ss)
Definition: modelanim.cpp:403
float h
Definition: pstypes.h:111
vec3d vmd_zero_vector
Definition: vecmat.cpp:24
SCP_map< int, animation_stack > Animation_map
Definition: modelanim.cpp:912
void start(queued_animation *q)
Definition: modelanim.cpp:94
char ship_name[NAME_LENGTH]
Definition: ship.h:604
#define TRIGGER_TYPE_INITIAL
Definition: modelanim.h:27
bool model_anim_pop_and_start_type(int stack_unique_id)
Definition: modelanim.cpp:928
float b
Definition: pstypes.h:111