/*
* This file is part of Deliantra, the Roguelike Realtime MMORPG.
*
* Copyright (©) 2005,2006,2007,2008,2009,2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
* Copyright (©) 2001 Mark Wedel & Crossfire Development Team
* Copyright (©) 1992 Frank Tore Johansen
*
* Deliantra is free software: you can redistribute it and/or modify it under
* the terms of the Affero GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the Affero GNU General Public License
* and the GNU General Public License along with this program. If not, see
* .
*
* The authors can be reached via e-mail to
*/
#include
#include
#include
#include
#include
#include
#include
// these must be in the inventory before they can be applied
static const struct apply_types_inv_only : typeset
{
apply_types_inv_only ()
{
set (WEAPON);
set (ARMOUR);
set (BOOTS);
set (GLOVES);
set (AMULET);
set (GIRDLE);
set (BRACERS);
set (SHIELD);
set (HELMET);
set (RING);
set (CLOAK);
set (WAND);
set (ROD);
set (HORN);
set (SKILL);
set (SPELL);
set (BOW);
set (RANGED);
set (BUILDER);
set (SKILL_TOOL);
}
} apply_types_inv_only;
// these only make sense for the player
static const struct apply_types_player_only : typeset
{
apply_types_player_only ()
{
set (EXIT);
set (BOOK);
set (SKILLSCROLL);
set (SPELLBOOK);
set (TREASURE);
set (SAVEBED);
set (ARMOUR_IMPROVER);
set (WEAPON_IMPROVER);
set (CLOCK);
set (MENU);
set (LIGHTER); /* for lighting torches/lanterns/etc */
set (INSCRIBABLE);
set (SIGN);
set (BOOK);
}
} apply_types_player_only;
// applying these _can_ be attempted, others cannot
// be applied at all. used by e.g. apply below.
static const struct apply_types : typeset
{
apply_types ()
: typeset ((typeset)apply_types_player_only | (typeset)apply_types_inv_only)
{
set (T_HANDLE);
set (TRIGGER);
set (SCROLL);
set (POTION);
set (CLOSE_CON);
set (CONTAINER);
set (LAMP);
set (TORCH);
set (DRINK);
set (FOOD);
set (FLESH);
set (POISON);
set (POWER_CRYSTAL);
set (ITEM_TRANSFORMER);
}
} apply_types;
/****************************************************************************
* Weapon improvement code follows
****************************************************************************/
/**
* This function just checks whether who can handle equipping an item
* with item_power.
*/
static bool
check_item_power (object *who, int item_power)
{
if (who->type == PLAYER
&& item_power
&& item_power + who->contr->item_power > settings.item_power_factor * who->level)
return false;
else
return true;
}
/**
* This returns the sum of nrof of item (arch name).
*/
static int
check_item (object *op, shstr_cmp item)
{
int count = 0;
if (!item)
return 0;
for (op = op->below; op; op = op->below)
if (op->arch->archname == item)
if (!QUERY_FLAG (op, FLAG_CURSED) && !QUERY_FLAG (op, FLAG_DAMNED)
&& /* Loophole bug? -FD- */ !QUERY_FLAG (op, FLAG_UNPAID))
count += op->number_of ();
return count;
}
/**
* This removes 'nrof' of what item->slaying says to remove.
* op is typically the player, which is only
* really used to determine what space to look at.
* Modified to only eat 'nrof' of objects.
*/
static void
eat_item (object *op, shstr_cmp item, uint32 nrof)
{
object *prev;
prev = op;
op = op->below;
while (op)
{
if (op->arch->archname == item)
{
if (op->nrof >= nrof)
{
op->decrease (nrof);
return;
}
else
{
op->decrease (nrof);
nrof -= op->nrof;
}
op = prev;
}
prev = op;
op = op->below;
}
}
/**
* Returns how many items of type improver->slaying there are under op.
* Will display a message if none found, and 1 if improver->slaying is NULL.
*/
static int
check_sacrifice (object *op, const object *improver)
{
int count = 0;
if (improver->slaying)
{
count = check_item (op, improver->slaying);
if (count < 1)
{
op->failmsg (format ("The gods want more %ss", &improver->slaying));
return 0;
}
}
else
count = 1;
return count;
}
/**
* Actually improves the weapon, and tells user.
*/
static int
improve_weapon_stat (object *op, object *improver, object *weapon, sint8 &stat, int sacrifice_count, const char *statname)
{
stat += sacrifice_count;
weapon->last_eat++;
improver->decrease ();
/* So it updates the players stats and the window */
op->update_stats ();
op->statusmsg (format (
"Your sacrifice was accepted.\n"
"Weapon's bonus to %s improved by %d.",
statname, sacrifice_count
));
return 1;
}
/* Types of improvements, hidden in the sp field. */
#define IMPROVE_PREPARE 1
#define IMPROVE_DAMAGE 2
#define IMPROVE_WEIGHT 3
#define IMPROVE_ENCHANT 4
#define IMPROVE_STR 5
#define IMPROVE_DEX 6
#define IMPROVE_CON 7
#define IMPROVE_WIS 8
#define IMPROVE_CHA 9
#define IMPROVE_INT 10
#define IMPROVE_POW 11
/**
* This does the prepare weapon scroll.
* Checks for sacrifice, and so on.
*/
static int
prepare_weapon (object *op, object *improver, object *weapon)
{
int sacrifice_count, i;
char buf[MAX_BUF];
if (weapon->level != 0)
{
op->failmsg ("Weapon is already prepared!");
return 0;
}
for (i = 0; i < NROFATTACKS; i++)
if (weapon->resist[i])
break;
/* If we break out, i will be less than nrofattacks, preventing
* improvement of items that already have protections.
*/
if (i < NROFATTACKS || weapon->stats.hp || /* regeneration */
(weapon->stats.sp && weapon->type == WEAPON) || /* sp regeneration */
weapon->stats.exp || /* speed */
weapon->stats.ac) /* AC - only taifu's I think */
{
op->failmsg ("You cannot prepare magic weapons. "
"H");
return 0;
}
sacrifice_count = check_sacrifice (op, improver);
if (sacrifice_count <= 0)
return 0;
weapon->level = isqrt (sacrifice_count);
eat_item (op, improver->slaying, sacrifice_count);
op->statusmsg (format (
"Your sacrifice was accepted."
"Your *%s may be improved %d times.",
&weapon->name, weapon->level
));
sprintf (buf, "%s's %s", &op->name, &weapon->name);
weapon->name = weapon->name_pl = buf;
weapon->nrof = 0; /* prevents preparing n weapons in the same
slot at once! */
improver->decrease ();
weapon->last_eat = 0;
return 1;
}
/**
* Does the dirty job for 'improve weapon' scroll, prepare or add something.
* This is the new improve weapon code.
* Returns 0 if it was not able to work for some reason.
*
* Checks if weapon was prepared, if enough potions on the floor, ...
*
* We are hiding extra information about the weapon in the level and
* last_eat numbers for an object. Hopefully this won't break anything ??
* level == max improve last_eat == current improve
*/
static int
improve_weapon (object *op, object *improver, object *weapon)
{
int sacrifice_count, sacrifice_needed = 0;
if (improver->stats.sp == IMPROVE_PREPARE)
return prepare_weapon (op, improver, weapon);
if (weapon->level == 0)
{
op->failmsg (
"This weapon has not been prepared."
" H");
return 0;
}
if (weapon->last_eat >= weapon->level // improvements used up
|| weapon->item_power >= 100) // or item_power >= arbitrary limit of 100
{
op->failmsg ("This weapon cannot be improved any more.");
return 0;
}
if (QUERY_FLAG (weapon, FLAG_APPLIED)
&& !check_item_power (op, weapon->item_power + 1))
{
op->failmsg ("Improving the weapon will make it too "
"powerful for you to use. Unready it if you "
"really want to improve it.");
return 0;
}
/* This just increases damage by 5 points, no matter what. No sacrifice
* is needed. Since stats.dam is now a 16 bit value and not 8 bit,
* don't put any maximum value on damage - the limit is how much the
* weapon can be improved.
*/
if (improver->stats.sp == IMPROVE_DAMAGE)
{
weapon->stats.dam += 5;
weapon->weight += 5000; /* 5 KG's */
op->statusmsg (format ("Damage has been increased by 5 to %d.", weapon->stats.dam));
weapon->last_eat++;
weapon->item_power++;
improver->decrease ();
return 1;
}
if (improver->stats.sp == IMPROVE_WEIGHT)
{
/* Reduce weight by 20% */
weapon->weight = (weapon->weight * 8) / 10;
if (weapon->weight < 1)
weapon->weight = 1;
op->statusmsg (format ("Weapon weight reduced to %6.1fkg.", (float) weapon->weight / 1000.0));
weapon->last_eat++;
weapon->item_power++;
improver->decrease ();
return 1;
}
if (improver->stats.sp == IMPROVE_ENCHANT)
{
weapon->magic++;
weapon->last_eat++;
op->statusmsg (format ("Weapon magic increased to %d.", weapon->magic));
improver->decrease ();
weapon->item_power++;
return 1;
}
sacrifice_needed = weapon->stats.Str + weapon->stats.Int + weapon->stats.Dex +
weapon->stats.Pow + weapon->stats.Con + weapon->stats.Cha + weapon->stats.Wis;
if (sacrifice_needed < 1)
sacrifice_needed = 1;
sacrifice_needed *= 2;
sacrifice_count = check_sacrifice (op, improver);
if (sacrifice_count < sacrifice_needed)
{
op->failmsg (format ("You need at least %d %s.", sacrifice_needed, &improver->slaying));
return 0;
}
eat_item (op, improver->slaying, sacrifice_needed);
weapon->item_power++;
switch (improver->stats.sp)
{
case IMPROVE_STR: return improve_weapon_stat (op, improver, weapon, weapon->stats.Str, 1, "strength");
case IMPROVE_DEX: return improve_weapon_stat (op, improver, weapon, weapon->stats.Dex, 1, "dexterity");
case IMPROVE_CON: return improve_weapon_stat (op, improver, weapon, weapon->stats.Con, 1, "constitution");
case IMPROVE_WIS: return improve_weapon_stat (op, improver, weapon, weapon->stats.Wis, 1, "wisdom");
case IMPROVE_CHA: return improve_weapon_stat (op, improver, weapon, weapon->stats.Cha, 1, "charisma");
case IMPROVE_INT: return improve_weapon_stat (op, improver, weapon, weapon->stats.Int, 1, "intelligence");
case IMPROVE_POW: return improve_weapon_stat (op, improver, weapon, weapon->stats.Pow, 1, "power");
default:
op->failmsg ("Unknown improvement type.");
}
LOG (llevError, "improve_weapon: Got to end of function\n");
return 0;
}
/**
* Handles the applying of improve/prepare/enchant weapon scroll.
* Checks a few things (not on a non-magic square, marked weapon, ...),
* then calls improve_weapon to do the dirty work.
*/
static int
check_improve_weapon (object *op, object *tmp)
{
object *otmp;
if (op->type != PLAYER)
return 0;
if (!QUERY_FLAG (op, FLAG_WIZCAST) && (get_map_flags (op->map, NULL, op->x, op->y, NULL, NULL) & P_NO_MAGIC))
{
op->failmsg ("Something blocks the magic of the scroll!");
return 0;
}
otmp = find_marked_object (op);
if (!otmp)
{
op->failmsg ("You need to mark a weapon object. H