diff --git a/dat/outfit.xml b/dat/outfit.xml index a64d6a7..8d39786 100644 --- a/dat/outfit.xml +++ b/dat/outfit.xml @@ -656,5 +656,16 @@ By adding quite a dense layer of Plasteel to your ship you can increase it's stu <specific type="fighter"> <ship>Lancelot</ship> </specific> - </outfit> +</outfit> + <outfit name="Large Civilian Vessel License"> + <general> + <max>1</max> + <tech>8</tech> + <mass>0</mass> + <price>75000</price> + <gfx_store>license</gfx_store> + <description>This license will authorize you to buy Cruise Ship and Bulk Carrier class ships.</description> + </general> + <specific type="license" /> +</outfit> </Outfits> diff --git a/dat/ship.xml b/dat/ship.xml index 12059eb..351c55c 100644 --- a/dat/ship.xml +++ b/dat/ship.xml @@ -326,6 +326,7 @@ <price>900000</price> <fabricator>Melendez Corp</fabricator> <tech>5</tech> + <license>Large Civilian Vessel License</license> <description>A heavy liner specialized in freighting cargo all over the universe. Also has good accomodations for passengers depending on the model allowing for enjoyable cruises. Was a favorite target of pirates until they added stock turrets to the new models which can now defend themselves properly.</description> <movement> <thrust>100</thrust> diff --git a/gfx/outfit/store/license.png b/gfx/outfit/store/license.png new file mode 100644 index 0000000..adb8406 Binary files /dev/null and b/gfx/outfit/store/license.png differ diff --git a/src/land.c b/src/land.c index e6afc6c..a28a573 100644 --- a/src/land.c +++ b/src/land.c @@ -270,7 +270,7 @@ static void outfits_open(void) { /* The descriptive text. */ window_addText(wid, 40+300+20, -60, - 80, 140, 0, "txtSDesc", &gl_smallFont, &cDConsole, + 80, 160, 0, "txtSDesc", &gl_smallFont, &cDConsole, "Name:\n" "Type:\n" "Owned:\n" @@ -279,12 +279,13 @@ static void outfits_open(void) { "Free Space:\n" "\n" "Price:\n" - "Money:\n"); + "Money:\n" + "License:\n"); window_addText(wid, 40+300+40+60, -60, - 250, 140, 0, "txtDDesc", &gl_smallFont, &cBlack, NULL); + 250, 160, 0, "txtDDesc", &gl_smallFont, &cBlack, NULL); - window_addText(wid, 20+300+40, -220, + window_addText(wid, 20+300+40, -240, OUTFITS_WIDTH-400, 180, 0, "txtDescription", &gl_smallFont, NULL, NULL); @@ -327,14 +328,14 @@ static void outfits_update(unsigned int wid, char* str) { (void)str; char* outfitname; Outfit* outfit; - char buf[128], buf2[16], buf3[16]; + char buf[PATH_MAX], buf2[16], buf3[16]; outfitname = toolkit_getList(wid, "iarOutfits"); if(strcmp(outfitname, "None")==0) { /* No outfits. */ window_modifyImage(wid, "imgOutfit", NULL); window_disableButton(wid, "btnBuyOutfit"); window_disableButton(wid, "btnSellOutfit"); - snprintf(buf, 128, + snprintf(buf, PATH_MAX, "None\n" "NA\n" "NA\n" @@ -343,6 +344,7 @@ static void outfits_update(unsigned int wid, char* str) { "%d\n" "\n" "NA\n" + "MA\m" "NA\n", pilot_freeSpace(player)); window_modifyText(wid, "txtDDesc", buf); @@ -368,7 +370,7 @@ static void outfits_update(unsigned int wid, char* str) { window_modifyText(wid, "txtDescription", outfit->description); credits2str(buf2, outfit->price, 2); credits2str(buf3, player->credits, 2); - snprintf(buf, 128, + snprintf(buf, PATH_MAX, "%s\n" "%s\n" "%d\n" @@ -377,14 +379,16 @@ static void outfits_update(unsigned int wid, char* str) { "%d\n" "\n" "%s SCred\n" - "%s SCred\n", + "%s SCred\n" + "%s\n", outfit->name, outfit_getType(outfit), player_outfitOwned(outfitname), outfit->mass, pilot_freeSpace(player), buf2, - buf3); + buf3, + (outfit->license != NULL) ? outfit->license : "None"); window_modifyText(wid, "txtDDesc", buf); } @@ -425,11 +429,25 @@ static int outfit_canBuy(Outfit* outfit, int q, int errmsg) { } return 0; } + /* Map alrady mapped. */ else if(outfit_isMap(outfit) && map_isMapped(NULL, outfit->u.map.radius)) { if(errmsg != 0) dialogue_alert("You already own this map."); return 0; } + /* Already has license. */ + else if(outfit_isLicense(outfit) && player_hasLicense(outfit->name)) { + if(errmsg != 0) + dialogue_alert("You already have this license."); + return 0; + } + /* Needs license. */ + else if((outfit->license != NULL) && !player_hasLicense(outfit->name)) { + if(errmsg != 0) + dialogue_alert("You need the '%s' license to buy this outfit.", + outfit->license); + return 0; + } return 1; } @@ -561,23 +579,24 @@ static void shipyard_open(void) { /* Text. */ window_addText(wid, 40+300+40, -55, - 80, 96, 0, "txtSDesc", &gl_smallFont, &cDConsole, + 80, 128, 0, "txtSDesc", &gl_smallFont, &cDConsole, "Name:\n" "Class:\n" "Fabricator:\n" "\n" "Price:\n" - "Money:\n"); + "Money:\n" + "License:\n"); window_addText(wid, 40+300+40+80, -55, - 130, 96, 0, "txtDDesc", &gl_smallFont, &cBlack, NULL); + 130, 128, 0, "txtDDesc", &gl_smallFont, &cBlack, NULL); - window_addText(wid, 20+310+40, -175, + window_addText(wid, 20+310+40, -55-128-20, SHIPYARD_WIDTH-(20+310+40) - 20, 185, 0, "txtDescription", &gl_smallFont, NULL, NULL); - /* Setup the ships to buy/sell. */ + /* Set up the ships to buy/sell. */ ships = ship_getTech(&nships, land_planet->tech, PLANET_TECH_MAX); if(nships <= 0) { @@ -614,7 +633,7 @@ static void shipyard_update(unsigned int wid, char* str) { (void)str; char* shipname; Ship* ship; - char buf[80], buf2[16], buf3[16]; + char buf[PATH_MAX], buf2[16], buf3[16]; shipname = toolkit_getList(wid, "iarShipyard"); @@ -623,12 +642,13 @@ static void shipyard_update(unsigned int wid, char* str) { window_modifyImage(wid, "imgTarget", NULL); window_disableButton(wid, "btnBuyShip"); window_disableButton(wid, "btnInfoShip"); - snprintf(buf, 80, + snprintf(buf, PATH_MAX, "None\n" "NA\n" "NA\n" "\n" "NA\n" + "NA\n" "NA\n"); window_modifyText(wid, "txtDDesc", buf); return; @@ -644,18 +664,20 @@ static void shipyard_update(unsigned int wid, char* str) { credits2str(buf2, ship->price, 2); credits2str(buf3, player->credits, 2); - snprintf(buf, 80, + snprintf(buf, PATH_MAX, "%s\n" "%s\n" "%s\n" "\n" "%s SCred\n" - "%s SCred\n", + "%s SCred\n" + "%s\n", ship->name, ship_class(ship), ship->fabricator, buf2, - buf3); + buf3, + (ship->license != NULL) ? ship->license : "None"); window_modifyText(wid, "txtDDesc", buf); @@ -686,6 +708,13 @@ static void shipyard_buy(unsigned int wid, char* str) { return; } + /* Must have enough credits. */ + if((ship->license != NULL) && !player_hasLicense(ship->license)) { + dialogue_alert("You do not have the '%s' license required to buy this ship.", + ship->license); + return; + } + if(pilot_cargoUsed(player) > ship->cap_cargo) { dialogue_alert("You won't have space to move your current cargo onto the new ship."); return; diff --git a/src/outfit.c b/src/outfit.c index b6ee512..d3c05cc 100644 --- a/src/outfit.c +++ b/src/outfit.c @@ -40,6 +40,7 @@ static void outfit_parseSJammer(Outfit* tmp, const xmlNodePtr parent); static void outfit_parseSFighterBay(Outfit* tmp, const xmlNodePtr parent); static void outfit_parseSFighter(Outfit* tmp, const xmlNodePtr parent); static void outfit_parseSMap(Outfit* tmp, const xmlNodePtr parent); +static void outfit_parseSLicense(Outfit* tmp, const xmlNodePtr parent); /* Return an outfit. */ Outfit* outfit_get(const char* name) { @@ -302,6 +303,14 @@ int outfit_isMap(const Outfit* o) { return(o->type == OUTFIT_TYPE_MAP); } +/** + * @brief Check if outfit is a licence. + * @param o Outfit to check. + * @return 1 if o is a map. + */ +int outfit_isLicense(const Outfit* o) { + return (o->type == OUTFIT_TYPE_LICENSE); +} /** * @fn glTexture* outfit_gfx(const Outfit* o) @@ -446,7 +455,8 @@ const char* outfit_getType(const Outfit* o) { "Jammer", "Fighter Bay", "Fighter", - "Map" + "Map", + "License" }; return outfit_typename[o->type]; @@ -471,6 +481,7 @@ const char* outfit_getTypeBroad(const Outfit* o) { else if(outfit_isFighterBay(o)) return "Fighter Bay"; else if(outfit_isFighter(o)) return "Fighter"; else if(outfit_isMap(o)) return "Map"; + else if(outfit_isLicense(o)) return "License"; else return "Unknown"; } @@ -505,10 +516,11 @@ static OutfitType outfit_strToOutfitType(char* buf) { O_CMP("missile swarm smart ammo", OUTFIT_TYPE_MISSILE_SWARM_SMART_AMMO); O_CMP("modification", OUTFIT_TYPE_MODIFICATION); O_CMP("afterburner", OUTFIT_TYPE_AFTERBURNER); - O_CMP("map", OUTFIT_TYPE_MAP); O_CMP("fighter bay", OUTFIT_TYPE_FIGHTER_BAY); O_CMP("fighter", OUTFIT_TYPE_FIGHTER); O_CMP("jammer", OUTFIT_TYPE_JAMMER); + O_CMP("map", OUTFIT_TYPE_MAP); + O_CMP("license", OUTFIT_TYPE_LICENSE); WARN("Invalid outfit type '%s'", buf); return OUTFIT_TYPE_NULL; @@ -835,6 +847,19 @@ static void outfit_parseSMap(Outfit* tmp, const xmlNodePtr parent) { WARN("Outfit '%s' missing/invalid 'radius' element", tmp->name); } +/** + * @brief Parses the license tidbits of the outfit. + * @param tmp Outfit to finish loading. + * @param parent Outfit's parent node. + */ +static void outfit_parseSLicense(Outfit* tmp, const xmlNodePtr parent) { + /* Licenses have no specific tidbits. */ + (void)tmp; + (void)parent; + + +} + /* Parses the jammer tidbits of the outfit. */ static void outfit_parseSJammer(Outfit* tmp, const xmlNodePtr parent) { xmlNodePtr node; @@ -876,6 +901,7 @@ static int outfit_parse(Outfit* tmp, const xmlNodePtr parent) { do { xmlr_int(cur, "max", tmp->max); xmlr_int(cur, "tech", tmp->tech); + xmlr_strd(cur, "license", tmp->license); xmlr_int(cur, "mass", tmp->mass); xmlr_int(cur, "price", tmp->price); xmlr_strd(cur, "description", tmp->description); @@ -923,6 +949,8 @@ static int outfit_parse(Outfit* tmp, const xmlNodePtr parent) { outfit_parseSFighter(tmp, node); else if(outfit_isMap(tmp)) outfit_parseSMap(tmp, node); + else if (outfit_isLicense(tmp)) + outfit_parseSLicense(tmp, node); } } while(xml_nextNode(node)); #define MELEMENT(o,s) if(o) WARN("Outfit '%s' missing '"s"' element", tmp->name) diff --git a/src/outfit.h b/src/outfit.h index b6ea718..daa958f 100644 --- a/src/outfit.h +++ b/src/outfit.h @@ -37,6 +37,7 @@ typedef enum OutfitType_ { OUTFIT_TYPE_FIGHTER_BAY, /**< Contains other ships. */ OUTFIT_TYPE_FIGHTER, /**< Ship contained in FIGHTER BAY. */ OUTFIT_TYPE_MAP, /**< Give the player more knowledge about systems. */ + OUTFIT_TYPE_LICENSE, /**< License that allows player to buy special stuff. */ OUTFIT_TYPE_SENTINEL /**< Indicates last type. */ } OutfitType; @@ -231,6 +232,7 @@ typedef struct Outfit_ { /* General specs. */ int max; /**< Max amount one can own. */ int tech; /**< Tech needed to sell it. */ + int license; /**< Licenses needed to buy it. */ int mass; /**< How much weapon capacity is needed. */ /* Store stuff. */ @@ -277,6 +279,7 @@ int outfit_isJammer(const Outfit* o); int outfit_isFighterBay(const Outfit* o); int outfit_isFighter(const Outfit* o); int outfit_isMap(const Outfit* o); +int outfit_isLicense(const Outfit* o); const char* outfit_getType(const Outfit* o); const char* outfit_getTypeBroad(const Outfit* o); diff --git a/src/pilot.c b/src/pilot.c index 1289844..c493041 100644 --- a/src/pilot.c +++ b/src/pilot.c @@ -59,6 +59,7 @@ extern double player_faceHyperspace(void); extern void player_dead(void); extern void player_destroyed(void); extern int gui_load(const char* name); +extern void player_addLicense(char* license); /* Internal. */ static int pilot_getStackPos(const unsigned int id); static void pilot_shootWeapon(Pilot* p, PilotOutfit* w); @@ -915,7 +916,14 @@ int pilot_addOutfit(Pilot* pilot, Outfit* outfit, int quantity) { /* Special case if it's a map. */ if(outfit_isMap(outfit)) { - map_map(NULL, outfit->u.map.radius); + if(pilot == player) /* Only player can get it. */ + map_map(NULL, outfit->u.map.radius); + return 1; /* Must return 1 for paying purposes. */ + } + /* Special case if it's a license. */ + else if(outfit_isLicense(outfit)) { + if(pilot == player) /* Only player can get it. */ + player_addLicense(outfit->name); return 1; /* Must return 1 for paying purposes. */ } diff --git a/src/player.c b/src/player.c index 15536b3..7c75571 100644 --- a/src/player.c +++ b/src/player.c @@ -58,6 +58,10 @@ static double player_px, player_py, player_vx, player_vy, player_dir; /**< More static int player_credits = 0; /**< Temp hack on create. */ static char* player_mission = NULL; /**< More hack. */ +/* Licenses. */ +static char** player_licenses = NULL; /**< Licenses player has. */ +static int player_nlicenses = 0; /**< Number of licenses player has. */ + /* * Player sounds. */ @@ -226,6 +230,7 @@ static void gui_cleanup(void); static int player_saveShip(xmlTextWriterPtr writer, Pilot* ship, char* loc); static int player_parse(xmlNodePtr parent); static int player_parseDone(xmlNodePtr parent); +static int player_parseLicenses(xmlNodePtr parent); static int player_parseShip(xmlNodePtr parent, int is_player); /* Externed. */ @@ -631,6 +636,15 @@ void player_cleanup(void) { missions_mdone = 0; } + /* Clean up licenses. */ + if(player_nlicenses > 0) { + for(i = 0; i < player_nlicenses; i++) + free(player_licenses[i]); + free(player_licenses); + player_licenses = NULL; + player_nlicenses = 0; + } + pilots_cleanAll(); if(player != NULL) { pilot_free(player); @@ -2320,27 +2334,63 @@ int player_missionAlreadyDone(int id) { return 0; } +/** + * @brief Check to see if player has license. + * @param license License to check to see if the player has. + * @return 1 if has license, 0 if doesn't. + */ +int player_hasLicense(char* license) { + int i; + for(i = 0; i < player_nlicenses; i++) + if(strcmp(license, player_licenses[i])==0) + return 1; + return 0; +} + +/** + * @brief Give the player a license. + * @brief license License to give the player. + */ +void player_addLicense(char* license) { + /* Player already has license. */ + if(player_hasLicense(license)) + return; + + /* Add the license. */ + player_nlicenses++; + player_licenses = realloc(player_licenses, sizeof(char*)*player_nlicenses); + player_licenses[player_nlicenses-1] = strdup(license); +} + /* Save the player in a freaking xmlfile. */ int player_save(xmlTextWriterPtr writer) { int i; MissionData* m; xmlw_startElem(writer, "player"); + + /* Standard player details. */ xmlw_attr(writer, "name", player_name); - xmlw_elem(writer, "rating", "%f", player_crating); - xmlw_elem(writer, "scred", "%d", player->credits); + xmlw_elem(writer, "credits", "%d", player->credits); xmlw_elem(writer, "time", "%d", ltime_get()); + /* Current ship. */ xmlw_elem(writer, "location", land_planet->name); player_saveShip(writer, player, NULL); /* Current ship. */ + /* Ships. */ xmlw_startElem(writer, "ships"); for(i = 0; i < player_nstack; i++) player_saveShip(writer, player_stack[i], player_lstack[i]); - xmlw_endElem(writer); /* Player. */ + /* Licenses. */ + xmlw_startElem(writer, "licenses"); + for(i = 0; i < player_nlicenses; i++) + xmlw_elem(writer, "license", player_licenses[i]); + xmlw_endElem(writer) /* "license" */ + /* Mission the player has done. */ xmlw_startElem(writer, "missions_done"); @@ -2466,15 +2516,16 @@ static int player_parse(xmlNodePtr parent) { if(xml_isNode(node, "ship")) player_parseShip(node, 1); - if(xml_isNode(node, "ships")) { + else if(xml_isNode(node, "ships")) { cur = node->xmlChildrenNode; do { if(xml_isNode(cur, "ship")) player_parseShip(cur, 0); } while(xml_nextNode(cur)); } - if(xml_isNode(node, "missions_done")) - player_parseDone(node); + else if(xml_isNode(node, "licenses")) + player_parseLicenses(node); + } while(xml_nextNode(node)); /* Set global thingies. */ @@ -2513,6 +2564,24 @@ static int player_parseDone(xmlNodePtr parent) { return 0; } +/** + * @brief Parse player's licenses. + * @param parent Node of the licenses. + * @return 0 on success. + */ +static int player_parseLicenses(xmlNodePtr parent) { + xmlNodePtr node; + + node = parent->xmlChildrenNode; + + do { + if(xml_isNode(node, "license")) + player_addLicense(xml_get(node)); + } while(xml_nextNode(node)); + + return 0; +} + static int player_parseShip(xmlNodePtr parent, int is_player) { char* name, *model, *loc, *q, *id; int i, n; diff --git a/src/player.h b/src/player.h index d44f496..293c0d2 100644 --- a/src/player.h +++ b/src/player.h @@ -71,6 +71,10 @@ void player_rmShip(char* shipname); void player_missionFinished(int id); int player_missionAlreadyDone(int id); +/* Licenses. */ +void player_addLicense(char* license); +int player_hasLicense(char* license); + /* Keybind actions. */ void player_targetHostile(void); void player_targetNext(void); diff --git a/src/ship.c b/src/ship.c index 4001c4e..5f29bcc 100644 --- a/src/ship.c +++ b/src/ship.c @@ -273,8 +273,9 @@ static Ship* ship_parse(xmlNodePtr parent) { if(xml_isNode(node, "class")) { tmp->class = ship_classFromString(xml_get(node)); } - xmlr_int(node, "price", tmp->price); - xmlr_int(node, "tech", tmp->tech); + xmlr_int( node, "price", tmp->price); + xmlr_int( node, "tech", tmp->tech); + xmlr_strd(node, "license", tmp->license); xmlr_strd(node, "fabricator", tmp->fabricator); xmlr_strd(node, "description", tmp->description); if(xml_isNode(node, "movement")) { diff --git a/src/ship.h b/src/ship.h index d4830ae..f42dc9e 100644 --- a/src/ship.h +++ b/src/ship.h @@ -52,10 +52,11 @@ typedef struct Ship_ { ShipClass class; /* Ship class. */ /* Store stuff. */ - unsigned int price; /* Price! */ - int tech; - char* fabricator; /* Manufacturer. */ - char* description; /* Sales pitch. */ + unsigned int price; /**< Price! */ + int tech; /**< Tech needed for it to be availabe. See space.c */ + char* license; /**< License needed to buy it. */ + char* fabricator; /**< Manufacturer. */ + char* description; /**< Sales pitch. */ /* Movement. */ double thrust, turn, speed;