#include #include "lephisto.h" #include "log.h" #include "pack.h" #include "xml.h" #include "toolkit.h" #include "ship.h" #define XML_ID "Ships" // XML section identifier. #define XML_SHIP "ship" #define SHIP_DATA "../dat/ship.xml" #define SHIP_GFX "../gfx/ship/" #define SHIP_EXT ".png" #define SHIP_TARGET "_target" #define VIEW_WIDTH 300 #define VIEW_HEIGHT 300 #define BUTTON_WIDTH 80 #define BUTTON_HEIGHT 30 static Ship* ship_stack = NULL; static int ships = 0; static Ship* ship_parse(xmlNodePtr parent); static void ship_view_close(char* btn); // Get a ship based on it's name. Ship* ship_get(const char* name) { Ship* tmp = ship_stack; int i; for(i = 0; i < ships; i++) if(strcmp((tmp+i)->name, name)==0) break; if(i == ships) // Ship doesn't exist, game will probably crash now. WARN("Ship %s does not exist", name); return tmp+i; } // Get the ship's classname. static char* ship_classes[] = { "NULL", "Civialian Light", "Civilian Medium", "Civilian Heavy" }; // Return all the ships in text form. char** ship_getAll(int* n) { char** shipnames = malloc(sizeof(Ship*) * ships); for((*n)=0; (*n) < ships; (*n)++) shipnames[*n] = strdup(ship_stack[*n].name); return shipnames; } char* ship_class(Ship* s) { return ship_classes[s->class]; } static Ship* ship_parse(xmlNodePtr parent) { xmlNodePtr cur, node; Ship* tmp = CALLOC_L(Ship); ShipOutfit* otmp, *ocur; char str[PATH_MAX] = "\0"; char* stmp; tmp->name = xml_nodeProp(parent, "name"); if(tmp->name == NULL) WARN("Ship in "SHIP_DATA" has invalid or no name"); node = parent->xmlChildrenNode; do { // Load all the data. if(xml_isNode(node,"GFX")) { snprintf(str, strlen(xml_get(node)) + sizeof(SHIP_GFX) + sizeof(SHIP_EXT), SHIP_GFX"%s"SHIP_EXT, xml_get(node)); tmp->gfx_space = gl_newSprite(str, 6, 6); // Target. snprintf(str, strlen(xml_get(node)) + sizeof(SHIP_GFX)+sizeof(SHIP_TARGET)+sizeof(SHIP_EXT), SHIP_GFX"%s"SHIP_TARGET SHIP_EXT, xml_get(node)); tmp->gfx_target = gl_newImage(str); } else if(xml_isNode(node, "GUI")) tmp->gui = strdup(xml_get(node)); else if(xml_isNode(node, "sound")) tmp->sound = sound_get(xml_get(node)); else if(xml_isNode(node, "class")) tmp->class = xml_getInt(node); else if(xml_isNode(node, "price")) tmp->price = xml_getInt(node); else if(xml_isNode(node, "fabricator")) tmp->fabricator = strdup(xml_get(node)); else if(xml_isNode(node, "description")) tmp->description = strdup(xml_get(node)); else if(xml_isNode(node, "movement")) { cur = node->children; do { if(xml_isNode(cur, "thrust")) tmp->thrust = xml_getInt(cur); else if(xml_isNode(cur, "turn")) tmp->turn = xml_getInt(cur); else if(xml_isNode(cur, "speed")) tmp->speed = xml_getInt(cur); } while((cur = cur->next)); } else if(xml_isNode(node, "health")) { cur = node->children; do { if(xml_isNode(cur, "armour")) tmp->armour = (double)xml_getInt(cur); else if(xml_isNode(cur, "shield")) tmp->shield = (double)xml_getInt(cur); else if(xml_isNode(cur, "energy")) tmp->energy = (double)xml_getInt(cur); else if(xml_isNode(cur, "armour_regen")) tmp->armour_regen = (double)(xml_getInt(cur))/60.0; else if(xml_isNode(cur, "shield_regen")) tmp->shield_regen = (double)(xml_getInt(cur))/60.0; else if(xml_isNode(cur, "energy_regen")) tmp->energy_regen = (double)(xml_getInt(cur))/60.0; } while((cur = cur->next)); } else if(xml_isNode(node, "characteristics")) { cur = node->children; do { if(xml_isNode(cur, "crew")) tmp->crew = xml_getInt(cur); else if(xml_isNode(cur, "mass")) tmp->mass = (double)xml_getInt(cur); else if(xml_isNode(cur, "cap_weapon")) tmp->cap_weapon = xml_getInt(cur); else if(xml_isNode(cur, "cap_cargo")) tmp->cap_cargo = xml_getInt(cur); } while((cur = cur->next)); } else if(xml_isNode(node, "outfits")) { cur = node->children; do { if(xml_isNode(cur, "outfit")) { otmp = MALLOC_L(ShipOutfit); otmp->data = outfit_get(xml_get(cur)); stmp = xml_nodeProp(cur, "quantity"); if(!stmp) WARN("Ship '%s' is missing tag 'quantity for outfit '%s'", tmp->name, otmp->data->name); otmp->quantity = atoi(stmp); free(stmp); otmp->next = NULL; if((ocur = tmp->outfit) == NULL) tmp->outfit = otmp; else { while(ocur->next) ocur = ocur->next; ocur->next = otmp; } } } while((cur = cur->next)); } } while((node = node->next)); tmp->thrust *= tmp->mass; // Helps keep number sane. #define MELEMENT(o,s) if(o) WARN("Ship '%s' missing '"s"' element", tmp->name) MELEMENT(tmp->name == NULL, "name"); MELEMENT(tmp->gfx_space == NULL, "GFX"); MELEMENT(tmp->gui == NULL, "GUI"); MELEMENT(tmp->class==0, "class"); MELEMENT(tmp->price==0, "price"); MELEMENT(tmp->fabricator==0, "fabricator"); MELEMENT(tmp->description==0, "description"); MELEMENT(tmp->thrust==0, "thrust"); MELEMENT(tmp->turn==0, "turn"); MELEMENT(tmp->speed==0, "speed"); MELEMENT(tmp->armour==0, "armour"); MELEMENT(tmp->armour_regen==0, "armour_regen"); MELEMENT(tmp->shield==0, "shield"); MELEMENT(tmp->shield_regen==0, "shield_regen"); MELEMENT(tmp->energy==0, "energy"); MELEMENT(tmp->energy_regen==0, "energt_regen"); MELEMENT(tmp->crew==0, "crew"); MELEMENT(tmp->mass==0, "mass"); MELEMENT(tmp->cap_cargo==0, "cap_cargo"); MELEMENT(tmp->cap_weapon==0, "cap_weapon"); #undef MELEMENT return tmp; } int ships_load(void) { uint32_t bufsize; char* buf = pack_readfile(DATA, SHIP_DATA, &bufsize); xmlNodePtr node; xmlDocPtr doc = xmlParseMemory(buf, bufsize); Ship* tmp = NULL; node = doc->xmlChildrenNode; // Ships node. if(strcmp((char*)node->name, XML_ID)) { ERR("Malformed "SHIP_DATA" file: missing root element '"XML_ID"'"); return -1; } node = node->xmlChildrenNode; // First ship node. if(node == NULL) { ERR("Malformed "SHIP_DATA" file: Does not contain elements"); return -1; } do { if(node->type == XML_NODE_START && strcmp((char*)node->name, XML_SHIP)==0) { tmp = ship_parse(node); ship_stack = realloc(ship_stack, sizeof(Ship)*(++ships)); memcpy(ship_stack+ships-1, tmp, sizeof(Ship)); free(tmp); } } while((node = node->next)); xmlFreeDoc(doc); free(buf); xmlCleanupParser(); DEBUG("Loaded %d ship%s", ships, (ships==1) ? "" : "s"); return 0; } void ships_free(void) { ShipOutfit* so, *sot; int i; for(i = 0; i < ships; i++) { // Free stored strings. if((ship_stack+i)->name) free(ship_stack[i].name); if((ship_stack+i)->description) free(ship_stack[i].description); if((ship_stack+i)->gui) free(ship_stack[i].gui); so = (ship_stack+i)->outfit; while(so) { // free the ship outfit. sot = so; so = so->next; free(sot); } gl_freeTexture((ship_stack+i)->gfx_space); gl_freeTexture((ship_stack+i)->gfx_target); } free(ship_stack); ship_stack = NULL; } // Used to visualize the ships status. void ship_view(char* shipname) { Ship* s; char buf[1024]; unsigned int wid; wid = window_create(shipname, -1, -1, VIEW_WIDTH, VIEW_HEIGHT); s = ship_get(shipname); window_addText(wid, 20, 0, 100, VIEW_HEIGHT-40, 0, "txtLabel", &gl_smallFont, &cDConsole, "Name:\n" "Class:\n" "Crew:\n" "Mass:\n" "\n" "Thrust:\n" "Max Speed:\n" "Turn:\n" "\n" "Shield:\n" "Armour:\n" "Energy:\n" "\n" "Weapon Space:\n" "Cargo Space:\n"); snprintf(buf, 1024, "%s\n" "%s\n" "%d\n" "%d Tons\n" "\n" "%.2f MN\n" "%.2f M/s\n" "%.2f Grad/s\n" "\n" "%.2f MJ (%.2f MJ/s)\n)" "%.2f MJ (%.2f MJ/s)\n)" "%.2f MJ (%.2f MJ/s)\n)" "\n" "%d Tons\n" "%d Tons\n", s->name, ship_class(s), s->crew, s->mass, s->thrust/s->mass, s->speed, s->turn, s->shield, s->shield_regen, s->armour, s->armour_regen, s->energy, s->energy_regen, s->cap_weapon, s->cap_cargo); window_addText(wid, 120, 0, VIEW_WIDTH-140, VIEW_HEIGHT-40, 0, "txtProperties", &gl_smallFont, &cBlack, buf); // Close the button. snprintf(buf, 37, "close%s", shipname); window_addButton(wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT, buf, "Close", ship_view_close); } static void ship_view_close(char* btn) { window_destroy(window_get(btn+5)); // "closefoo -> Foo" }