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