1 /*
2  *  Copyright (C) 2002  The Exult Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22 
23 #include <cstdio>
24 
25 #include "objs.h"
26 #include "game.h"
27 #include "items.h"
28 #include "gamewin.h"
29 #include "actors.h"
30 #include "shapeinf.h"
31 #include "frnameinf.h"
32 
33 #ifndef ATTR_PRINTF
34 #ifdef __GNUC__
35 #define ATTR_PRINTF(x,y) __attribute__((format(printf, (x), (y))))
36 #else
37 #define ATTR_PRINTF(x,y)
38 #endif
39 #endif
40 
41 using std::string;
42 using std::strchr;
43 
44 /*
45  *  For objects that can have a quantity, the name is in the format:
46  *      %1/%2/%3/%4
47  *  Where
48  *      %1 : singular prefix (e.g. "a")
49  *      %2 : main part of name
50  *      %3 : singular suffix
51  *      %4 : plural suffix (e.g. "s")
52  */
53 
54 /*
55  *  Extracts the first, second and third parts of the name string
56  */
get_singular_name(const char * name,string & output_name)57 static void get_singular_name(
58     const char *name,       // Raw name string from TEXT.FLX
59     string &output_name     // Output string
60 ) {
61 	if (*name != '/') {     // Output the first part
62 		do
63 			output_name += *name++;
64 		while (*name != '/' && *name != '\0');
65 		if (*name == '\0') { // should not happen
66 			output_name = "?";
67 			return;
68 		}
69 		// If there is a first part it is followed by a space
70 		output_name += ' ';
71 	}
72 	name++;
73 
74 	// Output the second part
75 	while (*name != '/' && *name != '\0')
76 		output_name += *name++;
77 	if (*name == '\0') {    // should not happen
78 		output_name = "?";
79 		return;
80 	}
81 	name++;
82 
83 	// Output the third part
84 	while (*name != '/' && *name != '\0')
85 		output_name += *name++;
86 	if (*name == '\0') {    // should not happen
87 		output_name = "?";
88 		return;
89 	}
90 	name++;
91 }
92 
93 /*
94  *  Extracts the second and fourth parts of the name string
95  */
get_plural_name(const char * name,int quantity,string & output_name)96 static void get_plural_name(
97     const char *name,
98     int quantity,
99     string &output_name
100 ) {
101 	char buf[20];
102 
103 	snprintf(buf, 20, "%d ", quantity); // Output the quantity
104 	output_name = buf;
105 
106 	// Skip the first part
107 	while (*name != '/' && *name != '\0')
108 		name++;
109 	if (*name == '\0') {    // should not happen
110 		output_name = "?";
111 		return;
112 	}
113 	name++;
114 	// Output the second part
115 	while (*name != '/' && *name != '\0')
116 		output_name += *name++;
117 	if (*name == '\0') {    // should not happen
118 		output_name = "?";
119 		return;
120 	}
121 	name++;
122 	// Skip the third part
123 	while (*name != '/' && *name != '\0')
124 		name++;
125 	if (*name == '\0') {    // should not happen
126 		output_name = "?";
127 		return;
128 	}
129 	name++;
130 	while (*name != '\0')       // Output the last part
131 		output_name += *name++;
132 }
133 
134 /*
135  *  Returns the string to be displayed when the item is clicked on
136  */
get_name() const137 string Game_object::get_name(
138 ) const {
139 	const Shape_info &info = get_info();
140 	int qual = info.has_quality() && !info.is_npc() ? get_quality() : -1;
141 	const Frame_name_info *nminf = info.get_frame_name(get_framenum(), qual);
142 	int shnum = get_shapenum();
143 	const char *name;
144 	const char *shpname = (shnum >= 0 && shnum < get_num_item_names()) ?
145 	                      get_item_name(shnum) : nullptr;
146 	int type = nminf ? nminf->get_type() : -255;
147 	int msgid;
148 	if (type < 0 && type != -255)   // This is a "catch all" default.
149 		return "";  // None.
150 	else if (type == -255 || (msgid = nminf->get_msgid()) >= get_num_misc_names())
151 		name = shpname;
152 	else if (!type)
153 		name = get_misc_name(msgid);
154 	else if (!info.has_quality() && !info.is_body_shape())
155 		name = shpname;     // Use default name for these.
156 	else {
157 		int othermsg = nminf->get_othermsg();
158 		bool defname = false;
159 		string msg;
160 		string other;
161 		if (type >= 3) {
162 			// Special names (in SI, corpse, urn).
163 			int npcnum = -1;
164 			if (!info.is_body_shape())
165 				npcnum = get_quality();
166 			else if (qual == 1)
167 				npcnum = get_live_npc_num();
168 			Actor *npc = gwin->get_npc(npcnum);
169 			if (npc && !npc->is_unused() &&
170 			        (!info.is_body_shape() || npc->get_flag(Obj_flags::met))) {
171 				other = npc->get_npc_name_string();
172 				if (other.empty())  // No name.
173 					defname = true;
174 				else
175 					msg = get_misc_name(msgid);
176 			} else  // Default name.
177 				defname = true;
178 		} else {
179 			msg = get_misc_name(msgid);
180 			other = (othermsg >= 0 && othermsg < get_num_misc_names()) ?
181 			        get_misc_name(othermsg) : (shpname ? shpname : "");
182 		}
183 		if (defname) {
184 			if (othermsg >= 0 && othermsg < get_num_misc_names())
185 				name = get_misc_name(othermsg);
186 			else if (othermsg < 0 && othermsg != -255)  // None.
187 				return "";
188 			else    // Use shape's.
189 				name = shpname;
190 		} else if (type & 1)
191 			return other + msg;
192 		else
193 			return msg + other;
194 	}
195 	int quantity;
196 	string display_name;
197 	if (name == nullptr)
198 		return "";
199 
200 	if (ShapeID::get_info(shnum).has_quantity())
201 		quantity = quality & 0x7f;
202 	else
203 		quantity = 1;
204 
205 	// If there are no slashes then it is simpler
206 	if (strchr(name, '/') == nullptr) {
207 		if (quantity <= 1)
208 			display_name = name;
209 		else {
210 			char buf[50];
211 
212 			snprintf(buf, 50, "%d %s", quantity, name);
213 			display_name = buf;
214 		}
215 	} else if (quantity <= 1)   // quantity might be zero?
216 		get_singular_name(name, display_name);
217 	else
218 		get_plural_name(name, quantity, display_name);
219 	return display_name;
220 }
221