1 /*
2  *
3  *  Copyright (c) 2010 Ari Mustonen
4  *
5  *  This file is part of Freedroid
6  *
7  *  Freedroid is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  Freedroid is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with Freedroid; see the file COPYING. If not, write to the
19  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  *  MA  02111-1307  USA
21  *
22  */
23 #include "defs.h"
24 #include "struct.h"
25 #include "global.h"
26 #include "proto.h"
27 
28 static struct dynarray *addon_specs = NULL;
29 
30 /**
31  * \brief Adds an upgrade socket to the item.
32  * \param it Item.
33  * \param type Upgrade socket type.
34  * \param addon Upgrade item type or NULL.
35  */
create_upgrade_socket(item * it,int type,const char * addon)36 void create_upgrade_socket(item *it, int type, const char *addon)
37 {
38 	struct upgrade_socket socket;
39 
40 	// Initialize upgrade socket data.
41 	socket.type = type;
42 	socket.addon = NULL;
43 	if (addon) {
44 		socket.addon = strdup(addon);
45 	}
46 
47 	// Append an upgrade socket to the socket array.
48 	dynarray_add((struct dynarray *) &it->upgrade_sockets, &socket, sizeof(struct upgrade_socket));
49 }
50 
51 /**
52  * \brief Deletes all upgrade sockets of an item.
53  * \param it Item.
54  */
delete_upgrade_sockets(item * it)55 void delete_upgrade_sockets(item *it)
56 {
57 	int i;
58 	for (i = 0 ; i < it->upgrade_sockets.size ; i++) {
59 		free(it->upgrade_sockets.arr[i].addon);
60 	}
61 	dynarray_free((struct dynarray *) &it->upgrade_sockets);
62 }
63 
64 /**
65  * \brief Copies upgrade sockets from an item to another.
66  *
67  * The destination item must have its sockets deleted prior to calling this.
68  * Otherwise, the old sockets will be leaked.
69  * \param srcitem Source item.
70  * \param dstitem Destination item.
71  */
copy_upgrade_sockets(item * srcitem,item * dstitem)72 void copy_upgrade_sockets(item *srcitem, item *dstitem)
73 {
74 	int i;
75 
76 	// Allocate new upgrade sockets.
77 	int size = srcitem->upgrade_sockets.size;
78 	dynarray_init((struct dynarray *) &dstitem->upgrade_sockets, size, sizeof(struct upgrade_socket));
79 
80 	// Duplicate socket data.
81 	for (i = 0 ; i < size ; i++) {
82 		dynarray_add((struct dynarray *) &dstitem->upgrade_sockets, &srcitem->upgrade_sockets.arr[i],
83 		              sizeof(struct upgrade_socket));
84 		if (srcitem->upgrade_sockets.arr[i].addon) {
85 			dstitem->upgrade_sockets.arr[i].addon = strdup(srcitem->upgrade_sockets.arr[i].addon);
86 		}
87 	}
88 }
89 
90 /**
91  * \brief Returns TRUE if the item is of a customizable type.
92  * \param it Item.
93  * \return TRUE if customizable, FALSE otherwise.
94  */
item_can_be_customized(item * it)95 int item_can_be_customized(item *it)
96 {
97 	itemspec *spec = &ItemMap[it->type];
98 
99 	return spec->slot != NO_SLOT;
100 }
101 
102 /**
103  * \brief Checks if the spec of an add-on is compatible with an item.
104  * \param addonspec Spec of the add-on whose compatibility to test.
105  * \param it Item to which the add-on would be installed.
106  * \return TRUE if compatible, FALSE if incompatible.
107  */
addon_is_compatible_with_item(struct addon_spec * addonspec,item * it)108 static int addon_is_compatible_with_item(struct addon_spec *addonspec, item *it)
109 {
110 	int ret = TRUE;
111 	const char *str = addonspec->requires_item;
112 
113 	if (str) {
114 		itemspec* spec = &ItemMap[it->type];
115 		if (!strcmp(str, "melee weapon")) {
116 			ret = (spec->slot == WEAPON_SLOT) &&
117 			      spec->weapon_is_melee;
118 		} else if (!strcmp(str, "ranged weapon")) {
119 			ret = (spec->slot == WEAPON_SLOT) &&
120 			      !spec->weapon_is_melee;
121 		} else if (!strcmp(str, "armor")) {
122 			ret = (spec->slot & (SHIELD_SLOT | HELM_SLOT | ARMOR_SLOT | BOOT_SLOT));
123 		} else if (!strcmp(str, "boots")) {
124 			ret = (spec->slot == BOOT_SLOT);
125 		} else if (!strcmp(str, "jacket")) {
126 			ret = (spec->slot == ARMOR_SLOT);
127 		} else if (!strcmp(str, "shield")) {
128 			ret = (spec->slot == SHIELD_SLOT);
129 		} else if (!strcmp(str, "helmet")) {
130 			ret = (spec->slot == HELM_SLOT);
131 		} else {
132 			ret = FALSE;
133 		}
134 	}
135 
136 	return ret;
137 }
138 
139 /**
140  * \brief Returns TRUE if the item is compatible with the socket.
141  * \param dstitem Destionation item.
142  * \param addon Add-on item.
143  * \param socketid Socket index.
144  * \return TRUE if compatible, FALSE otherwise.
145  */
item_can_be_installed_to_socket(item * dstitem,item * addon,int socketid)146 int item_can_be_installed_to_socket(item *dstitem, item *addon, int socketid)
147 {
148 	const char *str;
149 	struct addon_spec *spec;
150 	enum upgrade_socket_types addon_type;
151 	enum upgrade_socket_types socket_type;
152 
153 	// Make sure that the socket and the add-on exist.
154 	if (socketid >= dstitem->upgrade_sockets.size) {
155 		error_message(__FUNCTION__, "Socket index out of bounds", PLEASE_INFORM);
156 		return FALSE;
157 	}
158 	if (addon->type == -1) {
159 		error_message(__FUNCTION__, "Add-on type was -1", PLEASE_INFORM);
160 		return FALSE;
161 	}
162 
163 	// Make sure the item is an add-on.
164 	spec = get_addon_spec(addon->type);
165 	if (spec == NULL) {
166 		return FALSE;
167 	}
168 	str = spec->requires_socket;
169 	if (strcmp(str, "mechanical") == 0) {
170 		addon_type = UPGRADE_SOCKET_TYPE_MECHANICAL;
171 	} else if (strcmp(str, "electric") == 0) {
172 		addon_type = UPGRADE_SOCKET_TYPE_ELECTRIC;
173 	} else if (strcmp(str, "universal") == 0) {
174 		addon_type = UPGRADE_SOCKET_TYPE_UNIVERSAL;
175 	} else {
176 		return FALSE;
177 	}
178 
179 	// Check if the socket type is correct. The socket must be either of the
180 	// type specified in itemspec for the add-on or universal.
181 	socket_type = dstitem->upgrade_sockets.arr[socketid].type;
182 	if (socket_type != addon_type && socket_type != UPGRADE_SOCKET_TYPE_UNIVERSAL) {
183 		return FALSE;
184 	}
185 
186 	// Make sure the add-on is compatible with the type of the customized item.
187 	return addon_is_compatible_with_item(spec, dstitem);
188 }
189 
190 /**
191  * \brief Gets the add-on specification for an item type.
192  * \param item_type Item type number.
193  * \return Add-on specification or NULL if the item isn't an add-on.
194  */
get_addon_spec(int item_type)195 struct addon_spec *get_addon_spec(int item_type)
196 {
197 	int i;
198 	struct addon_spec *spec;
199 
200 	for (i = 0; i < addon_specs->size; i++) {
201 		spec = &((struct addon_spec *) addon_specs->arr)[i];
202 		if (spec->type == item_type) {
203 			return spec;
204 		}
205 	}
206 
207 	return NULL;
208 }
209 
210 /**
211  * \brief Gets the list of all add-on specs.
212  * \return A dynarray containing items of type addon_spec.
213  */
get_addon_specs()214 struct dynarray *get_addon_specs()
215 {
216 	return addon_specs;
217 }
218 
219 /**
220  * \brief Registers an add-on specification.
221  *
222  * Only the contents of the passed spec are stored to the add-on spec array.
223  * The spec pointer itself isn't used and needs to be freed by the caller.
224  * \param spec Add-on specification to register.
225  */
add_addon_spec(struct addon_spec * spec)226 void add_addon_spec(struct addon_spec *spec)
227 {
228 	if (addon_specs == NULL) {
229 		addon_specs = dynarray_alloc(32, sizeof(struct addon_spec));
230 	}
231 	dynarray_add(addon_specs, spec, sizeof(struct addon_spec));
232 }
233 
234 /**
235  * \brief Writes the item bonuses of an item to a string suitable for displaying in the UI.
236  * \param it Item.
237  * \param separator Separator string to use between bonuses.
238  * \param desc An allocated auto string to which to append the string.
239  */
get_item_bonus_string(item * it,const char * separator,struct auto_string * desc)240 void get_item_bonus_string(item *it, const char *separator, struct auto_string *desc)
241 {
242 	// Append the bonuses to the string.
243 	if (it->bonus_to_str) {
244 		// TRANSLATORS: Item's bonus: '10 to strength' means 'add 10 to player's strength'
245 		// TRANSLATORS: (trailing %s contains a text separator)
246 		autostr_append(desc, _("%+d to strength%s"), it->bonus_to_str, separator);
247 	}
248 	if (it->bonus_to_dex) {
249 		// TRANSLATORS: Item's bonus: '10 to dexterity' means 'add 10 to player's dexterity'
250 		// TRANSLATORS: (trailing %s contains a text separator)
251 		autostr_append(desc, _("%+d to dexterity%s"), it->bonus_to_dex, separator);
252 	}
253 	if (it->bonus_to_cooling) {
254 		// TRANSLATORS: Item's bonus: '10 to cooling' means 'add 10 to player's cooling capability'
255 		// TRANSLATORS: (trailing %s contains a text separator)
256 		autostr_append(desc, _("%+d to cooling%s"), it->bonus_to_cooling, separator);
257 	}
258 	if (it->bonus_to_physique) {
259 		// TRANSLATORS: Item's bonus: '10 to physique' means 'add 10 to player's physique'
260 		// TRANSLATORS: (trailing %s contains a text separator)
261 		autostr_append(desc, _("%+d to physique%s"), it->bonus_to_physique, separator);
262 	}
263 	if (it->bonus_to_health_points) {
264 		// TRANSLATORS: Item's bonus: '10 to health points' means 'add 10 to player's health'
265 		// TRANSLATORS: (trailing %s contains a text separator)
266 		autostr_append(desc, _("%+d health points%s"), it->bonus_to_health_points, separator);
267 	}
268 	if (it->bonus_to_health_recovery) {
269 		// TRANSLATORS: Item's bonus: '0.1 to health points per seconds' means 'add 0.1 to player's recovery rate'
270 		// TRANSLATORS: (trailing %s contains a text separator)
271 		autostr_append(desc, _("%+0.1f health points per second%s"), it->bonus_to_health_recovery, separator);
272 	}
273 	if (it->bonus_to_cooling_rate) {
274 		if (it->bonus_to_cooling_rate > 0) {
275 			// TRANSLATORS: Item's bonus: '0.1 cooling per seconds' means 'add 0.1 to player's cooling rate'
276 			// TRANSLATORS: (trailing %s contains a text separator)
277 			autostr_append(desc, _("%0.1f cooling per second%s"), it->bonus_to_cooling_rate, separator);
278 		} else {
279 			// TRANSLATORS: Item's bonus: '0.1 heating per seconds' means 'remove 0.1 to player's cooling rate'
280 			// TRANSLATORS: (trailing %s contains a text separator)
281 			autostr_append(desc, _("%0.1f heating per second%s"), -it->bonus_to_cooling_rate, separator);
282 		}
283 	}
284 	if (it->bonus_to_attack) {
285 		// TRANSLATORS: Item's bonus: '10 to attacks' means 'add 10 to player's attack capability'
286 		// TRANSLATORS: (trailing %s contains a text separator)
287 		autostr_append(desc, _("%+d%% to attack%s"), it->bonus_to_attack, separator);
288 	}
289 	if (it->bonus_to_all_attributes) {
290 		// TRANSLATORS: Item's bonus: '10 to all attributes' means 'add 10 to all player's capabilities'
291 		// TRANSLATORS: (trailing %s contains a text separator)
292 		autostr_append(desc, _("%+d to all attributes%s"), it->bonus_to_all_attributes, separator);
293 	}
294 	if (it->bonus_to_damage) {
295 		// TRANSLATORS: Item's bonus: '10 to damage' means 'add 10 to player's damage'
296 		// TRANSLATORS: (trailing %s contains a text separator)
297 		autostr_append(desc, _("%+d to damage%s"), it->bonus_to_damage, separator);
298 	}
299 	if (it->bonus_to_armor_class) {
300 		// TRANSLATORS: Item's bonus: '10 to armor' means 'add 10 to player's armor'
301 		// TRANSLATORS: (trailing %s contains a text separator)
302 		autostr_append(desc, _("%+d to armor%s"), it->bonus_to_armor_class, separator);
303 	}
304 	if (it->bonus_to_paralyze_enemy) {
305 		// TRANSLATORS: Item's bonus: '10 to paralyze enemy' means 'add 10 to player's paralyze capability'
306 		// TRANSLATORS: (trailing %s contains a text separator)
307 		autostr_append(desc, _("%+d to paralyze enemy%s"), it->bonus_to_paralyze_enemy, separator);
308 	}
309 	if (it->bonus_to_slow_enemy) {
310 		// TRANSLATORS: Item's bonus: '10 to slow enemy' means 'add 10 to player's capability to slow an ennemy'
311 		// TRANSLATORS: (trailing %s contains a text separator)
312 		autostr_append(desc, _("%+d to slow enemy%s"), it->bonus_to_slow_enemy, separator);
313 	}
314 	if (it->bonus_to_light_radius) {
315 		// TRANSLATORS: Item's bonus: '10 to light radius' means 'add 10 to the light radius around the player'
316 		// TRANSLATORS: (trailing %s contains a text separator)
317 		autostr_append(desc, _("%+d to light radius%s"), it->bonus_to_light_radius, separator);
318 	}
319 	if (it->bonus_to_experience_gain) {
320 		// TRANSLATORS: Item's bonus: '10 to experience gain' means 'add 10 to the player's experience multiplier'
321 		// TRANSLATORS: (trailing %s contains a text separator)
322 		autostr_append(desc, _("%+d%% to experience gain%s"), it->bonus_to_experience_gain, separator);
323 	}
324 }
325 
326 /**
327  * \brief Appends information about the add-on to the autostring.
328  * \param spec Add-on specification.
329  * \param desc An auto string to which to append the description.
330  */
print_addon_description(struct addon_spec * spec,struct auto_string * desc)331 void print_addon_description(struct addon_spec *spec, struct auto_string *desc)
332 {
333 	item temp_item;
334 	const char *str;
335 
336 	// Append socket type requirements.
337 	str = spec->requires_socket;
338 	if (str) {
339 		if (!strcmp(str, "mechanical")) {
340 			autostr_append(desc, _("Install to a mechanical socket\n"));
341 		} else if (!strcmp(str, "electric")) {
342 			autostr_append(desc, _("Install to an electric socket\n"));
343 		} else if (!strcmp(str, "universal")) {
344 			autostr_append(desc, _("Install to a universal socket\n"));
345 		}
346 	}
347 
348 	// Append item type requirements.
349 	str = spec->requires_item;
350 	if (str) {
351 		if (!strcmp(str, "melee weapon")) {
352 			autostr_append(desc, _("Install to a melee weapon\n"));
353 		} else if (!strcmp(str, "ranged weapon")) {
354 			autostr_append(desc, _("Install to a ranged weapon\n"));
355 		} else if (!strcmp(str, "armor")) {
356 			autostr_append(desc, _("Install to any armor\n"));
357 		} else if (!strcmp(str, "boots")) {
358 			autostr_append(desc, _("Install to boots\n"));
359 		} else if (!strcmp(str, "jacket")) {
360 			autostr_append(desc, _("Install to a jacket\n"));
361 		} else if (!strcmp(str, "shield")) {
362 			autostr_append(desc, _("Install to a shield\n"));
363 		} else if (!strcmp(str, "helmet")) {
364 			autostr_append(desc, _("Install to a helmet\n"));
365 		}
366 	}
367 
368 	// Append bonuses provided by the add-on. We use a temporary item of
369 	// an arbitrary type to calculate the effects of the add-on.
370 	init_item(&temp_item);
371 	temp_item.type = 1;
372 	create_upgrade_socket(&temp_item, UPGRADE_SOCKET_TYPE_UNIVERSAL, ItemMap[spec->type].id);
373 	calculate_item_bonuses(&temp_item);
374 	get_item_bonus_string(&temp_item, "\n", desc);
375 	DeleteItem(&temp_item);
376 }
377 
378 /**
379  * \brief Applies an add-on bonus to the item.
380  * \param it Item.
381  * \param bonus Bonus to apply.
382  */
apply_addon_bonus(item * it,struct addon_bonus * bonus)383 static void apply_addon_bonus(item *it, struct addon_bonus *bonus)
384 {
385 	if (!strcmp(bonus->name, "all_attributes")) {
386 		it->bonus_to_all_attributes += bonus->value;
387 	} else if (!strcmp(bonus->name, "attack")) {
388 		it->bonus_to_attack += bonus->value;
389 	} else if (!strcmp(bonus->name, "armor")) {
390 		it->bonus_to_armor_class += bonus->value;
391 	} else if (!strcmp(bonus->name, "cooling")) {
392 		it->bonus_to_cooling += bonus->value;
393 	} else if (!strcmp(bonus->name, "cooling_rate")) {
394 		it->bonus_to_cooling_rate += bonus->value;
395 	} else if (!strcmp(bonus->name, "damage")) {
396 		it->damage += bonus->value;
397 		it->bonus_to_damage += bonus->value;
398 	} else if (!strcmp(bonus->name, "dexterity")) {
399 		it->bonus_to_dex += bonus->value;
400 	} else if (!strcmp(bonus->name, "experience_gain")) {
401 		it->bonus_to_experience_gain += bonus->value;
402 	} else if (!strcmp(bonus->name, "health")) {
403 		it->bonus_to_health_points += bonus->value;
404 	} else if (!strcmp(bonus->name, "health_recovery")) {
405 		it->bonus_to_health_recovery += bonus->value;
406 	} else if (!strcmp(bonus->name, "light_radius")) {
407 		it->bonus_to_light_radius += bonus->value;
408 	} else if (!strcmp(bonus->name, "paralyze_enemy")) {
409 		it->bonus_to_paralyze_enemy += bonus->value;
410 	} else if (!strcmp(bonus->name, "physique")) {
411 		it->bonus_to_physique += bonus->value;
412 	} else if (!strcmp(bonus->name, "slow_enemy")) {
413 		it->bonus_to_slow_enemy += bonus->value;
414 	} else if (!strcmp(bonus->name, "strength")) {
415 		it->bonus_to_str += bonus->value;
416 	}
417 }
418 
419 /**
420  * \brief Calculates the bonuses of the item.
421  *
422  * Can be used to initialize the bonuses of an item or to recalculate them
423  * when, for example, adding add-ons to it.
424  * \param it Item.
425  */
calculate_item_bonuses(item * it)426 void calculate_item_bonuses(item *it)
427 {
428 	int i;
429 	int j;
430 	const char *addon;
431 	struct addon_spec *spec;
432 
433 	// Reset all the bonuses to defaults.
434 	it->bonus_to_dex = 0;
435 	it->bonus_to_str = 0;
436 	it->bonus_to_physique = 0;
437 	it->bonus_to_cooling = 0;
438 	it->bonus_to_health_points = 0;
439 	it->bonus_to_health_recovery = 0.0f;
440 	it->bonus_to_cooling_rate = 0.0f;
441 	it->bonus_to_attack = 0;
442 	it->bonus_to_all_attributes = 0;
443 	it->bonus_to_armor_class = 0;
444 	it->bonus_to_damage = 0;
445 	it->bonus_to_paralyze_enemy = 0;
446 	it->bonus_to_slow_enemy = 0;
447 	it->bonus_to_light_radius = 0;
448 	it->bonus_to_experience_gain = 0;
449 	if (it->type > 0) {
450 		it->damage = ItemMap[it->type].weapon_base_damage;
451 		it->damage_modifier = ItemMap[it->type].weapon_damage_modifier;
452 	} else {
453 		it->damage = 0;
454 		it->damage_modifier = 0;
455 		return; // End when the item type don't exist.
456 	}
457 
458 	// Apply bonuses from add-ons.
459 	for (i = 0; i < it->upgrade_sockets.size; i++) {
460 		addon = it->upgrade_sockets.arr[i].addon;
461 		if (addon) {
462 			spec = get_addon_spec(get_item_type_by_id(addon));
463 			for (j = 0; j < spec->bonuses.size; j++) {
464 				apply_addon_bonus(it, &((struct addon_bonus*) spec->bonuses.arr)[j]);
465 			}
466 		}
467 	}
468 }
469 
470 /**
471  * \brief Calculates the number of used sockets.
472  *
473  * Can be used to count and return the number of used sockets.
474  * \param it Item.
475  * \return Number of used sockets
476  */
count_used_sockets(item * it)477 int count_used_sockets(item *it)
478 {
479 	int i;
480 	int count = 0;
481 	const char *addon;
482 
483 	for (i = 0; i < it->upgrade_sockets.size; i++) {
484 		addon = it->upgrade_sockets.arr[i].addon;
485 		if (addon)
486 			count++;
487 	}
488 
489 	return count;
490 }
491