1 /****************************************************************************
4 Copyright (c) 1997-2005 Jerome Jacovella-St-Louis
8 ****************************************************************************/
15 #define TILE(a,b,d) ((b)*d.x+(a))
16 #define TILENUM(p,d) TILE((p).x,(p).y,d)
18 enum Direction : byte { North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest };
19 enum TileEvent { Standing = 2, Moving = 3 };
52 Point objective, door;
59 define MAX_ITERATIONS = 1000;
63 byte space, covering[MAXLAYERS];
93 Direction MapDirection(int xd, int yd)
97 else if((xd>0)&&(yd<0))
99 else if((xd>0)&&(yd==0))
101 else if((xd>0)&&(yd>0))
103 else if((xd==0)&&(yd>0))
105 else if((xd<0)&&(yd>0))
107 else if((xd<0)&&(yd==0))
109 else if((xd<0)&&(yd<0))
113 static void MapForward(Direction direction, Point result, uint16 distance)
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;
128 static uint16 MapLine(byte *map, Point dim, Point start, Point end, Point * path, bool free)
133 short errorterm=0, length, i;
137 xdiff=(short)(end.x-start.x);
138 ydiff=(short)(end.y-start.y);
158 for(i=0; i<length; i++)
163 if(map[TILE(position.x,position.y,dim)]!=(byte)free)
168 if(errorterm>xdiff/2)
178 for(i=0; i<length; i++)
183 if(map[TILE(position.x,position.y,dim)]!=(byte)free)
188 if(errorterm>ydiff/2)
198 void MapFree(TileMap * map)
202 if(map->aStar) AStarTerminate(map->aStar);
203 if(map->dim) delete map->dim;
204 if(map->tileImage) delete map->tileImage;
208 for(e = 0; e<map->numLayers; e++)
210 delete map->moves[e];
216 for(e = 0; e<map->numLayers; e++)
218 delete map->spaces[e];
221 if(map->regions) delete map->regions;
222 if(map->contents) delete map->contents;
223 if(map->frames) delete map->frames;
228 TileMap * MapCreate(int numLayers, int width, int height, int tileW, int tileH, int numTiles)
230 TileMap * result = null;
231 TileMap * map = new0 TileMap[1];
234 map->maxDim.x = width;
235 map->maxDim.y = height;
236 map->numLayers = numLayers;
237 map->numTiles = numTiles;
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)) )
255 TileUnit * MapFindUnit(TileMap * map, int vx, int vy, int vw, int vh, int x, int y)
258 for(s = map->numLayers-1; s >= 0; s--)
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++)
264 if((cx >= 0) && (cy >= 0) && (cx < map->dim[s].x) && (cy < map->dim[s].y))
267 if((unit = map->spaces[s][TILE(cx,cy,map->dim[s])]) && unit->pickable)
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))
283 void MapRedraw(TileMap * map, Surface surface, int vx, int vy, int vw, int vh)
288 surface.SetForeground(white);
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);
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);
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++)
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);
314 // ********** UNITS **********
315 Point UnitExact(TileMap * map, TileUnit * unit)
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);
325 TileUnit * UnitCreate(TileMap * map, int space, int x, int y, void * data)
327 TileUnit * unit = new0 TileUnit[1];
330 unit->space = (byte)space;
331 unit->covering[space] = (byte)bool::true;
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};
339 unit->pickable = true;
344 void UnitFree(TileUnit * unit)
350 void UnitPlace(TileMap * map, TileUnit *unit)
353 unit->displayed=true;
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;
361 for(x=0; x<unit->w; x++)
362 for(y=0; y<unit->h; y++)
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++)
369 if(unit->covering[s])
371 int crX = map->dim[unit->space].x / map->dim[s].x;
372 int crY = map->dim[unit->space].y / map->dim[s].y;
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;
382 void UnitRemove(TileMap * map, TileUnit * unit)
385 unit->displayed=false;
386 if(unit->displaced&&(unit->pathPos<MAXPATH))
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;
393 for(x=0; x<unit->w; x++)
394 for(y=0; y<unit->h; y++)
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++)
401 if(unit->covering[s])
403 int crX = map->dim[unit->space].x / map->dim[s].x;
404 int crY = map->dim[unit->space].y / map->dim[s].y;
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;
414 bool UnitGoTo(TileMap * map, TileUnit * unit, Point dest)
417 Point Position, altpath[MAXPATH];
422 Position=unit->path[unit->pathPos];
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])])
430 pathlen = MapLine(map->moves[unit->space],map->dim[unit->space],Position,
433 dest = altpath[pathlen-1];
436 //Try to find a linear path
437 pathlen=MapLine(map->moves[unit->space],map->dim[unit->space],Position,
439 if((altpath[pathlen-1].x == dest.x && altpath[pathlen-1].y == dest.y))
442 unit->pathPos=MAXPATH-pathlen;
443 for(p=0; p<pathlen; p++)
444 unit->path[unit->pathPos+p]=altpath[p+1];
448 //Find the path with A*
449 path = AStarFindPath(map->aStar, map->moves[unit->space],map->dim[unit->space],Position,dest, MAX_ITERATIONS);
452 AStarFreeNodes(map->aStar);
456 unit->pathPos=MAXPATH;
460 unit->path[unit->pathPos]=path->position;
463 AStarFreeNodes(map->aStar);
468 unit->path[unit->pathPos]=Position;
475 void UnitUpdate(TileMap * map, TileUnit *unit)
477 Sequence *seq = unit->sequence;
486 if(unit->pathPos<MAXPATH)
490 if(!map->moves[unit->space]
491 [TILENUM(unit->path[unit->pathPos],map->dim[unit->space])])
493 unit->event = Standing;
494 if(unit->tick<seq->frames[unit->event][unit->seqPos].wait)
499 if(unit->seqPos>=seq->numFrames[unit->event])
504 //target=map->spaces[unit->space][TILENUM(unit->dest,map->dim[unit->space])];
507 if(!UnitSurround(map, unit,unit->target))
512 if(UnitGoTo(map, unit, unit->dest))
518 //if(!UnitSurround(map,unit,unit->target))
527 unit->event = Moving;
528 if(unit->tick<seq->frames[unit->event][unit->seqPos].wait) return;
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);
536 unit->direction=MapDirection(xd,yd);
538 UnitRemove(map, unit);
539 if(unit->displaced>=(map->tileW*map->maxDim.x/map->dim[unit->space].x))
547 if(unit->pathPos==MAXPATH)
549 unit->event = Standing;
554 if((unit->pathPos==MAXPATH) && !(unit->pos.x == unit->dest.x && unit->pos.y == unit->dest.y))
556 if(UnitGoTo(map, unit, unit->dest))
561 target=map->spaces[unit->space][TILENUM(unit->dest,map->dim[unit->space])];
563 UnitSurround(map, unit,target);
566 if(unit->tick<seq->frames[unit->event][unit->seqPos].wait) return;
573 if(unit->seqPos>=seq->numFrames[unit->event])
591 static int Compare(const int * a, const int * b)
593 if(*a > *b) return 1;
594 else if(*a < *b) return -1;
598 bool UnitSurround(TileMap * map,TileUnit *unit,TileUnit * target)
600 bool surrounding=false;
603 SortPosition destinations[33];
615 dest.x+=2; dest.y+=2;
616 size.x-=2; size.y-=2;
618 else if(unit->space==1)
621 size.x+=dest.x%2; size.y+=dest.y%2;
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};
627 if(!target||(target==unit))
632 UnitRemove(map, unit);
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))
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))
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)
647 unit->objective=target->pos;
650 UnitPlace(map, unit);
651 unit->pathPos = MAXPATH;
654 destinations[numDest ].position.x = x;
655 destinations[numDest++].position.y = y;
658 UnitPlace(map, unit);
659 qsort(destinations,numDest,sizeof(SortPosition), Compare);
660 for(c=0; c<numDest; c++)
662 if(UnitGoTo(map,unit,destinations[c].position))
665 unit->dest=destinations[c].position;
666 unit->objective=target->pos;
676 void UnitMove(TileMap * map,TileUnit *unit, Point dest)
680 //Just to make sure the console input will look right
681 if((map->dim[unit->space].x<map->maxDim.x))
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);
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};
696 void UnitDisplay(TileMap * map, Surface surface, TileUnit * unit, int viewX, int viewY)
698 if(/*unit->sprite && */unit->displayed)
701 Point exact = UnitExact(map, unit);
702 bool square = unit->selected;
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);
720 surface.SetForeground(white);
722 exact = Point{exact.x-viewX*map->tileW, exact.y-viewY*map->tileH};
726 unit->sprite.DisplayFrame(surface,unit->seqPos,exact.x,exact.y,true,unit->filter);
731 if(unit->sequence->numFrames[unit->event])
732 frame=unit->sequence->frames[unit->event][unit->seqPos].frame;
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)
739 frame += (Direction)8-unit->direction;
740 unit->sprite.DisplayFrame(surface,frame,exact.x,exact.y,true,unit->filter);
744 frame += unit->direction;
745 unit->sprite.DisplayFrame(surface,frame,exact.x,exact.y,false,unit->filter);
751 void UnitExit(TileMap * map, TileUnit *unit)
755 SortPosition exits[32];
756 TileUnit * targetUnit=map->spaces[0][TILENUM(unit->objective,map->dim[0])];
759 dest=targetUnit->pos;
760 size.x=targetUnit->w;
761 size.y=targetUnit->h;
763 if(!targetUnit->space)
767 dest.x+=2; dest.y+=2;
768 size.x-=2; size.y-=2;
770 else if(unit->space==1)
773 size.x+=dest.x%2; size.y+=dest.y%2;
777 // if(!targetUnit->space && unit->space!=2)
779 // dest.x++; dest.y++;
780 //size.x+=dest.x%2; size.y+=dest.y%2;
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};
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])])
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);
799 qsort(exits,numExits,sizeof(SortPosition), Compare);
802 unit->pos=exits[0].position;
810 unit->event = Standing;
813 bool UnitSurroundNearest(TileMap * map,TileUnit *unit, TileUnit **list, uint16 count)
818 for(c=0; c<count; c++)
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);
824 qsort(goals,count,sizeof(SortUnit),Compare);
825 for(c=0; c<count; c++)
826 if(UnitSurround(map,unit, goals[c].unit))
831 void UnitEnter(TileMap * map, TileUnit *unit)
833 TileUnit * targetUnit=map->spaces[0][TILENUM(unit->objective,map->dim[0])];
834 Sequence *seq = unit->sequence;
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));
844 unit->event = Moving;
846 if(unit->tick<seq->frames[unit->event][unit->seqPos].wait)
849 unit->displaced+=seq->frames[unit->event][unit->seqPos].walk;
850 if(unit->displaced>=map->tileW)
852 UnitRemove(map,unit);
854 unit->entering = false;
859 void UnitStop(TileMap * map, TileUnit *unit)
861 if(unit->pathPos<MAXPATH)
862 UnitGoTo(map,unit,unit->path[unit->pathPos]);
864 UnitGoTo(map,unit,unit->pos);