• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Feature Vampiric weapons 0.2

zakius

Enter the Ninja!
Joined
Apr 30, 2009
Messages
2,635
Reaction score
65
Location
with Taiga
Vampiric weapons 0.3

updated again, now supports lifesteal attribute in any eq part​


Well, cause I and probably more people wanted to have lifestealing weapons, here it comes: code allowing you to make it easy!

in items.h find
Code:
int32_t attack, extraAttack,
and go to first ; after that, add
Code:
, lifestealpercent, manastealpercent
before it.

in items.cpp under
Code:
armor = 0;
add
Code:
lifestealpercent = manastealpercent = 0;
and under
Code:
else if(tmpStrValue == "attack")//rand
			{
				if(readXMLInteger(itemAttributesNode, "value", intValue))
                       it.attack = intValue;
			}
add
Code:
else if(tmpStrValue == "lifestealpercent")//rand
			{
				if(readXMLInteger(itemAttributesNode, "value", intValue))
                       it.lifestealpercent = intValue;
			}
			else if(tmpStrValue == "manastealpercent")//rand
			{
				if(readXMLInteger(itemAttributesNode, "value", intValue))
                       it.manastealpercent = intValue;
			}

in item.h under
Code:
int32_t getAttack() const;
add
Code:
		int32_t getLifestealPercent() const;
		int32_t getManastealPercent() const;
and under
Code:
inline int32_t Item::getAttack() const
{
	const int32_t* v = getIntegerAttribute("attack");
	if(v)
		return *v;

	return items[id].attack;
}
add
Code:
inline int32_t Item::getLifestealPercent() const
{
	const int32_t* v = getIntegerAttribute("lifestealpercent");
	if(v)
		return *v;

	return items[id].lifestealpercent;
}

