Beispiel #1
0
int32 Client::GetActSpellDuration(uint16 spell_id, int32 duration)
{
    int increase = 100;
    increase += GetFocusEffect(focusSpellDuration, spell_id);
    int tic_inc = 0;
    tic_inc = GetFocusEffect(focusSpellDurByTic, spell_id);

    if(IsBeneficialSpell(spell_id))
    {
        switch(GetAA(aaSpellCastingReinforcement)) {
        case 1:
            increase += 5;
            break;
        case 2:
            increase += 15;
            break;
        case 3:
            increase += 30;
            if (GetAA(aaSpellCastingReinforcementMastery) == 1)
                increase += 20;
            break;
        }
    }

    if(IsMezSpell(spell_id)) {
        tic_inc += GetAA(aaMesmerizationMastery);
    }

    return (((duration * increase) / 100) + tic_inc);
}
Beispiel #2
0
//need to rewrite this
void Client::IncrementAlternateAdvancementRank(int rank_id) {
	AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id);
	if(!rank) {
		return;
	}

	if(!rank->base_ability) {
		return;
	}

	int points = GetAA(rank_id);
	GrantAlternateAdvancementAbility(rank->base_ability->id, points + 1, true);
}
Beispiel #3
0
int Client::CalcAAReuseTimer(const AA_DBAction *caa) {

	if (!caa)
		return 0;

	int ReuseTime = caa->reuse_time;

	if (ReuseTime > 0)
	{
		int ReductionPercentage;

		if (caa->redux_aa > 0 && caa->redux_aa < aaHighestID)
		{
			ReductionPercentage = GetAA(caa->redux_aa) * caa->redux_rate;

			if (caa->redux_aa2 > 0 && caa->redux_aa2 < aaHighestID)
				ReductionPercentage += (GetAA(caa->redux_aa2) * caa->redux_rate2);

			ReuseTime = caa->reuse_time * (100 - ReductionPercentage) / 100;
		}

	}
	return ReuseTime;
}
Beispiel #4
0
//New AA
void Client::SendAlternateAdvancementTable() {
	for(auto &aa : zone->aa_abilities) {
		uint32 charges = 0;
		auto ranks = GetAA(aa.second->first_rank_id, &charges);
		if(ranks) {
			if(aa.second->GetMaxLevel(this) == ranks) {
				SendAlternateAdvancementRank(aa.first, ranks);
			} else {
				SendAlternateAdvancementRank(aa.first, ranks);
				SendAlternateAdvancementRank(aa.first, ranks + 1);
			}
		} else {
			SendAlternateAdvancementRank(aa.first, 1);
		}
	}
}
Beispiel #5
0
bool Client::CanIncreaseTradeskill(SkillType tradeskill) {
	uint32 rawskill = GetRawSkill(tradeskill);
	uint16 maxskill = MaxSkill(tradeskill);

	if (rawskill >= maxskill) //Max skill sanity check
		return false;

	uint8 Baking    = (GetRawSkill(BAKING) > 200) ? 1 : 0;
	uint8 Smithing  = (GetRawSkill(BLACKSMITHING) > 200) ? 1 : 0;
	uint8 Brewing   = (GetRawSkill(BREWING) > 200) ? 1 : 0;
	uint8 Fletching = (GetRawSkill(FLETCHING) > 200) ? 1 : 0;
	uint8 Jewelry   = (GetRawSkill(JEWELRY_MAKING) > 200) ? 1 : 0;
	uint8 Pottery   = (GetRawSkill(POTTERY) > 200) ? 1 : 0;
	uint8 Tailoring = (GetRawSkill(TAILORING) > 200) ? 1 : 0;
	uint8 SkillTotal = Baking + Smithing + Brewing + Fletching + Jewelry + Pottery + Tailoring; //Tradeskills above 200
	uint32 aaLevel  = GetAA(aaNewTanaanCraftingMastery); //New Tanaan AA: Each level allows an additional tradeskill above 200 (first one is free)
	
	switch (tradeskill) {
		case BAKING:
		case BLACKSMITHING:
		case BREWING:
		case FLETCHING:
		case JEWELRY_MAKING:
		case POTTERY:
		case TAILORING:
			if (aaLevel == 6)
				break; //Maxed AA
			if (SkillTotal == 0)
				break; //First tradeskill freebie
			if ((SkillTotal == (aaLevel + 1)) && (rawskill > 200))
				break; //One of the tradeskills already allowed to go over 200
			if ((SkillTotal >= (aaLevel + 1)) && (rawskill >= 200))
				return false; //One or more tradeskills already at or beyond limit
				break;
		default:
			break; //Other skills unchecked and ability to increase assumed true
	}
	return true;
}
Beispiel #6
0
int32 Client::GetActSpellCost(uint16 spell_id, int32 cost)
{
    // This formula was derived from the following resource:
    // http://www.eqsummoners.com/eq1/specialization-library.html
    // WildcardX
    float PercentManaReduction = 0.0f;
    float SpecializeSkill = GetSpecializeSkillValue(spell_id);

    if (SpecializeSkill > 0.0f)
    {
        PercentManaReduction = 1 + SpecializeSkill / 20.0f;
        switch(GetAA(aaSpellCastingMastery))
        {
        case 1:
            PercentManaReduction += 2.5f;
            break;
        case 2:
            PercentManaReduction += 5.0f;
            break;
        case 3:
            PercentManaReduction += 10.0f;
            break;
        }
    }

    int16 focus_redux = GetFocusEffect(focusManaCost, spell_id);

    if(focus_redux > 0)
    {
        PercentManaReduction += zone->random.Real(1, (double)focus_redux);
    }

    cost -= (cost * (PercentManaReduction / 100));

    if(cost < 0)
        cost = 0;

    return cost;
}
Beispiel #7
0
void Client::SendAlternateAdvancementPoints() {
	auto outapp = new EQApplicationPacket(OP_RespondAA, sizeof(AATable_Struct));
	AATable_Struct* aa2 = (AATable_Struct *)outapp->pBuffer;

	int i = 0;
	for(auto &aa : zone->aa_abilities) {
		uint32 charges = 0;
		auto ranks = GetAA(aa.second->first_rank_id, &charges);
		if(ranks) {
			AA::Rank *rank = aa.second->GetRankByPointsSpent(ranks);
			if(rank) {
				aa2->aa_list[i].AA = rank->id;
				aa2->aa_list[i].value = rank->total_cost;
				aa2->aa_list[i].charges = charges;
				i++;
			}
		}
	}


	aa2->aa_spent = GetSpentAA();
	QueuePacket(outapp);
	safe_delete(outapp);
}
Beispiel #8
0
int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {

	if (spells[spell_id].targettype == ST_Self)
		return value;

	if (IsNPC())
		value += value*CastToNPC()->GetSpellFocusDMG()/100;

	bool Critical = false;
	int32 value_BaseEffect = 0;
	int chance = 0;

	value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);

	// Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
	if ((spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40)
		value -= (GetLevel() - 40) * 20;

	//This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch.
	if (spell_id == SPELL_IMP_HARM_TOUCH && IsClient()) //Improved Harm Touch
		value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch

		chance = RuleI(Spells, BaseCritChance); //Wizard base critical chance is 2% (Does not scale with level)
		chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
		chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;

	//Crtical Hit Calculation pathway
	if (chance > 0 || (IsClient() && GetClass() == WIZARD && GetLevel() >= RuleI(Spells, WizCritLevel))) {

		 int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.

		//Improved Harm Touch is a guaranteed crit if you have at least one level of SCF.
		if (spell_id == SPELL_IMP_HARM_TOUCH && IsClient() && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0))
			 chance = 100;

		if (zone->random.Roll(chance)) {
			Critical = true;
			ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease;
			ratio += itembonuses.SpellCritDmgIncNoStack + spellbonuses.SpellCritDmgIncNoStack + aabonuses.SpellCritDmgIncNoStack;
		}

		else if ((IsClient() && GetClass() == WIZARD) || (IsMerc() && GetClass() == CASTERDPS)) {
			if ((GetLevel() >= RuleI(Spells, WizCritLevel)) && zone->random.Roll(RuleI(Spells, WizCritChance))){
				//Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. (20-70 is parse confirmed)
				ratio += zone->random.Int(20,70);
				Critical = true;
			}
		}

		if (IsClient() && GetClass() == WIZARD)
			ratio += RuleI(Spells, WizCritRatio); //Default is zero

		if (Critical){

			value = value_BaseEffect*ratio/100;

			value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;

			value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;

			if (target) {
				value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
				value -= target->GetFcDamageAmtIncoming(this, spell_id);
			}

			value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100;

			value -= GetFocusEffect(focusFcDamageAmt, spell_id);

			if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
				value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100;

			else if (IsNPC() && CastToNPC()->GetSpellScale())
				value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f);

			entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits,
					OTHER_CRIT_BLAST, GetName(), itoa(-value));

			if (IsClient())
				Message_StringID(MT_SpellCrits, YOU_CRIT_BLAST, itoa(-value));

			return value;
		}
	}
	//Non Crtical Hit Calculation pathway
	value = value_BaseEffect;

	value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;

	value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;

	if (target) {
		value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100;
		value -= target->GetFcDamageAmtIncoming(this, spell_id);
	}

	value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id);

	value -= GetFocusEffect(focusFcDamageAmt, spell_id);

	if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
		 value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);

	if (IsNPC() && CastToNPC()->GetSpellScale())
		value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f);

	return value;
}
Beispiel #9
0
//returns true on success
bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
 	if(spec == NULL)
		return(false);
	
	uint16 user_skill = GetSkill(spec->tradeskill);
	float chance = 0.0;
	float skillup_modifier = 0.0;
	int16 thirdstat = 0;
	int16 stat_modifier = 15;
	uint16 success_modifier = 0;

	// Rework based on the info on eqtraders.com
	// http://mboards.eqtraders.com/eq/showthread.php?t=22246
	// 09/10/2006 v0.1 (eq4me)
	// 09/11/2006 v0.2 (eq4me)
	// Todo:
	//     Implementing AAs
	//     Success modifiers based on recipes
	//     Skillup modifiers based on the rarity of the ingredients

	// Some tradeskills are more eqal then others. ;-)
	// If you want to customize the stage1 success rate do it here.
    // Remember: skillup_modifier is (float). Lower is better
	switch(spec->tradeskill) {
	case FLETCHING:
	case ALCHEMY:
	case JEWELRY_MAKING:
	case POTTERY:
		skillup_modifier = 4;
		break;
	case BAKING:
	case BREWING:
		skillup_modifier = 3;
		break;
	case RESEARCH:
		skillup_modifier = 1;
		break;
	default:
		skillup_modifier = 2;
		break;
	}

	// Some tradeskills take the higher of one additional stat beside INT and WIS
	// to determine the skillup rate. Additionally these tradeskills do not have an
	// -15 modifier on their statbonus.
	if (spec->tradeskill ==  FLETCHING || spec->tradeskill == MAKE_POISON) {
		thirdstat = GetDEX();
		stat_modifier = 0;
	} else if (spec->tradeskill == BLACKSMITHING) {
		thirdstat = GetSTR();
		stat_modifier = 0;
	}
	
	int16 higher_from_int_wis = (GetINT() > GetWIS()) ? GetINT() : GetWIS();
	int16 bonusstat = (higher_from_int_wis > thirdstat) ? higher_from_int_wis : thirdstat;
	
	vector< pair<uint32,uint8> >::iterator itr;


    //calculate the base success chance
	// For trivials over 68 the chance is (skill - 0.75*trivial) +51.5
    // For trivial up to 68 the chance is (skill - trivial) + 66
	if (spec->trivial >= 68) {
		chance = (user_skill - (0.75*spec->trivial)) + 51.5;
	} else {
		chance = (user_skill - spec->trivial) + 66;
	}
	
	int16 over_trivial = (int16)GetRawSkill(spec->tradeskill) - (int16)spec->trivial;

	//handle caps
	if(spec->nofail) {
		chance = 100;	//cannot fail.
		_log(TRADESKILLS__TRACE, "...This combine cannot fail.");
	} else if(over_trivial >= 0) {
		// At reaching trivial the chance goes to 95% going up an additional
		// percent for every 40 skillpoints above the trivial.
		// The success rate is not modified through stats.
		// Mastery AAs are unaccounted for so far.
		// chance_AA = chance + ((100 - chance) * mastery_modifier)
		// But the 95% limit with an additional 1% for every 40 skill points
		// above critical still stands.
		// Mastery modifier is: 10%/25%/50% for rank one/two/three
		chance = 95.0f + (float(user_skill - spec->trivial) / 40.0f);
		Message_StringID(MT_Emote, TRADESKILL_TRIVIAL);
	} else if(chance < 5) {
		// Minimum chance is always 5
		chance = 5;
	} else if(chance > 95) {
		//cap is 95, shouldent reach this before trivial, but just in case.
		chance = 95;
	}
	
	_log(TRADESKILLS__TRACE, "...Current skill: %d , Trivial: %d , Success chance: %f percent", user_skill , spec->trivial , chance);
	_log(TRADESKILLS__TRACE, "...Bonusstat: %d , INT: %d , WIS: %d , DEX: %d , STR: %d", bonusstat , GetINT() , GetWIS() , GetDEX() , GetSTR());
	
	float res = MakeRandomFloat(0, 99);
	int AAChance = 0;

	//AA modifiers
	//can we do this with nested switches?
	if(spec->tradeskill == ALCHEMY){
		switch(GetAA(aaAlchemyMastery)){
		case 1:
			AAChance = 10;
			break;
		case 2:
			AAChance = 25;
			break;
		case 3:
			AAChance = 50;
			break;
		}
	}

	if(spec->tradeskill == JEWELRY_MAKING){
		switch(GetAA(aaJewelCraftMastery)){
		case 1:
			AAChance = 10;
			break;
		case 2:
			AAChance = 25;
			break;
		case 3:
			AAChance = 50;
			break;
		}
	}
      const Item_Struct* item = NULL;

	if (spec->tradeskill == BLACKSMITHING) {
		switch(GetAA(aaBlacksmithingMastery)) {
		case 1:
			AAChance = 10;
			break;
		case 2:
			AAChance = 25;
			break;
		case 3:
			AAChance = 50;
			break;
		}
	}

	if (spec->tradeskill == BAKING) {
		switch(GetAA(aaBakingMastery)) {
		case 1:
			AAChance = 10;
			break;
		case 2:
			AAChance = 25;
			break;
		case 3:
			AAChance = 50;
			break;
		}
	}

	if (spec->tradeskill == BREWING) {
		switch(GetAA(aaBrewingMastery)) {
		case 1:
			AAChance = 10;
			break;
		case 2:
			AAChance = 25;
			break;
		case 3:
			AAChance = 50;
			break;
		}
	}

	if (spec->tradeskill == FLETCHING) {
		switch(GetAA(aaFletchingMastery2)) {
		case 1:
			AAChance = 10;
			break;
		case 2:
			AAChance = 25;
			break;
		case 3:
			AAChance = 50;
			break;
		}
	}

	if (spec->tradeskill == POTTERY) {
		switch(GetAA(aaPotteryMastery)) {
		case 1:
			AAChance = 10;
			break;
		case 2:
			AAChance = 25;
			break;
		case 3:
			AAChance = 50;
			break;
		}
	}

	if (spec->tradeskill == TAILORING) {
		switch(GetAA(aaTailoringMastery)) {
		case 1:
			AAChance = 10;
			break;
		case 2:
			AAChance = 25;
			break;
		case 3:
			AAChance = 50;
			break;
		}
	}
	
	if (spec->tradeskill == RESEARCH) {
		switch(GetAA(aaArcaneTongues)) {
		case 1:
			AAChance = 10;
			break;
		case 2:
			AAChance = 25;
			break;
		case 3:
			AAChance = 50;
			break;
		}
	}

	if (((spec->tradeskill==75) || GetGM() || (chance > res)) || MakeRandomInt(0, 99) < AAChance){
		success_modifier = 1;
		
		if(over_trivial < 0)
			CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill);
		
		Message_StringID(4,TRADESKILL_SUCCEED,spec->name.c_str());

		_log(TRADESKILLS__TRACE, "Tradeskill success");

		itr = spec->onsuccess.begin();
		while(itr != spec->onsuccess.end() && !spec->quest) {
			//should we check this crap?
			SummonItem(itr->first, itr->second);
                  item = database.GetItem(itr->first);
			if (this->GetGroup())
			{
				entity_list.MessageGroup(this,true,MT_Skills,"%s has successfully fashioned %s!",GetName(),item->Name);
			}
			if(RuleB(TaskSystem, EnableTaskSystem))
				UpdateTasksForItem(ActivityTradeSkill, itr->first, itr->second);
			itr++;
		}
		return(true);
	} else {
		success_modifier = 2; // Halves the chance
		
		if(over_trivial < 0)
			CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill);
		
		Message_StringID(MT_Emote,TRADESKILL_FAILED);

		_log(TRADESKILLS__TRACE, "Tradeskill failed");
            if (this->GetGroup())
		{
			entity_list.MessageGroup(this,true,MT_Skills,"%s was unsuccessful in %s tradeskill attempt.",GetName(),this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its");
		}
		
		itr = spec->onfail.begin();
		while(itr != spec->onfail.end()) {
			//should we check these arguments?
			SummonItem(itr->first, itr->second);
			itr++;
		}

        // Rolls on each item, is possible to return everything
        int SalvageChance = aabonuses.SalvageChance + itembonuses.SalvageChance + spellbonuses.SalvageChance;
        // Skip check if not a normal TS or if a quest recipe these should be nofail, but check amyways
        if(SalvageChance && spec->tradeskill != 75 && !spec->quest) {
            itr = spec->salvage.begin();
            uint8 sc = 0;
            while(itr != spec->salvage.end()) {
                for(sc = 0; sc < itr->second; sc++)
                    if(MakeRandomInt(0,99) < SalvageChance)
                        SummonItem(itr->first, 1);
                itr++;
            }
        }

	}
	return(false);
}
Beispiel #10
0
void Client::FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost) {
	int rank_id = rank->base_ability->first_rank_id;

	if(rank->base_ability->charges > 0) {
		uint32 charges = 0;
		GetAA(rank_id, &charges);

		if(charges > 0) {
			return;
		}

		SetAA(rank_id, rank->current_value, rank->base_ability->charges);
	}
	else {
		SetAA(rank_id, rank->current_value, 0);

		//if not max then send next aa
		if(rank->next) {
			SendAlternateAdvancementRank(rank->base_ability->id, rank->next->current_value);
		}
	}

	int cost = !ignore_cost ? rank->cost : 0;

	m_pp.aapoints -= cost ;
	SaveAA();

	SendAlternateAdvancementPoints();
	SendAlternateAdvancementStats();

	if(rank->prev) {
		Message_StringID(15, AA_IMPROVE,
						 std::to_string(rank->title_sid).c_str(),
						 std::to_string(rank->prev->current_value).c_str(),
						 std::to_string(cost).c_str(),
						 cost == 1 ? std::to_string(AA_POINT).c_str() : std::to_string(AA_POINTS).c_str());

		/* QS: Player_Log_AA_Purchases */
		if(RuleB(QueryServ, PlayerLogAAPurchases)) {
			std::string event_desc = StringFormat("Ranked AA Purchase :: aa_id:%i at cost:%i in zoneid:%i instid:%i", rank->id, cost, GetZoneID(), GetInstanceID());
			QServ->PlayerLogEvent(Player_Log_AA_Purchases, CharacterID(), event_desc);
		}
	}
	else {
		Message_StringID(15, AA_GAIN_ABILITY,
						 std::to_string(rank->title_sid).c_str(),
						 std::to_string(cost).c_str(),
						 cost == 1 ? std::to_string(AA_POINT).c_str() : std::to_string(AA_POINTS).c_str());
		/* QS: Player_Log_AA_Purchases */
		if(RuleB(QueryServ, PlayerLogAAPurchases)) {
			std::string event_desc = StringFormat("Initial AA Purchase :: aa_id:%i at cost:%i in zoneid:%i instid:%i", rank->id, cost, GetZoneID(), GetInstanceID());
			QServ->PlayerLogEvent(Player_Log_AA_Purchases, CharacterID(), event_desc);
		}
	}

	CalcBonuses();

	if(cost > 0) {
		if(title_manager.IsNewAATitleAvailable(m_pp.aapoints_spent, GetBaseClass()))
			NotifyNewTitlesAvailable();
	}
}
Beispiel #11
0
void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
	AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id);
	if(!rank) {
		return;
	}

	AA::Ability *ability = rank->base_ability;
	if(!ability) {
		return;
	}

	if(!IsValidSpell(rank->spell)) {
		return;
	}

	if(!CanUseAlternateAdvancementRank(rank)) {
		return;
	}

	//make sure it is not a passive
	if(!rank->effects.empty()) {
		return;
	}

	uint32 charges = 0;
	// We don't have the AA
	if (!GetAA(rank_id, &charges))
		return;

	//if expendable make sure we have charges
	if(ability->charges > 0 && charges < 1)
		return;

	//check cooldown
	if(!p_timers.Expired(&database, rank->spell_type + pTimerAAStart)) {
		uint32 aaremain = p_timers.GetRemainingTime(rank->spell_type + pTimerAAStart);
		uint32 aaremain_hr = aaremain / (60 * 60);
		uint32 aaremain_min = (aaremain / 60) % 60;
		uint32 aaremain_sec = aaremain % 60;

		if(aaremain_hr >= 1) {
			Message(13, "You can use this ability again in %u hour(s) %u minute(s) %u seconds",
			aaremain_hr, aaremain_min, aaremain_sec);
		}
		else {
			Message(13, "You can use this ability again in %u minute(s) %u seconds",
			aaremain_min, aaremain_sec);
		}

		return;
	}

	//calculate cooldown
	int cooldown = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank);
	if(cooldown < 0) {
		cooldown = 0;
	}

	if (!IsCastWhileInvis(rank->spell))
		CommonBreakInvisible();
	// Bards can cast instant cast AAs while they are casting another song
	if(spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
		if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), ALTERNATE_ABILITY_SPELL_SLOT, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
			return;
		}
		ExpendAlternateAdvancementCharge(ability->id);
	} else {
		if(!CastSpell(rank->spell, target_id, ALTERNATE_ABILITY_SPELL_SLOT, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
			return;
		}
	}

	CastToClient()->GetPTimers().Start(rank->spell_type + pTimerAAStart, cooldown);
	SendAlternateAdvancementTimer(rank->spell_type, 0, 0);
}
Beispiel #12
0
void Client::ActivateAA(aaID activate){
	if (activate < 0 || activate >= aaHighestID)
		return;
	if (IsStunned() || IsFeared() || IsMezzed() || IsSilenced() || IsPet() || IsSitting() || GetFeigned())
		return;

	int AATimerID = GetAATimerID(activate);

	SendAA_Struct* aa2 = nullptr;
	aaID aaid = activate;
	uint8 activate_val = GetAA(activate);
	//this wasn't taking into acct multi tiered act talents before...
	if (activate_val == 0){
		aa2 = zone->FindAA(activate);
		if (!aa2){
			int i;
			int a;
			for (i = 1; i<MAX_AA_ACTION_RANKS; i++){
				a = activate - i;
				if (a <= 0)
					break;

				aa2 = zone->FindAA(a);
				if (aa2 != nullptr)
					break;
			}
		}
		if (aa2){
			aaid = (aaID)aa2->id;
			activate_val = GetAA(aa2->id);
		}
	}

	if (activate_val == 0){
		return;
	}

	if (aa2)
	{
		if (aa2->account_time_required)
		{
			if ((Timer::GetTimeSeconds() + account_creation) < aa2->account_time_required)
			{
				return;
			}
		}
	}

	if (!p_timers.Expired(&database, AATimerID + pTimerAAStart))
	{
		uint32 aaremain = p_timers.GetRemainingTime(AATimerID + pTimerAAStart);
		uint32 aaremain_hr = aaremain / (60 * 60);
		uint32 aaremain_min = (aaremain / 60) % 60;
		uint32 aaremain_sec = aaremain % 60;

		if (aa2) {
			if (aaremain_hr >= 1)	//1 hour or more
				Message(CC_Red, "You can use the ability %s again in %u hour(s) %u minute(s) %u seconds",
				aa2->name, aaremain_hr, aaremain_min, aaremain_sec);
			else	//less than an hour
				Message(CC_Red, "You can use the ability %s again in %u minute(s) %u seconds",
				aa2->name, aaremain_min, aaremain_sec);
		}
		else {
			if (aaremain_hr >= 1)	//1 hour or more
				Message(CC_Red, "You can use this ability again in %u hour(s) %u minute(s) %u seconds",
				aaremain_hr, aaremain_min, aaremain_sec);
			else	//less than an hour
				Message(CC_Red, "You can use this ability again in %u minute(s) %u seconds",
				aaremain_min, aaremain_sec);
		}
		return;
	}

	if (activate_val > MAX_AA_ACTION_RANKS)
		activate_val = MAX_AA_ACTION_RANKS;
	activate_val--;		//to get array index.

	//get our current node, now that the indices are well bounded
	const AA_DBAction *caa = &AA_Actions[aaid][activate_val];

	if ((aaid == aaImprovedHarmTouch || aaid == aaLeechTouch) && !p_timers.Expired(&database, pTimerHarmTouch)){
		Message(CC_Red, "Ability recovery time not yet met.");
		return;
	}

	//everything should be configured out now

	uint16 target_id = 0;

	//figure out our target
	switch (caa->target) {
	case aaTargetUser:
	case aaTargetGroup:
		target_id = GetID();
		break;
	case aaTargetCurrent:
	case aaTargetCurrentGroup:
		if (GetTarget() == nullptr) {
			Message_StringID(MT_DefaultText, AA_NO_TARGET);	//You must first select a target for this ability!
			p_timers.Clear(&database, AATimerID + pTimerAAStart);
			return;
		}
		target_id = GetTarget()->GetID();
		break;
	case aaTargetPet:
		if (GetPet() == nullptr) {
			Message(0, "A pet is required for this skill.");
			return;
		}
		target_id = GetPetID();
		break;
	}

	//handle non-spell action
	if (caa->action != aaActionNone) {
		if (caa->mana_cost > 0) {
			if (GetMana() < caa->mana_cost) {
				Message_StringID(CC_Red, INSUFFICIENT_MANA);
				return;
			}
			SetMana(GetMana() - caa->mana_cost);
		}
		if (caa->reuse_time > 0)
		{
			uint32 timer_base = CalcAAReuseTimer(caa);
			if (activate == aaImprovedHarmTouch || activate == aaLeechTouch)
			{
				p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime);
			}
			p_timers.Start(AATimerID + pTimerAAStart, timer_base);
			SendAATimer(activate, static_cast<uint32>(time(nullptr)), static_cast<uint32>(time(nullptr)));
		}
		HandleAAAction(aaid);
	}

	//cast the spell, if we have one
	if (caa->spell_id > 0 && caa->spell_id < SPDAT_RECORDS) {

		if (caa->reuse_time > 0)
		{
			uint32 timer_base = CalcAAReuseTimer(caa);
			SendAATimer(activate, static_cast<uint32>(time(nullptr)), static_cast<uint32>(time(nullptr)));
			p_timers.Start(AATimerID + pTimerAAStart, timer_base);
			if (activate == aaImprovedHarmTouch || activate == aaLeechTouch)
			{
				p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime);
			}
			// Bards can cast instant cast AAs while they are casting another song
			if (spells[caa->spell_id].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
				if (!SpellFinished(caa->spell_id, entity_list.GetMob(target_id), 10, -1, -1, spells[caa->spell_id].ResistDiff, false)) {
					//Reset on failed cast
					SendAATimer(activate, 0, 0xFFFFFF);
					Message_StringID(CC_Yellow, ABILITY_FAILED);
					p_timers.Clear(&database, AATimerID + pTimerAAStart);
					return;
				}
			}
			else {
				if (!CastSpell(caa->spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) {
					//Reset on failed cast
					SendAATimer(activate, 0, 0xFFFFFF);
					Message_StringID(CC_Yellow, ABILITY_FAILED);
					p_timers.Clear(&database, AATimerID + pTimerAAStart);
					return;
				}
			}
		}
		else
		{
			if (!CastSpell(caa->spell_id, target_id))
				return;
		}
	}
}
Beispiel #13
0
void Client::BuyAA(AA_Action* action)
{
	Log.Out(Logs::Detail, Logs::AA, "Starting to buy AA %d", action->ability);

	//find the AA information from the database
	SendAA_Struct* aa2 = zone->FindAA(action->ability);
	if (!aa2) {
		//hunt for a lower level...
		int i;
		int a;
		for (i = 1; i<MAX_AA_ACTION_RANKS; i++){
			a = action->ability - i;
			if (a <= 0)
				break;
			Log.Out(Logs::Detail, Logs::AA, "Could not find AA %d, trying potential parent %d", action->ability, a);
			aa2 = zone->FindAA(a);
			if (aa2 != nullptr)
				break;
		}
	}
	if (aa2 == nullptr)
		return;	//invalid ability...

	if (aa2->special_category == 1 || aa2->special_category == 2)
		return; // Not purchasable progression style AAs

	if (aa2->special_category == 8 && aa2->cost == 0)
		return; // Not purchasable racial AAs(set a cost to make them purchasable)

	uint32 cur_level = GetAA(aa2->id);
	if ((aa2->id + cur_level) != action->ability) { //got invalid AA
		Log.Out(Logs::Detail, Logs::AA, "Unable to find or match AA %d (found %d + lvl %d)", action->ability, aa2->id, cur_level);
		return;
	}

	if (aa2->account_time_required)
	{
		if ((Timer::GetTimeSeconds() - account_creation) < aa2->account_time_required)
		{
			return;
		}
	}

	uint32 real_cost;
	std::map<uint32, AALevelCost_Struct>::iterator RequiredLevel = AARequiredLevelAndCost.find(action->ability);

	if (RequiredLevel != AARequiredLevelAndCost.end()) {
		real_cost = RequiredLevel->second.Cost;
	}
	else
		real_cost = aa2->cost + (aa2->cost_inc * cur_level);

	if (m_pp.aapoints >= real_cost && cur_level < aa2->max_level) {
		SetAA(aa2->id, cur_level + 1);

		Log.Out(Logs::Detail, Logs::AA, "Set AA %d to level %d", aa2->id, cur_level + 1);

		m_pp.aapoints -= real_cost;

		/* Do Player Profile rank calculations and set player profile */
		SaveAA();
		/* Save to Database to avoid having to write the whole AA array to the profile, only write changes*/
		// database.SaveCharacterAA(this->CharacterID(), aa2->id, (cur_level + 1));


		SendAATable();

		/*
		We are building these messages ourself instead of using the stringID to work around patch discrepencies
		these are AA_GAIN_ABILITY	(410) & AA_IMPROVE (411), respectively, in both Titanium & SoF. not sure about 6.2
		*/

		/* Initial purchase of an AA ability */
		if (cur_level < 1){
			Message(15, "You have gained the ability \"%s\" at a cost of %d ability %s.", aa2->name, real_cost, (real_cost>1) ? "points" : "point");

			/* QS: Player_Log_AA_Purchases */
			if (RuleB(QueryServ, PlayerLogAAPurchases)){
				std::string event_desc = StringFormat("Initial AA Purchase :: aa_name:%s aa_id:%i at cost:%i in zoneid:%i instid:%i", aa2->name, aa2->id, real_cost, this->GetZoneID(), this->GetInstanceID());
				QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc);
			}
		}
		/* Ranked purchase of an AA ability */
		else{
			Message(15, "You have improved %s %d at a cost of %d ability %s.", aa2->name, cur_level + 1, real_cost, (real_cost > 1) ? "points" : "point");

			/* QS: Player_Log_AA_Purchases */
			if (RuleB(QueryServ, PlayerLogAAPurchases)){
				std::string event_desc = StringFormat("Ranked AA Purchase :: aa_name:%s aa_id:%i at cost:%i in zoneid:%i instid:%i", aa2->name, aa2->id, real_cost, this->GetZoneID(), this->GetInstanceID());
				QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc);
			}
		}

		SendAAStats();

		CalcBonuses();

		//Bugs client, comment out for now until titles can be worked out.
		//if(title_manager.IsNewAATitleAvailable(m_pp.aapoints_spent, GetBaseClass()))
		//	NotifyNewTitlesAvailable();
	}
}
Beispiel #14
0
void Client::HandleAAAction(aaID activate) {
	if (activate < 0 || activate >= aaHighestID)
		return;

	uint8 activate_val = GetAA(activate);

	if (activate_val == 0)
		return;

	if (activate_val > MAX_AA_ACTION_RANKS)
		activate_val = MAX_AA_ACTION_RANKS;
	activate_val--;		//to get array index.

	//get our current node, now that the indices are well bounded
	const AA_DBAction *caa = &AA_Actions[activate][activate_val];

	uint16 timer_id = 0;
	uint16 timer_duration = caa->duration;
	aaTargetType target = aaTargetUser;

	uint16 spell_id = SPELL_UNKNOWN;	//gets cast at the end if not still unknown

	switch (caa->action) {
	case aaActionAETaunt:
		entity_list.AETaunt(this);
		break;

	case aaActionMassBuff:
		EnableAAEffect(aaEffectMassGroupBuff, 3600);
		Message_StringID(MT_Disciplines, MGB_STRING);	//The next group buff you cast will hit all targets in range.
		break;

	case aaActionFlamingArrows:
		//toggle it
		if (CheckAAEffect(aaEffectFlamingArrows))
			EnableAAEffect(aaEffectFlamingArrows);
		else
			DisableAAEffect(aaEffectFlamingArrows);
		break;

	case aaActionFrostArrows:
		if (CheckAAEffect(aaEffectFrostArrows))
			EnableAAEffect(aaEffectFrostArrows);
		else
			DisableAAEffect(aaEffectFrostArrows);
		break;

	case aaActionRampage:
		EnableAAEffect(aaEffectRampage, 10);
		break;

	case aaActionSharedHealth:
		if (CheckAAEffect(aaEffectSharedHealth))
			EnableAAEffect(aaEffectSharedHealth);
		else
			DisableAAEffect(aaEffectSharedHealth);
		break;

	case aaActionCelestialRegen: {
		//special because spell_id depends on a different AA
		switch (GetAA(aaCelestialRenewal)) {
		case 1:
			spell_id = 3250;
			break;
		case 2:
			spell_id = 3251;
			break;
		default:
			spell_id = 2740;
			break;
		}
		target = aaTargetCurrent;
		break;
	}

	case aaActionDireCharm: {
		//special because spell_id depends on class
		switch (GetClass())
		{
		case DRUID:
			spell_id = 2760;	//2644?
			break;
		case NECROMANCER:
			spell_id = 2759;	//2643?
			break;
		case ENCHANTER:
			spell_id = 2761;	//2642?
			break;
		}
		target = aaTargetCurrent;
		break;
	}

	case aaActionImprovedFamiliar: {
		//Spell IDs might be wrong...
		if (GetAA(aaAllegiantFamiliar))
			spell_id = 3264;	//1994?
		else
			spell_id = 2758;	//2155?
		break;
	}

	case aaActionActOfValor:
		if (GetTarget() != nullptr) {
			int curhp = GetTarget()->GetHP();
			target = aaTargetCurrent;
			GetTarget()->HealDamage(curhp, this);
			Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand);
		}
		break;

	case aaActionSuspendedMinion:
		if (GetPet()) {
			target = aaTargetPet;
			switch (GetAA(aaSuspendedMinion)) {
			case 1:
				spell_id = 3248;
				break;
			case 2:
				spell_id = 3249;
				break;
			}
			//do we really need to cast a spell?

			Message(0, "You call your pet to your side.");
			GetPet()->WipeHateList();
			GetPet()->GMMove(GetX(), GetY(), GetZ());
			if (activate_val > 1)
				entity_list.ClearFeignAggro(GetPet());
		}
		else {
			Message(0, "You have no pet to call.");
		}
		break;

	case aaActionProjectIllusion:
		EnableAAEffect(aaEffectProjectIllusion, 3600);
		Message(10, "The power of your next illusion spell will flow to your grouped target in your place.");
		break;


	case aaActionEscape:
		Escape();
		break;

		// Don't think this code is used any longer for Bestial Alignment as the aa.has a spell_id and no nonspell_action.
	case aaActionBeastialAlignment:
		switch (GetBaseRace()) {
		case BARBARIAN:
			spell_id = AA_Choose3(activate_val, 4521, 4522, 4523);
			break;
		case TROLL:
			spell_id = AA_Choose3(activate_val, 4524, 4525, 4526);
			break;
		case OGRE:
			spell_id = AA_Choose3(activate_val, 4527, 4527, 4529);
			break;
		case IKSAR:
			spell_id = AA_Choose3(activate_val, 4530, 4531, 4532);
			break;
		case VAHSHIR:
			spell_id = AA_Choose3(activate_val, 4533, 4534, 4535);
			break;
		}

	case aaActionLeechTouch:
		target = aaTargetCurrent;
		spell_id = SPELL_HARM_TOUCH2;
		EnableAAEffect(aaEffectLeechTouch, 1000);
		break;

	case aaActionFadingMemories:
		// Do nothing since spell effect works correctly, but mana isn't used.
		break;

		default:
			Log.Out(Logs::General, Logs::Error, "Unknown AA nonspell action type %d", caa->action);
			return;
	}


	uint16 target_id = 0;
	//figure out our target
	switch (target) {
	case aaTargetUser:
	case aaTargetGroup:
		target_id = GetID();
		break;
	case aaTargetCurrent:
	case aaTargetCurrentGroup:
		if (GetTarget() == nullptr) {
			Message_StringID(MT_DefaultText, AA_NO_TARGET);	//You must first select a target for this ability!
			p_timers.Clear(&database, timer_id + pTimerAAEffectStart);
			return;
		}
		target_id = GetTarget()->GetID();
		break;
	case aaTargetPet:
		if (GetPet() == nullptr) {
			Message(0, "A pet is required for this skill.");
			return;
		}
		target_id = GetPetID();
		break;
	}

	//cast the spell, if we have one
	if (IsValidSpell(spell_id)) {
		int aatid = GetAATimerID(activate);
		if (!CastSpell(spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, pTimerAAStart + aatid, CalcAAReuseTimer(caa), 1)) {
			SendAATimer(activate, 0, 0xFFFFFF);
			Message_StringID(CC_Yellow, ABILITY_FAILED);
			p_timers.Clear(&database, pTimerAAStart + aatid);
			return;
		}
	}

	//handle the duration timer if we have one.
	if (timer_id > 0 && timer_duration > 0) {
		p_timers.Start(pTimerAAEffectStart + timer_id, timer_duration);
	}
}
Beispiel #15
0
// Split from the basic MakePet to allow backward compatiblity with existing code while also 
// making it possible for petpower to be retained without the focus item having to
// stay equipped when the character zones. petpower of -1 means that the currently equipped petfocus
// of a client is searched for and used instead.
void Mob::MakePoweredPet(int16 spell_id, const char* pettype, sint16 petpower, const char *petname) {
	// Sanity and early out checking first.
	if(HasPet() || pettype == NULL)
		return;

	sint16 act_power = 0; // lets see if this works to randomize pet levels // The actual pet power we'll use.
	if (petpower == 0) {  // was == -1
		if (this->IsClient())
			act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);
	}
	if (petpower == -1)  {
		act_power = petpower + MakeRandomInt(0,5);
	}


	// optional rule: classic style variance in pets. Achieve this by
	// adding a random 0-4 to pet power, since it only comes in increments
	// of five from focus effects.

	//lookup our pets table record for this type
	PetRecord record;
	if(!database.GetPoweredPetEntry(pettype, act_power, &record)) {
		Message(13, "Unable to find data for pet %s", pettype);
		LogFile->write(EQEMuLog::Error, "Unable to find data for pet %s, check pets table.", pettype);
		return;
	}

	//find the NPC data for the specified NPC type
	const NPCType *base = database.GetNPCType(record.npc_type);
	if(base == NULL) {
		Message(13, "Unable to load NPC data for pet %s", pettype);
		LogFile->write(EQEMuLog::Error, "Unable to load NPC data for pet %s (NPC ID %d), check pets and npc_types tables.", pettype, record.npc_type);
		return;
	}

	//we copy the npc_type data because we need to edit it a bit
	NPCType *npc_type = new NPCType;
	memcpy(npc_type, base, sizeof(NPCType));

	// If pet power is set to -1 in the DB, use stat scaling
	if (this->IsClient() && petpower == -1) 
	{
		float scale_power = (float)act_power / 100.0f;
		if(scale_power > 0)
		{
			npc_type->max_hp *= (1 + scale_power);
			npc_type->cur_hp = npc_type->max_hp;
			npc_type->AC *= (1 + scale_power);
			npc_type->level += (float)scale_power * 50; //(int)act_power; // gains an additional level for every 25 pet power
			npc_type->min_dmg = (npc_type->min_dmg * (1 + (scale_power / 2)));
			npc_type->max_dmg = (npc_type->max_dmg * (1 + (scale_power / 2)));
			npc_type->size *= (1 + (scale_power / 2));
		}
		petpower = act_power;
	}
	
	switch (GetAA(aaElementalDurability))
	{
	case 1:
		npc_type->max_hp *= 1.02;
		npc_type->cur_hp = npc_type->max_hp;
		break;
	case 2:
		npc_type->max_hp *= 1.05;
		npc_type->cur_hp = npc_type->max_hp;
		break;
	case 3:
		npc_type->max_hp *= 1.10;
		npc_type->cur_hp = npc_type->max_hp;
		break;
	}

	//TODO: think about regen (engaged vs. not engaged)
	
	// Pet naming:
	// 0 - `s pet
	// 1 - `s familiar
	// 2 - `s Warder
	// 3 - Random name if client, `s pet for others
	// 4 - Keep DB name


	if (petname != NULL) {
		// Name was provided, use it.
		strn0cpy(npc_type->name, petname, 64);
	} else if (record.petnaming == 0) {
		strcpy(npc_type->name, this->GetCleanName());
		npc_type->name[25] = '\0';
		strcat(npc_type->name, "`s_pet");
	} else if (record.petnaming == 1) {
		strcpy(npc_type->name, this->GetName());
		npc_type->name[19] = '\0';
		strcat(npc_type->name, "`s_familiar");
	} else if (record.petnaming == 2) {
		strcpy(npc_type->name, this->GetName());
		npc_type->name[21] = 0;
		strcat(npc_type->name, "`s_Warder");
	} else if (record.petnaming == 4) {
		// Keep the DB name
	} else if (record.petnaming == 3 && IsClient()) {
		strcpy(npc_type->name, GetRandPetName());
	} else {
		strcpy(npc_type->name, this->GetCleanName());
		npc_type->name[25] = '\0';
		strcat(npc_type->name, "`s_pet");
	}
	
	//handle beastlord pet appearance
	if(record.petnaming == 2) 
	{
		switch(GetBaseRace()) 
		{
		case VAHSHIR: 
			npc_type->race = TIGER; 
			npc_type->size *= 0.8f; 
			break;
		case TROLL: 
			npc_type->race = ALLIGATOR; 
			npc_type->size *= 2.5f; 
			break;
		case OGRE: 
			npc_type->race = BEAR; 
			npc_type->texture = 3; 
			npc_type->gender = 2; 
			break;
		case BARBARIAN: 
			npc_type->race = WOLF; 
			npc_type->texture = 2;
			break;
		case IKSAR: 
			npc_type->race = WOLF; 
			npc_type->texture = 0; 
			npc_type->gender = 1; 
			npc_type->size *= 2.0f;
			npc_type->luclinface = 0;
			break;
		default: 
			npc_type->race = WOLF; 
			npc_type->texture = 0;
		}
	}

	// handle monster summoning pet appearance
	if(record.monsterflag) {
		char errbuf[MYSQL_ERRMSG_SIZE];
		char* query = 0;
		MYSQL_RES *result = NULL;
		MYSQL_ROW row = NULL;
		uint32 monsterid;
		
		// get a random npc id from the spawngroups assigned to this zone
		if (database.RunQuery(query,	MakeAnyLenString(&query,
			"SELECT npcID FROM (spawnentry INNER JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID) "
			"INNER JOIN npc_types ON npc_types.id = spawnentry.npcID "
			"WHERE spawn2.zone = '%s' AND npc_types.bodytype NOT IN (11, 33, 66, 67) "
			"AND npc_types.race NOT IN (0,1,2,3,4,5,6,7,8,9,10,11,12,44,55,67,71,72,73,77,78,81,90,92,93,94,106,112,114,127,128,130,139,141,183,236,237,238,239,254,266,330,378,379,380,381,382,383,404,522) "
			"ORDER BY RAND() LIMIT 1",	zone->GetShortName()), errbuf, &result))
		{
			row = mysql_fetch_row(result);
			if (row) 
				monsterid = atoi(row[0]);
			else 
				monsterid = 567;	// since we don't have any monsters, just make it look like an earth pet for now
		}
		else {	// if the database query failed
			LogFile->write(EQEMuLog::Error, "Error querying database for monster summoning pet in zone %s (%s)", zone->GetShortName(), errbuf);
			monsterid = 567;
		}
		
		// give the summoned pet the attributes of the monster we found
		const NPCType* monster = database.GetNPCType(monsterid);
		if(monster) {
			npc_type->race = monster->race;
			npc_type->size = monster->size;
			npc_type->texture = monster->texture;
			npc_type->gender = monster->gender;
		}
		else {
			LogFile->write(EQEMuLog::Error, "Error loading NPC data for monster summoning pet (NPC ID %d)", monsterid);
		}

		safe_delete_array(query);
	}

	//this takes ownership of the npc_type data
	Pet *npc = new Pet(npc_type, this, (PetType)record.petcontrol, spell_id, record.petpower);

	// Now that we have an actual object to interact with, load
	// the base items for the pet. These are always loaded
	// so that a rank 1 suspend minion does not kill things
	// like the special back items some focused pets may receive.
	int32 petinv[MAX_WORN_INVENTORY];
	memset(petinv, 0, sizeof(petinv));
	const Item_Struct *item = 0;

	if (database.GetBasePetItems(record.equipmentset, petinv)) {
		for (int i=0; i<MAX_WORN_INVENTORY; i++)
			if (petinv[i]) {
				item = database.GetItem(petinv[i]);
				npc->AddLootDrop(item, &npc->itemlist, 0, true, true);
			}
	}


	entity_list.AddNPC(npc, true, true);
	SetPetID(npc->GetID());
	// We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet
}
Beispiel #16
0
void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
	AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id);
	if(!rank) {
		return;
	}

	AA::Ability *ability = rank->base_ability;
	if(!ability) {
		return;
	}

	if(!IsValidSpell(rank->spell)) {
		return;
	}

	if(!CanUseAlternateAdvancementRank(rank)) {
		return;
	}

	//make sure it is not a passive
	if(!rank->effects.empty()) {
		return;
	}

	uint32 charges = 0;
	// We don't have the AA
	if (!GetAA(rank_id, &charges))
		return;

	//if expendable make sure we have charges
	if(ability->charges > 0 && charges < 1)
		return;

	//check cooldown
	if(!p_timers.Expired(&database, rank->spell_type + pTimerAAStart, false)) {
		uint32 aaremain = p_timers.GetRemainingTime(rank->spell_type + pTimerAAStart);
		uint32 aaremain_hr = aaremain / (60 * 60);
		uint32 aaremain_min = (aaremain / 60) % 60;
		uint32 aaremain_sec = aaremain % 60;

		if(aaremain_hr >= 1) {
			Message(13, "You can use this ability again in %u hour(s) %u minute(s) %u seconds",
			aaremain_hr, aaremain_min, aaremain_sec);
		}
		else {
			Message(13, "You can use this ability again in %u minute(s) %u seconds",
			aaremain_min, aaremain_sec);
		}

		return;
	}

	//calculate cooldown
	int cooldown = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank);
	if(cooldown < 0) {
		cooldown = 0;
	}

	if (!IsCastWhileInvis(rank->spell))
		CommonBreakInvisible();

	if (spells[rank->spell].sneak && (!hidden || (hidden && (Timer::GetCurrentTime() - tmHidden) < 4000))) {
		Message_StringID(MT_SpellFailure, SNEAK_RESTRICT);
		return;
	}
	//
	// Modern clients don't require pet targeted for AA casts that are ST_Pet
	if (spells[rank->spell].targettype == ST_Pet || spells[rank->spell].targettype == ST_SummonedPet)
		target_id = GetPetID();

	// extra handling for cast_not_standing spells
	if (!spells[rank->spell].cast_not_standing) {
		if (GetAppearance() == eaSitting) // we need to stand!
			SetAppearance(eaStanding, false);

		if (GetAppearance() != eaStanding) {
			Message_StringID(MT_SpellFailure, STAND_TO_CAST);
			return;
		}
	}

	// Bards can cast instant cast AAs while they are casting another song
	if(spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
		if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQEmu::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
			return;
		}
		ExpendAlternateAdvancementCharge(ability->id);
	} else {
		if(!CastSpell(rank->spell, target_id, EQEmu::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
			return;
		}
	}

	CastToClient()->GetPTimers().Start(rank->spell_type + pTimerAAStart, cooldown);
	SendAlternateAdvancementTimer(rank->spell_type, 0, 0);
}
Beispiel #17
0
int32 Client::GetActSpellCost(uint16 spell_id, int32 cost)
{
    // Formula = Unknown exact, based off a random percent chance up to mana cost(after focuses) of the cast spell
    if(this->itembonuses.Clairvoyance && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
    {
        int16 mana_back = this->itembonuses.Clairvoyance * MakeRandomInt(1, 100) / 100;
        // Doesnt generate mana, so best case is a free spell
        if(mana_back > cost)
            mana_back = cost;

        cost -= mana_back;
    }

    // This formula was derived from the following resource:
    // http://www.eqsummoners.com/eq1/specialization-library.html
    // WildcardX
    float PercentManaReduction = 0;
    float SpecializeSkill = GetSpecializeSkillValue(spell_id);
    int SuccessChance = MakeRandomInt(0, 100);

    float bonus = 1.0;
    switch(GetAA(aaSpellCastingMastery))
    {
    case 1:
        bonus += 0.05;
        break;
    case 2:
        bonus += 0.15;
        break;
    case 3:
        bonus += 0.30;
        break;
    }

    bonus += 0.05 * GetAA(aaAdvancedSpellCastingMastery);

    if(SuccessChance <= (SpecializeSkill * 0.3 * bonus))
    {
        PercentManaReduction = 1 + 0.05 * SpecializeSkill;
        switch(GetAA(aaSpellCastingMastery))
        {
        case 1:
            PercentManaReduction += 2.5;
            break;
        case 2:
            PercentManaReduction += 5.0;
            break;
        case 3:
            PercentManaReduction += 10.0;
            break;
        }

        switch(GetAA(aaAdvancedSpellCastingMastery))
        {
        case 1:
            PercentManaReduction += 2.5;
            break;
        case 2:
            PercentManaReduction += 5.0;
            break;
        case 3:
            PercentManaReduction += 10.0;
            break;
        }
    }

    int16 focus_redux = GetFocusEffect(focusManaCost, spell_id);

    if(focus_redux > 0)
    {
        PercentManaReduction += MakeRandomFloat(1, (double)focus_redux);
    }

    cost -= (cost * (PercentManaReduction / 100));

    // Gift of Mana - reduces spell cost to 1 mana
    if(focus_redux >= 100) {
        uint32 buff_max = GetMaxTotalSlots();
        for (int buffSlot = 0; buffSlot < buff_max; buffSlot++) {
            if (buffs[buffSlot].spellid == 0 || buffs[buffSlot].spellid >= SPDAT_RECORDS)
                continue;

            if(IsEffectInSpell(buffs[buffSlot].spellid, SE_ReduceManaCost)) {
                if(CalcFocusEffect(focusManaCost, buffs[buffSlot].spellid, spell_id) == 100)
                    cost = 1;
            }
        }
    }

    if(cost < 0)
        cost = 0;

    return cost;
}
Beispiel #18
0
bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price, bool check_grant) {
	AA::Ability *ability = rank->base_ability;

	if(!ability)
		return false;

	if(!CanUseAlternateAdvancementRank(rank)) {
		return false;
	}

	//You can't purchase grant only AAs they can only be assigned
	if(check_grant && ability->grant_only) {
		return false;
	}

	//check level req
	if(rank->level_req > GetLevel()) {
		return false;
	}

	uint32 current_charges = 0;
	auto points = GetAA(rank->id, &current_charges);

	//check that we are on previous rank already (if exists)
	//grant ignores the req to own the previous rank.
	if(check_grant && rank->prev) {
		if(points != rank->prev->current_value) {
			return false;
		}
	}

	//check that we aren't already on this rank or one ahead of us
	if(points >= rank->current_value) {
		return false;
	}

	//if expendable only let us purchase if we have no charges already
	//not quite sure on how this functions client side atm
	//I intend to look into it later to make sure the behavior is right
	if(ability->charges > 0 && current_charges > 0) {
		return false;
	}

	//check prereqs
	for(auto &prereq : rank->prereqs) {
		AA::Ability *prereq_ability = zone->GetAlternateAdvancementAbility(prereq.first);

		if(prereq_ability) {
			auto ranks = GetAA(prereq_ability->first_rank_id);
			if(ranks < prereq.second) {
				return false;
			}
		}
	}

	//check price, if client
	if(check_price && IsClient()) {
		if(rank->cost > CastToClient()->GetAAPoints()) {
			return false;
		}
	}

	return true;
}
Beispiel #19
0
int32 Client::GetActSpellDamage(uint16 spell_id, int32 value) {
    // Important variables:
    // value: the actual damage after resists, passed from Mob::SpellEffect
    // modifier: modifier to damage (from spells & focus effects?)
    // ratio: % of the modifier to apply (from AAs & natural bonus?)
    // chance: critital chance %

    int32 modifier = 100;
    int16 spell_dmg = 0;


    //Dunno if this makes sense:
    if (spells[spell_id].resisttype > 0)
        modifier += GetFocusEffect((focusType)(0-spells[spell_id].resisttype), spell_id);


    int tt = spells[spell_id].targettype;
    if (tt == ST_UndeadAE || tt == ST_Undead || tt == ST_Summoned) {
        //undead/summoned spells
        modifier += GetFocusEffect(focusImprovedUndeadDamage, spell_id);
    } else {
        //damage spells.
        modifier += GetFocusEffect(focusImprovedDamage, spell_id);
        modifier += GetFocusEffect(focusSpellEffectiveness, spell_id);
        modifier += GetFocusEffect(focusImprovedDamage2, spell_id);
    }

    // Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
    if ( spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) {
        if (this->GetLevel() > 40)
            value -= (this->GetLevel() - 40) * 20;
    }

    //This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch.
    if (spell_id == SPELL_IMP_HARM_TOUCH) {  //Improved Harm Touch
        value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
    }

    // This adds the extra damage for the AA's Consumption of the Soul and Improved Consumption of the Soul, 200 per level to the AA Leech Curse for Shadowknights.
    if (spell_id == SPELL_LEECH_TOUCH) {   //Leech Touch
        value -= GetAA(aaConsumptionoftheSoul) * 200; //Consumption of the Soul
        value -= GetAA(aaImprovedConsumptionofSoul) * 200; //Improved Consumption of the Soul
    }

    //spell crits, dont make sense if cast on self.
    if(tt != ST_Self) {
        // item SpellDmg bonus
        // Formula = SpellDmg * (casttime + recastime) / 7; Cant trigger off spell less than 5 levels below and cant cause more dmg than the spell itself.
        if(this->itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) {
            spell_dmg = this->itembonuses.SpellDmg * (spells[spell_id].cast_time + spells[spell_id].recast_time) / 7000;
            if(spell_dmg > -value)
                spell_dmg = -value;
        }

        // Spell-based SpellDmg adds directly but it restricted by focuses.
        spell_dmg += Additional_SpellDmg(spell_id);

        int chance = RuleI(Spells, BaseCritChance);
        int32 ratio = RuleI(Spells, BaseCritRatio);

        chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
        ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease;

        if(GetClass() == WIZARD) {
            if (GetLevel() >= RuleI(Spells, WizCritLevel)) {
                chance += RuleI(Spells, WizCritChance);
                ratio += RuleI(Spells, WizCritRatio);
            }
            if(aabonuses.SpellCritDmgIncrease > 0) // wizards get an additional bonus
                ratio +=  aabonuses.SpellCritDmgIncrease * 1.5; //108%, 115%, 124%, close to Graffe's 207%, 215%, & 225%
        }

        //Improved Harm Touch is a guaranteed crit if you have at least one level of SCF.
        if (spell_id == SPELL_IMP_HARM_TOUCH) {
            if ( (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0) )
                chance = 100;
        }

        /*
        //Handled in aa_effects will focus spells from 'spellgroup=99'. (SK life tap from buff procs)
        //If you are using an older spell file table (Pre SOF)...
        //Use SQL optional_EnableSoulAbrasionAA to update your spells table to properly use the effect.
        //If you do not want to update your table then you may want to enable this.
        if(tt == ST_Tap) {
        	if(spells[spell_id].classes[SHADOWKNIGHT-1] >= 254 && spell_id != SPELL_LEECH_TOUCH){
        		if(ratio < 100)	//chance increase and ratio are made up, not confirmed
        			ratio = 100;

        		switch (GetAA(aaSoulAbrasion))
        		{
        		case 1:
        			modifier += 100;
        			break;
        		case 2:
        			modifier += 200;
        			break;
        		case 3:
        			modifier += 300;
        			break;
        		}
        	}
        }
        */

        if (chance > 0) {
            mlog(SPELLS__CRITS, "Attempting spell crit. Spell: %s (%d), Value: %d, Modifier: %d, Chance: %d, Ratio: %d", spells[spell_id].name, spell_id, value, modifier, chance, ratio);
            if(MakeRandomInt(0,100) <= chance) {
                modifier += modifier*ratio/100;
                spell_dmg *= 2;
                mlog(SPELLS__CRITS, "Spell crit successful. Final damage modifier: %d, Final Damage: %d", modifier, (value * modifier / 100) - spell_dmg);
                entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s delivers a critical blast! (%d)", GetName(), (-value * modifier / 100) + spell_dmg);
            } else
                mlog(SPELLS__CRITS, "Spell crit failed. Final Damage Modifier: %d, Final Damage: %d", modifier, (value * modifier / 100) - spell_dmg);
        }
    }

    return ((value * modifier / 100) - spell_dmg);
}
Beispiel #20
0
uint16 Mob::GetInstrumentMod(uint16 spell_id) const {
	if(GetClass() != BARD)
		return(10);
	
	uint16 effectmod = 10;
	
	//this should never use spell modifiers...
	//if a spell grants better modifers, they are copied into the item mods
	//because the spells are supposed to act just like having the intrument.
	
	//item mods are in 10ths of percent increases
	switch(spells[spell_id].skill) {
		case PERCUSSION_INSTRUMENTS:
			if(itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0)
				effectmod = 10;
			else if(GetSkill(PERCUSSION_INSTRUMENTS) == 0)
				effectmod = 10;
			else if(itembonuses.percussionMod > spellbonuses.percussionMod)
				effectmod = itembonuses.percussionMod;
			else
				effectmod = spellbonuses.percussionMod;
			break;
		case STRINGED_INSTRUMENTS:
			if(itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0)
				effectmod = 10;
			else if(GetSkill(STRINGED_INSTRUMENTS) == 0)
				effectmod = 10;
			else if(itembonuses.stringedMod > spellbonuses.stringedMod)
				effectmod = itembonuses.stringedMod;
			else
				effectmod = spellbonuses.stringedMod;
			break;
		case WIND_INSTRUMENTS:
			if(itembonuses.windMod == 0 && spellbonuses.windMod == 0)
				effectmod = 10;
			else if(GetSkill(WIND_INSTRUMENTS) == 0)
				effectmod = 10;
			else if(itembonuses.windMod > spellbonuses.windMod)
				effectmod = itembonuses.windMod;
			else
				effectmod = spellbonuses.windMod;
			break;
		case BRASS_INSTRUMENTS:
			if(itembonuses.brassMod == 0 && spellbonuses.brassMod == 0)
				effectmod = 10;
			else if(GetSkill(BRASS_INSTRUMENTS) == 0)
				effectmod = 10;
			else if(itembonuses.brassMod > spellbonuses.brassMod)
				effectmod = itembonuses.brassMod;
			else
				effectmod = spellbonuses.brassMod;
			break;
		case SINGING:
			if(itembonuses.singingMod == 0 && spellbonuses.singingMod == 0)
				effectmod = 10;
			else if(itembonuses.singingMod > spellbonuses.singingMod)
				effectmod = itembonuses.singingMod;
			else
				effectmod = spellbonuses.singingMod;
			break;
		default:
			effectmod = 10;
			break;
	}
	
	if(spells[spell_id].skill == SINGING)
	{
		effectmod += 2*GetAA(aaSingingMastery);
		effectmod += 2*GetAA(aaImprovedSingingMastery);
	}
	else
	{
		effectmod += 2*GetAA(aaInstrumentMastery);
		effectmod += 2*GetAA(aaImprovedInstrumentMastery);
	}
	effectmod += 2*GetAA(aaAyonaesTutelage); //singing & instruments
	effectmod += 2*GetAA(aaEchoofTaelosia); //singing & instruments


	if(effectmod < 10)
		effectmod = 10;
	
	_log(SPELLS__BARDS, "%s::GetInstrumentMod() spell=%d mod=%d\n", GetName(), spell_id, effectmod);
	
	return(effectmod);
}