diff --git a/blakserv/astar.c b/blakserv/astar.c
new file mode 100644
index 0000000000..c6b887cfbc
--- /dev/null
+++ b/blakserv/astar.c
@@ -0,0 +1,504 @@
+// Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse.
+// All rights reserved.
+//
+// This software is distributed under a license that is described in
+// the LICENSE file that accompanies it.
+//
+// Meridian is a registered trademark.
+/*
+* astar.c
+*
+
+Improvements over the old implementation:
+ * Allocation of gridmemory is only done once when room is loaded
+ * Node information is split up between persistent data and data
+ which needs to be cleared for each algorithm and can be by single ZeroMemory
+ * Whether a node is in the closed set is not saved and looked up by an additional list,
+ but rather simply saved by a flag on that node.
+ * Uses a binary heap to store the open set
+*/
+
+#include "blakserv.h"
+
+// refers to the i-th element on the heap of a room
+#define HEAP(r, i) ((r)->Astar.NodesData[i].heapslot)
+
+bool AStarHeapCheck(room_type* Room, int i)
+{
+ int squares = Room->colshighres * Room->rowshighres;
+
+ if (i >= squares)
+ return true;
+
+ if (!HEAP(Room, i))
+ return true;
+
+ float our = HEAP(Room, i)->Data->combined;
+ int lchild = LCHILD(i);
+ int rchild = RCHILD(i);
+
+ if (lchild < squares)
+ {
+ astar_node* node = HEAP(Room, lchild);
+ if (node)
+ {
+ if (node->Data->combined < our)
+ return false;
+ }
+ }
+ if (rchild < squares)
+ {
+ astar_node* node = HEAP(Room, rchild);
+ if (node)
+ {
+ if (node->Data->combined < our)
+ return false;
+ }
+ }
+
+ bool retval = AStarHeapCheck(Room, lchild);
+ if (!retval)
+ return false;
+
+ return AStarHeapCheck(Room, rchild);
+}
+
+void AStarWriteHeapToFile(room_type* Room)
+{
+ int rows = Room->rowshighres;
+ int cols = Room->colshighres;
+ char* rowstring = (char *)AllocateMemory(MALLOC_ID_ROOM, 60000);
+ FILE *fp = fopen("heapdebug.txt", "a");
+ if (fp)
+ {
+ int maxlayer = 9;
+ int treesize = Room->colshighres * Room->rowshighres;
+ int rangestart = 0;
+ int rangesize = 1;
+ for (int i = 1; i < maxlayer; i++)
+ {
+ if (treesize < rangestart + rangesize)
+ break;
+
+ sprintf(rowstring, "L:%.2i", i);
+
+ for (int k = 0; k < rangesize; k++)
+ {
+ astar_node* node = Room->Astar.NodesData[rangestart + k].heapslot;
+
+ if (node)
+ sprintf(rowstring, "%s|%6.2f|", rowstring, node->Data->combined);
+ else
+ sprintf(rowstring, "%s|XXXXXX|", rowstring);
+ }
+
+ sprintf(rowstring, "%s\n", rowstring);
+ fputs(rowstring, fp);
+
+ rangestart = rangestart + rangesize;
+ rangesize *= 2;
+ }
+ sprintf(rowstring, "%s\n", rowstring);
+ fclose(fp);
+ }
+ FreeMemory(MALLOC_ID_ROOM, rowstring, 60000);
+}
+
+__inline void AStarHeapSwap(room_type* Room, int idx1, int idx2)
+{
+ astar_node* node1 = HEAP(Room, idx1);
+ astar_node* node2 = HEAP(Room, idx2);
+ HEAP(Room, idx1) = node2;
+ HEAP(Room, idx2) = node1;
+ node1->Data->heapindex = idx2;
+ node2->Data->heapindex = idx1;
+}
+
+__inline void AStarHeapMoveUp(room_type* Room, int Index)
+{
+ int i = Index;
+ while (i > 0 && HEAP(Room, i)->Data->combined < HEAP(Room, PARENT(i))->Data->combined)
+ {
+ AStarHeapSwap(Room, i, PARENT(i));
+ i = PARENT(i);
+ }
+}
+
+__inline void AStarHeapHeapify(room_type* Room, int Index)
+{
+ int i = Index;
+ do
+ {
+ int min = i;
+ if (HEAP(Room, LCHILD(i)) && HEAP(Room, LCHILD(i))->Data->combined < HEAP(Room, min)->Data->combined)
+ min = LCHILD(i);
+ if (HEAP(Room, RCHILD(i)) && HEAP(Room, RCHILD(i))->Data->combined < HEAP(Room, min)->Data->combined)
+ min = RCHILD(i);
+ if (min == i)
+ break;
+ AStarHeapSwap(Room, i, min);
+ i = min;
+ }
+ while (true);
+}
+
+__inline void AStarHeapInsert(room_type* Room, astar_node* Node)
+{
+ // save index of this node
+ Node->Data->heapindex = Room->Astar.HeapSize;
+
+ // add node at the end
+ HEAP(Room, Node->Data->heapindex) = Node;
+
+ // increment
+ Room->Astar.HeapSize++;
+
+ // push node up until in place
+ AStarHeapMoveUp(Room, Node->Data->heapindex);
+}
+
+__inline void AStarHeapRemoveFirst(room_type* Room)
+{
+ int lastIdx = Room->Astar.HeapSize - 1;
+
+ // no elements
+ if (lastIdx < 0)
+ return;
+
+ // decrement size
+ Room->Astar.HeapSize--;
+
+ // more than 1
+ if (lastIdx > 0)
+ {
+ // put last at root
+ AStarHeapSwap(Room, 0, lastIdx);
+
+ // zero out the previous root at swapped slot
+ HEAP(Room, lastIdx)->Data->heapindex = 0;
+ HEAP(Room, lastIdx) = NULL;
+
+ // reorder tree
+ AStarHeapHeapify(Room, 0);
+ }
+
+ // only one, clear head
+ else
+ {
+ HEAP(Room, 0)->Data->heapindex = 0;
+ HEAP(Room, 0) = NULL;
+ }
+}
+
+__inline void AStarAddBlockers(room_type *Room)
+{
+ Blocker* b;
+
+ b = Room->Blocker;
+ while (b)
+ {
+ // Don't add self.
+ if (b->ObjectID == Room->Astar.ObjectID)
+ {
+ b = b->Next;
+ continue;
+ }
+
+ // Get blocker coords
+ int row = (int)roundf(b->Position.Y / 256.0f);
+ int col = (int)roundf(b->Position.X / 256.0f);
+
+ // Don't add blockers at the target coords.
+ if (abs(row - Room->Astar.EndNode->Row) < DESTBLOCKIGNORE &&
+ abs(col - Room->Astar.EndNode->Col) < DESTBLOCKIGNORE)
+ {
+ b = b->Next;
+ continue;
+ }
+
+ // Mark these nodes in A* grid blocked (our coord and +2 highres each dir)
+ for (int rowoffset = -2; rowoffset < 3; rowoffset++)
+ {
+ for (int coloffset = -2; coloffset < 3; coloffset++)
+ {
+ int r = row + rowoffset;
+ int c = col + coloffset;
+
+ // outside
+ if (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres)
+ continue;
+
+ astar_node* node = &Room->Astar.Grid[r][c];
+ node->Data->isBlocked = true;
+ }
+ }
+ b = b->Next;
+ }
+}
+
+__inline bool AStarProcessNode(room_type* Room)
+{
+ // shortcuts
+ astar_node* node = HEAP(Room, 0);
+ astar_node* endNode = Room->Astar.EndNode;
+
+ Wall* blockWall;
+
+ // openlist empty, unreachable
+ if (!node)
+ return false;
+
+ // close enough, we're done, path found
+ if (abs(node->Col - endNode->Col) < CLOSEENOUGHDIST &&
+ abs(node->Row - endNode->Row) < CLOSEENOUGHDIST)
+ {
+ Room->Astar.LastNode = node;
+ return false;
+ }
+
+ // these 9 loop-iterations will go over all 8 adjacent squares and the node itself
+ // adjacent squares which are reachable will be added to the open-list with their costs
+ for (int rowoffset = -1; rowoffset < 2; rowoffset++)
+ {
+ for (int coloffset = -1; coloffset < 2; coloffset++)
+ {
+ // skip evaluating ourself
+ if (rowoffset == 0 && coloffset == 0)
+ continue;
+
+ // indices of node to process
+ int r = node->Row + rowoffset;
+ int c = node->Col + coloffset;
+
+ // outside
+ if (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres)
+ continue;
+
+ // get candidate node for indices
+ astar_node* candidate = &Room->Astar.Grid[r][c];
+
+ // skip any already examined, outside or blocked node
+ if (candidate->Data->isInClosedList || !candidate->Leaf || candidate->Data->isBlocked)
+ continue;
+
+ // can't move from node to this candidate
+ if (!BSPCanMoveInRoom(Room, &node->Location, &candidate->Location, Room->Astar.ObjectID, false, &blockWall, true))
+ continue;
+
+ // cost for diagonal is sqrt(2), otherwise 1
+ float stepcost = ((rowoffset != 0) && (coloffset != 0)) ? COST_DIAG : COST;
+
+ // heuristic (~ estimated distance from node to end)
+ // need to do this only once
+ if (candidate->Data->heuristic == 0.0f)
+ {
+ float dx = (float)abs(candidate->Col - endNode->Col);
+ float dy = (float)abs(candidate->Row - endNode->Row);
+
+ // octile-distance
+ candidate->Data->heuristic =
+ COST * (dx + dy) + (COST_DIAG - 2.0f * COST) * fminf(dx, dy);
+
+ // tie breaker and fixes h(nondiagonal) not lower exact cost
+ candidate->Data->heuristic *= 0.999f;
+ }
+
+ // CASE 1)
+ // if this candidate has no parent yet (never visited before)
+ if (!candidate->Data->parent)
+ {
+ // cost to candidate is cost to Node + one step
+ candidate->Data->parent = node;
+ candidate->Data->cost = node->Data->cost + stepcost;
+ candidate->Data->combined = candidate->Data->cost + candidate->Data->heuristic;
+
+ // add it sorted to the open list
+ AStarHeapInsert(Room, candidate);
+ }
+
+ // CASE 2)
+ // we already got a path to this candidate
+ else
+ {
+ // our cost to the candidate
+ float newcost = node->Data->cost + stepcost;
+
+ // we're cheaper, so update the candidate
+ // the real cost matters here, not including the heuristic
+ if (newcost < candidate->Data->cost)
+ {
+ candidate->Data->parent = node;
+ candidate->Data->cost = newcost;
+ candidate->Data->combined = newcost + candidate->Data->heuristic;
+
+ // reorder it upwards in the heap tree, don't care about downordering
+ // since costs are lower and heuristic is always the same,
+ // it's guaranteed to be moved up
+ AStarHeapMoveUp(Room, candidate->Data->heapindex);
+ }
+ }
+ }
+ }
+
+ /****************************************************************/
+
+ // mark this node processed
+ node->Data->isInClosedList = true;
+
+ // remove it from the open list
+ AStarHeapRemoveFirst(Room);
+
+ return true;
+}
+
+void AStarWriteGridToFile(room_type* Room)
+{
+ int rows = Room->rowshighres;
+ int cols = Room->colshighres;
+
+ char *rowstring = (char *)AllocateMemory(MALLOC_ID_ROOM, 50000);
+
+ FILE *fp = fopen("griddebug.txt", "w");
+ if (fp)
+ {
+ for (int row = 0; row < rows; row++)
+ {
+ sprintf(rowstring, "Row %3i- ", row);
+ for (int col = 0; col < cols; col++)
+ {
+ sprintf(rowstring, "%s|%7.3f|", rowstring, Room->Astar.Grid[row][col].Data->combined);
+ }
+ sprintf(rowstring, "%s \n", rowstring);
+ fputs(rowstring, fp);
+ }
+ fclose(fp);
+ }
+
+ FreeMemory(MALLOC_ID_ROOM, rowstring, 50000);
+}
+
+void AStarGenerateGrid(room_type* Room)
+{
+ // note: we allocate all memory for the astar_node_data of all squares together
+ // this is necessary so we can erase it with a single cheap ZeroMemory call..
+ Room->Astar.NodesDataSize = Room->colshighres * Room->rowshighres * sizeof(astar_node_data);
+
+ // allocate memory for all nodesdata (cleaned for each calculation)
+ Room->Astar.NodesData = (astar_node_data*)AllocateMemory(
+ MALLOC_ID_ASTAR, Room->Astar.NodesDataSize);
+
+ // allocate memory for the persistent nodesinfo (typical 2d array by **)
+ Room->Astar.Grid = (astar_node**)AllocateMemory(
+ MALLOC_ID_ASTAR, Room->rowshighres * sizeof(astar_node*));
+
+ // setup rows
+ for (int i = 0; i < Room->rowshighres; i++)
+ Room->Astar.Grid[i] = (astar_node*)AllocateMemory(
+ MALLOC_ID_ASTAR, Room->colshighres * sizeof(astar_node));
+
+ // setup all squares
+ for (int i = 0; i < Room->rowshighres; i++)
+ {
+ for (int j = 0; j < Room->colshighres; j++)
+ {
+ astar_node* node = &Room->Astar.Grid[i][j];
+ float f1, f2, f3;
+ node->Row = i;
+ node->Col = j;
+
+ // floatingpoint coordinates of the center of the square in ROO fineness (for queries)
+ node->Location.X = j * 256.0f;// +128.0f;
+ node->Location.Y = i * 256.0f;// +128.0f;
+
+ // mark if this square is inside or outside of room
+ BSPGetHeight(Room, &node->Location, &f1, &f2, &f3, &node->Leaf);
+
+ // setup reference to data (costs etc.) in erasable mem area
+ node->Data = &Room->Astar.NodesData[i*Room->colshighres + j];
+ }
+ }
+}
+
+void AStarFreeGrid(room_type* Room)
+{
+ // free workdata mem
+ FreeMemory(MALLOC_ID_ASTAR, Room->Astar.NodesData, Room->Astar.NodesDataSize);
+
+ // free each row mem
+ for (int i = 0; i < Room->rowshighres; i++)
+ FreeMemory(MALLOC_ID_ASTAR, Room->Astar.Grid[i], Room->colshighres * sizeof(astar_node));
+
+ // free rowsmem
+ FreeMemory(MALLOC_ID_ASTAR, Room->Astar.Grid, Room->rowshighres * sizeof(astar_node*));
+}
+
+bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID)
+{
+ // convert coordinates from ROO floatingpoint to
+ // highres scale in integers
+ int startrow = (int)roundf(S->Y / 256.0f);
+ int startcol = (int)roundf(S->X / 256.0f);
+ int endrow = (int)roundf(E->Y / 256.0f);
+ int endcol = (int)roundf(E->X / 256.0f);
+
+ // all 4 values must be within grid array bounds!
+ if (startrow < 0 || startrow >= Room->rowshighres ||
+ startcol < 0 || startcol >= Room->colshighres ||
+ endrow < 0 || endrow >= Room->rowshighres ||
+ endcol < 0 || endcol >= Room->colshighres)
+ return false;
+
+ /**********************************************************************/
+
+ // get start and endnode
+ astar_node* startnode = &Room->Astar.Grid[startrow][startcol];
+ Room->Astar.EndNode = &Room->Astar.Grid[endrow][endcol];
+
+ // init the astar struct
+ Room->Astar.LastNode = NULL;
+ Room->Astar.ObjectID = ObjectID;
+ Room->Astar.HeapSize = 0;
+
+ // prepare non-persistent astar grid data memory
+ ZeroMemory(Room->Astar.NodesData, Room->Astar.NodesDataSize);
+
+ /**********************************************************************/
+
+ // mark nodes blocked by objects
+ AStarAddBlockers(Room);
+
+ // insert startnode into heap-tree
+ AStarHeapInsert(Room, startnode);
+
+ // the algorithm finishes if we either hit a node close enough to endnode
+ // or if there is no more entries in the open list (unreachable)
+ while (AStarProcessNode(Room)) { }
+
+ //AStarWriteGridToFile(Room);
+ //AStarWriteHeapToFile(Room);
+ /**********************************************************************/
+
+ // now let's walk back our parent pointers starting from the LastNode
+ // this chain represents the path (if it was unreachable, it's null)
+ astar_node* parent = Room->Astar.LastNode;
+ while (parent && parent->Data->parent != startnode)
+ parent = parent->Data->parent;
+
+ // unreachable
+ if (!parent)
+ return false;
+
+ // for diagonal moves mark to be long step (required for timer elapse)
+ if (abs(parent->Col - startnode->Col) &&
+ abs(parent->Row - startnode->Row))
+ {
+ *Flags |= ESTATE_LONG_STEP;
+ }
+ else
+ *Flags &= ~ESTATE_LONG_STEP;
+
+ // set step endpoint
+ *P = parent->Location;
+
+ return true;
+}
diff --git a/blakserv/astar.h b/blakserv/astar.h
new file mode 100644
index 0000000000..d454e22eb2
--- /dev/null
+++ b/blakserv/astar.h
@@ -0,0 +1,67 @@
+// Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse.
+// All rights reserved.
+//
+// This software is distributed under a license that is described in
+// the LICENSE file that accompanies it.
+//
+// Meridian is a registered trademark.
+/*
+* astar.h:
+*/
+
+#ifndef _ASTAR_H
+#define _ASTAR_H
+
+#define CLOSEENOUGHDIST 3
+#define DESTBLOCKIGNORE 3
+#define ASTARENABLED 1
+
+#define LCHILD(x) (2 * x + 1)
+#define RCHILD(x) (2 * x + 2)
+#define PARENT(x) ((x-1) / 2)
+
+#define COST 1.0f
+#define COST_DIAG ((float)M_SQRT2)
+
+typedef struct room_type room_type;
+typedef struct BspLeaf BspLeaf;
+typedef struct astar_node_data astar_node_data;
+typedef struct astar_node astar_node;
+
+typedef struct astar_node_data
+{
+ float cost;
+ float heuristic;
+ float combined;
+ astar_node* parent;
+ astar_node* heapslot;
+ int heapindex;
+ bool isInClosedList;
+ bool isBlocked;
+} astar_node_data;
+
+typedef struct astar_node
+{
+ int Row;
+ int Col;
+ V2 Location;
+ BspLeaf* Leaf;
+ astar_node_data* Data;
+} astar_node;
+
+typedef struct astar
+{
+ astar_node_data* NodesData;
+ int NodesDataSize;
+ astar_node** Grid;
+ astar_node* EndNode;
+ astar_node* LastNode;
+ int ObjectID;
+ int HeapSize;
+} astar;
+
+void AStarGenerateGrid(room_type* Room);
+void AStarFreeGrid(room_type* Room);
+bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID);
+
+#endif /*#ifndef _ASTAR_H */
\ No newline at end of file
diff --git a/blakserv/blakserv.h b/blakserv/blakserv.h
index 912234cc9f..651c9760ce 100644
--- a/blakserv/blakserv.h
+++ b/blakserv/blakserv.h
@@ -241,6 +241,8 @@ char * GetLastErrorStr();
#include "stringinthash.h"
#include "intstringhash.h"
+#include "geometry.h"
+
#include "blakres.h"
#include "channel.h"
#include "kodbase.h"
@@ -257,6 +259,7 @@ char * GetLastErrorStr();
#include "system.h"
#include "loadrsc.h"
#include "loadgame.h"
+#include "astar.h"
#include "roofile.h"
#include "roomdata.h"
#include "files.h"
diff --git a/blakserv/blakserv.vcxproj b/blakserv/blakserv.vcxproj
index b17bf0ee56..d41514efc8 100644
--- a/blakserv/blakserv.vcxproj
+++ b/blakserv/blakserv.vcxproj
@@ -168,6 +168,7 @@ copy $(SolutionDir)bin\libcurl.dll $(SolutionDir)run\server
+
@@ -242,6 +243,7 @@ copy $(SolutionDir)bin\libcurl.dll $(SolutionDir)run\server
+
diff --git a/blakserv/blakserv.vcxproj.filters b/blakserv/blakserv.vcxproj.filters
index 4da1eda561..7e16fa6f6d 100644
--- a/blakserv/blakserv.vcxproj.filters
+++ b/blakserv/blakserv.vcxproj.filters
@@ -230,6 +230,9 @@
Header Files
+
+ Header Files
+
@@ -436,6 +439,9 @@
Source Files
+
+ Source Files
+
diff --git a/blakserv/ccode.c b/blakserv/ccode.c
index 6fa8ee4a6b..31e8855bd1 100644
--- a/blakserv/ccode.c
+++ b/blakserv/ccode.c
@@ -2675,7 +2675,7 @@ int C_CanMoveInRoomBSP(int object_id, local_var_type *local_vars,
e.Y = GRIDCOORDTOROO(row_dest.v.data, finerow_dest.v.data);
Wall* blockWall;
- ret_val.v.data = BSPCanMoveInRoom(&r->data, &s, &e, objectid.v.data, (move_outside_bsp.v.data != 0), &blockWall);
+ ret_val.v.data = BSPCanMoveInRoom(&r->data, &s, &e, objectid.v.data, (move_outside_bsp.v.data != 0), &blockWall, false);
#if DEBUGMOVE
//dprintf("MOVE:%i R:%i S:(%1.2f/%1.2f) E:(%1.2f/%1.2f)", ret_val.v.data, r->data.roomdata_id, s.X, s.Y, e.X, e.Y);
diff --git a/blakserv/makefile b/blakserv/makefile
index 005cd8f153..cc9436a9f3 100644
--- a/blakserv/makefile
+++ b/blakserv/makefile
@@ -91,7 +91,8 @@ OBJS = \
$(OUTDIR)\files.obj \
$(OUTDIR)\sprocket.obj \
$(OUTDIR)\database.obj \
-
+ $(OUTDIR)\astar.obj \
+
all : makedirs $(OUTDIR)\blakserv.exe
$(OUTDIR)\rscload.obj : $(TOPDIR)\util\rscload.c
diff --git a/blakserv/memory.c b/blakserv/memory.c
index dc7439ea9b..3b1dfdd80c 100644
--- a/blakserv/memory.c
+++ b/blakserv/memory.c
@@ -247,7 +247,7 @@ const char *memory_stat_names[] =
"Systimer", "Nameid",
"Class", "Message", "Object",
"List", "Object properties",
- "Configuration", "Rooms",
+ "Configuration", "Rooms", "Astar",
"Admin constants", "Buffers", "Game loading",
"Tables", "Socket blocks", "Game saving",
diff --git a/blakserv/memory.h b/blakserv/memory.h
index b2aedcfdf6..d9b63e0a43 100644
--- a/blakserv/memory.h
+++ b/blakserv/memory.h
@@ -21,7 +21,7 @@ enum
MALLOC_ID_SYSTIMER, MALLOC_ID_NAMEID,
MALLOC_ID_CLASS, MALLOC_ID_MESSAGE, MALLOC_ID_OBJECT,
MALLOC_ID_LIST, MALLOC_ID_OBJECT_PROPERTIES,
- MALLOC_ID_CONFIG, MALLOC_ID_ROOM,
+ MALLOC_ID_CONFIG, MALLOC_ID_ROOM, MALLOC_ID_ASTAR,
MALLOC_ID_ADMIN_CONSTANTS, MALLOC_ID_BUFFER, MALLOC_ID_LOAD_GAME,
MALLOC_ID_TABLE, MALLOC_ID_BLOCK, MALLOC_ID_SAVE_GAME,
diff --git a/blakserv/roofile.c b/blakserv/roofile.c
index a53796879e..b4a66ec201 100644
--- a/blakserv/roofile.c
+++ b/blakserv/roofile.c
@@ -634,7 +634,7 @@ bool BSPLineOfSight(room_type* Room, V3* S, V3* E)
/*********************************************************************************************/
/* BSPCanMoveInRoom: Checks if you can walk a straight line from (S)tart to (E)nd */
/*********************************************************************************************/
-bool BSPCanMoveInRoom(room_type* Room, V2* S, V2* E, int ObjectID, bool moveOutsideBSP, Wall** BlockWall)
+bool BSPCanMoveInRoom(room_type* Room, V2* S, V2* E, int ObjectID, bool moveOutsideBSP, Wall** BlockWall, bool SkipBlockers)
{
if (!Room || Room->TreeNodesCount == 0 || !S || !E)
return false;
@@ -655,6 +655,10 @@ bool BSPCanMoveInRoom(room_type* Room, V2* S, V2* E, int ObjectID, bool moveOuts
if (!roomok)
return false;
+ // we're done if no need to check object blockers
+ if (SkipBlockers)
+ return true;
+
// otherwise also check against blockers
Blocker* blocker = Room->Blocker;
while (blocker)
@@ -940,7 +944,7 @@ bool BSPGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags
// but must not give these back in piState
*Flags &= ~MSTATE_MOVE_OUTSIDE_BSP;
-
+
V2 se, stepend;
V2SUB(&se, E, S);
@@ -965,252 +969,265 @@ bool BSPGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags
V2SCALE(&se, scale);
/****************************************************/
- // 1) try direct step towards destination first
+ // 1) test direct line towards destination first
/****************************************************/
Wall* blockWall = NULL;
- // note: we must verify the location the object is actually going to end up in KOD,
- // this means we must round to the next closer kod-fineness value,
- // so these values are also exactly expressable in kod coordinates.
- // in fact this makes the vector a variable length between ~15.5 and ~16.5 fine units
- V2ADD(&stepend, S, &se);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
+ if (BSPCanMoveInRoom(Room, S, E, ObjectID, moveOutsideBSP, &blockWall, false))
{
- *P = stepend;
- *Flags &= ~ESTATE_AVOIDING;
- *Flags &= ~ESTATE_CLOCKWISE;
- return true;
+ // note: we must verify the location the object is actually going to end up in KOD,
+ // this means we must round to the next closer kod-fineness value,
+ // so these values are also exactly expressable in kod coordinates.
+ // in fact this makes the vector a variable length between ~15.5 and ~16.5 fine units
+ V2ADD(&stepend, S, &se);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags &= ~ESTATE_AVOIDING;
+ *Flags &= ~ESTATE_CLOCKWISE;
+ return true;
+ }
}
-
+
/****************************************************/
// 2) can't do direct step
/****************************************************/
- bool isAvoiding = ((*Flags & ESTATE_AVOIDING) == ESTATE_AVOIDING);
- bool isLeft = ((*Flags & ESTATE_CLOCKWISE) == ESTATE_CLOCKWISE);
-
- // not yet in clockwise or cclockwise mode
- if (!isAvoiding)
+ // try get next step from astar path if enabled
+ if (ASTARENABLED && AStarGetStepTowards(Room, S, E, P, Flags, ObjectID))
{
- // if not blocked by a wall, roll a dice to decide
- // how to get around the blocking obj.
- if (!blockWall)
- isLeft = (rand() % 2 == 1);
+ *Flags &= ~ESTATE_AVOIDING;
+ *Flags &= ~ESTATE_CLOCKWISE;
+ return true;
+ }
+ else
+ {
+ bool isAvoiding = ((*Flags & ESTATE_AVOIDING) == ESTATE_AVOIDING);
+ bool isLeft = ((*Flags & ESTATE_CLOCKWISE) == ESTATE_CLOCKWISE);
- // blocked by wall, go first into 'slide-along' direction
- // based on vector towards target
- else
+ // not yet in clockwise or cclockwise mode
+ if (!isAvoiding)
{
- V2 p1p2;
- V2SUB(&p1p2, &blockWall->P2, &blockWall->P1);
-
- // note: walls can be aligned in any direction like left->right, right->left,
- // same with up->down and same also with the movement vector.
- // The typical angle between vectors, acosf(..) is therefore insufficient to differ.
- // What is done here is a convert into polar-coordinates (= angle in 0..2pi from x-axis)
- // The difference (or sum) (-2pi..2pi) then provides up to 8 different cases (quadrants) which must be mapped
- // to the left or right decision.
- float f1 = atan2f(se.Y, se.X);
- float f2 = atan2f(p1p2.Y, p1p2.X);
- float df = f1 - f2;
-
- bool q1_pos = (df >= 0.0f && df <= (float)M_PI_2);
- bool q2_pos = (df >= (float)M_PI_2 && df <= (float)M_PI);
- bool q3_pos = (df >= (float)M_PI && df <= (float)(M_PI + M_PI_2));
- bool q4_pos = (df >= (float)(M_PI + M_PI_2) && df <= (float)M_PI*2.0f);
- bool q1_neg = (df <= 0.0f && df >= (float)-M_PI_2);
- bool q2_neg = (df <= (float)-M_PI_2 && df >= (float)-M_PI);
- bool q3_neg = (df <= (float)-M_PI && df >= (float)-(M_PI + M_PI_2));
- bool q4_neg = (df <= (float)-(M_PI + M_PI_2) && df >= (float)-M_PI*2.0f);
-
- isLeft = (q1_pos || q2_pos || q1_neg || q3_neg) ? false : true;
+ // if not blocked by a wall, roll a dice to decide
+ // how to get around the blocking obj.
+ if (!blockWall)
+ isLeft = (rand() % 2 == 1);
- /*if (isLeft)
- dprintf("trying left first r: %f", df);
+ // blocked by wall, go first into 'slide-along' direction
+ // based on vector towards target
else
- dprintf("trying right first r: %f", df);*/
+ {
+ V2 p1p2;
+ V2SUB(&p1p2, &blockWall->P2, &blockWall->P1);
+
+ // note: walls can be aligned in any direction like left->right, right->left,
+ // same with up->down and same also with the movement vector.
+ // The typical angle between vectors, acosf(..) is therefore insufficient to differ.
+ // What is done here is a convert into polar-coordinates (= angle in 0..2pi from x-axis)
+ // The difference (or sum) (-2pi..2pi) then provides up to 8 different cases (quadrants) which must be mapped
+ // to the left or right decision.
+ float f1 = atan2f(se.Y, se.X);
+ float f2 = atan2f(p1p2.Y, p1p2.X);
+ float df = f1 - f2;
+
+ bool q1_pos = (df >= 0.0f && df <= (float)M_PI_2);
+ bool q2_pos = (df >= (float)M_PI_2 && df <= (float)M_PI);
+ bool q3_pos = (df >= (float)M_PI && df <= (float)(M_PI + M_PI_2));
+ bool q4_pos = (df >= (float)(M_PI + M_PI_2) && df <= (float)M_PI*2.0f);
+ bool q1_neg = (df <= 0.0f && df >= (float)-M_PI_2);
+ bool q2_neg = (df <= (float)-M_PI_2 && df >= (float)-M_PI);
+ bool q3_neg = (df <= (float)-M_PI && df >= (float)-(M_PI + M_PI_2));
+ bool q4_neg = (df <= (float)-(M_PI + M_PI_2) && df >= (float)-M_PI*2.0f);
+
+ isLeft = (q1_pos || q2_pos || q1_neg || q3_neg) ? false : true;
+
+ /*if (isLeft)
+ dprintf("trying left first r: %f", df);
+ else
+ dprintf("trying right first r: %f", df);*/
+ }
}
- }
- // must run this possibly twice
- // e.g. left after right failed or right after left failed
- for (int i = 0; i < 2; i++)
- {
- if (isLeft)
+ // must run this possibly twice
+ // e.g. left after right failed or right after left failed
+ for (int i = 0; i < 2; i++)
{
- V2 v = se;
-
- // try 22.5° left
- V2ROTATE(&v, 0.5f * (float)-M_PI_4);
- V2ADD(&stepend, S, &v);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
- {
- *P = stepend;
- *Flags |= ESTATE_AVOIDING;
- *Flags |= ESTATE_CLOCKWISE;
- return true;
- }
-
- // try 45° left
- V2ROTATE(&v, 0.5f * (float)-M_PI_4);
- V2ADD(&stepend, S, &v);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
+ if (isLeft)
{
- *P = stepend;
- *Flags |= ESTATE_AVOIDING;
- *Flags |= ESTATE_CLOCKWISE;
- return true;
- }
+ V2 v = se;
+
+ // try 22.5° left
+ V2ROTATE(&v, 0.5f * (float)-M_PI_4);
+ V2ADD(&stepend, S, &v);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags |= ESTATE_AVOIDING;
+ *Flags |= ESTATE_CLOCKWISE;
+ return true;
+ }
- // try 67.5° left
- V2ROTATE(&v, 0.5f * (float)-M_PI_4);
- V2ADD(&stepend, S, &v);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
- {
- *P = stepend;
- *Flags |= ESTATE_AVOIDING;
- *Flags |= ESTATE_CLOCKWISE;
- return true;
- }
-
- // try 90° left
- V2ROTATE(&v, 0.5f * (float)-M_PI_4);
- V2ADD(&stepend, S, &v);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
- {
- *P = stepend;
- *Flags |= ESTATE_AVOIDING;
- *Flags |= ESTATE_CLOCKWISE;
- return true;
- }
+ // try 45° left
+ V2ROTATE(&v, 0.5f * (float)-M_PI_4);
+ V2ADD(&stepend, S, &v);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags |= ESTATE_AVOIDING;
+ *Flags |= ESTATE_CLOCKWISE;
+ return true;
+ }
- // try 112.5° left
- V2ROTATE(&v, 0.5f * (float)-M_PI_4);
- V2ADD(&stepend, S, &v);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
- {
- *P = stepend;
- *Flags |= ESTATE_AVOIDING;
- *Flags |= ESTATE_CLOCKWISE;
- return true;
- }
+ // try 67.5° left
+ V2ROTATE(&v, 0.5f * (float)-M_PI_4);
+ V2ADD(&stepend, S, &v);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags |= ESTATE_AVOIDING;
+ *Flags |= ESTATE_CLOCKWISE;
+ return true;
+ }
- // try 135° left
- V2ROTATE(&v, (float)-M_PI_4);
- V2ADD(&stepend, S, &v);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
- {
- *P = stepend;
- *Flags |= ESTATE_AVOIDING;
- *Flags |= ESTATE_CLOCKWISE;
- return true;
- }
+ // try 90° left
+ V2ROTATE(&v, 0.5f * (float)-M_PI_4);
+ V2ADD(&stepend, S, &v);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags |= ESTATE_AVOIDING;
+ *Flags |= ESTATE_CLOCKWISE;
+ return true;
+ }
- // failed to circumvent by going left, switch to right
- isLeft = false;
- *Flags |= ESTATE_AVOIDING;
- *Flags &= ~ESTATE_CLOCKWISE;
- }
- else
- {
- V2 v = se;
-
- // try 22.5° right
- V2ROTATE(&v, 0.5f * (float)M_PI_4);
- V2ADD(&stepend, S, &v);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
- {
- *P = stepend;
- *Flags |= ESTATE_AVOIDING;
- *Flags &= ~ESTATE_CLOCKWISE;
- return true;
- }
+ // try 112.5° left
+ V2ROTATE(&v, 0.5f * (float)-M_PI_4);
+ V2ADD(&stepend, S, &v);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags |= ESTATE_AVOIDING;
+ *Flags |= ESTATE_CLOCKWISE;
+ return true;
+ }
- // try 45° right
- V2ROTATE(&v, 0.5f * (float)M_PI_4);
- V2ADD(&stepend, S, &v);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
- {
- *P = stepend;
- *Flags |= ESTATE_AVOIDING;
- *Flags &= ~ESTATE_CLOCKWISE;
- return true;
- }
+ // try 135° left
+ V2ROTATE(&v, (float)-M_PI_4);
+ V2ADD(&stepend, S, &v);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags |= ESTATE_AVOIDING;
+ *Flags |= ESTATE_CLOCKWISE;
+ return true;
+ }
- // try 67.5° right
- V2ROTATE(&v, 0.5f * (float)M_PI_4);
- V2ADD(&stepend, S, &v);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
- {
- *P = stepend;
+ // failed to circumvent by going left, switch to right
+ isLeft = false;
*Flags |= ESTATE_AVOIDING;
*Flags &= ~ESTATE_CLOCKWISE;
- return true;
- }
-
- // try 90° right
- V2ROTATE(&v, 0.5f * (float)M_PI_4);
- V2ADD(&stepend, S, &v);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
- {
- *P = stepend;
- *Flags |= ESTATE_AVOIDING;
- *Flags &= ~ESTATE_CLOCKWISE;
- return true;
}
-
- // try 112.5° right
- V2ROTATE(&v, 0.5f * (float)M_PI_4);
- V2ADD(&stepend, S, &v);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
+ else
{
- *P = stepend;
- *Flags |= ESTATE_AVOIDING;
- *Flags &= ~ESTATE_CLOCKWISE;
- return true;
- }
+ V2 v = se;
+
+ // try 22.5° right
+ V2ROTATE(&v, 0.5f * (float)M_PI_4);
+ V2ADD(&stepend, S, &v);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags |= ESTATE_AVOIDING;
+ *Flags &= ~ESTATE_CLOCKWISE;
+ return true;
+ }
- // try 135° right
- V2ROTATE(&v, 0.5f * (float)M_PI_4);
- V2ADD(&stepend, S, &v);
- stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
- stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
- if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall))
- {
- *P = stepend;
+ // try 45° right
+ V2ROTATE(&v, 0.5f * (float)M_PI_4);
+ V2ADD(&stepend, S, &v);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags |= ESTATE_AVOIDING;
+ *Flags &= ~ESTATE_CLOCKWISE;
+ return true;
+ }
+
+ // try 67.5° right
+ V2ROTATE(&v, 0.5f * (float)M_PI_4);
+ V2ADD(&stepend, S, &v);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags |= ESTATE_AVOIDING;
+ *Flags &= ~ESTATE_CLOCKWISE;
+ return true;
+ }
+
+ // try 90° right
+ V2ROTATE(&v, 0.5f * (float)M_PI_4);
+ V2ADD(&stepend, S, &v);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags |= ESTATE_AVOIDING;
+ *Flags &= ~ESTATE_CLOCKWISE;
+ return true;
+ }
+
+ // try 112.5° right
+ V2ROTATE(&v, 0.5f * (float)M_PI_4);
+ V2ADD(&stepend, S, &v);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags |= ESTATE_AVOIDING;
+ *Flags &= ~ESTATE_CLOCKWISE;
+ return true;
+ }
+
+ // try 135° right
+ V2ROTATE(&v, 0.5f * (float)M_PI_4);
+ V2ADD(&stepend, S, &v);
+ stepend.X = ROUNDROOTOKODFINENESS(stepend.X);
+ stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y);
+ if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false))
+ {
+ *P = stepend;
+ *Flags |= ESTATE_AVOIDING;
+ *Flags &= ~ESTATE_CLOCKWISE;
+ return true;
+ }
+
+ // failed to circumvent by going right, switch to left
+ isLeft = true;
*Flags |= ESTATE_AVOIDING;
- *Flags &= ~ESTATE_CLOCKWISE;
- return true;
+ *Flags |= ESTATE_CLOCKWISE;
}
-
- // failed to circumvent by going right, switch to left
- isLeft = true;
- *Flags |= ESTATE_AVOIDING;
- *Flags |= ESTATE_CLOCKWISE;
}
}
@@ -1880,6 +1897,12 @@ bool BSPLoadRoom(char *fname, room_type *room)
}
}
+ /*************************************************************************/
+ /* GENERATE ASTAR PERSISTENT DATA */
+ /*************************************************************************/
+
+ AStarGenerateGrid(room);
+
/****************************************************************************/
/****************************************************************************/
@@ -1933,6 +1956,8 @@ void BSPFreeRoom(room_type *room)
BSPBlockerClear(room);
+ AStarFreeGrid(room);
+
/****************************************************************************/
/* SERVER PARTS */
/****************************************************************************/
diff --git a/blakserv/roofile.h b/blakserv/roofile.h
index 0d5f062340..071cf16ccc 100644
--- a/blakserv/roofile.h
+++ b/blakserv/roofile.h
@@ -13,8 +13,6 @@
#ifndef _ROOFILE_H
#define _ROOFILE_H
-#include "geometry.h"
-
#pragma region Macros
/**************************************************************************************************************/
/* MACROS */
@@ -53,6 +51,7 @@
#define ROUNDROOTOKODFINENESS(a) FINENESSKODTOROO(roundf(FINENESSROOTOKOD(a)))
// from blakston.khd, used in BSPGetNextStepTowards across calls
+#define ESTATE_LONG_STEP 0x00002000
#define ESTATE_AVOIDING 0x00004000
#define ESTATE_CLOCKWISE 0x00008000
@@ -210,7 +209,9 @@ typedef struct room_type
Side* Sides;
unsigned short SidesCount;
Sector* Sectors;
- unsigned short SectorsCount;
+ unsigned short SectorsCount;
+
+ astar Astar;
} room_type;
#pragma endregion
@@ -219,7 +220,7 @@ typedef struct room_type
/* METHODS */
/**************************************************************************************************************/
bool BSPGetHeight(room_type* Room, V2* P, float* HeightF, float* HeightFWD, float* HeightC, BspLeaf** Leaf);
-bool BSPCanMoveInRoom(room_type* Room, V2* S, V2* E, int ObjectID, bool moveOutsideBSP, Wall** BlockWall);
+bool BSPCanMoveInRoom(room_type* Room, V2* S, V2* E, int ObjectID, bool moveOutsideBSP, Wall** BlockWall, bool SkipBlockers);
bool BSPLineOfSight(room_type* Room, V3* S, V3* E);
void BSPChangeTexture(room_type* Room, unsigned int ServerID, unsigned short NewTexture, unsigned int Flags);
void BSPMoveSector(room_type* Room, unsigned int ServerID, bool Floor, float Height, float Speed);
diff --git a/kod/include/blakston.khd b/kod/include/blakston.khd
index 38a6f84cf5..e0b37dec85 100644
--- a/kod/include/blakston.khd
+++ b/kod/include/blakston.khd
@@ -1607,6 +1607,7 @@
STATE_MOVE = 0x00010
STATE_ZERO_MASK = 0xFF000
+ ESTATE_LONG_STEP = 0x02000
ESTATE_AVOIDING = 0x04000
ESTATE_CLOCKWISE = 0x08000
diff --git a/kod/object/active/holder/nomoveon/battler/monster.kod b/kod/object/active/holder/nomoveon/battler/monster.kod
index b2fa4765c2..321b465684 100644
--- a/kod/object/active/holder/nomoveon/battler/monster.kod
+++ b/kod/object/active/holder/nomoveon/battler/monster.kod
@@ -5352,6 +5352,13 @@ messages:
% see formula above
iTime = 10000 / (viSpeed * 4);
+ % diagonal move: scale-up the time according to the increased distance
+ if piState & ESTATE_LONG_STEP
+ {
+ % sqrt(2) = 1.41421356
+ iTime = (14142 * iTime) / 10000;
+ }
+
% finally we reduce the timer a bit (right now it's exact at ms)
% why? the client should get a new position before the last destination
% is reached. This prevents cases of stuttering if our message was a
diff --git a/kod/object/active/holder/room/monsroom/h5.kod b/kod/object/active/holder/room/monsroom/h5.kod
index a36f980dd1..04e08b4c3c 100644
--- a/kod/object/active/holder/room/monsroom/h5.kod
+++ b/kod/object/active/holder/room/monsroom/h5.kod
@@ -63,6 +63,16 @@ messages:
plGenerators = [ [19, 7], [22, 14], [27, 25], [25, 36],
[38, 34], [46, 26], [36, 17], [35, 8] ];
+
+ plPatrolPaths = [ [[14,11],[13,4],[19,7]],
+ [[26,10],[22,7],[22,14]],
+ [[25,34],[20,27],[27,25]],
+ [[24,22],[25,36]],
+ [[44,33],[50,27],[38,34]],
+ [[48,21],[43,21],[46,26]],
+ [[26,16],[36,17]],
+ [[27,12],[38,6],[44,12],[35, 8]] ];
+
propagate;
}
diff --git a/kod/object/active/holder/room/monsroom/h6.kod b/kod/object/active/holder/room/monsroom/h6.kod
index ba2a02ccde..016fd5821f 100644
--- a/kod/object/active/holder/room/monsroom/h6.kod
+++ b/kod/object/active/holder/room/monsroom/h6.kod
@@ -73,6 +73,21 @@ messages:
plGenerators = [ [8, 8], [9, 14], [5, 25], [10, 30], [18, 18],
[25, 19], [20, 3], [25, 9], [42, 5], [45, 12],
[41, 22], [35, 30], [47, 34] ];
+
+ plPatrolPaths = [ [$],
+ [[4,20],[9, 14]],
+ [$],
+ [$],
+ [[13,8],[25,18],[18, 18]],
+ [[21,32],[31,31],[25, 19]],
+ [$],
+ [$],
+ [[46,12],[37,12],[42, 5]],
+ [[45,18],[48,18],[45, 12]],
+ [[40,26],[37,26],[36,21],[40,19],[41, 22]],
+ [[54,33],[35, 30]],
+ [[17,11],[47, 34]] ];
+
propagate;
}
diff --git a/kod/object/active/holder/room/monsroom/i6.kod b/kod/object/active/holder/room/monsroom/i6.kod
index 8e0d34c624..b19f1b0986 100644
--- a/kod/object/active/holder/room/monsroom/i6.kod
+++ b/kod/object/active/holder/room/monsroom/i6.kod
@@ -65,6 +65,23 @@ messages:
plGenerators = [ [8, 9], [11, 24], [19, 10], [26, 21], [33, 7],
[36, 16], [35, 26], [41, 6], [43, 20], [46, 35],
[38, 43], [29, 43], [20, 33], [9, 35], [16, 48]];
+
+ plPatrolPaths = [ [[14,7],[7,15],[8,9]],
+ [[6,27],[10,21],[11,24]],
+ [[10,4],[19,10]],
+ [[27,35],[26,21]],
+ [[37,12],[33,15],[28,12],[33,7]],
+ [[41,12],[39,25],[36,16]],
+ [[46,33],[35,26]],
+ [[17,5],[41,6]],
+ [[27,20],[43,20]],
+ [[34,45],[46,35]],
+ [[27,22],[38,43]],
+ [[10,34],[21,29],[29,43]],
+ [[34,25],[20,33]],
+ [[37,44],[9,35]],
+ [[7,44],[16,48]] ];
+
propagate;
}
diff --git a/kod/object/active/holder/room/monsroom/i7.kod b/kod/object/active/holder/room/monsroom/i7.kod
index d288fb03b6..72969cf428 100644
--- a/kod/object/active/holder/room/monsroom/i7.kod
+++ b/kod/object/active/holder/room/monsroom/i7.kod
@@ -66,6 +66,23 @@ messages:
plGenerators = [ [9, 8], [4, 19], [18, 9], [18, 4], [20, 19],
[7, 25], [5, 39], [26, 31], [35, 35], [40, 30],
[32, 19], [40, 14], [41, 43], [44, 47], [33, 51] ];
+
+ plPatrolPaths = [ [[19,8],[8,2],[9, 8]],
+ [[4,15],[6,19],[3,22],[4,19]],
+ [[23,12],[18,9]],
+ [[19,6],[18,2],[18,4]],
+ [[12,24],[8,38],[20,19]],
+ [[11,27],[7,25]],
+ [[7,30],[5,39]],
+ [[20,34],[23,27],[29,31],[26,31]],
+ [[38,28],[35,35]],
+ [[41,4],[40,30]],
+ [$],
+ [[21,51],[40,14]],
+ [[43,49],[41,43]],
+ [[48,31],[44,47]],
+ [$] ];
+
propagate;
}
diff --git a/kod/object/active/holder/room/monsroom/i8.kod b/kod/object/active/holder/room/monsroom/i8.kod
index d5ae0738ec..48fd64eff4 100644
--- a/kod/object/active/holder/room/monsroom/i8.kod
+++ b/kod/object/active/holder/room/monsroom/i8.kod
@@ -65,6 +65,22 @@ messages:
plGenerators = [ [3, 3], [7, 7], [19, 12], [22, 19], [20, 27],
[16, 35], [26, 34], [30, 39], [30, 14], [36, 20],
[38, 29], [48, 25], [54, 27], [50, 35] ];
+
+ plPatrolPaths = [ [[9,3],[7,8],[3,3]],
+ [[11,5],[16,12],[18,8],[7,7]],
+ [[19,16],[21,26],[17,12],[19,12]],
+ [[24,21],[27,27],[22,19]],
+ [[17,38],[20,27]],
+ [[17,28],[20,36],[16,35]],
+ [[30,31],[31,39],[26,34]],
+ [[25,33],[30,39]],
+ [[32,26],[30,14]],
+ [[36,30],[36,20]],
+ [[52,39],[38,29]],
+ [[35,20],[57,28],[48,25]],
+ [[52,20],[54,27]],
+ [[54,40],[50,35]] ];
+
propagate;
}