inline int32_t Item::getManastealPercent() const
{
	const int32_t* v = getIntegerAttribute("manastealpercent");
	if(v)
		return *v;

	return items[id].manastealpercent;
}
in weapons.cpp under
Code:
bool Weapon::internalUseWeapon(Player* player, Item* item, Creature* target, int32_t damageModifier) const
{
	if(isScripted())
	{
		LuaVariant var;
		var.type = VARIANT_NUMBER;
		var.number = target->getID();
		executeUseWeapon(player, var);
	}
	else
	{
		int32_t damage = (getWeaponDamage(player, target, item) * damageModifier) / 100;
change combat::doCombatHealth line to
Code:
		int32_t lifestealpercent=0;
		int32_t manastealpercent=0;
		Item* item = NULL;
		for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot)
		{
			if(! player->getInventoryItem((slots_t)slot) || slot == 10) continue;
			item = player->getInventoryItem((slots_t)slot);
			const ItemType& it = Item::items[item->getID()];
			if (((slot == 5 || slot == 6) && (it.weaponType!=WEAPON_SWORD && it.weaponType!=WEAPON_CLUB &&
			it.weaponType!=WEAPON_AXE && it.weaponType!=WEAPON_DIST && it.weaponType!=WEAPON_SHIELD && it.weaponType!=WEAPON_FIST))) continue;
			lifestealpercent+=item->getLifestealPercent();
			manastealpercent+=item->getManastealPercent();
		}
        Combat::doCombatHealth(player, target, damage, damage, params, lifestealpercent, manastealpercent);
in combat.cpp change all
Code:
void Combat::doCombatHealth(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params)
function to
Code:
void Combat::doCombatHealth(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params, int lifestealpercent, int manastealpercent)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;

    CombatHealthFunc(caster, target, params, (void*)&var, lifestealpercent, manastealpercent);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
        
}
and next function to
Code:
void Combat::doCombatHealth(Creature* caster, const Position& pos, const CombatArea* area,
    int32_t minChange, int32_t maxChange, const CombatParams& params, int lifestealpercent, int manastealpercent)
{
    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;
    bool lol=CombatHealthFunc;
    CombatFunc(caster, pos, area, params, lol, (void*)&var);
}
and whole
Code:
bool Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, void* data
to
Code:
bool Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, void* data, int lifestealpercent, int manastealpercent)
{
    int32_t change = 0;
    if(Combat2Var* var = (Combat2Var*)data)
    {
        change = var->change;
        if(!change)
            change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL);
    }

    if(g_game.combatBlockHit(params.combatType, caster, target, change, params.blockedByShield, params.blockedByArmor))
        return false;

    if(change < 0 && caster && caster->getPlayer() && target->getPlayer() && target->getPlayer()->getSkull() != SKULL_BLACK)
        change = change / 2;

    if(!g_game.combatChangeHealth(params.combatType, caster, target, change, params.effects.hit, params.effects.color))
        return false;
    
    
    int32_t lifestolen = int32_t(change * lifestealpercent / 100);
    CombatParams lifesteal;
    lifesteal.combatType=COMBAT_HEALING;
    lifesteal.isAggressive=false;
    if(lifestolen<0)
        Combat::doCombatHealth(caster, caster, -lifestolen, -lifestolen, lifesteal);
    
    int32_t manastolen = int32_t(change * manastealpercent / 100);
    CombatParams manasteal;
    manasteal.combatType=COMBAT_MANADRAIN;
    manasteal.isAggressive=false;
    if(manastolen>0)
        Combat::doCombatMana(caster, caster, -manastolen, -manastolen, manasteal);
    
    CombatConditionFunc(caster, target, params, NULL);
    CombatDispelFunc(caster, target, params, NULL);
    return true;
}
under
Code:
void Combat::CombatFunc(Creature* caster, const Position& pos, const CombatArea* area,
    const CombatParams& params, COMBATFUNC func, void* data)
{
    std::list<Tile*> tileList;
    if(caster)
        getCombatArea(caster->getPosition(), pos, area, tileList);
    else
        getCombatArea(pos, pos, area, tileList);

    Combat2Var* var = (Combat2Var*)data;
    if(var && !params.differentAreaDamage)
        var->change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL);

    uint32_t maxX = 0, maxY = 0, diff;
    //calculate the max viewable range
    for(std::list<Tile*>::iterator it = tileList.begin(); it != tileList.end(); ++it)
    {
        diff = std::abs((*it)->getPosition().x - pos.x);
        if(diff > maxX)
            maxX = diff;

        diff = std::abs((*it)->getPosition().y - pos.y);
        if(diff > maxY)
            maxY = diff;
    }

    SpectatorVec list;
    g_game.getSpectators(list, pos, false, true, maxX + Map::maxViewportX, maxX + Map::maxViewportX,
        maxY + Map::maxViewportY, maxY + Map::maxViewportY);

    Tile* tile = NULL;
    for(std::list<Tile*>::iterator it = tileList.begin(); it != tileList.end(); ++it)
    {
        if(!(tile = (*it)) || canDoCombat(caster, (*it), params.isAggressive) != RET_NOERROR)
            continue;

        bool skip = true;
        if(CreatureVector* creatures = tile->getCreatures())
        {
            for(CreatureVector::iterator cit = creatures->begin(), cend = creatures->end(); skip && cit != cend; ++cit)
            {
                if(params.targetPlayersOrSummons && !(*cit)->getPlayer() && !(*cit)->isPlayerSummon())
                    continue;

                if(params.targetCasterOrTopMost)
                {
                    if(caster && caster->getTile() == tile)
                    {
                        if((*cit) == caster)
                            skip = false;
                    }
                    else if((*cit) == tile->getTopCreature())
                        skip = false;

                    if(skip)
                        continue;
                }

                if(!params.isAggressive || (caster != (*cit) && Combat::canDoCombat(caster, (*cit)) == RET_NOERROR))
                {
                    func(caster, (*cit), params, (void*)var);
                    if(params.targetCallback)
                        params.targetCallback->onTargetCombat(caster, (*cit));
                }
            }
        }

        combatTileEffects(list, caster, tile, params);
    }

    postCombatEffects(caster, pos, params);
}
add
Code:
void Combat::CombatFunc(Creature* caster, const Position& pos, const CombatArea* area,
    const CombatParams& params, bool hmm, void* data)
{
    std::list<Tile*> tileList;
    if(caster)
        getCombatArea(caster->getPosition(), pos, area, tileList);
    else
        getCombatArea(pos, pos, area, tileList);

    Combat2Var* var = (Combat2Var*)data;
    if(var && !params.differentAreaDamage)
        var->change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL);

    uint32_t maxX = 0, maxY = 0, diff;
    //calculate the max viewable range
    for(std::list<Tile*>::iterator it = tileList.begin(); it != tileList.end(); ++it)
    {
        diff = std::abs((*it)->getPosition().x - pos.x);
        if(diff > maxX)
            maxX = diff;

        diff = std::abs((*it)->getPosition().y - pos.y);
        if(diff > maxY)
            maxY = diff;
    }

    SpectatorVec list;
    g_game.getSpectators(list, pos, false, true, maxX + Map::maxViewportX, maxX + Map::maxViewportX,
        maxY + Map::maxViewportY, maxY + Map::maxViewportY);

    Tile* tile = NULL;
    for(std::list<Tile*>::iterator it = tileList.begin(); it != tileList.end(); ++it)
    {
        if(!(tile = (*it)) || canDoCombat(caster, (*it), params.isAggressive) != RET_NOERROR)
            continue;

        bool skip = true;
        if(CreatureVector* creatures = tile->getCreatures())
        {
            for(CreatureVector::iterator cit = creatures->begin(), cend = creatures->end(); skip && cit != cend; ++cit)
            {
                if(params.targetPlayersOrSummons && !(*cit)->getPlayer() && !(*cit)->isPlayerSummon())
                    continue;

                if(params.targetCasterOrTopMost)
                {
                    if(caster && caster->getTile() == tile)
                    {
                        if((*cit) == caster)
                            skip = false;
                    }
                    else if((*cit) == tile->getTopCreature())
                        skip = false;

                    if(skip)
                        continue;
                }

                if(!params.isAggressive || (caster != (*cit) && Combat::canDoCombat(caster, (*cit)) == RET_NOERROR))
                {
                    CombatHealthFunc(caster, (*cit), params, (void*)var);
                    if(params.targetCallback)
                        params.targetCallback->onTargetCombat(caster, (*cit));
                }
            }
        }

        combatTileEffects(list, caster, tile, params);
    }

    postCombatEffects(caster, pos, params);
}
in combat.h under
Code:
static void CombatFunc(Creature* caster, const Position& pos,
            const CombatArea* area, const CombatParams& params, COMBATFUNC func, void* data);
