856 lines
22 KiB
C
856 lines
22 KiB
C
#include <malloc.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
|
|
#include "log.h"
|
|
#include "lephisto.h"
|
|
#include "toolkit.h"
|
|
#include "space.h"
|
|
#include "opengl.h"
|
|
#include "mission.h"
|
|
#include "map.h"
|
|
|
|
#define WINDOW_WIDTH 650
|
|
#define WINDOW_HEIGHT 540
|
|
|
|
#define MAP_WIDTH (WINDOW_WIDTH-150)
|
|
#define MAP_HEIGHT (WINDOW_HEIGHT-100)
|
|
|
|
#define BUTTON_WIDTH 60
|
|
#define BUTTON_HEIGHT 40
|
|
|
|
static int map_wid = 0;
|
|
static double map_zoom = 1.; /* Zoom of the map. */
|
|
static double map_xpos = 0.; /* Map position. */
|
|
static double map_ypos = 0.;
|
|
static int map_selected = -1;
|
|
static StarSystem** map_path = NULL; /* The path to current selected system. */
|
|
static int map_npath = 0;
|
|
|
|
static int map_drag = 0; /* Is the user dragging the map? */
|
|
|
|
/* Extern. */
|
|
/* space.c */
|
|
extern StarSystem* systems_stack;
|
|
extern int systems_nstack;
|
|
/* player.c */
|
|
extern int planet_target;
|
|
extern int hyperspace_target;
|
|
|
|
static void map_close(char* str);
|
|
static void map_update(void);
|
|
static int map_inPath(StarSystem* sys);
|
|
static void map_render(double bx, double by, double w, double h);
|
|
static void map_mouse(SDL_Event* event, double mx, double my);
|
|
static void map_buttonZoom(char* str);
|
|
static void map_selectCur(void);
|
|
|
|
/* Open the map window. */
|
|
void map_open(void) {
|
|
if(map_wid) {
|
|
map_close(NULL);
|
|
return;
|
|
}
|
|
|
|
/* Set the position to focus on current system. */
|
|
map_xpos = cur_system->pos.x;
|
|
map_ypos = cur_system->pos.y;
|
|
|
|
/* Mark systems as needed. */
|
|
mission_sysMark();
|
|
|
|
/* Attempt to select current map if none is selected. */
|
|
if(map_selected == -1)
|
|
map_selectCur();
|
|
|
|
map_wid = window_create("Star Map", -1, -1, WINDOW_WIDTH, WINDOW_HEIGHT);
|
|
|
|
/*
|
|
* SIDE TEXT
|
|
*
|
|
* $System
|
|
*
|
|
* Faction:
|
|
* $Faction (or Multiple)
|
|
*
|
|
* Status:
|
|
* $Status
|
|
*
|
|
* Planets:
|
|
* $Planet1, $Planet2, ...
|
|
*
|
|
* Services:
|
|
* $Services
|
|
*
|
|
* ...
|
|
*
|
|
* [Close]
|
|
*/
|
|
|
|
/* System name. */
|
|
window_addText(map_wid, -20, -20, 100, 20, 1, "txtSysname",
|
|
&gl_defFont, &cDConsole, systems_stack[map_selected].name);
|
|
|
|
/* Faction. */
|
|
window_addText(map_wid, -20, -60, 90, 20, 0, "txtSFaction",
|
|
&gl_smallFont, &cDConsole, "Faction:");
|
|
|
|
window_addText(map_wid, -20, -60-gl_smallFont.h-5, 80, 100, 0, "txtFaction",
|
|
&gl_smallFont, &cBlack, NULL);
|
|
|
|
/* Standing. */
|
|
window_addText(map_wid, -20, -100, 90, 20, 0, "txtSStanding",
|
|
&gl_smallFont, &cDConsole, "Standing:");
|
|
|
|
window_addText(map_wid, -20, -100-gl_smallFont.h-5, 80, 100, 0, "txtStanding",
|
|
&gl_smallFont, &cBlack, NULL);
|
|
|
|
/* Planets. */
|
|
window_addText(map_wid, -20, -140, 90, 20, 0, "txtSPlanets",
|
|
&gl_smallFont, &cDConsole, "Planets:");
|
|
|
|
window_addText(map_wid, -20, -140-gl_smallFont.h-5, 80, 100, 0, "txtPlanets",
|
|
&gl_smallFont, &cBlack, NULL);
|
|
|
|
/* Services. */
|
|
window_addText(map_wid, -20, -180, 90, 20, 0, "txtSServices",
|
|
&gl_smallFont, &cDConsole, "Services:");
|
|
|
|
window_addText(map_wid, -20, -180-gl_smallFont.h-5, 80, 100, 0, "txtServices",
|
|
&gl_smallFont, &cBlack, NULL);
|
|
|
|
/* Close button. */
|
|
window_addButton(map_wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
|
|
"btnClose", "Close", map_close);
|
|
|
|
/* The map itself. */
|
|
window_addCust(map_wid, 20, -40, MAP_WIDTH, MAP_HEIGHT,
|
|
"cstMap", 1, map_render, map_mouse);
|
|
|
|
/*
|
|
* Bottom stuff.
|
|
*
|
|
* [+] [-] Nebulae, Asteroids, Interference.
|
|
*/
|
|
/* Zoom buttons. */
|
|
window_addButton(map_wid, 40, 20, 30, 30, "btnZoomIn", "+", map_buttonZoom);
|
|
window_addButton(map_wid, 80, 20, 30, 30, "btnZoomOut", "-", map_buttonZoom);
|
|
/* Situation text. */
|
|
window_addText(map_wid, 140, 10, WINDOW_WIDTH-80-30-30, 30, 0,
|
|
"txtSystemStatus", &gl_smallFont, &cBlack, NULL);
|
|
|
|
map_update();
|
|
}
|
|
|
|
static void map_close(char* str) {
|
|
(void)str;
|
|
if(map_wid) {
|
|
window_destroy(map_wid);
|
|
map_wid = 0;
|
|
}
|
|
}
|
|
|
|
static void map_update(void) {
|
|
int i;
|
|
StarSystem* sys;
|
|
int f, y, h, standing, nstanding;
|
|
unsigned int services;
|
|
char buf[128];
|
|
|
|
sys = &systems_stack[map_selected];
|
|
|
|
/* Right text. */
|
|
if(!sys_isKnown(sys)) { /* System isn't known. Erase all. */
|
|
/* Right text. */
|
|
window_modifyText(map_wid, "txtSysname", "Unknown");
|
|
window_modifyText(map_wid, "txtFaction", "Unknown");
|
|
/* Standing. */
|
|
window_moveWidget(map_wid, "txtSStanding", -20, -100);
|
|
window_moveWidget(map_wid, "txtStanding", -20, -100-gl_smallFont.h-5);
|
|
window_modifyText(map_wid, "txtStanding", "Unknown");
|
|
/* Planets. */
|
|
window_moveWidget(map_wid, "txtSPlanets", -20, -140);
|
|
window_moveWidget(map_wid, "txtPlanets", -20, -140-gl_smallFont.h-5);
|
|
window_modifyText(map_wid, "txtPlanets", "Unknown");
|
|
/* Services. */
|
|
window_moveWidget(map_wid, "txtSServices", -20, -180);
|
|
window_moveWidget(map_wid, "txtServices", -20, -180-gl_smallFont.h-5);
|
|
window_modifyText(map_wid, "txtServices", "Unknown");
|
|
|
|
/* Bottom text. */
|
|
window_modifyText(map_wid, "txtSystemStatus", NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
/* System is known. */
|
|
window_modifyText(map_wid, "txtSysname", sys->name);
|
|
|
|
if(sys->nplanets == 0) { /* No planets -> no factions. */
|
|
window_modifyText(map_wid, "txtFaction", "NA");
|
|
window_moveWidget(map_wid, "txtSStanding", -20, -100);
|
|
window_moveWidget(map_wid, "txtStanding", -20, -100-gl_smallFont.h-5);
|
|
window_modifyText(map_wid, "txtStanding", "NA");
|
|
y = -100;
|
|
} else {
|
|
standing = 0;
|
|
nstanding = 0;
|
|
f = -1;
|
|
for(i = 0; i < sys->nplanets; i++) {
|
|
if((f == -1) && (sys->planets[i].faction != 0)) {
|
|
f = sys->planets[i].faction;
|
|
standing += faction_getPlayer(f);
|
|
nstanding++;
|
|
}
|
|
else if(f != sys->planets[i].faction &&
|
|
(sys->planets[i].faction>0)) {
|
|
/* TODO: more verbosity. */
|
|
snprintf(buf, 100, "Multiple");
|
|
break;
|
|
}
|
|
}
|
|
if(i == sys->nplanets)
|
|
/* Saw them all, and all the same. */
|
|
snprintf(buf, 100, "%s", faction_longname(f));
|
|
|
|
/* Modify the text. */
|
|
window_modifyText(map_wid, "txtFaction", buf);
|
|
window_modifyText(map_wid, "txtStanding",
|
|
faction_getStanding(standing / nstanding));
|
|
|
|
/* Lower text if needed. */
|
|
h = gl_printHeight(&gl_smallFont, 80, buf);
|
|
y = -100 - (h - gl_smallFont.h);
|
|
window_moveWidget(map_wid, "txtSStanding", -20, y);
|
|
window_moveWidget(map_wid, "txtStanding", -20, y-gl_smallFont.h-5);
|
|
}
|
|
|
|
/* Get planets. */
|
|
if(sys->nplanets == 0)
|
|
window_modifyText(map_wid, "txtPlanets", "None");
|
|
else {
|
|
buf[0] = '\0';
|
|
if(sys->nplanets > 0)
|
|
strcat(buf, sys->planets[0].name);
|
|
for(i = 1; i < sys->nplanets; i++) {
|
|
strcat(buf, ",\n");
|
|
strcat(buf, sys->planets[i].name);
|
|
}
|
|
window_modifyText(map_wid, "txtPlanets", buf);
|
|
}
|
|
y -= 40;
|
|
window_moveWidget(map_wid, "txtSPlanets", -20, y);
|
|
window_moveWidget(map_wid, "txtPlanets", -20, y-gl_smallFont.h-5);
|
|
|
|
/* Get the services. */
|
|
h = gl_printHeight(&gl_smallFont, 80, buf);
|
|
y -= 40 +(h - gl_smallFont.h);
|
|
window_moveWidget(map_wid, "txtSServices", -20, y);
|
|
window_moveWidget(map_wid, "txtServices", -20, y-gl_smallFont.h-5);
|
|
services = 0;
|
|
for(i = 0; i < sys->nplanets; i++)
|
|
services |= sys->planets[i].services;
|
|
buf[0] = '\0';
|
|
if(services & PLANET_SERVICE_COMMODITY)
|
|
strcat(buf, "Commodity\n");
|
|
if(services & PLANET_SERVICE_OUTFITS)
|
|
strcat(buf, "Outfits\n");
|
|
if(services & PLANET_SERVICE_SHIPYARD)
|
|
strcat(buf, "Shipyard\n");
|
|
if(buf[0] == '\0')
|
|
strcat(buf, "None");
|
|
window_modifyText(map_wid, "txtServices", buf);
|
|
|
|
/* System status. */
|
|
buf[0] = '\0';
|
|
if(sys->nebu_density > 0.) { /* Has nebulae. */
|
|
/* Volatility. */
|
|
if(sys->nebu_volatility > 700.)
|
|
strcat(buf, " Volatile");
|
|
else if(sys->nebu_volatility > 300.)
|
|
strcat(buf, " Dangerous");
|
|
else if(sys->nebu_volatility > 0.)
|
|
strcat(buf, " Unstable");
|
|
/* Density */
|
|
if(sys->nebu_density > 700.)
|
|
strcat(buf, " Dense");
|
|
else if(sys->nebu_density < 300.)
|
|
strcat(buf, " Light");
|
|
strcat(buf, " Nebulae");
|
|
}
|
|
window_modifyText(map_wid, "txtSystemStatus", buf);
|
|
}
|
|
|
|
/* Return 1 if sys is part of the map_path. */
|
|
static int map_inPath(StarSystem* sys) {
|
|
int i, f;
|
|
|
|
f = pilot_getJumps(player) - 1;
|
|
for(i = 0; i < map_npath; i++)
|
|
if(map_path[i] == sys) {
|
|
if(i > f) {
|
|
return 2;
|
|
} else return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Render the map as a custom widget. */
|
|
static void map_render(double bx, double by, double w, double h) {
|
|
int i, j, n, m;
|
|
double x, y, r, tx, ty;
|
|
StarSystem* sys;
|
|
glColour* col;
|
|
|
|
r = 5.;
|
|
x = (bx - map_xpos + w/2) * 1.;
|
|
y = (by - map_ypos + h/2) * 1.;
|
|
/* Background */
|
|
COLOUR(cBlack);
|
|
glBegin(GL_QUADS);
|
|
glVertex2d(bx, by);
|
|
glVertex2d(bx, by+h);
|
|
glVertex2d(bx+w, by+h);
|
|
glVertex2d(bx+w, by);
|
|
glEnd();
|
|
|
|
/* Render the star systems. */
|
|
for(i = 0; i < systems_nstack; i++) {
|
|
sys = &systems_stack[i];
|
|
|
|
/* Check to make sure system is known of adjacent to known (or marked). */
|
|
if(!sys_isMarked(sys) && !space_sysReachable(sys)) continue;
|
|
|
|
/* System Colours. */
|
|
if(sys == cur_system) col = &cRadar_targ;
|
|
else if(!sys_isKnown(sys) || (sys->nplanets == 0)) col = &cInert;
|
|
else col = faction_getColour(sys->faction);
|
|
COLOUR(*col);
|
|
|
|
/* Draw the system. */
|
|
tx = x + sys->pos.x * map_zoom;
|
|
ty = y + sys->pos.y * map_zoom;
|
|
gl_drawCircleInRect(tx, ty, r, bx, by, w, h);
|
|
|
|
/* Mark the system if needed. */
|
|
if(sys_isMarked(sys)) {
|
|
COLOUR(cRed);
|
|
glBegin(GL_TRIANGLES);
|
|
glVertex2d(tx+r+9, ty+r+3);
|
|
glVertex2d(tx+r+3, ty+r+3);
|
|
glVertex2d(tx+r+3, ty+r+9);
|
|
glEnd();
|
|
}
|
|
|
|
/* Draw the system name. */
|
|
if(sys_isKnown(sys)) {
|
|
tx = x + 7. + sys->pos.x * map_zoom;
|
|
ty = y - 5. + sys->pos.y * map_zoom;
|
|
gl_print(&gl_smallFont,
|
|
tx + SCREEN_W/2., ty + SCREEN_H/2.,
|
|
&cWhite, sys->name);
|
|
}
|
|
|
|
if(!sys_isKnown(sys)) continue; /* We dont't draw hyperspace lines. */
|
|
|
|
/* Draw the hyperspace paths. */
|
|
glShadeModel(GL_SMOOTH);
|
|
/* Cheaply use transparency instead of actually */
|
|
/* calculating from x to y the line must go. :) */
|
|
for(j = 0; j < sys->njumps; j++) {
|
|
n = map_inPath(&systems_stack[sys->jumps[j]]);
|
|
m = map_inPath(sys);
|
|
/* Set the colours, is the route the current one? */
|
|
if((hyperspace_target != -1) &&
|
|
(((cur_system == sys) && (j == hyperspace_target)) ||
|
|
((cur_system == &systems_stack[sys->jumps[j]]) &&
|
|
(sys == &systems_stack[cur_system->jumps[hyperspace_target]]))))
|
|
col = &cGreen;
|
|
else if((n > 0) && (m > 0)) {
|
|
if((n == 2) || (m == 2)) /* Out of fuel. */
|
|
col = &cRed;
|
|
else
|
|
col = &cYellow;
|
|
}
|
|
else col = &cDarkBlue;
|
|
glBegin(GL_LINE_STRIP);
|
|
ACOLOUR(*col, 0.);
|
|
tx = x + sys->pos.x * map_zoom;
|
|
ty = y + sys->pos.y * map_zoom;
|
|
glVertex2d(tx, ty);
|
|
COLOUR(*col);
|
|
tx += (systems_stack[sys->jumps[j]].pos.x - sys->pos.x)/2. * map_zoom;
|
|
ty += (systems_stack[sys->jumps[j]].pos.y - sys->pos.y)/2. * map_zoom;
|
|
glVertex2d(tx, ty);
|
|
ACOLOUR(*col, 0.);
|
|
tx = x + systems_stack[sys->jumps[j]].pos.x * map_zoom;
|
|
ty = y + systems_stack[sys->jumps[j]].pos.y * map_zoom;
|
|
glVertex2d(tx, ty);
|
|
glEnd();
|
|
}
|
|
glShadeModel(GL_FLAT);
|
|
}
|
|
/* Selected planet. */
|
|
if(map_selected != -1) {
|
|
sys = &systems_stack[map_selected];
|
|
COLOUR(cRed);
|
|
gl_drawCircleInRect(x + sys->pos.x*map_zoom, y + sys->pos.y*map_zoom,
|
|
r+3., bx, by, w, h);
|
|
}
|
|
}
|
|
|
|
/* Map event handling. */
|
|
static void map_mouse(SDL_Event* event, double mx, double my) {
|
|
int i, j;
|
|
double x, y, t;
|
|
StarSystem* sys;
|
|
|
|
t = 13.*15.; /* Threshold. */
|
|
|
|
mx -= MAP_WIDTH/2 - map_xpos;
|
|
my -= MAP_HEIGHT/2 - map_ypos;
|
|
|
|
switch(event->type) {
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
if(event->button.button == SDL_BUTTON_WHEELUP)
|
|
map_buttonZoom("btnZoomOut");
|
|
else if(event->button.button == SDL_BUTTON_WHEELDOWN)
|
|
map_buttonZoom("btnZoomIn");
|
|
|
|
/* Selecting star system. */
|
|
else {
|
|
for(i = 0; i < systems_nstack; i++) {
|
|
sys = &systems_stack[i];
|
|
|
|
/* Must be reachable. */
|
|
if(!space_sysReachable(sys))
|
|
continue;
|
|
|
|
/* Get position. */
|
|
x = systems_stack[i].pos.x * map_zoom;
|
|
y = systems_stack[i].pos.y * map_zoom;
|
|
|
|
if((pow2(mx-x)+pow2(my-y)) < t) {
|
|
/* Select the current system and make a path to it. */
|
|
map_selected = i;
|
|
if(map_path)
|
|
free(map_path);
|
|
map_path = map_getJumpPath(&map_npath,
|
|
cur_system->name, sys->name, 0);
|
|
|
|
if(map_npath == 0)
|
|
hyperspace_target = -1;
|
|
else
|
|
/* See if it a valid hyperspace target. */
|
|
for(j = 0; j < cur_system->njumps; j++) {
|
|
if(map_path[0] == &systems_stack[cur_system->jumps[j]]) {
|
|
planet_target = -1; /* Override planet_target. */
|
|
hyperspace_target = j;
|
|
break;
|
|
}
|
|
}
|
|
map_update();
|
|
break;
|
|
}
|
|
}
|
|
map_drag = 1;
|
|
}
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
|
if(map_drag) map_drag = 0;
|
|
break;
|
|
|
|
case SDL_MOUSEMOTION:
|
|
if(map_drag) {
|
|
/* Axis is inverted. */
|
|
map_xpos -= event->motion.xrel;
|
|
map_ypos += event->motion.yrel;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void map_buttonZoom(char* str) {
|
|
if(strcmp(str, "btnZoomIn")==0) {
|
|
map_zoom += (map_zoom >= 1.) ? 0.5 : 0.25;
|
|
map_zoom = MIN(2.5, map_zoom);
|
|
}
|
|
|
|
else if(strcmp(str, "btnZoomOut")==0) {
|
|
map_zoom -= (map_zoom > 1.) ? 0.5 : 0.25;
|
|
map_zoom = MAX(0.5, map_zoom);
|
|
}
|
|
}
|
|
|
|
/* Set the map to sane defaults. */
|
|
void map_clear(void) {
|
|
map_zoom = 1.;
|
|
if(cur_system != NULL) {
|
|
map_xpos = cur_system->pos.x;
|
|
map_ypos = cur_system->pos.y;
|
|
} else {
|
|
map_xpos = 0.;
|
|
map_ypos = 0.;
|
|
}
|
|
if(map_path != NULL) {
|
|
free(map_path);
|
|
map_path = NULL;
|
|
map_npath = 0;
|
|
}
|
|
|
|
/* Default system is current system. */
|
|
map_selectCur();
|
|
}
|
|
|
|
static void map_selectCur(void) {
|
|
int i;
|
|
if(cur_system != NULL) {
|
|
for(i = 0; i < systems_nstack; i++) {
|
|
if(&systems_stack[i] == cur_system) {
|
|
map_selected = i;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
/* Probably going to seg fault now.. */
|
|
map_selected = -1;
|
|
}
|
|
}
|
|
|
|
/* Update the map after a jump. */
|
|
void map_jump(void) {
|
|
int j;
|
|
|
|
/* Set selected system to self. */
|
|
map_selectCur();
|
|
|
|
map_xpos = cur_system->pos.x;
|
|
map_ypos = cur_system->pos.y;
|
|
|
|
/* Update path if set. */
|
|
if(map_path != NULL) {
|
|
map_npath--;
|
|
if(map_npath == 0) {
|
|
/* Path is empty. */
|
|
free(map_path);
|
|
map_path = NULL;
|
|
} else {
|
|
/* Get rid of bottom of the path. */
|
|
memcpy(&map_path[0], &map_path[1], sizeof(StarSystem*) * map_npath);
|
|
map_path = realloc(map_path, sizeof(StarSystem*) * map_npath);
|
|
|
|
/* Set the next jump to be the next in path. */
|
|
for(j = 0; j < cur_system->njumps; j++) {
|
|
if(map_path[0] == &systems_stack[cur_system->jumps[j]]) {
|
|
planet_target = -1; /* Override planet_target. */
|
|
hyperspace_target = j;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* A* Algorithm fo shortest path finding. */
|
|
|
|
/* The node struct. */
|
|
typedef struct SysNode_ {
|
|
struct SysNode_* next, *gnext;
|
|
|
|
struct SysNode_* parent;
|
|
StarSystem* sys;
|
|
double r; /* Ranking. */
|
|
int g; /* Step. */
|
|
} SysNode;
|
|
|
|
static SysNode* A_gc;
|
|
/* Prototypes. */
|
|
static SysNode* A_newNode(StarSystem* sys, SysNode* parent);
|
|
static double A_h(StarSystem* n, StarSystem* g);
|
|
static double A_g(SysNode* n);
|
|
static SysNode* A_add(SysNode* first, SysNode* cur);
|
|
static SysNode* A_rm(SysNode* first, StarSystem* cur);
|
|
static SysNode* A_in(SysNode* first, StarSystem* cur);
|
|
static SysNode* A_lowest(SysNode* first);
|
|
static void A_freeList(SysNode* first);
|
|
|
|
/* Creates a new node link to star system. */
|
|
static SysNode* A_newNode(StarSystem* sys, SysNode* parent) {
|
|
SysNode* n;
|
|
|
|
n = malloc(sizeof(SysNode));
|
|
|
|
n->next = NULL;
|
|
n->parent = parent;
|
|
n->sys = sys;
|
|
n->r = DBL_MAX;
|
|
n->g = 0.;
|
|
|
|
n->gnext = A_gc;
|
|
A_gc = n;
|
|
|
|
return n;
|
|
}
|
|
|
|
static double A_h(StarSystem* n, StarSystem* g) {
|
|
(void)n;
|
|
(void)g;
|
|
/* Euclidean distance. */
|
|
/*return sqrt(pow2(n->pos.x - g->pos.x) + pow2(n->pos.y - g->pos.y))/100.; */
|
|
return 0.;
|
|
}
|
|
|
|
/* Get the g from a node. */
|
|
static double A_g(SysNode* n) {
|
|
return n->g;
|
|
}
|
|
|
|
/* Add a node to the linkes list. */
|
|
static SysNode* A_add(SysNode* first, SysNode* cur) {
|
|
SysNode* n;
|
|
|
|
if(first == NULL)
|
|
return cur;
|
|
|
|
n = first;
|
|
while(n->next != NULL)
|
|
n = n->next;
|
|
n->next = cur;
|
|
|
|
return first;
|
|
}
|
|
|
|
/* Remove a node from a linked list. */
|
|
static SysNode* A_rm(SysNode* first, StarSystem* cur) {
|
|
SysNode* n, *p;
|
|
|
|
if(first->sys == cur) {
|
|
n = first->next;
|
|
first->next = NULL;
|
|
return n;
|
|
}
|
|
|
|
p = first;
|
|
n = p->next;
|
|
do {
|
|
if(n->sys == cur) {
|
|
n->next = NULL;
|
|
p->next = n->next;
|
|
break;
|
|
}
|
|
} while((n=n->next) != NULL);
|
|
|
|
return first;
|
|
}
|
|
|
|
/* Check if node is in linked list. */
|
|
static SysNode* A_in(SysNode* first, StarSystem* cur) {
|
|
SysNode* n;
|
|
|
|
if(first == NULL)
|
|
return NULL;
|
|
|
|
n = first;
|
|
do {
|
|
if(n->sys == cur)
|
|
return n;
|
|
} while((n=n->next) != NULL);
|
|
return NULL;
|
|
}
|
|
|
|
/* Return the lowest ranking node from a linked list of nodes. */
|
|
static SysNode* A_lowest(SysNode* first) {
|
|
SysNode* lowest, *n;
|
|
|
|
if(first == NULL)
|
|
return NULL;
|
|
|
|
n = first;
|
|
lowest = n;
|
|
do {
|
|
if(n->r < lowest->r)
|
|
lowest = n;
|
|
} while((n=n->next) != NULL);
|
|
|
|
return lowest;
|
|
}
|
|
|
|
/* Free a linked list. */
|
|
static void A_freeList(SysNode* first) {
|
|
SysNode* p, *n;
|
|
|
|
if(first == NULL)
|
|
return;
|
|
|
|
p = NULL;
|
|
n = first;
|
|
do {
|
|
if(p != NULL)
|
|
free(p);
|
|
p = n;
|
|
} while((n=n->gnext) != NULL);
|
|
|
|
free(p);
|
|
}
|
|
|
|
StarSystem** map_getJumpPath(int* njumps, char* sysstart, char* sysend, int ignore_known) {
|
|
int i, cost;
|
|
|
|
StarSystem* sys, *ssys, *esys, **res;
|
|
|
|
SysNode* cur, *neighbour;
|
|
SysNode* open, *closed;
|
|
SysNode* ocost, *ccost;
|
|
|
|
A_gc = NULL;
|
|
|
|
/* Initial and target systems. */
|
|
ssys = system_get(sysstart); /* Start. */
|
|
esys = system_get(sysend); /* End. */
|
|
|
|
/* System target must be known and reachable. */
|
|
if(!ignore_known && !sys_isKnown(esys) && !space_sysReachable(esys)) {
|
|
/* Can't reach - Don't make path. */
|
|
(*njumps) = 0;
|
|
return NULL;
|
|
}
|
|
|
|
/* Start the linked lists. */
|
|
open = closed = NULL;
|
|
cur = A_newNode(ssys, NULL);
|
|
open = A_add(open, cur); /* Initial open node is the start system. */
|
|
|
|
while((cur = A_lowest(open))->sys != esys) {
|
|
/* Get best from open and toss to closed. */
|
|
open = A_rm(open, cur->sys);
|
|
closed = A_add(closed, cur);
|
|
cost = A_g(cur) + 1;
|
|
|
|
for(i = 0; i < cur->sys->njumps; i++) {
|
|
sys = &systems_stack[cur->sys->jumps[i]];
|
|
|
|
/* Make sure it's reachable. */
|
|
if(!ignore_known && (!sys_isKnown(sys) && !space_sysReachable(esys)))
|
|
continue;
|
|
|
|
neighbour = A_newNode(sys, NULL);
|
|
|
|
ocost = A_in(open, sys);
|
|
if((ocost != NULL) && (cost < ocost->g)) {
|
|
open = A_rm(open, sys); /* New path is better. */
|
|
}
|
|
|
|
ccost = A_in(closed, sys);
|
|
if(ccost != NULL) {
|
|
closed = A_rm(closed, sys); /* Shouldn't happen. */
|
|
}
|
|
|
|
if((ocost == NULL) && (ccost == NULL)) {
|
|
neighbour->g = cost;
|
|
neighbour->r = A_g(neighbour) + A_h(cur->sys, sys);
|
|
neighbour->parent = cur;
|
|
open = A_add(open, neighbour);
|
|
}
|
|
}
|
|
}
|
|
/* Build the path backwards. */
|
|
(*njumps) = A_g(cur);
|
|
res = malloc(sizeof(StarSystem*) * (*njumps));
|
|
for(i = 0; i < (*njumps); i++) {
|
|
res[(*njumps)-i-1] = cur->sys;
|
|
cur = cur->parent;
|
|
}
|
|
|
|
/* Free the linked list. */
|
|
A_freeList(A_gc);
|
|
return res;
|
|
}
|
|
|
|
/* Marks maps around a radius of current system as known. */
|
|
int map_map(char* targ_sys, int r) {
|
|
int i, dep;
|
|
StarSystem* sys, *jsys;
|
|
SysNode* closed, *open, *cur, *neighbour;
|
|
|
|
A_gc = NULL;
|
|
open = closed = NULL;
|
|
|
|
if(targ_sys == NULL) sys = cur_system;
|
|
else sys = system_get(targ_sys);
|
|
sys_setFlag(sys, SYSTEM_KNOWN);
|
|
open = A_newNode(sys, NULL);
|
|
open->r = 0;
|
|
|
|
while((cur = A_lowest(open)) != NULL) {
|
|
/* Mark system as known and go to next. */
|
|
sys = cur->sys;
|
|
dep = cur->r;
|
|
sys_setFlag(sys, SYSTEM_KNOWN);
|
|
open = A_rm(open, sys);
|
|
closed = A_add(closed, cur);
|
|
|
|
/* Check it's jumps. */
|
|
for(i = 0; i < sys->njumps; i++) {
|
|
jsys = &systems_stack[cur->sys->jumps[i]];
|
|
|
|
/* System has already been parsed or is too deep. */
|
|
if((A_in(closed, jsys) != NULL) || (dep+1 > r))
|
|
continue;
|
|
|
|
/* Create new node and such. */
|
|
neighbour = A_newNode(jsys, NULL);
|
|
neighbour->r = dep+1;
|
|
open = A_add(open, neighbour);
|
|
}
|
|
}
|
|
A_freeList(A_gc);
|
|
return 0;
|
|
}
|
|
|
|
/* Check to see if radius is mapped. */
|
|
int map_isMapped(char* targ_sys, int r) {
|
|
int i, dep, ret;
|
|
StarSystem* sys, *jsys;
|
|
SysNode* closed, *open, *cur, *neighbour;
|
|
|
|
A_gc = NULL;
|
|
open = closed = NULL;
|
|
|
|
if(targ_sys == NULL) sys = cur_system;
|
|
else sys = system_get(targ_sys);
|
|
open = A_newNode(sys, NULL);
|
|
open->r = 0;
|
|
ret = 1;
|
|
|
|
while((cur = A_lowest(open)) != NULL) {
|
|
/* Mark system as known and go to next. */
|
|
sys = cur->sys;
|
|
dep = cur->r;
|
|
if(!sys_isFlag(sys, SYSTEM_KNOWN)) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
open = A_rm(open, sys);
|
|
closed = A_add(closed, cur);
|
|
|
|
/* Check it's jumps. */
|
|
for(i = 0; i < sys->njumps; i++) {
|
|
jsys = &systems_stack[cur->sys->jumps[i]];
|
|
|
|
/* System has already been parsed or is too deep. */
|
|
if((A_in(closed, jsys) != NULL) || (dep+1 > r))
|
|
continue;
|
|
|
|
/* Create new node and such. */
|
|
neighbour = A_newNode(jsys, NULL);
|
|
neighbour->r = dep+1;
|
|
open = A_add(open, neighbour);
|
|
}
|
|
}
|
|
|
|
A_freeList(A_gc);
|
|
return ret;
|
|
}
|
|
|