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