Crossfire Server, Branch 1.12  R12190
weapon_improver.c
Go to the documentation of this file.
00001 /*
00002     CrossFire, A Multiplayer game for X-windows
00003 
00004     Copyright (C) 2007 Mark Wedel & Crossfire Development Team
00005     Copyright (C) 1992 Frank Tore Johansen
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00020 
00021     The authors can be reached via e-mail at crossfire-devel@real-time.com
00022 */
00023 
00027 #include <global.h>
00028 #include <ob_methods.h>
00029 #include <ob_types.h>
00030 #include <sounds.h>
00031 #include <sproto.h>
00032 
00033 static method_ret weapon_improver_type_apply(ob_methods *context, object *op, object *applier, int aflags);
00034 static int check_item(object *op, const char *item);
00035 static void eat_item(object *op, const char *item, uint32 nrof);
00036 static int check_sacrifice(object *op, const object *improver);
00037 static int improve_weapon_stat(object *op, object *improver, object *weapon, signed char *stat, int sacrifice_count, const char *statname);
00038 static int prepare_weapon(object *op, object *improver, object *weapon);
00039 static int improve_weapon(object *op, object *improver, object *weapon);
00040 
00044 void init_type_weapon_improver(void) {
00045     register_apply(WEAPON_IMPROVER, weapon_improver_type_apply);
00046 }
00047 
00057 static method_ret weapon_improver_type_apply(ob_methods *context, object *op, object *applier, int aflags) {
00058     object *oop;
00059 
00060     if (applier->type != PLAYER)
00061         return METHOD_ERROR;
00062     if (!QUERY_FLAG(applier, FLAG_WIZCAST)
00063     && (get_map_flags(applier->map, NULL, applier->x, applier->y, NULL, NULL)&P_NO_MAGIC)) {
00064         draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00065             "Something blocks the magic of the scroll.", NULL);
00066         return METHOD_ERROR;
00067     }
00068 
00069     oop = find_marked_object(applier);
00070     if (!oop) {
00071         draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00072             "You need to mark a weapon object.", NULL);
00073         return METHOD_ERROR;
00074     }
00075     if (oop->type != WEAPON && oop->type != BOW) {
00076         draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00077             "Marked item is not a weapon or bow", NULL);
00078         return METHOD_ERROR;
00079     }
00080     draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
00081         "Applied weapon builder.", NULL);
00082     improve_weapon(applier, op, oop);
00083     esrv_update_item(UPD_NAME|UPD_NROF|UPD_FLAGS, applier, oop);
00084     return METHOD_OK;
00085 }
00086 
00099 static int check_item(object *op, const char *item) {
00100     int count = 0;
00101 
00102     if (item == NULL)
00103         return 0;
00104     op = op->below;
00105     while (op != NULL) {
00106         if (strcmp(op->arch->name, item) == 0) {
00107             if (!QUERY_FLAG(op, FLAG_CURSED)
00108             && !QUERY_FLAG(op, FLAG_DAMNED)
00109             /* Loophole bug? -FD- */ && !QUERY_FLAG(op, FLAG_UNPAID)) {
00110                 if (op->nrof == 0)/* this is necessary for artifact sacrifices --FD-- */
00111                     count++;
00112                 else
00113                     count += op->nrof;
00114             }
00115         }
00116         op = op->below;
00117     }
00118     return count;
00119 }
00120 
00137 static void eat_item(object *op, const char *item, uint32 nrof) {
00138     object *prev;
00139 
00140     prev = op;
00141     op = op->below;
00142 
00143     while (op != NULL) {
00144         if (strcmp(op->arch->name, item) == 0) {
00145             if (op->nrof >= nrof) {
00146                 decrease_ob_nr(op, nrof);
00147                 return;
00148             } else {
00149                 decrease_ob_nr(op, op->nrof);
00150                 nrof -= op->nrof;
00151             }
00152             op = prev;
00153         }
00154         prev = op;
00155         op = op->below;
00156     }
00157 }
00158 
00172 static int check_sacrifice(object *op, const object *improver) {
00173     int count = 0;
00174 
00175     if (improver->slaying != NULL) {
00176         count = check_item(op, improver->slaying);
00177         if (count < 1) {
00178             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00179                 "The gods want more %ss",
00180                 "The gods want more %ss",
00181                 improver->slaying);
00182             return 0;
00183         }
00184     } else
00185         count = 1;
00186 
00187     return count;
00188 }
00189 
00208 static int improve_weapon_stat(object *op, object *improver, object *weapon, signed char *stat, int sacrifice_count, const char *statname) {
00209 
00210     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
00211         "Your sacrifice was accepted.", NULL);
00212 
00213     *stat += sacrifice_count;
00214     weapon->last_eat++;
00215 
00216     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
00217         "Weapon's bonus to %s improved by %d",
00218         "Weapon's bonus to %s improved by %d",
00219         statname, sacrifice_count);
00220 
00221     decrease_ob(improver);
00222 
00223         /* So it updates the players stats and the window */
00224     fix_object(op);
00225     return 1;
00226 }
00227 
00228 /* Types of improvements, hidden in the sp field. */
00229 #define IMPROVE_PREPARE 1   
00230 #define IMPROVE_DAMAGE 2    
00231 #define IMPROVE_WEIGHT 3    
00232 #define IMPROVE_ENCHANT 4   
00233 #define IMPROVE_STR 5       
00234 #define IMPROVE_DEX 6       
00235 #define IMPROVE_CON 7       
00236 #define IMPROVE_WIS 8       
00237 #define IMPROVE_CHA 9       
00238 #define IMPROVE_INT 10      
00239 #define IMPROVE_POW 11      
00255 static int prepare_weapon(object *op, object *improver, object *weapon) {
00256     int sacrifice_count, i;
00257     char buf[MAX_BUF];
00258 
00259     if (weapon->level != 0) {
00260         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00261             "Weapon already prepared.", NULL);
00262         return 0;
00263     }
00264     for (i = 0; i < NROFATTACKS; i++)
00265         if (weapon->resist[i])
00266             break;
00267 
00268         /* If we break out, i will be less than nrofattacks, preventing
00269          * improvement of items that already have protections.
00270          */
00271     if (i < NROFATTACKS
00272     || weapon->stats.hp        /* regeneration */
00273     || (weapon->stats.sp && weapon->type == WEAPON) /* sp regeneration */
00274     || weapon->stats.exp       /* speed */
00275     || weapon->stats.ac) {     /* AC - only taifu's I think */
00276         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00277             "Cannot prepare magic weapons.", NULL);
00278         return 0;
00279     }
00280 
00281     sacrifice_count = check_sacrifice(op, improver);
00282     if (sacrifice_count <= 0)
00283         return 0;
00284 
00285     /* We do not allow improving stacks, so split this off from
00286      * stack.  Only need to do this if weapon is part of a stack.
00287      * We set nrof of weapon to zero so it can not merge with other
00288      * items, so one can not do further improvements on a stack.
00289      * side effect of doing it before the insert_ob_in_ob is that
00290      * it won't merge back in.  We know from the code that marked
00291      * objects must be in the players inventory, so we know where
00292      * to put this.
00293      */
00294     if (weapon->nrof >1) {
00295         weapon = get_split_ob(weapon,1, NULL, 0);
00296         weapon->nrof = 0;
00297         insert_ob_in_ob(weapon, op); 
00298     } else {
00299         weapon->nrof = 0;
00300     }
00301         
00302 
00303     weapon->level = isqrt(sacrifice_count);
00304     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
00305         "Your sacrifice was accepted.", NULL);
00306     eat_item(op, improver->slaying, sacrifice_count);
00307 
00308 
00309     snprintf(buf, sizeof(buf), "%s's %s", op->name, weapon->name);
00310     FREE_AND_COPY(weapon->name, buf);
00311     FREE_AND_COPY(weapon->name_pl, buf);
00312 
00313     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
00314         "Your %s may be improved %d times.",
00315         "Your %s may be improved %d times.",
00316         weapon->name, weapon->level);
00317 
00318     decrease_ob(improver);
00319     weapon->last_eat = 0;
00320     esrv_update_item(UPD_NAME | UPD_NROF, op, weapon);
00321     return 1;
00322 }
00323 
00343 static int improve_weapon(object *op, object *improver, object *weapon) {
00344     int sacrifice_count, sacrifice_needed = 0;
00345 
00346     if (improver->stats.sp == IMPROVE_PREPARE) {
00347         return prepare_weapon(op, improver, weapon);
00348     }
00349 
00350     if (weapon->level == 0) {
00351         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00352             "This weapon has not been prepared.", NULL);
00353         return 0;
00354     }
00355 
00356     if (weapon->level == weapon->last_eat && weapon->item_power >= MAX_WEAPON_ITEM_POWER) {
00357         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00358             "This weapon cannot be improved any more.", NULL);
00359         return 0;
00360     }
00361 
00362     if (QUERY_FLAG(weapon, FLAG_APPLIED)
00363     && !check_weapon_power(op, weapon->last_eat+1)) {
00364         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00365             "Improving the weapon will make it too powerful for you to use.  Unready it if you really want to improve it.", NULL);
00366         return 0;
00367     }
00368 
00369     /* All improvements add to item power, so check if player can
00370      * still wear the weapon after improvement.
00371      */
00372     if (QUERY_FLAG(weapon, FLAG_APPLIED)
00373     && op->type == PLAYER
00374     && (op->contr->item_power+1) > (settings.item_power_factor*op->level)) {
00375         apply_special(op, weapon, AP_UNAPPLY);
00376         if (QUERY_FLAG(weapon, FLAG_APPLIED)) {
00377             /* Weapon is cursed, too bad */
00378             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00379                 "You can't enchant this weapon without unapplying it because it would consume your soul!", NULL);
00380             return 0;
00381         }
00382     }
00383 
00384     /* This just increases damage by 5 points, no matter what.  No
00385      * sacrifice is needed.  Since stats.dam is now a 16 bit value and
00386      * not 8 bit, don't put any maximum value on damage - the limit is
00387      * how much the weapon  can be improved.
00388      */
00389     if (improver->stats.sp == IMPROVE_DAMAGE) {
00390         weapon->stats.dam += 5;
00391         weapon->weight += 5000;         /* 5 KG's */
00392         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
00393             "Damage has been increased by 5 to %d",
00394             "Damage has been increased by 5 to %d",
00395             weapon->stats.dam);
00396         weapon->last_eat++;
00397 
00398         weapon->item_power++;
00399         decrease_ob(improver);
00400         return 1;
00401     }
00402 
00403     if (improver->stats.sp == IMPROVE_WEIGHT) {
00404         /* Reduce weight by 20% */
00405         weapon->weight = (weapon->weight*8)/10;
00406         if (weapon->weight < 1)
00407             weapon->weight = 1;
00408         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
00409             "Weapon weight reduced to %6.1f kg",
00410             "Weapon weight reduced to %6.1f kg",
00411             (float)weapon->weight/1000.0);
00412         weapon->last_eat++;
00413         weapon->item_power++;
00414         decrease_ob(improver);
00415         return 1;
00416     }
00417 
00418     if (improver->stats.sp == IMPROVE_ENCHANT) {
00419         weapon->magic++;
00420         weapon->last_eat++;
00421         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
00422             "Weapon magic increased to %d",
00423             "Weapon magic increased to %d",
00424             weapon->magic);
00425         decrease_ob(improver);
00426         weapon->item_power++;
00427         return 1;
00428     }
00429 
00430     sacrifice_needed = weapon->stats.Str
00431         +weapon->stats.Int
00432         +weapon->stats.Dex
00433         +weapon->stats.Pow
00434         +weapon->stats.Con
00435         +weapon->stats.Cha
00436         +weapon->stats.Wis;
00437 
00438     if (sacrifice_needed < 1)
00439         sacrifice_needed = 1;
00440     sacrifice_needed *= 2;
00441 
00442     sacrifice_count = check_sacrifice(op, improver);
00443     if (sacrifice_count < sacrifice_needed) {
00444         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00445             "You need at least %d %s",
00446             "You need at least %d %s",
00447             sacrifice_needed, improver->slaying);
00448         return 0;
00449     }
00450     eat_item(op, improver->slaying, sacrifice_needed);
00451     weapon->item_power++;
00452 
00453     switch (improver->stats.sp) {
00454     case IMPROVE_STR:
00455         return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Str), 1, "strength");
00456 
00457     case IMPROVE_DEX:
00458         return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Dex), 1, "dexterity");
00459 
00460     case IMPROVE_CON:
00461         return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Con), 1, "constitution");
00462 
00463     case IMPROVE_WIS:
00464         return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Wis), 1, "wisdom");
00465 
00466     case IMPROVE_CHA:
00467         return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Cha), 1, "charisma");
00468 
00469     case IMPROVE_INT:
00470         return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Int), 1, "intelligence");
00471 
00472     case IMPROVE_POW:
00473         return improve_weapon_stat(op, improver, weapon, (signed char *)&(weapon->stats.Pow), 1, "power");
00474 
00475     default:
00476         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00477             "Unknown improvement type.", NULL);
00478     }
00479 
00480     LOG(llevError, "improve_weapon: Got to end of function\n");
00481     return 0;
00482 }