Crossfire Server, Trunk  R20513
weapon_improver.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
18 #include "global.h"
19 
20 #include <string.h>
21 
22 #include "ob_methods.h"
23 #include "ob_types.h"
24 #include "sounds.h"
25 #include "sproto.h"
26 
27 static method_ret weapon_improver_type_apply(ob_methods *context, object *op, object *applier, int aflags);
28 static int check_item(object *op, const char *item);
29 static void eat_item(object *op, const char *item, uint32_t nrof);
30 static int check_sacrifice(object *op, const object *improver);
31 static int improve_weapon_stat(object *op, object *improver, object *weapon, signed char *stat, int sacrifice_count, const char *statname);
32 static int prepare_weapon(object *op, object *improver, object *weapon);
33 static int improve_weapon(object *op, object *improver, object *weapon);
34 
40 }
41 
51 static method_ret weapon_improver_type_apply(ob_methods *context, object *op, object *applier, int aflags) {
52  object *oop;
53 
54  if (applier->type != PLAYER)
55  return METHOD_ERROR;
56  if (!QUERY_FLAG(applier, FLAG_WIZCAST)
57  && (get_map_flags(applier->map, NULL, applier->x, applier->y, NULL, NULL)&P_NO_MAGIC)) {
59  "Something blocks the magic of the scroll.");
60  return METHOD_ERROR;
61  }
62 
63  oop = find_marked_object(applier);
64  if (!oop) {
66  "You need to mark a weapon object.");
67  return METHOD_ERROR;
68  }
69  if (oop->type != WEAPON && oop->type != BOW) {
71  "Marked item is not a weapon or bow");
72  return METHOD_ERROR;
73  }
75  "Applied weapon builder.");
76  improve_weapon(applier, op, oop);
78  return METHOD_OK;
79 }
80 
93 static int check_item(object *op, const char *item) {
94  int count = 0;
95 
96  if (item == NULL)
97  return 0;
98 
99  FOR_BELOW_PREPARE(op, tmp) {
100  if (strcmp(tmp->arch->name, item) == 0) {
101  if (!QUERY_FLAG(tmp, FLAG_CURSED)
102  && !QUERY_FLAG(tmp, FLAG_DAMNED)
103  /* Loophole bug? -FD- */ && !QUERY_FLAG(tmp, FLAG_UNPAID)) {
104  if (tmp->nrof == 0)/* this is necessary for artifact sacrifices --FD-- */
105  count++;
106  else
107  count += tmp->nrof;
108  }
109  }
110  } FOR_BELOW_FINISH();
111  return count;
112 }
113 
130 static void eat_item(object *op, const char *item, uint32_t nrof) {
131  object *prev;
132 
133  prev = op;
134  op = op->below;
135 
136  while (op != NULL) {
137  if (strcmp(op->arch->name, item) == 0) {
138  if (op->nrof >= nrof) {
139  object_decrease_nrof(op, nrof);
140  return;
141  } else {
142  object_decrease_nrof(op, op->nrof);
143  nrof -= op->nrof;
144  }
145  op = prev;
146  }
147  prev = op;
148  op = op->below;
149  }
150 }
151 
165 static int check_sacrifice(object *op, const object *improver) {
166  int count = 0;
167 
168  if (improver->slaying != NULL) {
169  count = check_item(op, improver->slaying);
170  if (count < 1) {
172  "The gods want more %ss",
173  improver->slaying);
174  return 0;
175  }
176  } else
177  count = 1;
178 
179  return count;
180 }
181 
200 static int improve_weapon_stat(object *op, object *improver, object *weapon, signed char *stat, int sacrifice_count, const char *statname) {
202  "Your sacrifice was accepted.");
203 
204  *stat += sacrifice_count;
205  weapon->last_eat++;
206 
208  "Weapon's bonus to %s improved by %d",
209  statname, sacrifice_count);
210 
211  object_decrease_nrof_by_one(improver);
212 
213  /* So it updates the players stats and the window */
214  fix_object(op);
215  return 1;
216 }
217 
218 /* Types of improvements, hidden in the sp field. */
219 #define IMPROVE_PREPARE 1
220 #define IMPROVE_DAMAGE 2
221 #define IMPROVE_WEIGHT 3
222 #define IMPROVE_ENCHANT 4
223 #define IMPROVE_STR 5
224 #define IMPROVE_DEX 6
225 #define IMPROVE_CON 7
226 #define IMPROVE_WIS 8
227 #define IMPROVE_CHA 9
228 #define IMPROVE_INT 10
229 #define IMPROVE_POW 11
245 static int prepare_weapon(object *op, object *improver, object *weapon) {
246  int sacrifice_count, i;
247  char buf[MAX_BUF];
248 
249  if (weapon->level != 0) {
251  "Weapon already prepared.");
252  return 0;
253  }
254  for (i = 0; i < NROFATTACKS; i++)
255  if (weapon->resist[i])
256  break;
257 
258  /* If we break out, i will be less than nrofattacks, preventing
259  * improvement of items that already have protections.
260  */
261  if (i < NROFATTACKS
262  || weapon->stats.hp /* regeneration */
263  || (weapon->stats.sp && weapon->type == WEAPON) /* sp regeneration */
264  || weapon->stats.exp /* speed */
265  || weapon->stats.ac) { /* AC - only taifu's I think */
267  "Cannot prepare magic weapons.");
268  return 0;
269  }
270 
271  sacrifice_count = check_sacrifice(op, improver);
272  if (sacrifice_count <= 0)
273  return 0;
274 
275  /* We do not allow improving stacks, so split this off from
276  * stack. Only need to do this if weapon is part of a stack.
277  * We set nrof of weapon to zero so it can not merge with other
278  * items, so one can not do further improvements on a stack.
279  * side effect of doing it before the object_insert_in_ob() is that
280  * it won't merge back in. We know from the code that marked
281  * objects must be in the players inventory, so we know where
282  * to put this.
283  */
284  if (weapon->nrof >1) {
285  weapon = object_split(weapon,1, NULL, 0);
286  weapon->nrof = 0;
287  object_insert_in_ob(weapon, op);
288  } else {
289  weapon->nrof = 0;
290  }
291 
292 
293  weapon->level = isqrt(sacrifice_count);
295  "Your sacrifice was accepted.");
296  eat_item(op, improver->slaying, sacrifice_count);
297 
298 
299  snprintf(buf, sizeof(buf), "%s's %s", op->name, weapon->name);
300  FREE_AND_COPY(weapon->name, buf);
301  FREE_AND_COPY(weapon->name_pl, buf);
302 
304  "Your %s may be improved %d times.",
305  weapon->name, weapon->level);
306 
307  object_decrease_nrof_by_one(improver);
308  weapon->last_eat = 0;
309  esrv_update_item(UPD_NAME | UPD_NROF, op, weapon);
310  return 1;
311 }
312 
332 static int improve_weapon(object *op, object *improver, object *weapon) {
333  int sacrifice_count, sacrifice_needed = 0;
334 
335  if (improver->stats.sp == IMPROVE_PREPARE) {
336  return prepare_weapon(op, improver, weapon);
337  }
338 
339  if (weapon->level == 0) {
341  "This weapon has not been prepared.");
342  return 0;
343  }
344 
345  if (weapon->level == weapon->last_eat && weapon->item_power >= MAX_WEAPON_ITEM_POWER) {
347  "This weapon cannot be improved any more.");
348  return 0;
349  }
350 
351  if (QUERY_FLAG(weapon, FLAG_APPLIED)
352  && !apply_check_weapon_power(op, weapon->last_eat+1)) {
354  "Improving the weapon will make it too powerful for you to use. Unready it if you really want to improve it.");
355  return 0;
356  }
357 
358  /* All improvements add to item power, so check if player can
359  * still wear the weapon after improvement.
360  */
361  if (QUERY_FLAG(weapon, FLAG_APPLIED)
362  && op->type == PLAYER
363  && (op->contr->item_power+1) > (settings.item_power_factor*op->level)) {
364  apply_special(op, weapon, AP_UNAPPLY);
365  if (QUERY_FLAG(weapon, FLAG_APPLIED)) {
366  /* Weapon is cursed, too bad */
368  "You can't enchant this weapon without unapplying it because it would consume your soul!");
369  return 0;
370  }
371  }
372 
373  /* This just increases damage by 5 points, no matter what. No
374  * sacrifice is needed. Since stats.dam is now a 16 bit value and
375  * not 8 bit, don't put any maximum value on damage - the limit is
376  * how much the weapon can be improved.
377  */
378  if (improver->stats.sp == IMPROVE_DAMAGE) {
379  weapon->stats.dam += 5;
380  weapon->weight += 5000; /* 5 KG's */
382  "Damage has been increased by 5 to %d",
383  weapon->stats.dam);
384  weapon->last_eat++;
385 
386  weapon->item_power++;
387  object_decrease_nrof_by_one(improver);
388  esrv_update_item(UPD_WEIGHT, op, weapon);
389  return 1;
390  }
391 
392  if (improver->stats.sp == IMPROVE_WEIGHT) {
393  /* Reduce weight by 20% */
394  weapon->weight = (weapon->weight*8)/10;
395  if (weapon->weight < 1)
396  weapon->weight = 1;
398  "Weapon weight reduced to %6.1f kg",
399  (float)weapon->weight/1000.0);
400  weapon->last_eat++;
401  weapon->item_power++;
402  object_decrease_nrof_by_one(improver);
403  esrv_update_item(UPD_WEIGHT, op, weapon);
404  return 1;
405  }
406 
407  if (improver->stats.sp == IMPROVE_ENCHANT) {
408  weapon->magic++;
409  weapon->last_eat++;
411  "Weapon magic increased to %d",
412  weapon->magic);
413  object_decrease_nrof_by_one(improver);
414  weapon->item_power++;
415  return 1;
416  }
417 
418  sacrifice_needed = weapon->stats.Str
419  +weapon->stats.Int
420  +weapon->stats.Dex
421  +weapon->stats.Pow
422  +weapon->stats.Con
423  +weapon->stats.Cha
424  +weapon->stats.Wis;
425 
426  if (sacrifice_needed < 1)
427  sacrifice_needed = 1;
428  sacrifice_needed *= 2;
429 
430  sacrifice_count = check_sacrifice(op, improver);
431  if (sacrifice_count < sacrifice_needed) {
433  "You need at least %d %s",
434  sacrifice_needed, improver->slaying);
435  return 0;
436  }
437  eat_item(op, improver->slaying, sacrifice_needed);
438  weapon->item_power++;
439 
440  switch (improver->stats.sp) {
441  case IMPROVE_STR:
442  return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Str), 1, "strength");
443 
444  case IMPROVE_DEX:
445  return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Dex), 1, "dexterity");
446 
447  case IMPROVE_CON:
448  return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Con), 1, "constitution");
449 
450  case IMPROVE_WIS:
451  return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Wis), 1, "wisdom");
452 
453  case IMPROVE_CHA:
454  return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Cha), 1, "charisma");
455 
456  case IMPROVE_INT:
457  return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Int), 1, "intelligence");
458 
459  case IMPROVE_POW:
460  return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Pow), 1, "power");
461 
462  default:
464  "Unknown improvement type.");
465  }
466 
467  LOG(llevError, "improve_weapon: Got to end of function\n");
468  return 0;
469 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Sends message to player(s).
Definition: main.c:315
Error, serious thing.
Definition: logger.h:11
#define AP_UNAPPLY
Item is to be remvoed.
Definition: define.h:611
int8_t Int
Definition: living.h:35
int apply_special(object *who, object *op, int aflags)
Apply an object.
Definition: apply.c:1082
Sound-related defines.
#define FLAG_DAMNED
The object is very cursed.
Definition: define.h:318
#define FLAG_UNPAID
Object hasn&#39;t been paid for yet.
Definition: define.h:236
#define IMPROVE_INT
Increase intelligence bonus.
Typedefs for ob_methods.
Definition: ob_methods.h:45
#define METHOD_ERROR
Definition: ob_methods.h:17
#define P_NO_MAGIC
Spells (some) can&#39;t pass this object.
Definition: map.h:227
#define IMPROVE_WEIGHT
Decrease weight.
void esrv_update_item(int flags, object *pl, object *op)
Updates object *op for player *pl.
Definition: main.c:342
static int improve_weapon(object *op, object *improver, object *weapon)
Does the dirty job for &#39;improve weapon&#39; scroll, prepare or add something.
See Weapon.
Definition: object.h:119
int isqrt(int n)
Compute the square root.
Definition: utils.c:585
const char * slaying
Which race to do double damage to.
Definition: object.h:319
#define IMPROVE_ENCHANT
Increase magic.
#define IMPROVE_PREPARE
Prepare the weapon.
static int prepare_weapon(object *op, object *improver, object *weapon)
This does the prepare weapon scroll.
int16_t sp
Spell points.
Definition: living.h:41
#define IMPROVE_CHA
Increase charisma bonus.
Global type definitions and header inclusions.
void init_type_weapon_improver(void)
Initializer for the WEAPON_IMPROVER object type.
#define IMPROVE_WIS
Increase wisdom bonus.
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Sends message to player(s).
Definition: main.c:310
#define MSG_TYPE_APPLY
Applying objects.
Definition: newclient.h:384
int8_t Con
Definition: living.h:35
char method_ret
Define some standard return values for callbacks which don&#39;t need to return any other results...
Definition: ob_methods.h:14
int apply_check_weapon_power(const object *who, int improves)
This checks to see of the player (who) is sufficient level to use a weapon with improves improvements...
Definition: apply.c:1057
static int check_sacrifice(object *op, const object *improver)
Returns how many items of type improver->slaying there are under op.
int16_t y
Position in the map for this object.
Definition: object.h:326
See Shooting Weapon.
Definition: object.h:118
#define IMPROVE_CON
Increase constitution bonus.
#define IMPROVE_DAMAGE
Increase damage.
object * object_insert_in_ob(object *op, object *where)
This function inserts the object op in the linked list inside the object environment.
Definition: object.c:2690
static void eat_item(object *op, const char *item, uint32_t nrof)
This removes &#39;nrof&#39; items with specified archetype.
int32_t weight
Attributes of the object.
Definition: object.h:365
#define IMPROVE_POW
Increase power bonus.
#define METHOD_OK
Definition: ob_methods.h:15
const char *const statname[NUM_STATS]
Name of stats.
Definition: living.c:183
int8_t Wis
Definition: living.h:35
struct mapdef * map
Pointer to the map in which this object is present.
Definition: object.h:297
#define snprintf
Definition: win32.h:46
#define MSG_TYPE_APPLY_ERROR
Definition: newclient.h:596
int16_t dam
How much damage this object does when hitting.
Definition: living.h:45
struct obj * below
Pointer to the object stacked below this one.
Definition: object.h:287
#define MAX_WEAPON_ITEM_POWER
Maximum item power an item can have.
Definition: define.h:475
uint32_t nrof
How many of the objects.
Definition: object.h:333
int8_t Cha
Definition: living.h:35
#define UPD_FLAGS
Definition: newclient.h:290
void register_apply(int ob_type, apply_func method)
Registers the apply method for the given type.
Definition: ob_types.c:62
struct pl * contr
Pointer to the player which control this object.
Definition: object.h:276
int8_t item_power
Power rating of the object.
Definition: object.h:362
object * find_marked_object(object *op)
Return the object the player has marked with the &#39;mark&#39; command below.
Definition: c_object.c:1256
#define UPD_WEIGHT
Definition: newclient.h:291
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
int16_t x
Definition: object.h:326
int32_t last_eat
How long since we last ate.
Definition: object.h:356
Object type variables.
int8_t Str
Definition: living.h:35
#define FLAG_CURSED
The object is cursed.
Definition: define.h:317
See Player.
Definition: object.h:107
unsigned int uint32_t
Definition: win32.h:162
object * object_split(object *orig_ob, uint32_t nr, char *err, size_t size)
object_split(ob,nr) splits up ob into two parts.
Definition: object.c:2463
object * object_decrease_nrof(object *op, uint32_t i)
Decreases a specified number from the amount of an object.
Definition: object.c:2505
#define object_decrease_nrof_by_one(xyz)
Definition: compat.h:28
#define FREE_AND_COPY(sv, nv)
Release the shared string if not NULL, and make it a reference to nv.
Definition: global.h:213
#define UPD_NROF
Definition: newclient.h:296
living stats
Str, Con, Dex, etc.
Definition: object.h:368
#define FLAG_WIZCAST
The wizard can cast spells in no-magic area.
Definition: define.h:290
int8_t Dex
Definition: living.h:35
struct archt * arch
Pointer to archetype.
Definition: object.h:412
#define IMPROVE_DEX
Increase dexterity bonus.
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:338
struct Settings settings
Server settings.
Definition: init.c:40
#define FLAG_APPLIED
Object is ready for use by living.
Definition: define.h:235
#define NROFATTACKS
Definition: attack.h:17
#define UPD_NAME
Definition: newclient.h:293
#define MSG_TYPE_APPLY_SUCCESS
Was able to apply object.
Definition: newclient.h:598
int8_t Pow
Definition: living.h:35
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
This rolls up wall, blocks_magic, blocks_view, etc, all into one function that just returns a P_...
Definition: map.c:302
#define NDI_UNIQUE
Print immediately, don&#39;t buffer.
Definition: newclient.h:245
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
int16_t item_power
Total item power of objects equipped.
Definition: player.h:117
static method_ret weapon_improver_type_apply(ob_methods *context, object *op, object *applier, int aflags)
Attempts to apply weapon_improver.
#define FOR_BELOW_PREPARE(op_, it_)
Constructs a loop iterating over all objects below an object.
Definition: define.h:739
static int check_item(object *op, const char *item)
Counts suitable items with specified archetype name.
Object type functions and variables.
int16_t level
Level of creature or object.
Definition: object.h:351
void fix_object(object *op)
Updates all abilities given by applied objects in the inventory of the given object.
Definition: living.c:1120
#define IMPROVE_STR
Increase strength bonus.
int8_t magic
Any magical bonuses to this item.
Definition: object.h:348
float item_power_factor
See note in setings file.
Definition: global.h:301
const char * name
More definite name, like "generate_kobold".
Definition: object.h:466
#define FOR_BELOW_FINISH()
Finishes FOR_BELOW_PREPARE().
Definition: define.h:746
static int improve_weapon_stat(object *op, object *improver, object *weapon, signed char *stat, int sacrifice_count, const char *statname)
Actually improves the weapon, and tells user.