Crossfire Server, Branch 1.12
R12190
|
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 }