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))
114 static void MapForward(Direction direction, Point result, uint16 distance)
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;
129 static uint16 MapLine(byte *map, Point dim, Point start, Point end, Point * path, bool free)
134 short errorterm=0, length, i;
138 xdiff=(short)(end.x-start.x);
139 ydiff=(short)(end.y-start.y);
158 length=(short)(xdiff+1);
159 for(i=0; i<length; i++)
164 if(map[TILE(position.x,position.y,dim)]!=(byte)free)
169 if(errorterm>xdiff/2)
178 length=(short)(ydiff+1);
179 for(i=0; i<length; i++)
184 if(map[TILE(position.x,position.y,dim)]!=(byte)free)
189 if(errorterm>ydiff/2)
199 void MapFree(TileMap * map)
203 if(map->aStar) AStarTerminate(map->aStar);
204 if(map->dim) delete map->dim;
205 if(map->tileImage) delete map->tileImage;
209 for(e = 0; e<map->numLayers; e++)
211 delete map->moves[e];
217 for(e = 0; e<map->numLayers; e++)
219 delete map->spaces[e];
222 if(map->regions) delete map->regions;
223 if(map->contents) delete map->contents;
224 if(map->frames) delete map->frames;
229 TileMap * MapCreate(int numLayers, int width, int height, int tileW, int tileH, int numTiles)
231 TileMap * result = null;
232 TileMap * map = new0 TileMap[1];
235 map->maxDim.x = width;
236 map->maxDim.y = height;
237 map->numLayers = numLayers;
238 map->numTiles = numTiles;
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)) )
256 TileUnit * MapFindUnit(TileMap * map, int vx, int vy, int vw, int vh, int x, int y)
259 for(s = map->numLayers-1; s >= 0; s--)
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++)
265 if((cx >= 0) && (cy >= 0) && (cx < map->dim[s].x) && (cy < map->dim[s].y))
268 if((unit = map->spaces[s][TILE(cx,cy,map->dim[s])]) && unit->pickable)
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))
284 void MapRedraw(TileMap * map, Surface surface, int vx, int vy, int vw, int vh)
289 surface.SetForeground(white);
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);
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);
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++)
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);
315 // ********** UNITS **********
316 Point UnitExact(TileMap * map, TileUnit * unit)
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);
326 TileUnit * UnitCreate(TileMap * map, int space, int x, int y, void * data)
328 TileUnit * unit = new0 TileUnit[1];
331 unit->space = (byte)space;
332 unit->covering[space] = (byte)bool::true;
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};
340 unit->pickable = true;
345 void UnitFree(TileUnit * unit)
351 void UnitPlace(TileMap * map, TileUnit *unit)
354 unit->displayed=true;
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;
362 for(x=0; x<unit->w; x++)
363 for(y=0; y<unit->h; y++)
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++)
370 if(unit->covering[s])
372 int crX = map->dim[unit->space].x / map->dim[s].x;
373 int crY = map->dim[unit->space].y / map->dim[s].y;
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;
383 void UnitRemove(TileMap * map, TileUnit * unit)
386 unit->displayed=false;
387 if(unit->displaced&&(unit->pathPos<MAXPATH))
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;
394 for(x=0; x<unit->w; x++)
395 for(y=0; y<unit->h; y++)
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++)
402 if(unit->covering[s])
404 int crX = map->dim[unit->space].x / map->dim[s].x;
405 int crY = map->dim[unit->space].y / map->dim[s].y;
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;
415 bool UnitGoTo(TileMap * map, TileUnit * unit, Point dest)
418 Point Position, altpath[MAXPATH];
423 Position=unit->path[unit->pathPos];
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])])
431 pathlen = MapLine(map->moves[unit->space],map->dim[unit->space],Position,
434 dest = altpath[pathlen-1];
437 //Try to find a linear path
438 pathlen=MapLine(map->moves[unit->space],map->dim[unit->space],Position,
440 if((altpath[pathlen-1].x == dest.x && altpath[pathlen-1].y == dest.y))
443 unit->pathPos=MAXPATH-pathlen;
444 for(p=0; p<pathlen; p++)
445 unit->path[unit->pathPos+p]=altpath[p+1];
449 //Find the path with A*
450 path = AStarFindPath(map->aStar, map->moves[unit->space],map->dim[unit->space],Position,dest, MAX_ITERATIONS);
453 AStarFreeNodes(map->aStar);
457 unit->pathPos=MAXPATH;
461 unit->path[unit->pathPos]=path->position;
464 AStarFreeNodes(map->aStar);
469 unit->path[unit->pathPos]=Position;
476 void UnitUpdate(TileMap * map, TileUnit *unit)
478 Sequence *seq = unit->sequence;
487 if(unit->pathPos<MAXPATH)
491 if(!map->moves[unit->space]
492 [TILENUM(unit->path[unit->pathPos],map->dim[unit->space])])
494 unit->event = Standing;
495 if(unit->tick<seq->frames[unit->event][unit->seqPos].wait)
500 if(unit->seqPos>=seq->numFrames[unit->event])
505 //target=map->spaces[unit->space][TILENUM(unit->dest,map->dim[unit->space])];
508 if(!UnitSurround(map, unit,unit->target))
513 if(UnitGoTo(map, unit, unit->dest))
519 //if(!UnitSurround(map,unit,unit->target))
528 unit->event = Moving;
529 if(unit->tick<seq->frames[unit->event][unit->seqPos].wait) return;
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);
537 unit->direction=MapDirection(xd,yd);
539 UnitRemove(map, unit);
540 if(unit->displaced>=(map->tileW*map->maxDim.x/map->dim[unit->space].x))
548 if(unit->pathPos==MAXPATH)
550 unit->event = Standing;
555 if((unit->pathPos==MAXPATH) && !(unit->pos.x == unit->dest.x && unit->pos.y == unit->dest.y))
557 if(UnitGoTo(map, unit, unit->dest))
562 target=map->spaces[unit->space][TILENUM(unit->dest,map->dim[unit->space])];
564 UnitSurround(map, unit,target);
567 if(unit->tick<seq->frames[unit->event][unit->seqPos].wait) return;
574 if(unit->seqPos>=seq->numFrames[unit->event])
592 static int Compare(const int * a, const int * b)
594 if(*a > *b) return 1;
595 else if(*a < *b) return -1;
599 bool UnitSurround(TileMap * map,TileUnit *unit,TileUnit * target)
601 bool surrounding=false;
604 SortPosition destinations[33];
616 dest.x+=2; dest.y+=2;
617 size.x-=2; size.y-=2;
619 else if(unit->space==1)
622 size.x+=dest.x%2; size.y+=dest.y%2;
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};
628 if(!target||(target==unit))
633 UnitRemove(map, unit);
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))
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))
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)
648 unit->objective=target->pos;
651 UnitPlace(map, unit);
652 unit->pathPos = MAXPATH;
655 destinations[numDest ].position.x = x;
656 destinations[numDest++].position.y = y;
659 UnitPlace(map, unit);
660 qsort(destinations,numDest,sizeof(SortPosition), Compare);
661 for(c=0; c<numDest; c++)
663 if(UnitGoTo(map,unit,destinations[c].position))
666 unit->dest=destinations[c].position;
667 unit->objective=target->pos;
677 void UnitMove(TileMap * map,TileUnit *unit, Point dest)
681 //Just to make sure the console input will look right
682 if((map->dim[unit->space].x<map->maxDim.x))
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);
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};
697 void UnitDisplay(TileMap * map, Surface surface, TileUnit * unit, int viewX, int viewY)
699 if(/*unit->sprite && */unit->displayed)
702 Point exact = UnitExact(map, unit);
703 bool square = unit->selected;
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);
721 surface.SetForeground(white);
723 exact = Point{exact.x-viewX*map->tileW, exact.y-viewY*map->tileH};
727 unit->sprite.DisplayFrame(surface,unit->seqPos,exact.x,exact.y,true,unit->filter);
732 if(unit->sequence->numFrames[unit->event])
733 frame=unit->sequence->frames[unit->event][unit->seqPos].frame;
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)
740 frame += (Direction)8-unit->direction;
741 unit->sprite.DisplayFrame(surface,frame,exact.x,exact.y,true,unit->filter);
745 frame += unit->direction;
746 unit->sprite.DisplayFrame(surface,frame,exact.x,exact.y,false,unit->filter);
752 void UnitExit(TileMap * map, TileUnit *unit)
756 SortPosition exits[32];
757 TileUnit * targetUnit=map->spaces[0][TILENUM(unit->objective,map->dim[0])];
760 dest=targetUnit->pos;
761 size.x=targetUnit->w;
762 size.y=targetUnit->h;
764 if(!targetUnit->space)
768 dest.x+=2; dest.y+=2;
769 size.x-=2; size.y-=2;
771 else if(unit->space==1)
774 size.x+=dest.x%2; size.y+=dest.y%2;
778 // if(!targetUnit->space && unit->space!=2)
780 // dest.x++; dest.y++;
781 //size.x+=dest.x%2; size.y+=dest.y%2;
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};
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])])
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);
800 qsort(exits,numExits,sizeof(SortPosition), Compare);
803 unit->pos=exits[0].position;
811 unit->event = Standing;
814 bool UnitSurroundNearest(TileMap * map,TileUnit *unit, TileUnit **list, uint16 count)
819 for(c=0; c<count; c++)
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);
825 qsort(goals,count,sizeof(SortUnit),Compare);
826 for(c=0; c<count; c++)
827 if(UnitSurround(map,unit, goals[c].unit))
832 void UnitEnter(TileMap * map, TileUnit *unit)
834 TileUnit * targetUnit=map->spaces[0][TILENUM(unit->objective,map->dim[0])];
835 Sequence *seq = unit->sequence;
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));
845 unit->event = Moving;
847 if(unit->tick<seq->frames[unit->event][unit->seqPos].wait)
850 unit->displaced+=seq->frames[unit->event][unit->seqPos].walk;
851 if(unit->displaced>=map->tileW)
853 UnitRemove(map,unit);
855 unit->entering = false;
860 void UnitStop(TileMap * map, TileUnit *unit)
862 if(unit->pathPos<MAXPATH)
863 UnitGoTo(map,unit,unit->path[unit->pathPos]);
865 UnitGoTo(map,unit,unit->pos);