Initial git commit -- Transition from CodeGuard repository
[sdk] / extras / tiles / tiles.ec
1 /****************************************************************************
2    ECERE Tile Engine
3
4    Copyright (c) 1997-2005 Jerome Jacovella-St-Louis
5    All Rights Reserved.
6    
7    tiles.ec - Main Module
8 ****************************************************************************/
9 import "astar.ec"
10 import "sequence.ec"
11 import "sprite.ec"
12
13 define MAXLAYERS = 8;
14
15 #define TILE(a,b,d) ((b)*d.x+(a))
16 #define TILENUM(p,d) TILE((p).x,(p).y,d)
17
18 enum Direction : byte { North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest };
19 enum TileEvent { Standing = 2, Moving = 3 };
20
21 struct TileMap
22 {
23    // Bitmaps
24    int numTiles;
25    int tileW, tileH;
26    Bitmap * tileImage;
27
28    // Geography Stuff
29    uint16 * regions;
30    uint16 * contents;
31    uint16 * frames;
32    
33    // Dimension Stuff
34    int numLayers;
35    Point maxDim;
36
37    // For each layer
38    Point * dim;
39    byte ** moves;
40    TileUnit *** spaces;
41    void * aStar;
42
43    // What's this for??
44    Direction direction;
45    uint16 displaced; 
46    byte retries;
47    Point path[256];
48    bool chase;
49    void *target;
50    int pathPos;
51    TileEvent event;
52    Point objective, door;
53    uint16 * mapTiles;
54
55    uint16 terrainType;
56 };
57
58 define MAXPATH         = 256;
59 define MAX_ITERATIONS  = 1000;
60
61 struct TileUnit
62 {
63    byte space, covering[MAXLAYERS];
64    int boxW,boxH;
65    int w,h;
66    Sequence * sequence;
67    Sprite sprite;
68    bool selected;
69    int flash;
70    bool pickable;
71    byte * filter;
72    int tick;
73    int seqPos;
74    void * data;
75
76    // READ ONLY
77    void *target;
78    bool entering;
79    bool displayed;
80    Point pos;
81    Point dest;
82    int pathPos;
83
84    Direction direction;
85    uint16 displaced; 
86    byte retries;
87    Point path[MAXPATH];
88    bool chase;
89    TileEvent event;
90    Point objective,door;
91 };
92
93 Direction MapDirection(int xd, int yd)
94 {
95    if((xd==0)&&(yd<0))
96       return North;
97    else if((xd>0)&&(yd<0))
98       return NorthEast;
99    else if((xd>0)&&(yd==0))
100       return East;
101    else if((xd>0)&&(yd>0))
102       return SouthEast;
103    else if((xd==0)&&(yd>0))
104       return South;
105    else if((xd<0)&&(yd>0))
106       return SouthWest;
107    else if((xd<0)&&(yd==0))
108       return West;
109    else if((xd<0)&&(yd<0))
110       return NorthWest;
111 }
112
113 static void MapForward(Direction direction, Point result, uint16 distance)
114 {
115    switch(direction)
116    {
117       case North:                           result.y-=distance; break;
118       case NorthEast:   result.x+=distance; result.y-=distance; break;
119       case East:        result.x+=distance;                      break;
120       case SouthEast:   result.x+=distance; result.y+=distance; break;
121       case South:                           result.y+=distance; break;
122       case SouthWest:   result.x-=distance; result.y+=distance; break;
123       case West:        result.x-=distance;                      break;
124       case NorthWest:   result.x-=distance; result.y-=distance; break;
125    }
126 }
127
128 static uint16 MapLine(byte *map, Point dim, Point start, Point end, Point * path, bool free)
129 {
130    Point position;
131    short xdiff, ydiff;
132    short xunit, yunit;
133    short errorterm=0, length, i;
134
135    position=start;
136
137    xdiff=(short)(end.x-start.x);
138    ydiff=(short)(end.y-start.y);
139    if(xdiff<0)
140    {
141       xdiff=-xdiff;
142       xunit=(short)-1;
143    }
144    else
145       xunit=1;
146
147    if(ydiff<0)
148    {
149       ydiff=-ydiff;
150       yunit=(short)-1;
151    }
152    else
153       yunit=1;
154
155    if(xdiff>ydiff)
156    {
157       length=xdiff+1;
158       for(i=0; i<length; i++)
159       {
160          //Here
161          path[i] = position;
162
163          if(map[TILE(position.x,position.y,dim)]!=(byte)free)
164             return i;
165
166          position.x+=xunit;
167          errorterm+=ydiff;
168          if(errorterm>xdiff/2)
169          {
170             errorterm-=xdiff;
171             position.y+=yunit;
172          }
173       }
174    }
175    else
176    {
177       length=ydiff+1;
178       for(i=0; i<length; i++)
179       {
180          //Here
181          path[i]=position;
182
183          if(map[TILE(position.x,position.y,dim)]!=(byte)free)
184             return i;
185
186          position.y+=yunit;
187          errorterm+=xdiff;
188          if(errorterm>ydiff/2)
189          {
190             errorterm-=ydiff;
191             position.x+=xunit;
192          }
193       }
194    }
195    return i;
196 }
197
198 void MapFree(TileMap * map)
199 {
200    if(map)
201    {
202       if(map->aStar) AStarTerminate(map->aStar);
203       if(map->dim) delete map->dim;
204       if(map->tileImage) delete map->tileImage;
205       if(map->moves)
206       {
207          int e;
208          for(e = 0; e<map->numLayers; e++)
209             if(map->moves[e])
210                delete map->moves[e];
211          delete map->moves;
212       }
213       if(map->spaces)
214       {
215          int e;
216          for(e = 0; e<map->numLayers; e++)
217             if(map->spaces[e])
218                delete map->spaces[e];
219          delete map->spaces;
220       }
221       if(map->regions) delete map->regions;
222       if(map->contents) delete map->contents;
223       if(map->frames) delete map->frames;
224       delete map;
225    }
226 }
227
228 TileMap * MapCreate(int numLayers, int width, int height, int tileW, int tileH, int numTiles)
229 {
230    TileMap * result = null;
231    TileMap * map = new0 TileMap[1];
232    if(map)
233    {
234       map->maxDim.x = width;
235       map->maxDim.y = height;
236       map->numLayers = numLayers;
237       map->numTiles = numTiles;
238       map->tileW = tileW;
239       map->tileH = tileH;
240      if( (map->dim = new0 Point[map->numLayers])                                &&
241          (!numTiles || (map->tileImage = new0 Bitmap[map->numTiles]))          &&
242          (map->moves = new0 byte *[map->numLayers])                            &&
243          (map->spaces = new0 TileUnit **[map->numLayers])                      &&
244          (map->regions = new0 uint16[map->maxDim.x*map->maxDim.y])               &&
245          (map->contents= new0 uint16[map->maxDim.x*map->maxDim.y])               &&
246          (map->frames  = new0 uint16[map->maxDim.x*map->maxDim.y])               &&
247          (map->aStar = AStarInitialize(map->maxDim.x, map->maxDim.y, MAXPATH))       )
248          result = map;
249       if(!result)
250          MapFree(map);
251    }
252    return result;
253 }
254
255 TileUnit * MapFindUnit(TileMap * map, int vx, int vy, int vw, int vh, int x, int y)
256 {
257    int s;
258    for(s = map->numLayers-1; s >= 0; s--)
259    {
260       int cx, cy;
261       for(cy = vy*map->dim[s].y/map->maxDim.y-2; cy < vy*map->dim[s].y/map->maxDim.y-2 + vh*map->dim[s].y/map->maxDim.y+3; cy++)
262          for(cx = vx*map->dim[s].x/map->maxDim.x-2; cx < vx*map->dim[s].x/map->maxDim.x-2 + vw*map->dim[s].x/map->maxDim.x+3; cx++)
263          {
264             if((cx >= 0) && (cy >= 0) && (cx < map->dim[s].x) && (cy < map->dim[s].y))
265             {
266                TileUnit * unit;
267                if((unit = map->spaces[s][TILE(cx,cy,map->dim[s])]) && unit->pickable)
268                {
269                   Point exact = UnitExact(map, unit);
270                   int x1 = exact.x - (unit->boxW - unit->w * map->tileW) / 2 - vx * map->tileW;
271                   int y1 = exact.y - (unit->boxH - unit->h * map->tileH) / 2 - vy * map->tileH;
272                   int x2 = x1 + unit->boxW;
273                   int y2 = y1 + unit->boxH;
274                   if((x2 > x) && (y2 > y) && (x1 < x) && (y1 < y))
275                      return unit;
276                }
277             }
278          }
279    }
280    return null;
281 }
282
283 void MapRedraw(TileMap * map, Surface surface, int vx, int vy, int vw, int vh)
284 {
285    TileUnit * unit;
286    int s,x,y;
287
288    surface.SetForeground(white);
289    for(x=0; x<vw; x++)
290       for(y=0; y<vh; y++)
291          surface.Blit(
292             map->tileImage[map->frames[TILE(x+vx,y+vy,map->maxDim)]],
293             x*map->tileW,y*map->tileH,0,0,map->tileW,map->tileH);
294
295    //BUILDINGS
296    for(y = vy; y < vy + vh; y++)
297       for(x = vx; x < vx + vw; x++)
298          if((unit=map->spaces[0][TILE(x,y,map->dim[0])]))
299             if((unit->pos.x == x || x == vx) && (unit->pos.y == y || y == vy))
300                UnitDisplay(map, surface, unit, vx, vy);
301
302    //UNITS
303    for(s=1; s<map->numLayers; s++)
304    for(y = vy*map->dim[s].y/map->maxDim.y-2; y < vy*map->dim[s].y/map->maxDim.y-2 + vh*map->dim[s].y/map->maxDim.y+4; y++)
305       for(x = vx*map->dim[s].x/map->maxDim.x-2; x < vx*map->dim[s].x/map->maxDim.x-2 + vw*map->dim[s].x/map->maxDim.x+4; x++)
306       {
307          if(x>=0 && y>=0 && x<map->dim[s].x && y<map->dim[s].y)
308             if((unit=map->spaces[s][TILE(x,y,map->dim[s])]))
309                if(unit->pos.x == x && unit->pos.y == y)
310                   UnitDisplay(map, surface, unit, vx, vy);
311       }
312 }
313
314 // ********** UNITS **********
315 Point UnitExact(TileMap * map, TileUnit * unit)
316 {
317    Point exact;
318    
319    exact.x = unit->pos.x*map->tileW*map->maxDim.x/map->dim[unit->space].x;
320    exact.y = unit->pos.y*map->tileH*map->maxDim.y/map->dim[unit->space].y;
321    MapForward(unit->direction,&exact,unit->displaced);
322    return exact;
323 }
324
325 TileUnit * UnitCreate(TileMap * map, int space, int x, int y, void * data)
326 {
327    TileUnit * unit = new0 TileUnit[1];
328    if(unit)
329    {
330       unit->space = (byte)space;
331       unit->covering[space] = (byte)bool::true;
332       if(unit->space)
333          unit->direction = (Direction)GetRandom(0,Direction::enumSize-1);
334       unit->displayed = true;
335       unit->event = Standing;
336       unit->pathPos = MAXPATH;
337       unit->dest = unit->pos = Point { x*map->dim[unit->space].x/map->dim[2].x, y*map->dim[unit->space].y/map->dim[2].y};
338       unit->data = data;
339       unit->pickable = true;
340    }
341    return unit;
342 }
343
344 void UnitFree(TileUnit * unit)
345 {
346    if(unit)
347       delete unit;
348 }
349
350 void UnitPlace(TileMap * map, TileUnit *unit)
351 {
352    int x,y;
353    unit->displayed=true;
354    if(unit->displaced)
355    {
356       map->spaces[unit->space]
357             [TILENUM(unit->path[unit->pathPos],map->dim[unit->space])] = unit;
358       map->moves [unit->space]
359             [TILENUM(unit->path[unit->pathPos],map->dim[unit->space])] = (byte)bool::false;
360    }
361    for(x=0; x<unit->w; x++)
362       for(y=0; y<unit->h; y++)
363       {
364          int s;
365          map->spaces[unit->space]
366                [(unit->pos.y+y)*map->dim[unit->space].x+unit->pos.x+x]=unit;
367          for(s = 0; s<map->numLayers; s++)
368          {
369             if(unit->covering[s])
370             {
371                int crX = map->dim[unit->space].x / map->dim[s].x;
372                int crY = map->dim[unit->space].y / map->dim[s].y;
373                crX = Max(crX, 1);
374                crY = Max(crY, 1);
375                if(!((unit->pos.x+x)%crX)&&!((unit->pos.y+y)%crY))
376                   map->moves[s][(unit->pos.y+y)/crY*map->dim[s].x+(unit->pos.x+x)/crX] = (byte)bool::false;
377             }
378          }
379       }
380 }
381
382 void UnitRemove(TileMap * map, TileUnit * unit)
383 {
384    int x,y;
385    unit->displayed=false;
386    if(unit->displaced&&(unit->pathPos<MAXPATH))
387    {
388       map->spaces[unit->space]
389             [TILENUM(unit->path[unit->pathPos],map->dim[unit->space])] = null;
390       map->moves [unit->space]
391             [TILENUM(unit->path[unit->pathPos],map->dim[unit->space])] = (byte)bool::true;
392    }
393    for(x=0; x<unit->w; x++)
394       for(y=0; y<unit->h; y++)
395       {
396          int s;
397          map->spaces[unit->space]
398                [(unit->pos.y+y)*map->dim[unit->space].x+unit->pos.x+x]=null;
399          for(s = 0; s<map->numLayers; s++)
400          {
401             if(unit->covering[s])
402             {
403                int crX = map->dim[unit->space].x / map->dim[s].x;
404                int crY = map->dim[unit->space].y / map->dim[s].y;
405                crX = Max(crX, 1);
406                crY = Max(crY, 1);
407                if(!((unit->pos.x+x)%crX)&&!((unit->pos.y+y)%crY))
408                   map->moves[s][(unit->pos.y+y)/crY*map->dim[s].x+(unit->pos.x+x)/crX] = (byte)bool::true;
409             }
410          }
411       }
412 }
413
414 bool UnitGoTo(TileMap * map, TileUnit * unit, Point dest)
415 {
416    ASNode *path;
417    Point Position, altpath[MAXPATH];
418    uint16 p,pathlen;
419    bool result=true;
420
421    if(unit->displaced)
422       Position=unit->path[unit->pathPos];
423    else
424       Position=unit->pos;
425
426    UnitRemove(map,unit);
427     //If destination can't be reached, go straight toward it
428    if(!map->moves[unit->space][TILENUM(dest,map->dim[unit->space])])
429    {
430       pathlen = MapLine(map->moves[unit->space],map->dim[unit->space],Position,
431                        dest,altpath,true);
432
433       dest = altpath[pathlen-1];
434       result = false;
435    }
436    //Try to find a linear path
437    pathlen=MapLine(map->moves[unit->space],map->dim[unit->space],Position,
438                     dest,altpath,true);
439    if((altpath[pathlen-1].x == dest.x && altpath[pathlen-1].y == dest.y))
440    {
441       pathlen--;
442       unit->pathPos=MAXPATH-pathlen;
443       for(p=0; p<pathlen; p++)
444          unit->path[unit->pathPos+p]=altpath[p+1];
445    }
446    else
447    {
448       //Find the path with A*
449       path = AStarFindPath(map->aStar, map->moves[unit->space],map->dim[unit->space],Position,dest, MAX_ITERATIONS);
450       if(!path)
451       {
452          AStarFreeNodes(map->aStar);
453          UnitPlace(map,unit);
454          return false;
455       }
456       unit->pathPos=MAXPATH;
457       while (path->parent)
458       {
459          unit->pathPos--;
460          unit->path[unit->pathPos]=path->position;
461          path = path->parent;
462       }
463       AStarFreeNodes(map->aStar);
464    }
465    if(unit->displaced)
466    {
467       unit->pathPos--;
468       unit->path[unit->pathPos]=Position;
469    }
470    UnitPlace(map,unit);
471
472    return result;
473 }
474
475 void UnitUpdate(TileMap * map, TileUnit *unit)
476 {
477    Sequence *seq = unit->sequence;
478    if(seq)
479    {
480       Point next;
481       int xd,yd;
482       TileUnit *target;
483
484       if(unit->flash)
485          unit->flash--;
486       if(unit->pathPos<MAXPATH)
487       {
488          if(!unit->displaced)
489          {
490             if(!map->moves[unit->space]
491                 [TILENUM(unit->path[unit->pathPos],map->dim[unit->space])])
492             {
493                unit->event = Standing;
494                if(unit->tick<seq->frames[unit->event][unit->seqPos].wait)
495                   return;
496                unit->tick=0;
497
498                unit->seqPos++;
499                if(unit->seqPos>=seq->numFrames[unit->event])
500                   unit->seqPos=0;
501
502                if(!unit->entering)
503                {
504                   //target=map->spaces[unit->space][TILENUM(unit->dest,map->dim[unit->space])];
505                   if(unit->target)
506                   {
507                      if(!UnitSurround(map, unit,unit->target))
508                         return;
509                   }
510                   else
511                   {
512                      if(UnitGoTo(map, unit, unit->dest))
513                      {
514                      }
515                      //else
516                      //{
517                         //if(target)
518                            //if(!UnitSurround(map,unit,unit->target))
519                               //return;
520                      //}
521                   }
522                }
523                return;
524             }
525          }
526
527          unit->event = Moving;
528          if(unit->tick<seq->frames[unit->event][unit->seqPos].wait) return;
529          unit->tick=0;
530
531          unit->displaced+=seq->frames[unit->event][unit->seqPos].walk;
532          next=unit->path[unit->pathPos];
533          xd=Sgn(next.x-unit->pos.x);
534          yd=Sgn(next.y-unit->pos.y);
535          if(xd||yd)
536             unit->direction=MapDirection(xd,yd);
537
538          UnitRemove(map, unit);
539          if(unit->displaced>=(map->tileW*map->maxDim.x/map->dim[unit->space].x))
540          {
541             unit->displaced=0;
542             unit->pathPos++;
543             unit->pos=next;
544          }
545          UnitPlace(map,unit);
546
547          if(unit->pathPos==MAXPATH)
548          {
549             unit->event = Standing;
550          }
551       }
552       else
553       {
554          if((unit->pathPos==MAXPATH) && !(unit->pos.x == unit->dest.x && unit->pos.y == unit->dest.y))
555          {
556             if(UnitGoTo(map, unit, unit->dest))
557             {
558             }
559             else
560             {
561                target=map->spaces[unit->space][TILENUM(unit->dest,map->dim[unit->space])];
562                if(target)
563                   UnitSurround(map, unit,target);
564             }
565          }
566          if(unit->tick<seq->frames[unit->event][unit->seqPos].wait) return;
567          unit->tick=0;
568          unit->retries=0;
569       }
570       if(unit->space)
571       {
572          unit->seqPos++;
573          if(unit->seqPos>=seq->numFrames[unit->event])
574             unit->seqPos=0;
575       }
576    }
577 }
578
579 typedef struct
580 {
581    int cost;
582    Point position;
583 } SortPosition;
584
585 typedef struct
586 {
587    int cost;
588    TileUnit * unit;
589 } SortUnit;
590
591 static int Compare(const int * a, const int * b)
592 {
593    if(*a > *b)      return  1;
594    else if(*a < *b) return -1;
595    else             return  0;
596 }
597
598 bool UnitSurround(TileMap * map,TileUnit *unit,TileUnit * target)
599 {
600    bool surrounding=false;
601    if(!unit->entering)
602    {
603       SortPosition destinations[33];
604       uint16 c,numDest=0;
605       Point dest,size;
606       int x,y;
607
608       dest=target->pos;
609       size.x = target->w;
610       size.y = target->h;
611       if(!target->space)
612       {
613          if(unit->space==3)
614          {
615             dest.x+=2;          dest.y+=2;
616             size.x-=2; size.y-=2;
617          }
618          else if(unit->space==1)
619          {
620             dest.x++;         dest.y++;
621             size.x+=dest.x%2; size.y+=dest.y%2;
622          }
623       }
624       dest = Point{dest.x*map->dim[unit->space].x/map->dim[target->space].x, dest.y*map->dim[unit->space].y/map->dim[target->space].y};
625       size = Point{size.x*map->dim[unit->space].x/map->dim[target->space].x, size.y*map->dim[unit->space].y/map->dim[target->space].y};
626
627       if(!target||(target==unit))
628          return false;
629  
630       unit->chase=1;
631       unit->target=target;
632       UnitRemove(map, unit);
633
634       //Find out cases around Target
635       for(y=dest.y-1; y<dest.y+size.y+1; y++)
636          for(x=dest.x-1; x<dest.x+size.x+1; x++)
637             if((x>=0)&&(y>=0)&&(x<map->dim[unit->space].x)&&(y<map->dim[unit->space].y))
638                if((x<dest.x)||(y<dest.y)||(x>=dest.x+size.x)||(y>=dest.y+size.y))
639                {
640                   if(map->moves[unit->space][TILE(x,y,map->dim[unit->space])]
641                      ||(map->spaces[unit->space][TILE(x,y,map->dim[unit->space])]==unit))
642                   {
643                      destinations[numDest].cost=(unit->pos.x-x)*(unit->pos.x-x)+
644                                                 (unit->pos.y-y)*(unit->pos.y-y);
645                      if(!destinations[numDest].cost)
646                      {
647                         unit->objective=target->pos;
648                         unit->dest.x = x;
649                         unit->dest.y = y;
650                         UnitPlace(map, unit);
651                         unit->pathPos = MAXPATH;
652                         return true;
653                      }
654                      destinations[numDest  ].position.x = x;
655                      destinations[numDest++].position.y = y;
656                   }
657                }
658       UnitPlace(map, unit);
659       qsort(destinations,numDest,sizeof(SortPosition), Compare);
660       for(c=0; c<numDest; c++)
661       {
662          if(UnitGoTo(map,unit,destinations[c].position))
663          {
664             surrounding=true;
665             unit->dest=destinations[c].position;
666             unit->objective=target->pos;
667             break;
668          }
669       }
670       if(!surrounding)
671          unit->target = null;
672    }
673    return surrounding;
674 }
675
676 void UnitMove(TileMap * map,TileUnit *unit, Point dest)
677 {
678    if(!unit->entering)
679    {
680       //Just to make sure the console input will look right
681       if((map->dim[unit->space].x<map->maxDim.x))
682       {
683          if((Abs(dest.x/2-unit->pos.x)<=1)&&(dest.x%2))
684             dest.x-=Sgn(dest.x/2-unit->pos.x);
685          if((Abs(dest.y/2-unit->pos.y)<=1)&&(dest.y%2))
686             dest.y-=Sgn(dest.y/2-unit->pos.y);
687       }
688
689       UnitGoTo(map,unit,Point{dest.x*map->dim[unit->space].x/map->dim[2].x, dest.y*map->dim[unit->space].y/map->dim[2].y});
690       unit->dest = Point{dest.x*map->dim[unit->space].x/map->dim[2].x, dest.y*map->dim[unit->space].y/map->dim[2].y};
691       unit->chase = 0;
692       unit->target = null;
693    }
694 }
695
696 void UnitDisplay(TileMap * map, Surface surface, TileUnit * unit, int viewX, int viewY)
697 {
698    if(/*unit->sprite && */unit->displayed)
699    {
700       int x1,y1;
701       Point exact = UnitExact(map, unit);
702       bool square = unit->selected;
703
704       if(unit->flash)
705       {
706          if(unit->flash%8<4)
707             square=true;
708          else
709             square=false;
710       }
711       if(square)
712       {
713          x1=exact.x-viewX*map->tileW;
714          y1=exact.y-viewY*map->tileH;
715          x1-=(unit->boxW-unit->w*map->tileW)/2;
716          y1-=(unit->boxH-unit->h*map->tileH)/2;
717          surface.SetForeground(Color { 40,255,40 });
718          surface.Rectangle(x1,y1,x1+unit->boxW, y1+unit->boxH);
719       }
720       surface.SetForeground(white);
721
722       exact = Point{exact.x-viewX*map->tileW, exact.y-viewY*map->tileH};
723    
724       if(!unit->space)
725       {
726          unit->sprite.DisplayFrame(surface,unit->seqPos,exact.x,exact.y,true,unit->filter);
727       }
728       else
729       {
730          int frame;
731          if(unit->sequence->numFrames[unit->event])
732             frame=unit->sequence->frames[unit->event][unit->seqPos].frame;
733          else
734             frame=0;
735          exact.x+=(map->tileW/2)-(unit->sprite.maxWidth /2);
736          exact.y+=(map->tileH/2)-(unit->sprite.maxHeight/2);
737          if(unit->direction>South)
738          {
739             frame += (Direction)8-unit->direction;
740             unit->sprite.DisplayFrame(surface,frame,exact.x,exact.y,true,unit->filter);
741          }
742          else
743          {
744             frame += unit->direction;
745             unit->sprite.DisplayFrame(surface,frame,exact.x,exact.y,false,unit->filter);
746          }         
747       }
748    }
749 }
750
751 void UnitExit(TileMap * map, TileUnit *unit)
752 {
753    int x,y;
754    uint16 numExits=0;
755    SortPosition exits[32];
756    TileUnit * targetUnit=map->spaces[0][TILENUM(unit->objective,map->dim[0])];
757    Point dest, size;
758
759    dest=targetUnit->pos;
760    size.x=targetUnit->w;
761    size.y=targetUnit->h;
762
763    if(!targetUnit->space)
764    {
765       if(unit->space==3)
766       {
767          dest.x+=2;          dest.y+=2;
768          size.x-=2; size.y-=2;
769       }
770       else if(unit->space==1)
771       {
772          dest.x++;         dest.y++;
773          size.x+=dest.x%2; size.y+=dest.y%2;
774       }
775    }
776
777 //   if(!targetUnit->space && unit->space!=2)
778    //{
779      // dest.x++;         dest.y++;
780       //size.x+=dest.x%2; size.y+=dest.y%2;
781    //}
782
783    dest = Point{dest.x*map->dim[unit->space].x/map->dim[2].x, dest.y*map->dim[unit->space].y/map->dim[targetUnit->space].y};
784    size = Point{size.x*map->dim[unit->space].x/map->dim[2].x, size.y*map->dim[unit->space].y/map->dim[targetUnit->space].y};
785
786    if(unit->displayed) return;
787       for(y=dest.y-1; y<dest.y+size.y+1; y++)
788          for(x=dest.x-1; x<dest.x+size.x+1; x++)
789             if((x>=0)&&(y>=0)&&(x<map->dim[unit->space].x)&&(y<map->dim[unit->space].y))
790                if((x<dest.x)||(y<dest.y)||(x>=dest.x+size.x)||(y>=dest.y+size.y))
791                   if(//!SPACES[unit->space][TILE(x,y,dim[unit->space])]&&
792                      map->moves[unit->space][TILE(x,y,map->dim[unit->space])])
793                   {
794                      exits[numExits].position.x = x;
795                      exits[numExits].position.y = y;
796                      exits[numExits++].cost=(unit->door.x-x)*(unit->door.x-x)+
797                                             (unit->door.y-y)*(unit->door.y-y);
798                   }
799    qsort(exits,numExits,sizeof(SortPosition), Compare);
800    if(numExits)
801    {
802       unit->pos=exits[0].position;
803       unit->displaced=0;
804       UnitPlace(map,unit);
805    }
806    else
807    {
808       //exits closed
809    }
810    unit->event = Standing;
811 }
812
813 bool UnitSurroundNearest(TileMap * map,TileUnit *unit, TileUnit **list, uint16 count)
814 {
815    SortUnit goals[32];
816    uint16 c;
817
818    for(c=0; c<count; c++)
819    {
820       goals[c].unit=(void *)list[c];
821       goals[c].cost=(unit->pos.x-list[c]->pos.x)*(unit->pos.x-list[c]->pos.x)+
822                     (unit->pos.y-list[c]->pos.y)*(unit->pos.y-list[c]->pos.y);
823    }
824    qsort(goals,count,sizeof(SortUnit),Compare);
825    for(c=0; c<count; c++)
826       if(UnitSurround(map,unit, goals[c].unit))
827          return true;
828    return false;
829 }
830
831 void UnitEnter(TileMap * map, TileUnit *unit)
832 {
833    TileUnit * targetUnit=map->spaces[0][TILENUM(unit->objective,map->dim[0])];
834    Sequence *seq = unit->sequence;
835
836    if(targetUnit)
837    {
838       unit->entering = true;
839       unit->door=unit->pos;
840       unit->direction=MapDirection(
841          Sgn(unit->objective.x+targetUnit->w/2.0-0.5-unit->pos.x),
842          Sgn(unit->objective.y+targetUnit->h/2.0-0.5-unit->pos.y));
843
844       unit->event = Moving;
845
846       if(unit->tick<seq->frames[unit->event][unit->seqPos].wait) 
847          return;
848
849       unit->displaced+=seq->frames[unit->event][unit->seqPos].walk;
850       if(unit->displaced>=map->tileW)
851       {
852          UnitRemove(map,unit);
853          unit->displaced=0;
854          unit->entering = false;
855       }
856    }
857 }
858
859 void UnitStop(TileMap * map, TileUnit *unit)
860 {
861    if(unit->pathPos<MAXPATH)
862       UnitGoTo(map,unit,unit->path[unit->pathPos]);
863    else
864       UnitGoTo(map,unit,unit->pos);
865 }