/* =========================================================================== ARX FATALIS GPL Source Code Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company. This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code'). Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see . In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "Minos_PathFinder.h" #include #include #define _CRTDBG_MAP_ALLOC #include const Float MIN_RADIUS(110.F); Float fac3(300.0F); Float fac5(130.0F); static const unsigned long SEED = 43; static const unsigned long MODULO = 2147483647; static const unsigned long FACTOR = 16807; static const unsigned long SHIFT = 91; static unsigned long __current(SEED); static unsigned long Random() { return __current = (__current * FACTOR + SHIFT) % MODULO; } ULong InitSeed() { __current = (ULong)time(NULL); return Random(); } #define frnd() (1.0F - 2 * rnd()) /////////////////////////////////////////////////////////////////////////////// // // // Constructor and destructor // // // /////////////////////////////////////////////////////////////////////////////// // Default constructor // PathFinder::PathFinder(const ULong & map_size, _ANCHOR_DATA * map_data, const ULong & slight_count, EERIE_LIGHT ** slight_list, const ULong & dlight_count, EERIE_LIGHT ** dlight_list) : heuristic(MINOS_DEFAULT_HEURISTIC), map_s(map_size), map_d(map_data), slight_c(slight_count), slight_l(slight_list), dlight_c(dlight_count), dlight_l(dlight_list), height(MINOS_DEFAULT_RADIUS), radius(MINOS_DEFAULT_HEIGHT) { InitSeed(); } // Destructor // PathFinder::~PathFinder() { Clean(); } /////////////////////////////////////////////////////////////////////////////// // // // Setup // // // /////////////////////////////////////////////////////////////////////////////// void PathFinder::SetHeuristic(const Float & _heuristic) { heuristic = _heuristic >= MINOS_HEURISTIC_MAX ? 0.5F : _heuristic < 0.0F ? MINOS_HEURISTIC_MIN : _heuristic; } void PathFinder::SetCylinder(const Float & _radius, const Float & _height) { radius = _radius; height = _height; } /////////////////////////////////////////////////////////////////////////////// // // // Methods // // // /////////////////////////////////////////////////////////////////////////////// UBool PathFinder::Move(const ULong & flags, const ULong & f, const ULong & t, SLong * rstep, UWord ** rlist) { MINOSNode * node, *child; long _from, _to; //Init open and close lists Clean(); if (!rlist || !rstep) return UFALSE; if (f == t) { *rlist = (UWord *)malloc(sizeof(UWord)); ** rlist = (UWord)t; *rstep = 1; return UTRUE; } _from = f, _to = t; //Create start node and put it on open list if (!(node = CreateNode(_from, NULL))) { Clean(); // Cyril *rstep = 0; return UFALSE; } node->g_cost = 0; node->f_cost = Distance(map_d[_from].pos, map_d[_to].pos); if (flags & MINOS_STEALTH) AddEnlightmentCost(node); if (open.Append(node)) { free(node); Clean(); // Cyril *rstep = 0; return UFALSE; } //A* main loop while (node = GetBestNode()) { //If it's the goal node then we've done if (node->data == _to) { if (close.Append(node)) { free(node); Clean(); *rstep = 0; return UFALSE; } if (BuildPath(rlist, rstep)) { Clean(); *rstep = 0; return UFALSE; } Clean(); // Cyril return UTRUE; } //Otherwise, generate child from current node long _pipo(node->data); for (SWord i(0); i < map_d[_pipo].nblinked; i++) { //Create new child child = CreateNode(map_d[_pipo].linked[i], node); if (!child) { free(node); Clean(); // Cyril *rstep = 0; return UFALSE; } //Cost to reach this node if ((map_d[child->data].flags & ANCHOR_FLAG_BLOCKED) || map_d[child->data].height > height || map_d[child->data].radius < radius) free(child); else { child->g_cost = node->g_cost + Distance(map_d[child->data].pos, map_d[node->data].pos); if (flags & MINOS_STEALTH) AddEnlightmentCost(child); if (Check(child)) { if (open.Append(child)) { free(node); free(child); *rstep = 0; return UFALSE; } //Get total cost for this node child->f_cost = heuristic * child->g_cost + (1.0F - heuristic) * Distance(map_d[child->data].pos, map_d[_to].pos); } else free(child); } } //Put node onto close list as we have now examined this node if (close.Append(node)) { free(node); Clean(); // Cyril *rstep = 0; return UFALSE; } } //No path found!!! Clean(); *rstep = 0; return UFALSE; } UBool PathFinder::Flee(const ULong & flags, const ULong & f, const EERIE_3D & danger, const Float & safe_dist, SLong * rstep, UWord ** rlist) { MINOSNode * node, *child; long _from; //Init open and close lists Clean(); if (!rlist || !rstep) return UFALSE; if (Distance(map_d[f].pos, danger) >= safe_dist) { *rlist = (UWord *)malloc(sizeof(UWord)); ** rlist = (UWord)f; *rstep = 1; return UTRUE; } _from = f; //Create start node and put it on open list if (!(node = CreateNode(_from, NULL))) { Clean(); *rstep = 0; return UFALSE; } node->g_cost = 0; if (flags & MINOS_STEALTH) AddEnlightmentCost(node); node->f_cost = safe_dist - Distance(map_d[_from].pos, danger); if (node->f_cost < 0.0F) node->f_cost = 0.0F; node->f_cost += node->g_cost; if (open.Append(node)) { free(node); Clean(); *rstep = 0; return UFALSE; } //A* main loop while (node = GetBestNode()) { //If it's the goal node then we've done if (Distance(map_d[node->data].pos, danger) >= safe_dist) { if (close.Append(node)) { free(node); *rstep = 0; return UFALSE; } //BuildPath(rlist, rstep); if (BuildPath(rlist, rstep)) { Clean(); *rstep = 0; return UFALSE; } Clean(); return UTRUE; } //Otherwise, generate child from current node long _pipo = node->data; for (SWord i(0); i < map_d[_pipo].nblinked; i++) { child = CreateNode(map_d[_pipo].linked[i], node); if (!child) { free(node); Clean(); *rstep = 0; return UFALSE; } //Cost to reach this node if ((map_d[child->data].flags & ANCHOR_FLAG_BLOCKED) || map_d[child->data].height > height || map_d[child->data].radius < radius) free(child); else { child->g_cost = node->g_cost + Distance(map_d[child->data].pos, map_d[node->data].pos); if (flags & MINOS_STEALTH) AddEnlightmentCost(child); if (Check(child)) { Float dist; if (open.Append(child)) { free(node); free(child); *rstep = 0; return UFALSE; } //Get total cost for this node child->f_cost = child->g_cost; dist = Distance(map_d[child->data].pos, danger); if ((dist = safe_dist - dist) > 0.0F) child->f_cost += fac5 * dist; } else free(child); } } //Put node onto close list as we have now examined this node if (close.Append(node)) { free(node); Clean(); *rstep = 0; return UFALSE; } } Clean(); *rstep = 0; //No path found!!! return UFALSE; } UBool PathFinder::WanderAround(const ULong & flags, const ULong & f, const Float & rad, SLong * rstep, UWord ** rlist) { Void * ptr; ULong step_c, last, next; SLong temp_c(0), path_c(0); UWord * temp_d = NULL, *path_d = NULL; Clean(); //Check if params are valid if (!rlist || !rstep) return UFALSE; if (!map_d[f].nblinked) { *rstep = 0; return UFALSE; } if (rad <= MIN_RADIUS) { *rlist = (UWord *)malloc(sizeof(UWord)); ** rlist = (UWord)f; *rstep = 1; return UTRUE; } last = f; step_c = Random() % 5 + 5; for (ULong i(0); i < step_c; i++) { ULong nb = ULong(rad * rnd() * DIV50); long _current = f; while (nb) { if ((map_d[_current].nblinked) ) { long notfinished = 4; while (notfinished--) { ULong r = ULong(rnd() * (Float)map_d[_current].nblinked); if (r >= (ULong)map_d[_current].nblinked) r = ULong(map_d[_current].nblinked - 1); if ((!(map_d[map_d[_current].linked[r]].flags & ANCHOR_FLAG_BLOCKED)) && (map_d[map_d[_current].linked[r]].nblinked) && (map_d[map_d[_current].linked[r]].height <= height) && (map_d[map_d[_current].linked[r]].radius >= radius)) { _current = map_d[_current].linked[r]; notfinished = 0; } } } nb--; } if (_current < 0) continue; next = nb = _current; if (Move(flags, last, next, &temp_c, &temp_d) && temp_c) { if (!(ptr = realloc(path_d, sizeof(UWord) * (path_c + temp_c)))) { free(temp_d); free(path_d); Clean(); *rstep = 0; return UFALSE; } //Add temp path to wander around path path_d = (UWord *)ptr; memcpy(&path_d[path_c], temp_d, sizeof(UWord) * temp_c); path_c += temp_c; //Free temp path free(temp_d), temp_d = NULL, temp_c = 0; } else i--; last = next; } //Close wander around path (return to start position) if (!path_c || !Move(flags, last, f, &temp_c, &temp_d)) { *rstep = 0; return UFALSE; } if (!(ptr = realloc(path_d, sizeof(UWord) * (path_c + temp_c)))) { free(temp_d); free(path_d); Clean(); *rstep = 0; return UFALSE; } //Add temp path to wander around path path_d = (UWord *)ptr; memcpy(&path_d[path_c], temp_d, sizeof(UWord) * temp_c); path_c += temp_c; free(temp_d); *rlist = path_d; *rstep = path_c; Clean(); return UTRUE; } ULong PathFinder::GetNearestNode(const EERIE_3D & pos) const { ULong best(0); Float dist, b_dist(FLT_MAX); for (ULong i(0); i < map_s; i++) { dist = Distance(map_d[i].pos, pos); if (dist < b_dist && map_d[i].nblinked) best = i, b_dist = dist; } return best; } UBool PathFinder::LookFor(const ULong & flags, const ULong & f, const EERIE_3D & pos, const Float & radius, SLong * rstep, UWord ** rlist) { Void * ptr; ULong step_c, to, last, next; SLong temp_c(0), path_c(0); UWord * temp_d = NULL, *path_d = NULL; Clean(); //Check if params are valid if (!rlist || !rstep) { Clean(); *rstep = 0; return UFALSE; } if (radius <= MIN_RADIUS) { *rlist = (UWord *)malloc(sizeof(UWord)); ** rlist = (UWord)f; *rstep = 1; Clean(); return UTRUE; } to = GetNearestNode(pos); last = f; step_c = Random() % 5 + 5; for (ULong i(0); i < step_c; i++) { EERIE_3D pos; pos.x = map_d[to].pos.x + radius * frnd(); pos.y = map_d[to].pos.y + radius * frnd(); pos.z = map_d[to].pos.z + radius * frnd(); next = GetNearestNode(pos); if (Move(flags, last, next, &temp_c, &temp_d) && temp_c) { if ((path_c + temp_c - 1) <= 0) { if (temp_d) { free(temp_d); temp_d = NULL; } if (path_d) { free(path_d); path_d = NULL; } Clean(); *rstep = 0; return UFALSE; } if (!(ptr = realloc(path_d, sizeof(UWord) * (path_c + temp_c - 1)))) { if (temp_d) { free(temp_d); temp_d = NULL; } Clean(); *rstep = 0; return UFALSE; } //Add temp path to wander around path path_d = (UWord *)ptr; memcpy(&path_d[path_c], temp_d, sizeof(UWord) *(temp_c - 1)); path_c += temp_c - 1; //Free temp path free(temp_d), temp_d = NULL, temp_c = 0; } else i--; last = next; } //Close wander around path (return to start position) if (!path_c) { Clean(); // Cyril *rstep = 0; return UFALSE; } *rlist = path_d; *rstep = path_c; Clean(); // Cyril return UTRUE; } Void PathFinder::Clean() { ULong i; for (i = 0; i < close.Count(); i++) free(close[i]); close.Free(); for (i = 0; i < open.Count(); i++) free(open[i]); open.Free(); } // Return best node (lower cost) from open list or NULL if list is empty MINOSNode * PathFinder::GetBestNode() { MINOSNode * node; ULong best(0); Float cost(FLT_MAX); if (!open.Count()) return NULL; for (ULong i(0); i < open.Count(); i++) if (open[i]->f_cost < cost) cost = open[i]->f_cost, best = i; node = open[best]; open.Remove(best); return node; } UBool PathFinder::Check(MINOSNode * node) { ULong i; //Check if node is already in close list for (i = 0; i < close.Count(); i++) if (close[i]->data == node->data) return UFALSE; //Check if node is already in open list for (i = 0; i < open.Count(); i++) if (open[i]->data == node->data) { if (open[i]->g_cost < node->g_cost) return UFALSE; free(open[i]), open.Remove(i); } return UTRUE; } SBool PathFinder::BuildPath(UWord ** rlist, SLong * rstep) { Void * ptr; MINOSNode * next; UWord path_c(0); UWord * path_d = NULL; next = close[close.Count() - 1]; while (next) { if (!(ptr = realloc(path_d, (path_c + 1) << 1))) return SFALSE; path_d = (UWord *)ptr; path_d[path_c++] = (UWord)next->data; next = next->parent; } if (!rlist || !(*rlist = (UWord *)malloc(sizeof(UWord) * path_c))) { free(path_d); return SFALSE; } for (ULong i(0); i < path_c; i++)(*rlist)[i] = path_d[path_c - i - 1]; free(path_d); if (rstep) *rstep = path_c; return STRUE; } MINOSNode * PathFinder::CreateNode(long data, MINOSNode * parent) { MINOSNode * node; node = (MINOSNode *)malloc(sizeof(MINOSNode)); if (!node) return NULL; node->data = data; node->parent = parent; return node; } Void PathFinder::AddEnlightmentCost(MINOSNode * node) { for (ULong i(0); i < slight_c; i++) { if (!slight_l[i] || !slight_l[i]->exist || !slight_l[i]->status) continue; Float dist = Distance(slight_l[i]->pos, map_d[node->data].pos); if (slight_l[i]->fallend >= dist) { Float l_cost(fac3); l_cost *= slight_l[i]->intensity * (slight_l[i]->rgb.r + slight_l[i]->rgb.g + slight_l[i]->rgb.b) * DIV3; if (slight_l[i]->fallstart >= dist) node->g_cost += l_cost; else node->g_cost += l_cost * ((dist - slight_l[i]->fallstart) / (slight_l[i]->fallend - slight_l[i]->fallstart)); } } } inline Float PathFinder::Distance(const EERIE_3D & from, const EERIE_3D & to) const { Float x, y, z; x = from.x - to.x; y = from.y - to.y; z = from.z - to.z; return Float(EEsqrt(x * x + y * y + z * z)); }