mirror of
https://github.com/EQEmu/Server.git
synced 2026-09-04 18:46:37 +00:00
Merge from master
This commit is contained in:
+144
-2
@@ -3516,7 +3516,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
if(spell_id == SPELL_UNKNOWN) {
|
||||
damage = ReduceDamage(damage);
|
||||
mlog(COMBAT__HITS, "Melee Damage reduced to %d", damage);
|
||||
ReduceAllDamage(damage);
|
||||
damage = ReduceAllDamage(damage);
|
||||
TryTriggerThreshHold(damage, SE_TriggerMeleeThreshold, attacker);
|
||||
} else {
|
||||
int32 origdmg = damage;
|
||||
@@ -3529,7 +3529,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
//Kayen: Probably need to add a filter for this - Not sure if this msg is correct but there should be a message for spell negate/runes.
|
||||
Message(263, "%s tries to cast on YOU, but YOUR magical skin absorbs the spell.",attacker->GetCleanName());
|
||||
}
|
||||
ReduceAllDamage(damage);
|
||||
damage = ReduceAllDamage(damage);
|
||||
TryTriggerThreshHold(damage, SE_TriggerSpellThreshold, attacker);
|
||||
}
|
||||
|
||||
@@ -4803,3 +4803,145 @@ void Mob::CommonBreakInvisible()
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
}
|
||||
|
||||
void Mob::SetAttackTimer()
|
||||
{
|
||||
attack_timer.SetAtTrigger(4000, true);
|
||||
}
|
||||
|
||||
void Client::SetAttackTimer()
|
||||
{
|
||||
float PermaHaste = GetPermaHaste();
|
||||
|
||||
//default value for attack timer in case they have
|
||||
//an invalid weapon equipped:
|
||||
attack_timer.SetAtTrigger(4000, true);
|
||||
|
||||
Timer *TimerToUse = nullptr;
|
||||
const Item_Struct *PrimaryWeapon = nullptr;
|
||||
|
||||
for (int i = MainRange; i <= MainSecondary; i++) {
|
||||
//pick a timer
|
||||
if (i == MainPrimary)
|
||||
TimerToUse = &attack_timer;
|
||||
else if (i == MainRange)
|
||||
TimerToUse = &ranged_timer;
|
||||
else if (i == MainSecondary)
|
||||
TimerToUse = &attack_dw_timer;
|
||||
else //invalid slot (hands will always hit this)
|
||||
continue;
|
||||
|
||||
const Item_Struct *ItemToUse = nullptr;
|
||||
|
||||
//find our item
|
||||
ItemInst *ci = GetInv().GetItem(i);
|
||||
if (ci)
|
||||
ItemToUse = ci->GetItem();
|
||||
|
||||
//special offhand stuff
|
||||
if (i == MainSecondary) {
|
||||
//if we have a 2H weapon in our main hand, no dual
|
||||
if (PrimaryWeapon != nullptr) {
|
||||
if (PrimaryWeapon->ItemClass == ItemClassCommon
|
||||
&& (PrimaryWeapon->ItemType == ItemType2HSlash
|
||||
|| PrimaryWeapon->ItemType == ItemType2HBlunt
|
||||
|| PrimaryWeapon->ItemType == ItemType2HPiercing)) {
|
||||
attack_dw_timer.Disable();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//if we cant dual wield, skip it
|
||||
if (!CanThisClassDualWield()) {
|
||||
attack_dw_timer.Disable();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//see if we have a valid weapon
|
||||
if (ItemToUse != nullptr) {
|
||||
//check type and damage/delay
|
||||
if (ItemToUse->ItemClass != ItemClassCommon
|
||||
|| ItemToUse->Damage == 0
|
||||
|| ItemToUse->Delay == 0) {
|
||||
//no weapon
|
||||
ItemToUse = nullptr;
|
||||
}
|
||||
// Check to see if skill is valid
|
||||
else if ((ItemToUse->ItemType > ItemTypeLargeThrowing) &&
|
||||
(ItemToUse->ItemType != ItemTypeMartial) &&
|
||||
(ItemToUse->ItemType != ItemType2HPiercing)) {
|
||||
//no weapon
|
||||
ItemToUse = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99);
|
||||
int speed = 0;
|
||||
|
||||
//if we have no weapon..
|
||||
if (ItemToUse == nullptr) {
|
||||
//above checks ensure ranged weapons do not fall into here
|
||||
// Work out if we're a monk
|
||||
if ((GetClass() == MONK) || (GetClass() == BEASTLORD))
|
||||
speed = static_cast<int>((GetMonkHandToHandDelay() * (100 + DelayMod) / 100) * PermaHaste);
|
||||
else
|
||||
speed = static_cast<int>((36 * (100 + DelayMod) / 100) * PermaHaste);
|
||||
} else {
|
||||
//we have a weapon, use its delay
|
||||
// Convert weapon delay to timer resolution (milliseconds)
|
||||
//delay * 100
|
||||
speed = static_cast<int>((ItemToUse->Delay * (100 + DelayMod) / 100) * PermaHaste);
|
||||
if (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing) {
|
||||
float quiver_haste = GetQuiverHaste();
|
||||
if (quiver_haste > 0)
|
||||
speed *= quiver_haste;
|
||||
}
|
||||
}
|
||||
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
|
||||
|
||||
if (i == MainPrimary)
|
||||
PrimaryWeapon = ItemToUse;
|
||||
}
|
||||
}
|
||||
|
||||
void NPC::SetAttackTimer()
|
||||
{
|
||||
float PermaHaste = GetPermaHaste();
|
||||
|
||||
//default value for attack timer in case they have
|
||||
//an invalid weapon equipped:
|
||||
attack_timer.SetAtTrigger(4000, true);
|
||||
|
||||
Timer *TimerToUse = nullptr;
|
||||
|
||||
for (int i = MainRange; i <= MainSecondary; i++) {
|
||||
//pick a timer
|
||||
if (i == MainPrimary)
|
||||
TimerToUse = &attack_timer;
|
||||
else if (i == MainRange)
|
||||
TimerToUse = &ranged_timer;
|
||||
else if (i == MainSecondary)
|
||||
TimerToUse = &attack_dw_timer;
|
||||
else //invalid slot (hands will always hit this)
|
||||
continue;
|
||||
|
||||
//special offhand stuff
|
||||
if (i == MainSecondary) {
|
||||
//NPCs get it for free at 13
|
||||
if(GetLevel() < 13) {
|
||||
attack_dw_timer.Disable();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99);
|
||||
|
||||
// Technically NPCs should do some logic for weapons, but the effect is minimal
|
||||
// What they do is take the lower of their set delay and the weapon's
|
||||
// ex. Mob's delay set to 20, weapon set to 19, delay 19
|
||||
// Mob's delay set to 20, weapon set to 21, delay 20
|
||||
int speed = static_cast<int>((attack_delay * (100 + DelayMod) / 100) * PermaHaste);
|
||||
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-8
@@ -8350,12 +8350,10 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
|
||||
return;
|
||||
|
||||
float HasteModifier = 0;
|
||||
if(GetHaste() >= 0){
|
||||
if (GetHaste())
|
||||
HasteModifier = 10000 / (100 + GetHaste());
|
||||
}
|
||||
else {
|
||||
HasteModifier = (100 - GetHaste());
|
||||
}
|
||||
else
|
||||
HasteModifier = 100;
|
||||
int32 dmg = 0;
|
||||
|
||||
uint16 skill_to_use = -1;
|
||||
@@ -8981,10 +8979,8 @@ int32 Bot::CalcMaxMana() {
|
||||
|
||||
void Bot::SetAttackTimer() {
|
||||
float PermaHaste;
|
||||
if(GetHaste() > 0)
|
||||
if (GetHaste())
|
||||
PermaHaste = 1 / (1 + (float)GetHaste()/100);
|
||||
else if(GetHaste() < 0)
|
||||
PermaHaste = 1 * (1 - (float)GetHaste()/100);
|
||||
else
|
||||
PermaHaste = 1.0f;
|
||||
|
||||
|
||||
+42
-42
@@ -3949,54 +3949,38 @@ void Client::SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const
|
||||
|
||||
void Client::KeyRingLoad()
|
||||
{
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
query = new char[256];
|
||||
|
||||
sprintf(query, "SELECT item_id FROM keyring WHERE char_id='%i' ORDER BY item_id",character_id);
|
||||
if (database.RunQuery(query, strlen(query), errbuf, &result))
|
||||
{
|
||||
safe_delete_array(query);
|
||||
while(0 != (row = mysql_fetch_row(result))){
|
||||
keyring.push_back(atoi(row[0]));
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}else {
|
||||
std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << errbuf << std::endl;
|
||||
safe_delete_array(query);
|
||||
std::string query = StringFormat("SELECT item_id FROM keyring "
|
||||
"WHERE char_id = '%i' ORDER BY item_id", character_id);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << results.ErrorMessage() << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row)
|
||||
keyring.push_back(atoi(row[0]));
|
||||
|
||||
}
|
||||
|
||||
void Client::KeyRingAdd(uint32 item_id)
|
||||
{
|
||||
if(0==item_id)return;
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
uint32 affected_rows = 0;
|
||||
query = new char[256];
|
||||
bool bFound = KeyRingCheck(item_id);
|
||||
if(!bFound){
|
||||
sprintf(query, "INSERT INTO keyring(char_id,item_id) VALUES(%i,%i)",character_id,item_id);
|
||||
if(database.RunQuery(query, strlen(query), errbuf, 0, &affected_rows)) {
|
||||
Message(4,"Added to keyring.");
|
||||
if(0==item_id)
|
||||
return;
|
||||
|
||||
/* QS: PlayerLogKeyringAddition */
|
||||
if (RuleB(QueryServ, PlayerLogKeyringAddition)){
|
||||
std::string event_desc = StringFormat("itemid:%i in zoneid:%i instid:%i", item_id, this->GetZoneID(), this->GetInstanceID());
|
||||
QServ->PlayerLogEvent(Player_Log_Keyring_Addition, this->CharacterID(), event_desc);
|
||||
}
|
||||
safe_delete_array(query);
|
||||
}
|
||||
else {
|
||||
std::cerr << "Error in Doors::HandleClick query '" << query << "' " << errbuf << std::endl;
|
||||
safe_delete_array(query);
|
||||
return;
|
||||
}
|
||||
keyring.push_back(item_id);
|
||||
}
|
||||
bool found = KeyRingCheck(item_id);
|
||||
if (found)
|
||||
return;
|
||||
|
||||
std::string query = StringFormat("INSERT INTO keyring(char_id, item_id) VALUES(%i, %i)", character_id, item_id);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
std::cerr << "Error in Doors::HandleClick query '" << query << "' " << results.ErrorMessage() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
Message(4,"Added to keyring.");
|
||||
|
||||
keyring.push_back(item_id);
|
||||
}
|
||||
|
||||
bool Client::KeyRingCheck(uint32 item_id)
|
||||
@@ -8282,3 +8266,19 @@ void Client::ShowNumHits()
|
||||
return;
|
||||
}
|
||||
|
||||
float Client::GetQuiverHaste()
|
||||
{
|
||||
float quiver_haste = 0;
|
||||
for (int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++) {
|
||||
const ItemInst *pi = GetInv().GetItem(r);
|
||||
if (!pi)
|
||||
continue;
|
||||
if (pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver) {
|
||||
float temp_wr = (pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv));
|
||||
quiver_haste = std::max(temp_wr, quiver_haste);
|
||||
}
|
||||
}
|
||||
if (quiver_haste > 0)
|
||||
quiver_haste = 1.0f / (1.0f + static_cast<float>(quiver_haste) / 100.0f);
|
||||
return quiver_haste;
|
||||
}
|
||||
|
||||
+5
-3
@@ -218,6 +218,8 @@ public:
|
||||
virtual Group* GetGroup() { return entity_list.GetGroupByClient(this); }
|
||||
virtual inline bool IsBerserk() { return berserk; }
|
||||
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
|
||||
virtual void SetAttackTimer();
|
||||
float GetQuiverHaste();
|
||||
|
||||
void AI_Init();
|
||||
void AI_Start(uint32 iMoveDelay = 0);
|
||||
@@ -1193,9 +1195,9 @@ public:
|
||||
int32 mod_client_xp(int32 in_exp, NPC *npc);
|
||||
uint32 mod_client_xp_for_level(uint32 xp, uint16 check_level);
|
||||
int mod_client_haste_cap(int cap);
|
||||
int mod_consume(Item_Struct *item, ItemUseTypes type, int change);
|
||||
int mod_food_value(const Item_Struct *item, int change);
|
||||
int mod_drink_value(const Item_Struct *item, int change);
|
||||
int mod_consume(Item_Struct *item, ItemUseTypes type, int change);
|
||||
int mod_food_value(const Item_Struct *item, int change);
|
||||
int mod_drink_value(const Item_Struct *item, int change);
|
||||
|
||||
void SetEngagedRaidTarget(bool value) { EngagedRaidTarget = value; }
|
||||
bool GetEngagedRaidTarget() const { return EngagedRaidTarget; }
|
||||
|
||||
@@ -6781,7 +6781,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
|
||||
tobe_auged = user_inv.GetItem(slot_id);
|
||||
auged_with = user_inv.GetItem(MainCursor);
|
||||
|
||||
if(tobe_auged && auged_with)
|
||||
if (tobe_auged && auged_with)
|
||||
{
|
||||
if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) &&
|
||||
(tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots)))
|
||||
@@ -9432,11 +9432,17 @@ void Client::CompleteConnect() {
|
||||
//reapply some buffs
|
||||
uint32 buff_count = GetMaxTotalSlots();
|
||||
for (uint32 j1 = 0; j1 < buff_count; j1++) {
|
||||
if (buffs[j1].spellid >(uint32)SPDAT_RECORDS)
|
||||
if (!IsValidSpell(buffs[j1].spellid))
|
||||
continue;
|
||||
|
||||
const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid];
|
||||
|
||||
int NimbusEffect = GetNimbusEffect(buffs[j1].spellid);
|
||||
if(NimbusEffect) {
|
||||
if(!IsNimbusEffectActive(NimbusEffect))
|
||||
SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true);
|
||||
}
|
||||
|
||||
for (int x1 = 0; x1 < EFFECT_COUNT; x1++) {
|
||||
switch (spell.effectid[x1]) {
|
||||
case SE_IllusionCopy:
|
||||
|
||||
@@ -6555,6 +6555,7 @@ void command_npcedit(Client *c, const Seperator *sep)
|
||||
c->Message(0, "#npcedit qglobal - Sets an NPC's quest global flag");
|
||||
c->Message(0, "#npcedit limit - Sets an NPC's spawn limit counter");
|
||||
c->Message(0, "#npcedit Attackspeed - Sets an NPC's attack speed modifier");
|
||||
c->Message(0, "#npcedit Attackdelay - Sets an NPC's attack delay");
|
||||
c->Message(0, "#npcedit findable - Sets an NPC's findable flag");
|
||||
c->Message(0, "#npcedit wep1 - Sets an NPC's primary weapon model");
|
||||
c->Message(0, "#npcedit wep2 - Sets an NPC's secondary weapon model");
|
||||
@@ -7056,6 +7057,15 @@ void command_npcedit(Client *c, const Seperator *sep)
|
||||
c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
}
|
||||
else if ( strcasecmp( sep->arg[1], "Attackdelay" ) == 0 )
|
||||
{
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
c->Message(15,"NPCID %u now has attack_delay set to %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2]));
|
||||
database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set attack_delay=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf);
|
||||
c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
}
|
||||
else if ( strcasecmp( sep->arg[1], "findable" ) == 0 )
|
||||
{
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
|
||||
+10
-6
@@ -356,9 +356,11 @@ void Client::GoFish()
|
||||
inst = m_inv.GetItem(MainCursor);
|
||||
}
|
||||
|
||||
std::vector<EQEmu::Any> args;
|
||||
args.push_back(inst);
|
||||
parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args);
|
||||
if(inst) {
|
||||
std::vector<EQEmu::Any> args;
|
||||
args.push_back(inst);
|
||||
parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -470,9 +472,11 @@ void Client::ForageItem(bool guarantee) {
|
||||
inst = m_inv.GetItem(MainCursor);
|
||||
}
|
||||
|
||||
std::vector<EQEmu::Any> args;
|
||||
args.push_back(inst);
|
||||
parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst->GetID(), &args);
|
||||
if(inst) {
|
||||
std::vector<EQEmu::Any> args;
|
||||
args.push_back(inst);
|
||||
parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst->GetID(), &args);
|
||||
}
|
||||
}
|
||||
|
||||
int ChanceSecondForage = aabonuses.ForageAdditionalItems + itembonuses.ForageAdditionalItems + spellbonuses.ForageAdditionalItems;
|
||||
|
||||
@@ -561,6 +561,18 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender)
|
||||
}
|
||||
}
|
||||
|
||||
/* This may seem pointless but the case above does not cover the following situation:
|
||||
* Group has Leader a, member b, member c
|
||||
* b and c are out of zone
|
||||
* a disconnects/quits
|
||||
* b or c zone back in and disconnects/quits
|
||||
* a is still "leader" from GetLeader()'s perspective and will crash the zone when we DelMember(b)
|
||||
* Ultimately we should think up a better solution to this.
|
||||
*/
|
||||
if(oldmember == GetLeader()) {
|
||||
SetLeader(nullptr);
|
||||
}
|
||||
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct));
|
||||
ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer;
|
||||
gl->gid = GetID();
|
||||
|
||||
+22
-43
@@ -165,7 +165,7 @@ void Client::SendGuildSpawnAppearance() {
|
||||
switch (rank) {
|
||||
case 0: { rank = 5; break; } // GUILD_MEMBER 0
|
||||
case 1: { rank = 3; break; } // GUILD_OFFICER 1
|
||||
case 2: { rank = 1; break; } // GUILD_LEADER 2
|
||||
case 2: { rank = 1; break; } // GUILD_LEADER 2
|
||||
default: { break; } // GUILD_NONE
|
||||
}
|
||||
}
|
||||
@@ -417,57 +417,36 @@ void Client::GuildChangeRank(const char* name, uint32 guild_id, uint32 oldrank,
|
||||
}*/
|
||||
|
||||
|
||||
bool ZoneDatabase::CheckGuildDoor(uint8 doorid,uint16 guild_id,const char* zone) {
|
||||
MYSQL_ROW row;
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
MYSQL_RES *result;
|
||||
if (!RunQuery(query, MakeAnyLenString(&query,
|
||||
"SELECT guild FROM doors where doorid=%i AND zone='%s'",
|
||||
doorid-128, zone), errbuf, &result))
|
||||
{
|
||||
LogFile->write(EQEMuLog::Error, "Error in CheckGuildDoor query '%s': %s", query, errbuf);
|
||||
safe_delete_array(query);
|
||||
return false;
|
||||
} else {
|
||||
if (mysql_num_rows(result) == 1) {
|
||||
row = mysql_fetch_row(result);
|
||||
if (atoi(row[0]) == guild_id)
|
||||
{
|
||||
mysql_free_result(result);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mysql_free_result(result);
|
||||
return false;
|
||||
}
|
||||
bool ZoneDatabase::CheckGuildDoor(uint8 doorid, uint16 guild_id, const char* zone) {
|
||||
|
||||
// code below will never be reached
|
||||
mysql_free_result(result);
|
||||
return false;
|
||||
}
|
||||
std::string query = StringFormat("SELECT guild FROM doors WHERE doorid = %i AND zone = '%s'",
|
||||
doorid-128, zone);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Error in CheckGuildDoor query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
if (results.RowCount() != 1)
|
||||
return false;
|
||||
|
||||
auto row = results.begin();
|
||||
return atoi(row[0]) == guild_id;
|
||||
}
|
||||
|
||||
bool ZoneDatabase::SetGuildDoor(uint8 doorid,uint16 guild_id, const char* zone) {
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
uint32 affected_rows = 0;
|
||||
|
||||
if (doorid > 127)
|
||||
doorid = doorid - 128;
|
||||
if (!RunQuery(query, MakeAnyLenString(&query,
|
||||
"UPDATE doors SET guild = %i WHERE (doorid=%i) AND (zone='%s')",
|
||||
guild_id, doorid, zone), errbuf, 0,&affected_rows))
|
||||
{
|
||||
LogFile->write(EQEMuLog::Error, "Error in SetGuildDoor query '%s': %s", query, errbuf);
|
||||
safe_delete_array(query);
|
||||
|
||||
std::string query = StringFormat("UPDATE doors SET guild = %i WHERE (doorid=%i) AND (zone='%s')",
|
||||
guild_id, doorid, zone);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Error in SetGuildDoor query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
return(affected_rows > 0);
|
||||
return (results.RowsAffected() > 0);
|
||||
}
|
||||
|
||||
|
||||
+7
-15
@@ -850,24 +850,16 @@ bool GuildBankManager::AddItem(uint32 GuildID, uint8 Area, uint32 ItemID, int32
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *Query="INSERT INTO `guild_bank` (`guildid`, `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `WhoFor`) "
|
||||
"VALUES (%i, %i, %i, %i, %i, '%s', %i, '%s')";
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
|
||||
char* query = 0;
|
||||
|
||||
if(!database.RunQuery(query, MakeAnyLenString(&query, Query, GuildID, Area, Slot, ItemID, QtyOrCharges, Donator, Permissions, WhoFor), errbuf))
|
||||
{
|
||||
_log(GUILDS__BANK_ERROR, "Insert Error: %s : %s", query, errbuf);
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
std::string query = StringFormat("INSERT INTO `guild_bank` "
|
||||
"(`guildid`, `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `WhoFor`) "
|
||||
"VALUES (%i, %i, %i, %i, %i, '%s', %i, '%s')",
|
||||
GuildID, Area, Slot, ItemID, QtyOrCharges, Donator, Permissions, WhoFor);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(GUILDS__BANK_ERROR, "Insert Error: %s : %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
const Item_Struct *Item = database.GetItem(ItemID);
|
||||
|
||||
GuildBankItemUpdate_Struct gbius;
|
||||
|
||||
+13
-156
@@ -174,8 +174,9 @@ Mob::Mob(const char* in_name,
|
||||
drakkin_heritage = in_drakkin_heritage;
|
||||
drakkin_tattoo = in_drakkin_tattoo;
|
||||
drakkin_details = in_drakkin_details;
|
||||
attack_speed= 0;
|
||||
slow_mitigation= 0;
|
||||
attack_speed = 0;
|
||||
attack_delay = 0;
|
||||
slow_mitigation = 0;
|
||||
findable = false;
|
||||
trackable = true;
|
||||
has_shieldequiped = false;
|
||||
@@ -1940,159 +1941,6 @@ void Mob::Kill() {
|
||||
Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand);
|
||||
}
|
||||
|
||||
void Mob::SetAttackTimer() {
|
||||
float PermaHaste;
|
||||
if(GetHaste() > 0)
|
||||
PermaHaste = 1 / (1 + (float)GetHaste()/100);
|
||||
else if(GetHaste() < 0)
|
||||
PermaHaste = 1 * (1 - (float)GetHaste()/100);
|
||||
else
|
||||
PermaHaste = 1.0f;
|
||||
|
||||
//default value for attack timer in case they have
|
||||
//an invalid weapon equipped:
|
||||
attack_timer.SetAtTrigger(4000, true);
|
||||
|
||||
Timer* TimerToUse = nullptr;
|
||||
const Item_Struct* PrimaryWeapon = nullptr;
|
||||
|
||||
for (int i=MainRange; i<=MainSecondary; i++) {
|
||||
|
||||
//pick a timer
|
||||
if (i == MainPrimary)
|
||||
TimerToUse = &attack_timer;
|
||||
else if (i == MainRange)
|
||||
TimerToUse = &ranged_timer;
|
||||
else if(i == MainSecondary)
|
||||
TimerToUse = &attack_dw_timer;
|
||||
else //invalid slot (hands will always hit this)
|
||||
continue;
|
||||
|
||||
const Item_Struct* ItemToUse = nullptr;
|
||||
|
||||
//find our item
|
||||
if (IsClient()) {
|
||||
ItemInst* ci = CastToClient()->GetInv().GetItem(i);
|
||||
if (ci)
|
||||
ItemToUse = ci->GetItem();
|
||||
} else if(IsNPC())
|
||||
{
|
||||
//The code before here was fundementally flawed because equipment[]
|
||||
//isn't the same as PC inventory and also:
|
||||
//NPCs don't use weapon speed to dictate how fast they hit anyway.
|
||||
ItemToUse = nullptr;
|
||||
}
|
||||
|
||||
//special offhand stuff
|
||||
if(i == MainSecondary) {
|
||||
//if we have a 2H weapon in our main hand, no dual
|
||||
if(PrimaryWeapon != nullptr) {
|
||||
if( PrimaryWeapon->ItemClass == ItemClassCommon
|
||||
&& (PrimaryWeapon->ItemType == ItemType2HSlash
|
||||
|| PrimaryWeapon->ItemType == ItemType2HBlunt
|
||||
|| PrimaryWeapon->ItemType == ItemType2HPiercing)) {
|
||||
attack_dw_timer.Disable();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//clients must have the skill to use it...
|
||||
if(IsClient()) {
|
||||
//if we cant dual wield, skip it
|
||||
if (!CanThisClassDualWield()) {
|
||||
attack_dw_timer.Disable();
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
//NPCs get it for free at 13
|
||||
if(GetLevel() < 13) {
|
||||
attack_dw_timer.Disable();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//see if we have a valid weapon
|
||||
if(ItemToUse != nullptr) {
|
||||
//check type and damage/delay
|
||||
if(ItemToUse->ItemClass != ItemClassCommon
|
||||
|| ItemToUse->Damage == 0
|
||||
|| ItemToUse->Delay == 0) {
|
||||
//no weapon
|
||||
ItemToUse = nullptr;
|
||||
}
|
||||
// Check to see if skill is valid
|
||||
else if((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) {
|
||||
//no weapon
|
||||
ItemToUse = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int16 DelayMod = itembonuses.HundredHands + spellbonuses.HundredHands;
|
||||
if (DelayMod < -99)
|
||||
DelayMod = -99;
|
||||
|
||||
//if we have no weapon..
|
||||
if (ItemToUse == nullptr) {
|
||||
//above checks ensure ranged weapons do not fall into here
|
||||
// Work out if we're a monk
|
||||
if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) {
|
||||
//we are a monk, use special delay
|
||||
int speed = (int)( (GetMonkHandToHandDelay()*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
|
||||
// 1200 seemed too much, with delay 10 weapons available
|
||||
if(speed < RuleI(Combat, MinHastedDelay)) //lower bound
|
||||
speed = RuleI(Combat, MinHastedDelay);
|
||||
TimerToUse->SetAtTrigger(speed, true); // Hand to hand, delay based on level or epic
|
||||
} else {
|
||||
//not a monk... using fist, regular delay
|
||||
int speed = (int)((36 *(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
|
||||
if(speed < RuleI(Combat, MinHastedDelay) && IsClient()) //lower bound
|
||||
speed = RuleI(Combat, MinHastedDelay);
|
||||
TimerToUse->SetAtTrigger(speed, true); // Hand to hand, non-monk 2/36
|
||||
}
|
||||
} else {
|
||||
//we have a weapon, use its delay
|
||||
// Convert weapon delay to timer resolution (milliseconds)
|
||||
//delay * 100
|
||||
int speed = (int)((ItemToUse->Delay*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
|
||||
if(speed < RuleI(Combat, MinHastedDelay))
|
||||
speed = RuleI(Combat, MinHastedDelay);
|
||||
|
||||
if(ItemToUse && (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing))
|
||||
{
|
||||
if(IsClient())
|
||||
{
|
||||
float max_quiver = 0;
|
||||
for(int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++)
|
||||
{
|
||||
const ItemInst *pi = CastToClient()->GetInv().GetItem(r);
|
||||
if(!pi)
|
||||
continue;
|
||||
if(pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver)
|
||||
{
|
||||
float temp_wr = ( pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv) );
|
||||
if(temp_wr > max_quiver)
|
||||
{
|
||||
max_quiver = temp_wr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(max_quiver > 0)
|
||||
{
|
||||
float quiver_haste = 1 / (1 + max_quiver / 100);
|
||||
speed *= quiver_haste;
|
||||
}
|
||||
}
|
||||
}
|
||||
TimerToUse->SetAtTrigger(speed, true);
|
||||
}
|
||||
|
||||
if(i == MainPrimary)
|
||||
PrimaryWeapon = ItemToUse;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Mob::CanThisClassDualWield(void) const {
|
||||
if(!IsClient()) {
|
||||
return(GetSkill(SkillDualWield) > 0);
|
||||
@@ -4528,6 +4376,15 @@ void Mob::SpreadVirus(uint16 spell_id, uint16 casterID)
|
||||
|
||||
void Mob::RemoveNimbusEffect(int effectid)
|
||||
{
|
||||
if (effectid == nimbus_effect1)
|
||||
nimbus_effect1 = 0;
|
||||
|
||||
else if (effectid == nimbus_effect2)
|
||||
nimbus_effect2 = 0;
|
||||
|
||||
else if (effectid == nimbus_effect3)
|
||||
nimbus_effect3 = 0;
|
||||
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RemoveNimbusEffect, sizeof(RemoveNimbusEffect_Struct));
|
||||
RemoveNimbusEffect_Struct* rne = (RemoveNimbusEffect_Struct*)outapp->pBuffer;
|
||||
rne->spawnid = GetID();
|
||||
@@ -5068,7 +4925,7 @@ void Mob::ClearSpecialAbilities() {
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::ProcessSpecialAbilities(const std::string str) {
|
||||
void Mob::ProcessSpecialAbilities(const std::string &str) {
|
||||
ClearSpecialAbilities();
|
||||
|
||||
std::vector<std::string> sp = SplitString(str, '^');
|
||||
|
||||
+3
-1
@@ -684,6 +684,7 @@ public:
|
||||
inline bool GetInvul(void) { return invulnerable; }
|
||||
inline void SetExtraHaste(int Haste) { ExtraHaste = Haste; }
|
||||
virtual int GetHaste();
|
||||
inline float GetPermaHaste() { return GetHaste() ? 100.0f / (1.0f + static_cast<float>(GetHaste()) / 100.0f) : 100.0f; }
|
||||
|
||||
uint8 GetWeaponDamageBonus(const Item_Struct* Weapon);
|
||||
uint16 GetDamageTable(SkillUseTypes skillinuse);
|
||||
@@ -842,7 +843,7 @@ public:
|
||||
void StopSpecialAbilityTimer(int ability);
|
||||
Timer *GetSpecialAbilityTimer(int ability);
|
||||
void ClearSpecialAbilities();
|
||||
void ProcessSpecialAbilities(const std::string str);
|
||||
void ProcessSpecialAbilities(const std::string &str);
|
||||
|
||||
Shielders_Struct shielder[MAX_SHIELDERS];
|
||||
Trade* trade;
|
||||
@@ -1052,6 +1053,7 @@ protected:
|
||||
Timer attack_dw_timer;
|
||||
Timer ranged_timer;
|
||||
float attack_speed; //% increase/decrease in attack speed (not haste)
|
||||
int8 attack_delay; //delay between attacks in 10ths of seconds
|
||||
float slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%)
|
||||
Timer tic_timer;
|
||||
Timer mana_timer;
|
||||
|
||||
@@ -248,6 +248,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
||||
delaytimer = false;
|
||||
combat_event = false;
|
||||
attack_speed = d->attack_speed;
|
||||
attack_delay = d->attack_delay;
|
||||
slow_mitigation = d->slow_mitigation;
|
||||
|
||||
EntityList::RemoveNumbers(name);
|
||||
@@ -935,6 +936,8 @@ NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z,
|
||||
npc_type->WIS = 150;
|
||||
npc_type->CHA = 150;
|
||||
|
||||
npc_type->attack_delay = 30;
|
||||
|
||||
npc_type->prim_melee_type = 28;
|
||||
npc_type->sec_melee_type = 28;
|
||||
|
||||
|
||||
+8
-8
@@ -134,7 +134,6 @@ public:
|
||||
void CalcNPCRegen();
|
||||
void CalcNPCDamage();
|
||||
|
||||
|
||||
int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr);
|
||||
int32 GetActSpellHealing(uint16 spell_id, int32 value, Mob* target = nullptr);
|
||||
inline void SetSpellFocusDMG(int32 NewSpellFocusDMG) {SpellFocusDMG = NewSpellFocusDMG;}
|
||||
@@ -158,6 +157,7 @@ public:
|
||||
virtual void InitializeBuffSlots();
|
||||
virtual void UninitializeBuffSlots();
|
||||
|
||||
virtual void SetAttackTimer();
|
||||
virtual void RangedAttack(Mob* other);
|
||||
virtual void ThrowingAttack(Mob* other) { }
|
||||
int32 GetNumberOfAttacks() const { return attack_count; }
|
||||
@@ -388,16 +388,16 @@ public:
|
||||
inline void SetHealScale(float amt) { healscale = amt; }
|
||||
inline float GetHealScale() { return healscale; }
|
||||
|
||||
uint32 GetSpawnKillCount();
|
||||
int GetScore();
|
||||
void SetMerchantProbability(uint8 amt) { probability = amt; }
|
||||
uint32 GetSpawnKillCount();
|
||||
int GetScore();
|
||||
void SetMerchantProbability(uint8 amt) { probability = amt; }
|
||||
uint8 GetMerchantProbability() { return probability; }
|
||||
void mod_prespawn(Spawn2 *sp);
|
||||
int mod_npc_damage(int damage, SkillUseTypes skillinuse, int hand, const Item_Struct* weapon, Mob* other);
|
||||
void mod_prespawn(Spawn2 *sp);
|
||||
int mod_npc_damage(int damage, SkillUseTypes skillinuse, int hand, const Item_Struct* weapon, Mob* other);
|
||||
void mod_npc_killed_merit(Mob* c);
|
||||
void mod_npc_killed(Mob* oos);
|
||||
void AISpellsList(Client *c);
|
||||
|
||||
void AISpellsList(Client *c);
|
||||
|
||||
bool IsRaidTarget() const { return raid_target; };
|
||||
|
||||
protected:
|
||||
|
||||
+6
-7
@@ -165,13 +165,12 @@ void QGlobalCache::LoadByGlobalContext()
|
||||
LoadBy(query);
|
||||
}
|
||||
|
||||
void QGlobalCache::LoadBy(const std::string query)
|
||||
void QGlobalCache::LoadBy(const std::string &query)
|
||||
{
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return;
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row)
|
||||
AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]? atoi(row[5]): 0xFFFFFFFF));
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return;
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row)
|
||||
AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]? atoi(row[5]): 0xFFFFFFFF));
|
||||
}
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ public:
|
||||
void LoadByZoneID(uint32 zoneID); //zone
|
||||
void LoadByGlobalContext(); //zone
|
||||
protected:
|
||||
void LoadBy(const std::string query);
|
||||
void LoadBy(const std::string &query);
|
||||
std::list<QGlobal> qGlobalBucket;
|
||||
};
|
||||
|
||||
|
||||
@@ -1413,10 +1413,8 @@ void NPC::DoClassAttacks(Mob *target) {
|
||||
return;
|
||||
|
||||
float HasteModifier = 0;
|
||||
if(GetHaste() > 0)
|
||||
if (GetHaste())
|
||||
HasteModifier = 10000 / (100 + GetHaste());
|
||||
else if(GetHaste() < 0)
|
||||
HasteModifier = (100 - GetHaste());
|
||||
else
|
||||
HasteModifier = 100;
|
||||
|
||||
@@ -1464,7 +1462,7 @@ void NPC::DoClassAttacks(Mob *target) {
|
||||
}
|
||||
}
|
||||
|
||||
reuse = KickReuseTime * 1000;
|
||||
reuse = (KickReuseTime + 3) * 1000;
|
||||
DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse);
|
||||
did_attack = true;
|
||||
}
|
||||
@@ -1484,7 +1482,7 @@ void NPC::DoClassAttacks(Mob *target) {
|
||||
}
|
||||
}
|
||||
|
||||
reuse = BashReuseTime * 1000;
|
||||
reuse = (BashReuseTime + 3) * 1000;
|
||||
DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse);
|
||||
did_attack = true;
|
||||
}
|
||||
@@ -1537,7 +1535,7 @@ void NPC::DoClassAttacks(Mob *target) {
|
||||
}
|
||||
}
|
||||
|
||||
reuse = KickReuseTime * 1000;
|
||||
reuse = (KickReuseTime + 3) * 1000;
|
||||
DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse);
|
||||
did_attack = true;
|
||||
}
|
||||
@@ -1562,7 +1560,7 @@ void NPC::DoClassAttacks(Mob *target) {
|
||||
}
|
||||
}
|
||||
|
||||
reuse = BashReuseTime * 1000;
|
||||
reuse = (BashReuseTime + 3) * 1000;
|
||||
DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse);
|
||||
did_attack = true;
|
||||
}
|
||||
|
||||
+117
-11
@@ -202,7 +202,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
effect_value = GetMaxHP();
|
||||
|
||||
if (GetSpellPowerDistanceMod())
|
||||
effect_value = effect_value*(GetSpellPowerDistanceMod()/100);
|
||||
effect_value = effect_value*GetSpellPowerDistanceMod()/100;
|
||||
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
effect_desc[0] = 0;
|
||||
@@ -3127,26 +3127,42 @@ snare has both of them negative, yet their range should work the same:
|
||||
|
||||
case 110: // confirmed 2/6/04
|
||||
//is there a reason we dont use updownsign here???
|
||||
result = ubase + (caster_level / 5); break;
|
||||
result = ubase + (caster_level / 6);
|
||||
break;
|
||||
|
||||
case 111:
|
||||
result = updownsign * (ubase + 6 * (caster_level - GetMinLevel(spell_id))); break;
|
||||
result = updownsign * (ubase + 6 * (caster_level - 16));
|
||||
break;
|
||||
case 112:
|
||||
result = updownsign * (ubase + 8 * (caster_level - GetMinLevel(spell_id))); break;
|
||||
result = updownsign * (ubase + 8 * (caster_level - 24));
|
||||
break;
|
||||
case 113:
|
||||
result = updownsign * (ubase + 10 * (caster_level - GetMinLevel(spell_id))); break;
|
||||
result = updownsign * (ubase + 10 * (caster_level - 34));
|
||||
break;
|
||||
case 114:
|
||||
result = updownsign * (ubase + 15 * (caster_level - GetMinLevel(spell_id))); break;
|
||||
result = updownsign * (ubase + 15 * (caster_level - 44));
|
||||
break;
|
||||
|
||||
//these formula were updated according to lucy 10/16/04
|
||||
case 115: // this is only in symbol of transal
|
||||
result = ubase + 6 * (caster_level - GetMinLevel(spell_id)); break;
|
||||
result = ubase;
|
||||
if (caster_level > 15)
|
||||
result += 7 * (caster_level - 15);
|
||||
break;
|
||||
case 116: // this is only in symbol of ryltan
|
||||
result = ubase + 8 * (caster_level - GetMinLevel(spell_id)); break;
|
||||
result = ubase;
|
||||
if (caster_level > 24)
|
||||
result += 10 * (caster_level - 24);
|
||||
break;
|
||||
case 117: // this is only in symbol of pinzarn
|
||||
result = ubase + 12 * (caster_level - GetMinLevel(spell_id)); break;
|
||||
result = ubase;
|
||||
if (caster_level > 34)
|
||||
result += 13 * (caster_level - 34);
|
||||
break;
|
||||
case 118: // used in naltron and a few others
|
||||
result = ubase + 20 * (caster_level - GetMinLevel(spell_id)); break;
|
||||
result = ubase;
|
||||
if (caster_level > 44)
|
||||
result += 20 * (caster_level - 44);
|
||||
break;
|
||||
|
||||
case 119: // confirmed 2/6/04
|
||||
result = ubase + (caster_level / 8); break;
|
||||
@@ -3166,6 +3182,93 @@ snare has both of them negative, yet their range should work the same:
|
||||
result = MakeRandomInt(ubase, abs(max));
|
||||
break;
|
||||
|
||||
case 124: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += caster_level - 50;
|
||||
break;
|
||||
|
||||
case 125: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 2 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 126: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 3 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 127: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 4 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 128: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 5 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 129: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 10 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 130: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 15 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 131: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 20 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 132: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 25 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 137: // used in berserker AA desperation
|
||||
result = ubase - (ubase * (GetHPRatio() / 100.0f));
|
||||
break;
|
||||
|
||||
case 138: { // unused on live?
|
||||
int maxhps = GetMaxHP() / 2;
|
||||
if (GetHP() <= maxhps)
|
||||
result = -(ubase * GetHP() / maxhps);
|
||||
else
|
||||
result = -ubase;
|
||||
break;
|
||||
}
|
||||
|
||||
case 139: // check sign
|
||||
result = ubase + (caster_level > 30 ? (caster_level - 30) / 2 : 0);
|
||||
break;
|
||||
|
||||
case 140: // check sign
|
||||
result = ubase + (caster_level > 30 ? caster_level - 30 : 0);
|
||||
break;
|
||||
|
||||
case 141: // check sign
|
||||
result = ubase + (caster_level > 30 ? (3 * caster_level - 90) / 2 : 0);
|
||||
break;
|
||||
|
||||
case 142: // check sign
|
||||
result = ubase + (caster_level > 30 ? 2 * caster_level - 60 : 0);
|
||||
break;
|
||||
|
||||
case 143: // check sign
|
||||
result = ubase + (3 * caster_level / 4);
|
||||
break;
|
||||
|
||||
//these are used in stacking effects... formula unknown
|
||||
case 201:
|
||||
case 203:
|
||||
@@ -4016,6 +4119,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
|
||||
if (!found_numhits)
|
||||
Numhits(false);
|
||||
}
|
||||
|
||||
if (spells[buffs[slot].spellid].NimbusEffect > 0)
|
||||
RemoveNimbusEffect(spells[buffs[slot].spellid].NimbusEffect);
|
||||
|
||||
buffs[slot].spellid = SPELL_UNKNOWN;
|
||||
if(IsPet() && GetOwner() && GetOwner()->IsClient()) {
|
||||
|
||||
+7
-9
@@ -4501,13 +4501,12 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
else
|
||||
{
|
||||
resist_chance -= roll;
|
||||
if(resist_chance < 1)
|
||||
{
|
||||
resist_chance = 1;
|
||||
}
|
||||
|
||||
int partial_modifier = ((150 * (roll - resist_chance)) / resist_chance);
|
||||
int partial_modifier = ((150 * (resist_chance - roll)) / resist_chance);
|
||||
|
||||
if(IsNPC())
|
||||
{
|
||||
@@ -4535,17 +4534,16 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
}
|
||||
|
||||
if(partial_modifier < 0)
|
||||
if(partial_modifier <= 0)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
else if(partial_modifier >= 100)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(partial_modifier > 100)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
return partial_modifier;
|
||||
return (100.0f - partial_modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+83
-133
@@ -31,25 +31,17 @@ bool TitleManager::LoadTitles()
|
||||
{
|
||||
Titles.clear();
|
||||
|
||||
TitleEntry Title;
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = nullptr;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
|
||||
if (!database.RunQuery(query, MakeAnyLenString(&query,
|
||||
"SELECT `id`, `skill_id`, `min_skill_value`, `max_skill_value`, `min_aa_points`, `max_aa_points`, `class`, `gender`, "
|
||||
"`char_id`, `status`, `item_id`, `prefix`, `suffix`, `title_set` from titles"), errbuf, &result))
|
||||
{
|
||||
LogFile->write(EQEMuLog::Error, "Unable to load titles: %s : %s", query, errbuf);
|
||||
safe_delete_array(query);
|
||||
return(false);
|
||||
std::string query = "SELECT `id`, `skill_id`, `min_skill_value`, `max_skill_value`, "
|
||||
"`min_aa_points`, `max_aa_points`, `class`, `gender`, `char_id`, "
|
||||
"`status`, `item_id`, `prefix`, `suffix`, `title_set` FROM titles";
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Unable to load titles: %s : %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
while ((row = mysql_fetch_row(result))) {
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
TitleEntry Title;
|
||||
Title.TitleID = atoi(row[0]);
|
||||
Title.SkillID = (SkillUseTypes) atoi(row[1]);
|
||||
Title.MinSkillValue = atoi(row[2]);
|
||||
@@ -66,9 +58,8 @@ bool TitleManager::LoadTitles()
|
||||
Title.TitleSet = atoi(row[13]);
|
||||
Titles.push_back(Title);
|
||||
}
|
||||
mysql_free_result(result);
|
||||
|
||||
return(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
EQApplicationPacket *TitleManager::MakeTitlesPacket(Client *c)
|
||||
@@ -244,92 +235,70 @@ bool TitleManager::IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue)
|
||||
return false;
|
||||
}
|
||||
|
||||
void TitleManager::CreateNewPlayerTitle(Client *c, const char *Title)
|
||||
void TitleManager::CreateNewPlayerTitle(Client *client, const char *title)
|
||||
{
|
||||
if(!c || !Title)
|
||||
if(!client || !title)
|
||||
return;
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = nullptr;
|
||||
MYSQL_RES *result;
|
||||
char *escTitle = new char[strlen(title) * 2 + 1];
|
||||
|
||||
char *EscTitle = new char[strlen(Title) * 2 + 1];
|
||||
client->SetAATitle(title);
|
||||
|
||||
c->SetAATitle(Title);
|
||||
|
||||
database.DoEscapeString(EscTitle, Title, strlen(Title));
|
||||
|
||||
if (database.RunQuery(query, MakeAnyLenString(&query,
|
||||
"SELECT `id` from titles where `prefix` = '%s' and char_id = %i", EscTitle, c->CharacterID()), errbuf, &result))
|
||||
{
|
||||
if(mysql_num_rows(result) > 0)
|
||||
{
|
||||
mysql_free_result(result);
|
||||
safe_delete_array(query);
|
||||
safe_delete_array(EscTitle);
|
||||
return;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
database.DoEscapeString(escTitle, title, strlen(title));
|
||||
auto query = StringFormat("SELECT `id` FROM titles "
|
||||
"WHERE `prefix` = '%s' AND char_id = %i",
|
||||
escTitle, client->CharacterID());
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (results.Success() && results.RowCount() > 0){
|
||||
safe_delete_array(escTitle);
|
||||
return;
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT into titles (`char_id`, `prefix`) VALUES(%i, '%s')",
|
||||
c->CharacterID(), EscTitle), errbuf))
|
||||
LogFile->write(EQEMuLog::Error, "Error adding title: %s %s", query, errbuf);
|
||||
else
|
||||
{
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
safe_delete_array(query);
|
||||
safe_delete_array(EscTitle);
|
||||
query = StringFormat("INSERT INTO titles (`char_id`, `prefix`) VALUES(%i, '%s')",
|
||||
client->CharacterID(), escTitle);
|
||||
safe_delete_array(escTitle);
|
||||
results = database.QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Error adding title: %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
void TitleManager::CreateNewPlayerSuffix(Client *c, const char *Suffix)
|
||||
void TitleManager::CreateNewPlayerSuffix(Client *client, const char *suffix)
|
||||
{
|
||||
if(!c || !Suffix)
|
||||
if(!client || !suffix)
|
||||
return;
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = nullptr;
|
||||
MYSQL_RES *result;
|
||||
client->SetTitleSuffix(suffix);
|
||||
|
||||
char *EscSuffix = new char[strlen(Suffix) * 2 + 1];
|
||||
char *escSuffix = new char[strlen(suffix) * 2 + 1];
|
||||
database.DoEscapeString(escSuffix, suffix, strlen(suffix));
|
||||
|
||||
c->SetTitleSuffix(Suffix);
|
||||
|
||||
database.DoEscapeString(EscSuffix, Suffix, strlen(Suffix));
|
||||
|
||||
if (database.RunQuery(query, MakeAnyLenString(&query,
|
||||
"SELECT `id` from titles where `suffix` = '%s' and char_id = %i", EscSuffix, c->CharacterID()), errbuf, &result))
|
||||
{
|
||||
if(mysql_num_rows(result) > 0)
|
||||
{
|
||||
mysql_free_result(result);
|
||||
safe_delete_array(query);
|
||||
safe_delete_array(EscSuffix);
|
||||
std::string query = StringFormat("SELECT `id` FROM titles "
|
||||
"WHERE `suffix` = '%s' AND char_id = %i",
|
||||
escSuffix, client->CharacterID());
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (results.Success() && results.RowCount() > 0) {
|
||||
safe_delete_array(escSuffix);
|
||||
return;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT into titles (`char_id`, `suffix`) VALUES(%i, '%s')",
|
||||
c->CharacterID(), EscSuffix), errbuf))
|
||||
LogFile->write(EQEMuLog::Error, "Error adding title suffix: %s %s", query, errbuf);
|
||||
else
|
||||
{
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
safe_delete_array(query);
|
||||
safe_delete_array(EscSuffix);
|
||||
query = StringFormat("INSERT INTO titles (`char_id`, `suffix`) VALUES(%i, '%s')",
|
||||
client->CharacterID(), escSuffix);
|
||||
safe_delete_array(escSuffix);
|
||||
results = database.QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Error adding title suffix: %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
void Client::SetAATitle(const char *Title)
|
||||
@@ -368,67 +337,48 @@ void Client::SetTitleSuffix(const char *Suffix)
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::EnableTitle(int titleset) {
|
||||
void Client::EnableTitle(int titleSet) {
|
||||
|
||||
if (CheckTitle(titleset)) {
|
||||
if (CheckTitle(titleSet))
|
||||
return;
|
||||
}
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
std::string query = StringFormat("INSERT INTO player_titlesets "
|
||||
"(char_id, title_set) VALUES (%i, %i)",
|
||||
CharacterID(), titleSet);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if(!results.Success())
|
||||
LogFile->write(EQEMuLog::Error, "Error in EnableTitle query for titleset %i and charid %i", titleSet, CharacterID());
|
||||
|
||||
if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO player_titlesets (char_id, title_set) VALUES (%i, %i)", CharacterID(), titleset), errbuf)) {
|
||||
LogFile->write(EQEMuLog::Error, "Error in EnableTitle query for titleset %i and charid %i", titleset, CharacterID());
|
||||
safe_delete_array(query);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
safe_delete_array(query);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::CheckTitle(int titleset) {
|
||||
bool Client::CheckTitle(int titleSet) {
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
MYSQL_RES *result;
|
||||
|
||||
if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT `id` FROM player_titlesets WHERE `title_set`=%i AND `char_id`=%i LIMIT 1", titleset, CharacterID()), errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
if (mysql_num_rows(result) >= 1) {
|
||||
mysql_free_result(result);
|
||||
return(true);
|
||||
}
|
||||
mysql_free_result(result);
|
||||
std::string query = StringFormat("SELECT `id` FROM player_titlesets "
|
||||
"WHERE `title_set`=%i AND `char_id`=%i LIMIT 1",
|
||||
titleSet, CharacterID());
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Error in CheckTitle query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
else {
|
||||
LogFile->write(EQEMuLog::Error, "Error in CheckTitle query '%s': %s", query, errbuf);
|
||||
safe_delete_array(query);
|
||||
}
|
||||
if (results.RowCount() == 0)
|
||||
return false;
|
||||
|
||||
return(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::RemoveTitle(int titleset) {
|
||||
void Client::RemoveTitle(int titleSet) {
|
||||
|
||||
if (!CheckTitle(titleset)) {
|
||||
if (!CheckTitle(titleSet))
|
||||
return;
|
||||
}
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
std::string query = StringFormat("DELETE FROM player_titlesets "
|
||||
"WHERE `title_set` = %i AND `char_id` = %i",
|
||||
titleSet, CharacterID());
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
LogFile->write(EQEMuLog::Error, "Error in RemoveTitle query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
|
||||
if (database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM player_titlesets WHERE `title_set`=%i AND `char_id`=%i", titleset, CharacterID()), errbuf)) {
|
||||
safe_delete_array(query);
|
||||
}
|
||||
|
||||
else {
|
||||
LogFile->write(EQEMuLog::Error, "Error in RemoveTitle query '%s': %s", query, errbuf);
|
||||
safe_delete_array(query);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+15
-22
@@ -567,7 +567,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
{
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
|
||||
|
||||
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
|
||||
@@ -696,7 +696,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
{
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr); + 2.0f;
|
||||
|
||||
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
|
||||
@@ -821,7 +821,7 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
|
||||
{
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
|
||||
|
||||
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
|
||||
@@ -1007,26 +1007,19 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
|
||||
}
|
||||
|
||||
int ZoneDatabase::GetHighestGrid(uint32 zoneid) {
|
||||
char *query = 0;
|
||||
char errbuff[MYSQL_ERRMSG_SIZE];
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
int res = 0;
|
||||
if (RunQuery(query, MakeAnyLenString(&query,
|
||||
"SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i",
|
||||
zoneid),errbuff,&result)) {
|
||||
safe_delete_array(query);
|
||||
if (mysql_num_rows(result) == 1) {
|
||||
row = mysql_fetch_row(result);
|
||||
res = atoi( row[0] );
|
||||
}
|
||||
mysql_free_result(result);
|
||||
} else {
|
||||
LogFile->write(EQEMuLog::Error, "Error in GetHighestGrid query '%s': %s", query, errbuff);
|
||||
safe_delete_array(query);
|
||||
}
|
||||
|
||||
return(res);
|
||||
std::string query = StringFormat("SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", zoneid);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Error in GetHighestGrid query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (results.RowCount() != 1)
|
||||
return 0;
|
||||
|
||||
auto row = results.begin();
|
||||
return atoi(row[0]);
|
||||
}
|
||||
|
||||
uint8 ZoneDatabase::GetGridType2(uint32 grid, uint16 zoneid) {
|
||||
|
||||
+1572
-2212
File diff suppressed because it is too large
Load Diff
@@ -111,6 +111,7 @@ struct NPCType
|
||||
uint8 spawn_limit; //only this many may be in zone at a time (0=no limit)
|
||||
uint8 mount_color; //only used by horse class
|
||||
float attack_speed; //%+- on attack delay of the mob.
|
||||
uint8 attack_delay; //delay between attacks in 10ths of a second
|
||||
int accuracy_rating; //10 = 1% accuracy
|
||||
int avoidance_rating; //10 = 1% avoidance
|
||||
bool findable; //can be found with find command
|
||||
|
||||
Reference in New Issue
Block a user