add
Code:
static void CombatFunc(Creature* caster, const Position& pos, const CombatArea* area, const CombatParams& params, bool, void* data);
change
Code:
static bool CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, void* data);
to
Code:
static bool CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, void* data, int lifestealpercent=0, int manastealpercent=0);
,
Code:
static void doCombatHealth(Creature* caster, Creature* target,
            int32_t minChange, int32_t maxChange, const CombatParams& params);
to
Code:
static void doCombatHealth(Creature* caster, Creature* target,
            int32_t minChange, int32_t maxChange, const CombatParams& params, int lifestealpercent=0, int manastealpercent=0);

Code:
static void doCombatHealth(Creature* caster, const Position& pos,
            const CombatArea* area, int32_t minChange, int32_t maxChange, const CombatParams& params);
to
Code:
static void doCombatHealth(Creature* caster, const Position& pos,
            const CombatArea* area, int32_t minChange, int32_t maxChange, const CombatParams& params, int lifestealpercent=0, int manastealpercent=0);
now rebuild all and you can just put in items.xml attribute lifestealpercent to regen health or manastealpercent to regen mana ammount based on hit.

Also you can add/change this attribute in lua just like attack or armor

hmm, I dont have any inteligent idea how to describe it in text shown onLook, but if you have any nice idea I'll add info how to do it


