home *** CD-ROM | disk | FTP | other *** search
/ Virtual Reality Homebrewer's Handbook / vr.iso / vr386 / statmach.c < prev    next >
C/C++ Source or Header  |  1996-03-19  |  23KB  |  906 lines

  1. /* threaded state machine animation system */
  2.  
  3. /* Written by Dave Stampe Oct. 1992  for REND386 V5.0 */
  4.  
  5. // works pretty well, with automatic time-independent animation.
  6. // Expensive on memory: eats 200 bytes per program line, and
  7. // little error trapping!
  8.  
  9. // Need something more programmer-accessible too.
  10. // This will be added in future API release
  11. // This code is ported to VR-386 API, and uses the
  12. // auto-update list method
  13.  
  14.  
  15. /*
  16.  This code is part of the VR-386 project, created by Dave Stampe.
  17.  VR-386 is a desendent of REND386, created by Dave Stampe and
  18.  Bernie Roehl.  Almost all the code has been rewritten by Dave
  19.  Stampre for VR-386.
  20.  
  21.  Copyright (c) 1994 by Dave Stampe:
  22.  May be freely used to write software for release into the public domain
  23.  or for educational use; all commercial endeavours MUST contact Dave Stampe
  24.  (dstampe@psych.toronto.edu) for permission to incorporate any part of
  25.  this software or source code into their products!  Usually there is no
  26.  charge for under 50-100 items for low-cost or shareware products, and terms
  27.  are reasonable.  Any royalties are used for development, so equipment is
  28.  often acceptable payment.
  29.  
  30.  ATTRIBUTION:  If you use any part of this source code or the libraries
  31.  in your projects, you must give attribution to VR-386 and Dave Stampe,
  32.  and any other authors in your documentation, source code, and at startup
  33.  of your program.  Let's keep the freeware ball rolling!
  34.  
  35.  DEVELOPMENT: VR-386 is a effort to develop the process started by
  36.  REND386, improving programmer access by rewriting the code and supplying
  37.  a standard API.  If you write improvements, add new functions rather
  38.  than rewriting current functions.  This will make it possible to
  39.  include you improved code in the next API release.  YOU can help advance
  40.  VR-386.  Comments on the API are welcome.
  41.  
  42.  CONTACT: dstampe@psych.toronto.edu
  43. */
  44.  
  45.  
  46. #include <stdio.h>
  47. #include <stdlib.h>      /* malloc() */
  48. #include <conio.h>      /* kbhit() */
  49. #include <string.h>
  50.  
  51. #include "vr_api.h"
  52. #include "intmath.h"
  53. #include "segment.h"
  54.  
  55. extern void connect_body(OBJECT *s);
  56. extern void disconnect_body();
  57.  
  58. /* from world.c */
  59.  
  60. typedef struct _name NAMEREF;
  61.  
  62. struct _name {
  63.     char *name;
  64.     void *value;
  65.     NAMEREF *next;
  66. };
  67.  
  68. extern NAMEREF *add_name(NAMEREF **list, char *name, void *value);
  69. extern void del_namelist(NAMEREF *list);
  70. extern void *find_name(NAMEREF *list, char *name);
  71. extern OBJECT *find_seg(char *name);
  72.  
  73. extern int flymode;
  74.  
  75. #define FREE     0      /* state (instruction) type */
  76. #define STEP     1
  77. #define MOVETO   2
  78. #define POSTEST  3
  79. #define ROTTEST  4
  80. #define SET      5
  81. #define SENSOR   6
  82. #define SELECTED 7
  83. #define TIMER    8
  84. #define GRAVITY  9
  85. #define POSLIMIT 10
  86. #define ROTLIMIT 11
  87. #define USEREP   12
  88. #define ABSTEP   13
  89. #define ATTACH   14
  90. #define POPTEXT  15
  91.  
  92. #define SAME     0              /* status of timer and gravity */
  93. #define CHANGED  1
  94. #define EXPIRED  2
  95.  
  96. typedef struct _state {
  97.             int type;               /* type of operation       */
  98.             int status;             /* marks timer, gravity    */
  99.             struct _state *next;    /* next operation in state */
  100.             int tst1v, tst1i;       /* evaluation tests        */
  101.             int tst2v, tst2i;
  102.             struct _state *sstate;  /* "set" assignments, next state */
  103.             int set1v, set1i;
  104.             int set2v, set2i;
  105.             int set3v, set3i;
  106.             struct _state *rstate;  /* "reset" same    */
  107.             int rst1v, rst1i;
  108.             int rst2v, rst2i;
  109.             int rst3v, rst3i;
  110.             OBJECT *seg;           /* segment to use  */
  111.             long x1, y1, z1;        /* numerical arg's */
  112.             long x2, y2, z2;        /* 1 = po's'n 2 = rotate */
  113.             long x3, y3, z3;        /* 3 = gravity accel */
  114.             unsigned xo1, yo1, zo1; /* underflows */
  115.             unsigned xo2, yo2, zo2;
  116.             struct _state *ulist;   /* list of seg's moved */
  117.             } STATE;
  118.  
  119. typedef struct _ani {
  120.          struct _ani *next;       /* next animation task      */
  121.          long last_time;          /* last time run            */
  122.          long period;             /* delay between runs       */
  123.          STATE *lstate;           /* set if state has changed */
  124.          STATE *state;            /* current state in machine */
  125.         } ANIMATION;
  126.  
  127. static ANIMATION *anilist = NULL;
  128. static ANIMATION *cur_ani = NULL;
  129. static STATE *cur_state   = NULL;
  130. static STATE *update_list = NULL;
  131. static STATE *update_end  = NULL;
  132.  
  133. NAMEREF *statelist  = NULL;
  134. NAMEREF *varlist    = NULL;
  135.  
  136. #define VARIABLES 200
  137. static int vars[VARIABLES];
  138. static int varcount = 0;
  139.  
  140. extern NAMEREF *figurelist;
  141. extern NAMEREF *fixedolist;
  142. extern OBJECT *body_seg, *head_seg, *wrist_seg;
  143.  
  144. static long ctime;
  145.  
  146. void create_default_segs()
  147. {
  148.  if(!body_seg)  body_seg  = create_invisible_object();
  149.  if(!head_seg)  head_seg  = create_invisible_object();
  150.  if(!wrist_seg) wrist_seg = create_invisible_object();
  151.  
  152.  add_name(&figurelist, "user_body", body_seg);
  153.  add_name(&figurelist, "user_head", head_seg);
  154.  add_name(&figurelist, "user_hand", wrist_seg);
  155. }
  156.  
  157. static void dump_states(void)    /* remove states in preparation for new animation */
  158. {
  159.  del_namelist(statelist);
  160.  statelist = NULL;
  161. }
  162.  
  163.  
  164. static int varindex(char *name)     /* return index of variable, or allocate new */
  165. {
  166.  void *v;
  167.  
  168.  if ((v = find_name(varlist, name))!=NULL)
  169.   {
  170.     return (int)v;
  171.   }
  172.  else
  173.   {
  174.    if (varcount == VARIABLES-1) return 0;
  175.    else
  176.     {
  177.       varcount++;
  178.       v = (void *) varcount;
  179.       if (add_name(&varlist, name, v)) return varcount;
  180.       else return 0;
  181.     }
  182.   }
  183. }
  184.  
  185.  
  186. static STATE * stateptr(char *name)   /* return adr. of state, or allocate new */
  187. {
  188.  STATE *v;
  189.  
  190.  if ((v = find_name(statelist, name))!=NULL)
  191.    {
  192.      return v;
  193.    }
  194.  else
  195.    {
  196.      v = (STATE *) malloc(sizeof(STATE));
  197.      if (v == NULL) return NULL;
  198.      else
  199.        {                   /* mark as usable as first command in state */
  200.      v->type = FREE;
  201.      if (add_name(&statelist, name, v)) return v;
  202.      else return NULL;
  203.        }
  204.    }
  205. }
  206.  
  207.  
  208. static char ps[] = " ,\t\n";
  209. static char st[] = "%s";
  210.  
  211. void parse_ani()
  212. {
  213.   float period = 10;
  214.   ANIMATION *ani, *a;
  215.  
  216.   dump_states();
  217.   period = atof(strtok(NULL,ps));
  218.   ani = (ANIMATION *)malloc(sizeof(ANIMATION));
  219.   if (ani == NULL)
  220.     {
  221.       cur_ani = NULL;
  222.       return;
  223.     }
  224.   ani->period = get_ticks_per_second()/period;
  225.   ani->last_time = -10000000L;            /* set to run immediately */
  226.   ani->lstate = NULL;
  227.   ani->state = NULL;
  228.  
  229.   ani->next = NULL;                 /* put it at end of list  */
  230.   if (anilist == NULL) anilist = ani;
  231.   else
  232.     {
  233.       for(a = anilist;a->next;a=a->next);
  234.       a->next = ani;
  235.     }
  236.   cur_ani = ani;
  237. }
  238.  
  239.  
  240. void parse_state()
  241. {
  242.   char name[40];
  243.  
  244.   sscanf(strtok(NULL,ps),st, name);
  245.   cur_state = stateptr(name);             /* get new/old state adr.     */
  246.   if (cur_ani == NULL) return;               /* sequence error!            */
  247.   if (cur_ani->state == NULL)
  248.     cur_ani->state = cur_state;          /* set initial state if first */
  249. }
  250.  
  251.  
  252. static STATE *new_state(void)
  253. {
  254.   STATE *s;
  255.  
  256.   if (cur_state == NULL) return NULL;
  257.   if (cur_state->type == FREE) return cur_state;
  258.   else
  259.     {
  260.       s = (STATE *)malloc(sizeof(STATE));
  261.       if (s == NULL)
  262.     {
  263.       cur_state = NULL;
  264.       return NULL;
  265.     }
  266.       s->type = FREE;
  267.     }
  268.   return s;
  269. }
  270.  
  271.  
  272. static void parse_var(char *c, int *v, int *i)
  273. {
  274.   char *cc;
  275.  
  276.   *i = *v = 0;
  277.   if ((cc = strchr(c, '=')) == NULL) return;
  278.   *cc++ = 0;
  279.   *i = varindex(c);
  280.   *v = atoi(cc);
  281. }
  282.  
  283.  
  284. static STATE *parse_as(char *c, int *v, int *i)
  285. {
  286.   char *cc;
  287.  
  288.   if(c==NULL)       // syntax error! stub off
  289.    {
  290.     *v = *i = 0;
  291.     return NULL;
  292.    }
  293.  
  294.   *i = *v = 0;
  295.   if (!strcmpi(c, "EXIT"))     /* keyword "exit" */
  296.     {
  297.       *v = -1;
  298.       *i = -1;
  299.       return NULL;
  300.     }
  301.   if ((cc = strchr(c, '=')) == NULL) return NULL;
  302.   *cc++ = 0;
  303.   if (strcmpi(c, "STATE"))
  304.     {
  305.       *v = atoi(cc);
  306.       *i = varindex(c);
  307.       return NULL;
  308.     }
  309.   else
  310.     {
  311.       *v = *i = 0;
  312.       return stateptr(cc);
  313.     }
  314. }
  315.  
  316.  
  317. static char *parse_assgn(char *ci, STATE *s, int set)
  318. {
  319.   int v1, v2, v3, i1, i2, i3;
  320.   STATE *ss, *sp = NULL;
  321.   char *c, *r;
  322.  
  323.   if (set)
  324.     {
  325.       s->set1i = s->set2i = s->set3i = 0;
  326.       s->sstate = NULL;
  327.     }
  328.   else
  329.     {
  330.       s->rst1i = s->rst2i = s->rst3i = 0;
  331.       s->rstate = NULL;
  332.     }
  333.  
  334.   if ((c = strchr(ci,'[')) == NULL) return ci;   /* verify arguments   */
  335.   if ((r = strchr(c, ']')) == NULL) return ci;   /* trim off enclosure */
  336.   c++;  *r++ = 0;
  337.  
  338.   if ((ss = parse_as(strtok(c, " ,\t"),    &v1, &i1)) != NULL) sp = ss; ;
  339.   if ((ss = parse_as(strtok(NULL, " ,\t"), &v2, &i2)) != NULL) sp = ss; ;
  340.   if ((ss = parse_as(strtok(NULL, " ,\t"), &v3, &i3)) != NULL) sp = ss; ;
  341.   if (set)
  342.     {
  343.       s->set1v = v1;
  344.       s->set2v = v2;
  345.       s->set3v = v3;
  346.       s->set1i = i1;
  347.       s->set2i = i2;
  348.       s->set3i = i3;
  349.       s->sstate = sp;
  350.     }
  351.   else
  352.     {
  353.       s->rst1v = v1;
  354.       s->rst2v = v2;
  355.       s->rst3v = v3;
  356.       s->rst1i = i1;
  357.       s->rst2i = i2;
  358.       s->rst3i = i3;
  359.       s->rstate = sp;
  360.     }
  361.  return r;
  362. }
  363.  
  364.  
  365. static long getarg(char *ci, float scale, long x)
  366. {
  367.   char *c;
  368.   double f;
  369.  
  370.   c = strtok(ci, " \t\n,");
  371.   if (c == NULL || !sscanf(c, "%lf", &f) ) return x;
  372.   return (long) (scale * f);
  373. }
  374.  
  375.  
  376.  
  377. static void parse_rest(char *ci, STATE *s)
  378. {
  379.   char cmd[40]="", var[40]="", segn[40]="";
  380.   char *c, *cc, *cr;
  381.   OBJECT *sp;
  382.   POSE p = ZERO_POSE;
  383.  
  384.   sscanf(ci, "%[^([\n]", cmd);   /* get segment and command */
  385.   if (strchr(cmd,'='))
  386.     {
  387.       c = strchr(cmd, '=');        /* split */
  388.       *c++ = 0;
  389.       sscanf(cmd, "%s", segn);      /* strip off whitespace */
  390.       sscanf(c,"%s", var);
  391.       sp = find_seg(segn);          /* find segment if any  */
  392.       if (!stricmp(var,"SELECTED"))
  393.     {
  394.       if (!sp) sp = find_name(fixedolist, segn);
  395.       if (sp == NULL) return;
  396.     }
  397.       else
  398.     {
  399.       if (sp == NULL) return;
  400.       find_root_object(sp);   /* make sure root is current!    */
  401.       get_object_pose(sp, &p); /* and cached angle */
  402.     }
  403.       s->seg = sp;
  404.       if (stricmp(var,"SET"))
  405.     {
  406.       if ((c = strchr(ci,'(')) == NULL) return;   /* verify arguments */
  407.       if ((cr = strchr(ci,')')) == NULL) return;
  408.       c++; *cr++ = 0;
  409.     }
  410.       else cr = strchr(ci,'[');
  411.       if (!stricmp(var, "MOVETO"))
  412.     {
  413.       s->x1 = getarg(c,    1, DONTCARE);
  414.       s->y1 = getarg(NULL, 1, DONTCARE);
  415.       s->z1 = getarg(NULL, 1, DONTCARE);
  416.       s->x2 = getarg(NULL, 65536, DONTCARE);
  417.       s->y2 = getarg(NULL, 65536, DONTCARE);
  418.       s->z2 = getarg(NULL, 65536, DONTCARE);
  419.       parse_assgn(cr, s, 1);
  420.       s->type = MOVETO;
  421.     }
  422.       else if (!stricmp(var, "USEREP"))
  423.     {
  424.       OBJECT *o;
  425.       if (!is_object_visible(sp)) return;
  426.       s->seg = sp;
  427.       s->x1 = getarg(c, 1, 0) - 1;
  428.       parse_assgn(cr, s, 1);
  429.       s->type = USEREP;
  430.     }
  431.       else if (!stricmp(var, "ATTACH"))
  432.     {
  433.       if (sp == NULL) return;
  434.       s->x1 = getarg(c, 1, 1);
  435.       parse_assgn(cr, s, 1);
  436.       s->type = ATTACH;
  437.     }
  438.       else if (!stricmp(var, "STEP"))
  439.     {
  440.       s->x1 = getarg(c,    256, 0);
  441.       s->y1 = getarg(NULL, 256, 0);
  442.       s->z1 = getarg(NULL, 256, 0);
  443.       s->x2 = getarg(NULL, 65536, 0);
  444.       s->y2 = getarg(NULL, 65536, 0);
  445.       s->z2 = getarg(NULL, 65536, 0);
  446.       s->xo1 = s->yo1 = s->zo1 = 0;
  447.       parse_assgn(cr, s, 1);
  448.       s->type = STEP;
  449.     }
  450.       else if (!stricmp(var, "ABSTEP"))
  451.     {
  452.       s->x1 = getarg(c,    256, 0);
  453.       s->y1 = getarg(NULL, 256, 0);
  454.       s->z1 = getarg(NULL, 256, 0);
  455.       s->x2 = getarg(NULL, 65536, 0);
  456.       s->y2 = getarg(NULL, 65536, 0);
  457.       s->z2 = getarg(NULL, 65536, 0);
  458.       s->xo1 = s->yo1 = s->zo1 = 0;
  459.       parse_assgn(cr, s, 1);
  460.       s->type = ABSTEP;
  461.     }
  462.       else if (!stricmp(var, "GRAVITY"))
  463.     {
  464.       s->x2 = getarg(c,    256, 0);         /* initial */
  465.       s->y2 = getarg(NULL, 256, 0);
  466.       s->z2 = getarg(NULL, 256, 0);
  467.       s->x3 = getarg(NULL, 65536, 0);    /* accel */
  468.       s->y3 = getarg(NULL, 65536, 0);
  469.       s->z3 = getarg(NULL, 65536, 0);
  470.       s->xo1 = s->yo1 = s->zo1 = 0;
  471.       s->xo2 = s->yo2 = s->zo2 = 0;
  472.       parse_assgn(cr, s, 1);
  473.       s->type = GRAVITY;
  474.     }
  475.       else if (!stricmp(var, "POSTEST"))
  476.     {
  477.       s->x1 = getarg(c,    1, -1000000000L);
  478.       s->y1 = getarg(NULL, 1, -1000000000L);
  479.       s->z1 = getarg(NULL, 1, -1000000000L);
  480.       s->x2 = getarg(NULL, 1,  1000000000L);
  481.       s->y2 = getarg(NULL, 1,  1000000000L);
  482.       s->z2 = getarg(NULL, 1,  1000000000L);
  483.       cr = parse_assgn(cr, s, 1);
  484.       parse_assgn(cr, s, 0);
  485.       s->type = POSTEST;
  486.     }
  487.       else if (!stricmp(var, "ROTTEST"))
  488.     {
  489.       s->x1 = getarg(c,    65536, DONTCARE);
  490.       s->y1 = getarg(NULL, 65536, DONTCARE);
  491.       s->z1 = getarg(NULL, 65536, DONTCARE);
  492.       s->x2 = getarg(NULL, 65536, DONTCARE);
  493.       s->y2 = getarg(NULL, 65536, DONTCARE);
  494.       s->z2 = getarg(NULL, 65536, DONTCARE);
  495.       cr = parse_assgn(cr, s, 1);
  496.       parse_assgn(cr, s, 0);
  497.       s->type = ROTTEST;
  498.     }
  499.       else if (!stricmp(var, "POSLIMIT"))
  500.     {
  501.       s->x1 = getarg(c,    1, DONTCARE);
  502.       s->y1 = getarg(NULL, 1, DONTCARE);
  503.       s->z1 = getarg(NULL, 1, DONTCARE);
  504.       s->x2 = getarg(NULL, 1, DONTCARE);
  505.       s->y2 = getarg(NULL, 1, DONTCARE);
  506.       s->z2 = getarg(NULL, 1, DONTCARE);
  507.       cr = parse_assgn(cr, s, 1);
  508.       parse_assgn(cr, s, 0);
  509.       s->type = POSLIMIT;
  510.     }
  511.       else if (!stricmp(var, "ROTLIMIT"))
  512.     {
  513.       s->x1 = getarg(c,    65536, DONTCARE);
  514.       s->y1 = getarg(NULL, 65536, DONTCARE);
  515.       s->z1 = getarg(NULL, 65536, DONTCARE);
  516.       s->x2 = getarg(NULL, 65536, DONTCARE);
  517.       s->y2 = getarg(NULL, 65536, DONTCARE);
  518.       s->z2 = getarg(NULL, 65536, DONTCARE);
  519.       cr = parse_assgn(cr, s, 1);
  520.       parse_assgn(cr, s, 0);
  521.       s->type = ROTLIMIT;
  522.     }
  523.       else if (!stricmp(var, "SELECTED"))
  524.     {
  525.       s->x1 = getarg(c,    1, 0);
  526.       cr = parse_assgn(cr, s, 1);
  527.       parse_assgn(cr, s, 0);
  528.       s->type = SELECTED;
  529.     }
  530.       else return;
  531.     }
  532.   else
  533.     {
  534.       sscanf(cmd,"%s", var);
  535.       if (!stricmp(var, "SET"))
  536.     {
  537.       parse_assgn(ci, s, 1);
  538.       s->type = SET;
  539.     }
  540.       else
  541.     {
  542.       if ((c=strchr(ci,'('))==NULL) return;   /* verify arguments */
  543.       if ((cr=strchr(c,')'))==NULL) return;
  544.       c++;
  545.       *cr++ = 0;
  546.       if (!stricmp(var, "SENSOR"))
  547.         {
  548.           s->x1 = getarg(c,    1, -1000000000L);
  549.           s->y1 = getarg(NULL, 1, -1000000000L);
  550.           s->z1 = getarg(NULL, 1, -1000000000L);
  551.           s->x2 = getarg(NULL, 1,  1000000000L);
  552.           s->y2 = getarg(NULL, 1,  1000000000L);
  553.           s->z2 = getarg(NULL, 1,  1000000000L);
  554.           cr = parse_assgn(cr, s, 1);
  555.           parse_assgn(cr, s, 0);
  556.           s->type = SENSOR;
  557.         }
  558.       else if (!stricmp(var, "POPTEXT"))
  559.         {
  560.           s->x1 = getarg(c, 180, 90);
  561.           cr = parse_assgn(cr, s, 1);
  562.           c = strtok(cr, "#\n");
  563.           if (!c) return;
  564.           s->seg = strdup(c);
  565.           s->type = POPTEXT;
  566.         }
  567.       else if (!stricmp(var, "TIMER"))
  568.         {
  569.           s->x1 = getarg(c, 180, 0L);
  570.           s->y1 = DONTCARE;
  571.           cr = parse_assgn(cr, s, 1);
  572.           parse_assgn(cr, s, 0);
  573.           s->type = TIMER;
  574.         }
  575.       else return;
  576.     }
  577.      }
  578.   s->next = NULL;                 /* put it at end of list  */
  579.   s->status = 1;                  /* force a restart        */
  580.   if (cur_state==s) return;
  581.   cur_state->next = s;
  582.   cur_state = s;
  583. }
  584.  
  585.  
  586. void parse_if()
  587. {
  588.   STATE *s;
  589.   char *c, *r;
  590.  
  591.   if ((s = new_state()) == NULL) return;
  592.   c = strtok(NULL,"\n");
  593.   if ((c = strchr(c,'(')) == NULL) return;   /* verify conditional terminators */
  594.   if ((r = strchr(c,')')) == NULL) return;
  595.   *r++ = 0;                            /* trim length */
  596.   parse_var(strtok(c+1," \t\n,"),   &(s->tst1v), &(s->tst1i) );
  597.   parse_var(strtok(NULL, " \t\n,"), &(s->tst2v), &(s->tst2i) );
  598.   parse_rest(r, s);
  599. }
  600.  
  601.  
  602. void parse_do()
  603. {
  604.   STATE *s;
  605.  
  606.   if ((s = new_state()) == NULL) return;
  607.   s->tst1i = 0;
  608.   parse_rest(strtok(NULL,"\n"), s);
  609. }
  610.  
  611.  
  612. /********************* EXECUTE *******************/
  613.  
  614. static long elapsed = 0;
  615.  
  616. extern void scaled_rotate(STATE *s, long *nrx,long *nry,long *nrz, long elpsed, long tps);
  617. extern void scaled_move(STATE *s, long *nx,long *ny,long *nz, long elapsed, long tps);
  618. extern void scaled_gravity(STATE *s, long elpsed, long tps);
  619.  
  620.  
  621.  
  622.  
  623.  
  624. static void exec_state(STATE *s)
  625. {
  626.   STATE *old_state = s;
  627.   int changed = (s==cur_ani->lstate) ? 0 : 1 ;
  628.   long x, y, z, rx, ry, rz;
  629.   long nx, ny, nz, nrx, nry, nrz;
  630.   int seg_changed = 0;
  631.   long tps = get_ticks_per_second();
  632.   int endstate = 0;
  633.   POSE p;
  634.   POSE op;
  635.  
  636.   cur_state = s;
  637.   while((cur_ani->state==old_state) && s)
  638.     {
  639.      if (s->type == FREE) goto next_state;
  640.      if (changed) s->status |= CHANGED;
  641.      if (s->type == TIMER && changed) s->y1 = DONTCARE;   /* reset on entry */
  642.      if(s->type == GRAVITY && changed)
  643.        {
  644.      s->x1 = s->x2;
  645.      s->y1 = s->y2;
  646.      s->z1 = s->z2;
  647.      s->xo1 = s->yo1 = s->zo1 = 0;
  648.      s->xo2 = s->yo2 = s->zo2 = 0;
  649.        }
  650.      if (s->tst1i)
  651.        {
  652.      if (vars[s->tst1i] != s->tst1v) goto next_state;
  653.      if (s->tst2i)
  654.        {
  655.          if (vars[s->tst2i] != s->tst2v) goto next_state;
  656.        }
  657.        }
  658.      switch(s->type)
  659.        {
  660.      case SET:
  661.        goto set;
  662.  
  663.      case SELECTED:
  664.        if (is_object_selected(s->seg))
  665.          {
  666.            if (s->x1) unhighlight_object(s->seg);
  667.            goto reset;
  668.          }
  669.        else goto set;
  670.  
  671.      case USEREP:
  672.        {
  673.          if (s->x1 < 0) unlock_current_representation(s->seg);
  674.          else lock_current_representation(s->seg, s->x1);
  675.          world_changed++;
  676.          goto set;
  677.        }
  678.  
  679.      case ATTACH:
  680.        {
  681.          if (s->x1 == 0)
  682.            {
  683.          disconnect_body();
  684.          if (!flymode)
  685.            {
  686.              body_pose->rz = 0;
  687.            }
  688.         }
  689.          else connect_body(s->seg);
  690.          goto set;
  691.        }
  692.  
  693.      case POPTEXT:
  694.        {
  695.          long t;
  696.          if (s->seg) popmsg(s->seg);
  697.          t = current_time()+s->x1;
  698.          while(current_time() < t) if (kbhit()) break;
  699.          while(kbhit()) getkey();
  700.          world_changed++;
  701.          goto set;
  702.        }
  703.  
  704.      case SENSOR:
  705.        {
  706.          POSE p;
  707.          get_camera_worldpose(current_camera, &p);
  708.          if (s->x1 > p.x) goto set;
  709.          if (s->x2 < p.x) goto set;
  710.          if (s->y1 > p.y) goto set;
  711.          if (s->y2 < p.y) goto set;
  712.          if (s->z1 > p.z) goto set;
  713.          if (s->z2 < p.z) goto set;
  714.          goto reset;
  715.        }
  716.  
  717.      case POSTEST:
  718.        {
  719.          get_object_world_position(s->seg, &p.x, &p.y, &p.z);
  720.          if (s->x1 > p.x) goto set;
  721.          if (s->x2 < p.x) goto set;
  722.          if (s->y1 > p.y) goto set;
  723.          if (s->y2 < p.y) goto set;
  724.          if (s->z1 > p.z) goto set;
  725.          if (s->z2 < p.z) goto set;
  726.          goto reset;
  727.        }
  728.  
  729.      case ROTTEST:
  730.        {
  731.          get_object_pose(s->seg, &p);
  732.          if (s->x1 != DONTCARE && s->x1>p.rx) goto set;
  733.          if (s->x2 != DONTCARE && s->x2<p.rx) goto set;
  734.          if (s->y1 != DONTCARE && s->y1>p.ry) goto set;
  735.          if (s->y2 != DONTCARE && s->y2<p.ry) goto set;
  736.          if (s->z1 != DONTCARE && s->z1>p.rz) goto set;
  737.          if (s->z2 != DONTCARE && s->z2<p.rz) goto set;
  738.          goto reset;
  739.        }
  740.  
  741.      case POSLIMIT:
  742.        {
  743.          get_object_pose(s->seg, &p);
  744.          p.rx = p.ry = p.rz = DONTCARE;
  745.          if (s->x1 != DONTCARE && s->x1 > p.x) { p.x = s->x1; goto rstp; }
  746.          if (s->x2 != DONTCARE && s->x2 < p.x) { p.x = s->x2; goto rstp; }
  747.          if (s->y1 != DONTCARE && s->y1 > p.y) { p.y = s->y1; goto rstp; }
  748.          if (s->y2 != DONTCARE && s->y2 < p.y) { p.y = s->y2; goto rstp; }
  749.          if (s->z1 != DONTCARE && s->z1 > p.z) { p.z = s->z1; goto rstp; }
  750.          if (s->z2 != DONTCARE && s->z2 < p.z) { p.z = s->z2; goto rstp; }
  751.          goto reset;
  752.      rstp:
  753.          seg_changed++;
  754.          set_object_pose(s->seg, &p);
  755.          goto move_seg;
  756.        }
  757.  
  758.      case ROTLIMIT:
  759.        {
  760.          get_object_pose(s->seg, &p);
  761.          p.x = p.y = p.z = DONTCARE;
  762.          if (s->x1 != DONTCARE && s->x1 > p.rx) { p.rx = s->x1; goto rstr; }
  763.          if (s->x2 != DONTCARE && s->x2 < p.rx) { p.rx = s->x2; goto rstr; }
  764.          if (s->y1 != DONTCARE && s->y1 > p.ry) { p.ry = s->y1; goto rstr; }
  765.          if (s->y2 != DONTCARE && s->y2 < p.ry) { p.ry = s->y2; goto rstr; }
  766.          if (s->z1 != DONTCARE && s->z1 > p.rz) { p.rz = s->z1; goto rstr; }
  767.          if (s->z2 != DONTCARE && s->z2 < p.rz) { p.rz = s->z2; goto rstr; }
  768.          goto reset;
  769.      rstr:
  770.          seg_changed++;
  771.          set_object_pose(s->seg, &p);
  772.          goto move_seg;
  773.        }
  774.  
  775.      case MOVETO:
  776.        {
  777.          get_object_pose(s->seg, &p);
  778.          op = p;
  779.          if (s->x1 != DONTCARE){ p.x = s->x1; s->xo1 = 0; }   /* set pos'n, zero underflow */
  780.          if (s->y1 != DONTCARE){ p.y = s->y1; s->yo1 = 0; }
  781.          if (s->z1 != DONTCARE){ p.z = s->z1; s->zo1 = 0; }
  782.          if (s->x2 != DONTCARE) p.rx = s->x2;
  783.          if (s->y2 != DONTCARE) p.ry = s->y2;
  784.          if (s->z2 != DONTCARE) p.rz = s->z2;
  785.          if (memcmp(&p,&op,sizeof(POSE)))
  786.            {
  787.          seg_changed++;
  788.          set_object_pose(s->seg, &p);
  789.            }
  790.          if (seg_changed) goto move_seg; else goto set;
  791.        }
  792.  
  793.      case ABSTEP:
  794.        tps = 0;      // turns off time scaling
  795.      case STEP:
  796.        {
  797.        do_posstep:
  798.          get_object_pose(s->seg, &p);
  799.          op = p;
  800.             // asm code to rotate, scaled by time
  801.          scaled_rotate(s, &p.rx, &p.ry, &p.rz, elapsed, tps);
  802.              // asm code to translate, scaled by time
  803.          scaled_move(s, &p.x, &p.y, &p.z, elapsed, tps);
  804.  
  805.          if (memcmp(&p,&op,sizeof(POSE)))
  806.            {
  807.          seg_changed++;
  808.          set_object_pose(s->seg, &p);
  809.            }
  810.          if (seg_changed) goto move_seg; else goto set;
  811.        }
  812.  
  813.      case GRAVITY:
  814.        {
  815.             // asm code to update velocities
  816.  
  817.          scaled_gravity(s, elapsed, tps);
  818.  
  819.          goto do_posstep;        /* now treat as normal motion */
  820.        }
  821.  
  822.      case TIMER:
  823.        {
  824.          if (s->y1 == DONTCARE) s->y1 = ctime+s->x1;
  825.          if (s->y1 < ctime)
  826.             goto reset;
  827.          else goto set;
  828.        }
  829.  
  830.      reset:
  831.        if (s->rst1i > 0) vars[s->rst1i] = s->rst1v;   /* do all setup stuff  */
  832.        else if (s->rst1i < 0) endstate++;
  833.        if (s->rst2i > 0) vars[s->rst2i] = s->rst2v;
  834.        else if (s->rst2i < 0) endstate++;
  835.        if (s->rst3i > 0) vars[s->rst3i] = s->rst3v;
  836.        else if (s->rst3i < 0) endstate++;
  837.        if (s->rstate)
  838.          {
  839.            cur_ani->state = s->rstate;
  840.            goto newstate;
  841.          }
  842.       break;
  843.  
  844.   move_seg:   // AUTO UPDATE LIST
  845.           add_to_object_update_list(s->seg);
  846.  
  847.      set:
  848.        if (s->set1i > 0) vars[s->set1i] = s->set1v;
  849.        else if (s->set1i < 0) endstate++;
  850.        if (s->set2i > 0) vars[s->set2i] = s->set2v;
  851.        else if (s->set2i < 0) endstate++;
  852.        if (s->set3i > 0) vars[s->set3i] = s->set3v;
  853.        else if (s->set3i < 0) endstate++;
  854.        if (s->sstate)
  855.          {
  856.            cur_ani->state = s->sstate;
  857.            goto newstate;
  858.          }
  859.        break;
  860.  
  861.      default: break;
  862.        }
  863.  
  864.    next_state:
  865.      if (endstate) s = NULL;
  866.      else s = s->next;
  867.    }
  868.  
  869. newstate:
  870.   cur_ani->lstate = old_state;
  871. }
  872.  
  873.  
  874.  
  875. void do_animations()
  876. {
  877.   SEGMENT *s;
  878.   unsigned flags;
  879.   ANIMATION *ani = anilist;
  880.  
  881.   ctime = current_time();
  882.   update_list = NULL;
  883.  
  884.   while(ani)
  885.     {
  886.       if (ani->last_time < ctime)       /* time to animate? */
  887.     {
  888.       elapsed = ctime+ani->period-ani->last_time;
  889.       if (elapsed > 2000) elapsed = 2000;  // at least once ev. 2 sec
  890.       ani->last_time = ctime+ani->period;
  891.       cur_ani = ani;
  892.       exec_state(ani->state);
  893.     }
  894.       ani = ani->next;
  895.     }
  896.  
  897.         // PROCESS ALL CHANGES AUTOMATICALLY
  898.   world_changed |= process_object_update_list();
  899. }
  900.  
  901.  
  902.  
  903.  
  904.  
  905.  
  906.