(guys who have prev version: in weapons.cpp delete
Code:
if(item->getLifestealPercent())
        {
            int32_t lifestolen = int32_t(damage * item->getLifestealPercent() / 100);
            CombatParams lifesteal;
            lifesteal.combatType=COMBAT_HEALING;
            lifesteal.isAggressive=false;
            Combat::doCombatHealth(player, player, -lifestolen, -lifestolen, lifesteal);
        }
        if(item->getManastealPercent())
        {
            int32_t manastolen = int32_t(damage * item->getManastealPercent() / 100);
            CombatParams manasteal;
            manasteal.combatType=COMBAT_MANADRAIN;
            manasteal.isAggressive=false;
            Combat::doCombatMana(player, player, -manastolen, -manastolen, manasteal);
        }
)
 
Last edited:
can you add these features to lifedrain and manadrain elements? to take the healing from the damage of the element

something like this for activate steal:
Code:
<attribute key="elementLifedrain" steal="1" value="11" />

and if this can be made standart for make spells with life/mana drain damage stealing damage will be better!
 
Exedion: it can be a bit problematic, cause I rewritten all item.abilities.elementType and item.abilities.elementDamage to item.elementHEREPUTNAMEOFELEMENT, but sure, I can try
And making it for spells(hmm, maybe just make all lifedrain and manadrain damage regen 100% of drained damage? cause putting additional value[percent] into spell formula can be REALLY hard, or at least very problematic)

About version: 0.3.6 for sure, big chance that will work for 0.4 and possibility for older versions(no idea about 0.2, but if you know c++ basics you can try)
 
Exedion: it can be a bit problematic, cause I rewritten all item.abilities.elementType and item.abilities.elementDamage to item.elementHEREPUTNAMEOFELEMENT, but sure, I can try
And making it for spells(hmm, maybe just make all lifedrain and manadrain damage regen 100% of drained damage? cause putting additional value[percent] into spell formula can be REALLY hard, or at least very problematic)

About version: 0.3.6 for sure, big chance that will work for 0.4 and possibility for older versions(no idea about 0.2, but if you know c++ basics you can try)

yes, is problematic... for spells i can make in other way, with lua and onStatsChange can be possible, but i dont know if these function work with monster... i need make you a request, how i can make a new ammunition type or/and make a weapon suitable for bolts and arrow at same time?
 
to amke it suitable for both types of ammo youl steel need to declare another type of am,mo probably and use sth like:
if weapon.ammotype=="third" then check if item in arrow slot is ammo of first or second type, umm...
I can check it, but it can be more complicated that it seems

in tools.cpp you have sth like
Code:
AmmoTypeNames ammoTypeNames[] =
{
	{"spear",		AMMO_SPEAR},
	{"arrow",		AMMO_ARROW},
	{"poisonarrow",		AMMO_ARROW},
	{"burstarrow",		AMMO_ARROW},
	{"bolt",		AMMO_BOLT},
	{"powerbolt",		AMMO_BOLT},
	{"smallstone",		AMMO_STONE},
	{"largerock",		AMMO_STONE},
	{"throwingstar",	AMMO_THROWINGSTAR},
	{"throwingknife",	AMMO_THROWINGKNIFE},
	{"snowball",		AMMO_SNOWBALL},
	{"huntingspear",	AMMO_SPEAR},
	{"royalspear",		AMMO_SPEAR},
	{"enchantedspear",	AMMO_SPEAR},
	{"sniperarrow",		AMMO_ARROW},
	{"onyxarrow",		AMMO_ARROW},
	{"piercingbolt",	AMMO_BOLT},
	{"infernalbolt",	AMMO_BOLT},
	{"flasharrow",		AMMO_ARROW},
	{"flammingarrow",	AMMO_ARROW},
	{"flamingarrow",	AMMO_ARROW},
	{"shiverarrow",		AMMO_ARROW},
	{"eartharrow",		AMMO_ARROW},
	{"etherealspear",	AMMO_SPEAR}
};
, here you can add: on left string used in items.xml as ammotype, on right AMMOTYPE from enum(next step)(now just put:
Code:
,
	{"dualammo",	AMMO_DUAL}

find
Code:
enum Ammo_t
{
	AMMO_NONE = 0,
	AMMO_BOLT,
	AMMO_ARROW,
	AMMO_SPEAR,
	AMMO_THROWINGSTAR,
	AMMO_THROWINGKNIFE,
	AMMO_STONE,
	AMMO_SNOWBALL
};
via find in files and here add what you added on right in prev step,(in this case
Code:
AMMO_DUAL
(it was easy part)
and in player.cpp change
Code:
Item* Player::getWeapon(bool ignoreAmmo)
{
	if(weapon)
		return weapon;

	Item* item = NULL;
	for(int32_t slot = SLOT_RIGHT; slot <= SLOT_LEFT; ++slot)
	{
		if(!(item = getEquippedItem((slots_t)slot)) || item->getWeaponType() != WEAPON_DIST)
			continue;

		if(!ignoreAmmo && item->getAmmoType() != AMMO_NONE)
		{
			Item* ammoItem = getInventoryItem(SLOT_AMMO);
			if(ammoItem && ammoItem->getAmmoType() == item->getAmmoType())
			{
				if(g_weapons->getWeapon(ammoItem))
				{
					shootRange = item->getShootRange();
					return ammoItem;
				}
			}
		}
		else if(g_weapons->getWeapon(item))
		{
			shootRange = item->getShootRange();
			return item;
		}
	}

	return NULL;
}
to(you have to change few things cause I dunno what const you used for the DOUBLE ammo weapon)
Code:
Item* Player::getWeapon(bool ignoreAmmo)
{
	if(weapon)
		return weapon;

	Item* item = NULL;
	for(int32_t slot = SLOT_RIGHT; slot <= SLOT_LEFT; ++slot)
	{
		if(!(item = getEquippedItem((slots_t)slot)) || item->getWeaponType() != WEAPON_DIST)
			continue;

		if(!ignoreAmmo && item->getAmmoType() != AMMO_NONE)
		{
			Item* ammoItem = getInventoryItem(SLOT_AMMO);
			if(ammoItem && (ammoItem->getAmmoType() == item->getAmmoType())||((ammoItem->getAmmoType() == AMMO_ARROW || ammoItem->getAmmoType() == AMMO_BOLT) && item->getAmmoType()== AMMO_DUAL)
			{
				if(g_weapons->getWeapon(ammoItem))
				{
					shootRange = item->getShootRange();
					return ammoItem;
				}
			}
		}
		else if(g_weapons->getWeapon(item))
		{
			shootRange = item->getShootRange();
			return item;
		}
	}

	return NULL;
}
rebuild all and pray ^^
not tested so make backup of your source, but it should work
and you also should know how to add normal type of ammo now
 
Last edited:
strange, I have to check it

try changing
Code:
Combat::doCombatHealth(player, player, -lifestolen, -lifestolen, lifesteal);
to
Code:
 if (lifestolen>0)  Combat::doCombatHealth(player, player, -lifestolen, -lifestolen, lifesteal);
and analogically the mana thing, just idea, dont know if it will change anything
 
hmm, anyway I need to rewrite it a bit, cause it generates regen basing not reduced dmg(with like 5% it wont make big difference, but with low skill and 100% steal it regens ~2x more than hits when attacking stupid troll...)
 
fixed known bugs, updated main post
should work good with defences, when attacking players etc
 
Also you can add/change this attribute in lua just like attack or armor

please explain better this!

offtopic: i need you help one more time, if you se the function "onStatsChange" in creature events, this have a parameter to get the damage taken named "value", how i can add these parameter to "onCombat" function in creature events?
 
doItemSetAttribute()
in default tfs its impossible to affect resistances or elemental dmg this way

about passing additional variables to lua functions: I'll have to play with this a bit, maybe tommorow...
 
Something is wrong because when i start server with your code my monsters witch attack

XML:
    <attack name="fire" interval="300" chance="60" range="4" radius="1" target="1" min="-200" max="-500">
      <attribute key="shootEffect" value="fire"/>
    </attack>

attack me 2 damege.
but when i start server without your code all is good 200-500dmg.
 
I have that errors in rebuild:
33 C:\forgottenserver-0.3.6pl1\0.3.6pl1\actions.cpp In file included from ../actions.cpp
263 C:\WejOTS\forgottenserver-0.3.6pl1\0.3.6pl1\combat.h expected `,' or `...' before ';' token
 

Similar threads

Back
Top