Some work on Runic Power and Milling Spell Effect

This commit is contained in:
tomrus88 2008-11-30 13:53:14 +03:00
parent bd3eca6904
commit 700d3cf609
20 changed files with 379 additions and 76 deletions

View file

@ -0,0 +1,13 @@
DROP TABLE IF EXISTS `milling_loot_template`;
CREATE TABLE `milling_loot_template` (
`entry` mediumint(8) unsigned NOT NULL default '0',
`item` mediumint(8) unsigned NOT NULL default '0',
`ChanceOrQuestChance` float NOT NULL default '100',
`groupid` tinyint(3) unsigned NOT NULL default '0',
`mincountOrRef` mediumint(9) NOT NULL default '1',
`maxcount` tinyint(3) unsigned NOT NULL default '1',
`lootcondition` tinyint(3) unsigned NOT NULL default '0',
`condition_value1` mediumint(8) unsigned NOT NULL default '0',
`condition_value2` mediumint(8) unsigned NOT NULL default '0',
PRIMARY KEY (`entry`,`item`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System';

View file

@ -613,15 +613,18 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
if(ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(pCurrChar->getClass()))
{
data.Initialize(SMSG_TRIGGER_CINEMATIC, 4);
data << uint32(cEntry->CinematicSequence);
SendPacket( &data );
}
else if(ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace()))
{
data.Initialize(SMSG_TRIGGER_CINEMATIC, 4);
data << uint32(rEntry->CinematicSequence);
SendPacket( &data );
if(cEntry->CinematicSequence)
{
data.Initialize(SMSG_TRIGGER_CINEMATIC, 4);
data << uint32(cEntry->CinematicSequence);
SendPacket( &data );
}
else if(ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace()))
{
data.Initialize(SMSG_TRIGGER_CINEMATIC, 4);
data << uint32(rEntry->CinematicSequence);
SendPacket( &data );
}
}
}
@ -674,7 +677,6 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
pCurrChar->LoadCorpse();
// setting Ghost+speed if dead
//if ( pCurrChar->m_deathState == DEAD )
if (pCurrChar->m_deathState != ALIVE)
{
// not blizz like, we must correctly save and load player instead...
@ -682,22 +684,6 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
pCurrChar->CastSpell(pCurrChar, 20584, true, 0);// auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form)
pCurrChar->CastSpell(pCurrChar, 8326, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
//pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+41, 8326);
//pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+42, 20584);
//pCurrChar->SetUInt32Value(UNIT_FIELD_AURAFLAGS+6, 238);
//pCurrChar->SetUInt32Value(UNIT_FIELD_AURALEVELS+11, 514);
//pCurrChar->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS+11, 65535);
//pCurrChar->SetUInt32Value(UNIT_FIELD_DISPLAYID, 1825);
//if (pCurrChar->getRace() == RACE_NIGHTELF)
//{
// pCurrChar->SetSpeed(MOVE_RUN, 1.5f*1.2f, true);
// pCurrChar->SetSpeed(MOVE_SWIM, 1.5f*1.2f, true);
//}
//else
//{
// pCurrChar->SetSpeed(MOVE_RUN, 1.5f, true);
// pCurrChar->SetSpeed(MOVE_SWIM, 1.5f, true);
//}
pCurrChar->SetMovement(MOVE_WATER_WALK);
}
@ -1171,13 +1157,13 @@ void WorldSession::HandleRemoveGlyph( WorldPacket & recv_data )
uint32 slot;
recv_data >> slot;
if(slot > 5)
{
sLog.outDebug("Client sent wrong glyph slot number in opcode CMSG_REMOVE_GLYPH %u", slot);
return;
}
if(slot > 5)
{
sLog.outDebug("Client sent wrong glyph slot number in opcode CMSG_REMOVE_GLYPH %u", slot);
return;
}
if(uint32 glyph = _player->GetGlyph(slot))
if(uint32 glyph = _player->GetGlyph(slot))
{
if(GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph))
{

View file

@ -258,6 +258,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "item_enchantment_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadItemEnchantementsCommand, "", NULL },
{ "item_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesItemCommand, "", NULL },
{ "mangos_string", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadMangosStringCommand, "", NULL },
{ "milling_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMillingCommand, "", NULL },
{ "npc_gossip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcGossipCommand, "", NULL },
{ "npc_option", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcOptionCommand, "", NULL },
{ "npc_trainer", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcTrainerCommand, "", NULL },

View file

@ -225,6 +225,7 @@ class ChatHandler
bool HandleReloadLootTemplatesFishingCommand(const char* args);
bool HandleReloadLootTemplatesGameobjectCommand(const char* args);
bool HandleReloadLootTemplatesItemCommand(const char* args);
bool HandleReloadLootTemplatesMillingCommand(const char* args);
bool HandleReloadLootTemplatesPickpocketingCommand(const char* args);
bool HandleReloadLootTemplatesProspectingCommand(const char* args);
bool HandleReloadLootTemplatesReferenceCommand(const char* args);

View file

@ -210,6 +210,7 @@ class MANGOS_DLL_SPEC Item : public Object
void SetBinding(bool val) { ApplyModFlag(ITEM_FIELD_FLAGS,ITEM_FLAGS_BINDED,val); }
bool IsSoulBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BINDED); }
bool IsAccountBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BOA); }
bool IsBindedNotWith(uint64 guid) const { return IsSoulBound() && GetOwnerGUID()!= guid; }
bool IsBoundByEnchant() const;
virtual void SaveToDB();

View file

@ -294,6 +294,15 @@ bool ChatHandler::HandleReloadLootTemplatesItemCommand(const char*)
return true;
}
bool ChatHandler::HandleReloadLootTemplatesMillingCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`milling_loot_template`)" );
LoadLootTemplates_Milling();
LootTemplates_Milling.CheckLootRefs();
SendGlobalSysMessage("DB table `milling_loot_template` reloaded.");
return true;
}
bool ChatHandler::HandleReloadLootTemplatesPickpocketingCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`pickpocketing_loot_template`)" );

View file

@ -39,6 +39,7 @@ LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenc
LootStore LootTemplates_Fishing( "fishing_loot_template", "area id");
LootStore LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry");
LootStore LootTemplates_Item( "item_loot_template", "item entry");
LootStore LootTemplates_Milling( "milling_loot_template", "item entry");
LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid");
LootStore LootTemplates_Prospecting( "prospecting_loot_template", "item entry");
LootStore LootTemplates_QuestMail( "quest_mail_loot_template", "quest id");
@ -1135,6 +1136,21 @@ void LoadLootTemplates_Item()
LootTemplates_Item.ReportUnusedIds(ids_set);
}
void LoadLootTemplates_Milling()
{
LootIdSet ids_set;
LootTemplates_Milling.LoadAndCollectLootIds(ids_set);
// remove real entries and check existence loot
for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i))
if(ids_set.count(proto->ItemId))
ids_set.erase(proto->ItemId);
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Milling.ReportUnusedIds(ids_set);
}
void LoadLootTemplates_Pickpocketing()
{
LootIdSet ids_set, ids_setUsed;
@ -1227,6 +1243,7 @@ void LoadLootTemplates_Reference()
LootTemplates_Fishing.CheckLootRefs(&ids_set);
LootTemplates_Gameobject.CheckLootRefs(&ids_set);
LootTemplates_Item.CheckLootRefs(&ids_set);
LootTemplates_Milling.CheckLootRefs(&ids_set);
LootTemplates_Pickpocketing.CheckLootRefs(&ids_set);
LootTemplates_Skinning.CheckLootRefs(&ids_set);
LootTemplates_Disenchant.CheckLootRefs(&ids_set);

View file

@ -295,6 +295,7 @@ extern LootStore LootTemplates_Creature;
extern LootStore LootTemplates_Fishing;
extern LootStore LootTemplates_Gameobject;
extern LootStore LootTemplates_Item;
extern LootStore LootTemplates_Milling;
extern LootStore LootTemplates_Pickpocketing;
extern LootStore LootTemplates_Skinning;
extern LootStore LootTemplates_Disenchant;
@ -305,6 +306,7 @@ void LoadLootTemplates_Creature();
void LoadLootTemplates_Fishing();
void LoadLootTemplates_Gameobject();
void LoadLootTemplates_Item();
void LoadLootTemplates_Milling();
void LoadLootTemplates_Pickpocketing();
void LoadLootTemplates_Skinning();
void LoadLootTemplates_Disenchant();
@ -318,6 +320,7 @@ inline void LoadLootTables()
LoadLootTemplates_Fishing();
LoadLootTemplates_Gameobject();
LoadLootTemplates_Item();
LoadLootTemplates_Milling();
LoadLootTemplates_Pickpocketing();
LoadLootTemplates_Skinning();
LoadLootTemplates_Disenchant();

View file

@ -430,6 +430,7 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this)
m_contestedPvPTimer = 0;
m_declinedname = NULL;
m_runes = NULL;
}
Player::~Player ()
@ -474,6 +475,7 @@ Player::~Player ()
itr->second.save->RemovePlayer(this);
delete m_declinedname;
delete m_runes;
}
void Player::CleanupsBeforeDelete()
@ -504,13 +506,6 @@ bool Player::Create( uint32 guidlow, std::string name, uint8 race, uint8 class_,
for (int i = 0; i < PLAYER_SLOTS_COUNT; i++)
m_items[i] = NULL;
//for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; j++)
//{
// SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1+j*2,0);
// SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1+j,0);
// SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1+j,0);
//}
m_race = race;
m_class = class_;
@ -526,9 +521,9 @@ bool Player::Create( uint32 guidlow, std::string name, uint8 race, uint8 class_,
uint8 powertype = cEntry->powerType;
uint32 unitfield;
//uint32 unitfield;
switch(powertype)
/*switch(powertype)
{
case POWER_ENERGY:
case POWER_MANA:
@ -543,10 +538,10 @@ bool Player::Create( uint32 guidlow, std::string name, uint8 race, uint8 class_,
default:
sLog.outError("Invalid default powertype %u for player (class %u)",powertype,class_);
return false;
}
}*/
SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE );
SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f );
SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE);
SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f);
switch(gender)
{
@ -569,12 +564,12 @@ bool Player::Create( uint32 guidlow, std::string name, uint8 race, uint8 class_,
uint32 RaceClassGender = ( race ) | ( class_ << 8 ) | ( gender << 16 );
SetUInt32Value(UNIT_FIELD_BYTES_0, ( RaceClassGender | ( powertype << 24 ) ) );
SetUInt32Value(UNIT_FIELD_BYTES_1, unitfield);
//SetUInt32Value(UNIT_FIELD_BYTES_1, unitfield);
SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP );
SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE );
SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // fix cast time showed in spell tooltip on client
//-1 is default value
// -1 is default value
SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1));
SetUInt32Value(PLAYER_BYTES, (skin | (face << 8) | (hairStyle << 16) | (hairColor << 24)));
@ -586,6 +581,7 @@ bool Player::Create( uint32 guidlow, std::string name, uint8 race, uint8 class_,
SetUInt32Value( PLAYER_GUILD_TIMESTAMP, 0 );
SetUInt64Value( PLAYER__FIELD_KNOWN_TITLES, 0 ); // 0=disabled
SetUInt64Value( PLAYER__FIELD_KNOWN_TITLES1, 0 ); // 0=disabled
SetUInt32Value( PLAYER_CHOSEN_TITLE, 0 );
SetUInt32Value( PLAYER_FIELD_KILLS, 0 );
SetUInt32Value( PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 0 );
@ -605,6 +601,8 @@ bool Player::Create( uint32 guidlow, std::string name, uint8 race, uint8 class_,
else
SetUInt32Value( UNIT_FIELD_LEVEL, sWorld.getConfig(CONFIG_START_PLAYER_LEVEL) );
InitRunes();
// Played time
m_Last_tick = time(NULL);
m_Played_time[0] = 0;
@ -626,6 +624,14 @@ bool Player::Create( uint32 guidlow, std::string name, uint8 race, uint8 class_,
SetPower(POWER_MANA,GetMaxPower(POWER_MANA));
}
if(getPowerType() == POWER_RUNIC_POWER)
{
SetPower(POWER_RUNE, 8);
SetMaxPower(POWER_RUNE, 8);
SetPower(POWER_RUNIC_POWER, 0);
SetMaxPower(POWER_RUNIC_POWER, 1000);
}
// original spells
learnDefaultSpells(true);
@ -1822,14 +1828,17 @@ void Player::RegenerateAll()
{
RegenerateHealth();
if (!isInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN))
{
Regenerate(POWER_RAGE);
Regenerate(POWER_RUNIC_POWER);
}
}
Regenerate( POWER_ENERGY );
Regenerate( POWER_MANA );
Regenerate( POWER_RUNIC_POWER );
Regenerate( POWER_RUNE );
m_regenTimer = regenDelay;
}
@ -1866,8 +1875,16 @@ void Player::Regenerate(Powers power)
addvalue = 20;
break;
case POWER_RUNIC_POWER:
addvalue = 100; // TODO: find correct regen rate
break;
{
float RunicPowerDecreaseRate = sWorld.getRate(RATE_POWER_RUNICPOWER_LOSS);
addvalue = 30 * RunicPowerDecreaseRate; // 3 RunicPower by tick
} break;
case POWER_RUNE:
{
for(uint32 i = 0; i < 6; ++i)
if(uint8 cd = GetRuneCooldown(i)) // if we have cooldown, reduce it...
SetRuneCooldown(i, cd - 1); // by 2 sec (because update is every 2 sec)
} break;
case POWER_FOCUS:
case POWER_HAPPINESS:
break;
@ -1883,7 +1900,7 @@ void Player::Regenerate(Powers power)
addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f;
}
if (power != POWER_RAGE)
if (power != POWER_RAGE && power != POWER_RUNIC_POWER)
{
curValue += uint32(addvalue);
if (curValue > maxValue)
@ -7217,6 +7234,17 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this);
}
}
else if(loot_type == LOOT_MILLING)
{
loot = &item->loot;
if(!item->m_lootGenerated)
{
item->m_lootGenerated = true;
loot->clear();
loot->FillLoot(item->GetEntry(), LootTemplates_Milling, this);
}
}
else
{
loot = &item->loot;
@ -7410,8 +7438,8 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
conditional_list = itr->second;
}
// LOOT_PICKPOCKETING, LOOT_PROSPECTING, LOOT_DISENCHANTING and LOOT_INSIGNIA unsupported by client, sending LOOT_SKINNING instead
if(loot_type == LOOT_PICKPOCKETING || loot_type == LOOT_DISENCHANTING || loot_type == LOOT_PROSPECTING || loot_type == LOOT_INSIGNIA)
// LOOT_PICKPOCKETING, LOOT_PROSPECTING, LOOT_DISENCHANTING, LOOT_INSIGNIA and LOOT_MILLING unsupported by client, sending LOOT_SKINNING instead
if(loot_type == LOOT_PICKPOCKETING || loot_type == LOOT_DISENCHANTING || loot_type == LOOT_PROSPECTING || loot_type == LOOT_INSIGNIA || loot_type == LOOT_MILLING)
loot_type = LOOT_SKINNING;
if(loot_type == LOOT_FISHINGHOLE)
@ -14183,6 +14211,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
_LoadDeclinedNames(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES));
InitRunes();
m_achievementMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS), holder->GetResult(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS));
m_achievementMgr.CheckAllAchievementCriteria();
return true;
@ -18824,3 +18854,25 @@ void Player::SetTitle(CharTitlesEntry const* title)
SetFlag(PLAYER__FIELD_KNOWN_TITLES+fieldIndexOffset, flag);
}
void Player::ConvertRune(uint8 index, uint8 newType)
{
// SMSG_CONVERT_RUNE
}
void Player::InitRunes()
{
if(getClass() != CLASS_DEATH_KNIGHT)
return;
m_runes = new Runes;
for(uint32 i = 0; i < MAX_RUNES; ++i)
{
SetBaseRune(i, i / 2);
SetCurrentRune(i, i / 2);
SetRuneCooldown(i, 0);
}
for(uint32 i = 0; i < NUM_RUNES; ++i)
SetFloatValue(PLAYER_RUNE_REGEN_1 + i, 0.1f);
}

View file

@ -224,6 +224,29 @@ struct Areas
float y2;
};
#define MAX_RUNES 6
enum RuneType
{
RUNE_BLOOD = 0,
RUNE_FROST = 1,
RUNE_UNHOLY = 2,
RUNE_DEATH = 3,
NUM_RUNES = 4
};
struct RuneInfo
{
uint8 BaseRune:1;
uint8 CurrentRune:1;
uint8 Cooldown:1;
};
struct Runes
{
RuneInfo runes[6];
};
enum FactionFlags
{
FACTION_FLAG_VISIBLE = 0x01, // makes visible in client (set or can be set at interaction with target of this faction)
@ -481,7 +504,8 @@ enum LootType
LOOT_DISENCHANTING = 5, // unsupported by client, sending LOOT_SKINNING instead
LOOT_PROSPECTING = 6, // unsupported by client, sending LOOT_SKINNING instead
LOOT_INSIGNIA = 7, // unsupported by client, sending LOOT_SKINNING instead
LOOT_FISHINGHOLE = 8 // unsupported by client, sending LOOT_FISHING instead
LOOT_FISHINGHOLE = 8, // unsupported by client, sending LOOT_FISHING instead
LOOT_MILLING = 9 // unsupported by client, sending LOOT_SKINNING instead
};
enum MirrorTimerType
@ -519,10 +543,10 @@ typedef std::map<uint32, QuestStatusData> QuestStatusMap;
enum QuestSlotOffsets
{
QUEST_ID_OFFSET = 0,
QUEST_STATE_OFFSET = 1,
QUEST_ID_OFFSET = 0,
QUEST_STATE_OFFSET = 1,
QUEST_COUNTS_OFFSET = 2,
QUEST_TIME_OFFSET = 3
QUEST_TIME_OFFSET = 3
};
#define MAX_QUEST_OFFSET 4
@ -2067,7 +2091,21 @@ class MANGOS_DLL_SPEC Player : public Unit
WorldLocation& GetTeleportDest() { return m_teleport_dest; }
DeclinedName const* GetDeclinedNames() const { return m_declinedname; }
RuneInfo const* GetRuneInfo(uint8 index) { return &m_runes->runes[index]; }
uint8 GetBaseRune(uint8 index) { return m_runes->runes[index].BaseRune; }
uint8 GetCurrentRune(uint8 index) { return m_runes->runes[index].CurrentRune; }
uint8 GetRuneCooldown(uint8 index) { return m_runes->runes[index].Cooldown; }
void SetRuneInfo(uint8 index, uint8 baseRune, uint8 currentRune, uint8 cooldown)
{
m_runes->runes[index].BaseRune = baseRune;
m_runes->runes[index].CurrentRune = currentRune;
m_runes->runes[index].Cooldown = cooldown;
}
void SetBaseRune(uint8 index, uint8 baseRune) { m_runes->runes[index].BaseRune = baseRune; }
void SetCurrentRune(uint8 index, uint8 currentRune) { m_runes->runes[index].BaseRune = currentRune; }
void SetRuneCooldown(uint8 index, uint8 cooldown) { m_runes->runes[index].BaseRune = cooldown; }
void ConvertRune(uint8 index, uint8 newType);
void InitRunes();
AchievementMgr& GetAchievementMgr() { return m_achievementMgr; }
bool HasTitle(uint32 bitIndex);
bool HasTitle(CharTitlesEntry const* title) { return HasTitle(title->bit_index); }
@ -2302,6 +2340,7 @@ class MANGOS_DLL_SPEC Player : public Unit
WorldLocation m_teleport_dest;
DeclinedName *m_declinedname;
Runes *m_runes;
AchievementMgr m_achievementMgr;
private:
// internal common parts for CanStore/StoreItem functions

View file

@ -664,7 +664,7 @@ enum SpellEffects
SPELL_EFFECT_155 = 155,
SPELL_EFFECT_156 = 156,
SPELL_EFFECT_157 = 157,
SPELL_EFFECT_158 = 158,
SPELL_EFFECT_MILLING = 158,
SPELL_EFFECT_159 = 159,
TOTAL_SPELL_EFFECTS = 160
};
@ -1817,6 +1817,7 @@ inline uint32 SkillByQuestSort(int32 QuestSort)
case QUEST_SORT_COOKING: return SKILL_COOKING;
case QUEST_SORT_FIRST_AID: return SKILL_FIRST_AID;
case QUEST_SORT_JEWELCRAFTING: return SKILL_JEWELCRAFTING;
case QUEST_SORT_INSCRIPTION: return SKILL_INSCRIPTION;
}
return 0;
}

View file

@ -593,6 +593,7 @@ void Spell::FillTargetMap()
case SPELL_EFFECT_DISENCHANT:
case SPELL_EFFECT_FEED_PET:
case SPELL_EFFECT_PROSPECTING:
case SPELL_EFFECT_MILLING:
if(m_targets.getItemTarget())
AddItemTarget(m_targets.getItemTarget(), i);
break;
@ -3115,6 +3116,12 @@ void Spell::TakePower()
Powers powerType = Powers(m_spellInfo->powerType);
if(powerType == POWER_RUNE)
{
TakeRunePower();
return;
}
m_caster->ModifyPower(powerType, -(int32)m_powerCost);
// Set the five second timer
@ -3122,6 +3129,105 @@ void Spell::TakePower()
m_caster->SetLastManaUse(getMSTime());
}
uint8 Spell::CheckRuneCost(uint32 runeCostID)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return 0;
Player *plr = (Player*)m_caster;
if(plr->getClass() != CLASS_DEATH_KNIGHT)
return 0;
SpellRuneCostEntry const *src = sSpellRuneCostStore.LookupEntry(runeCostID);
if(!src)
return 0;
if(src->NoRuneCost())
return 0;
int32 runeCost[NUM_RUNES]; // blood, frost, unholy, death
for(uint32 i = 0; i < RUNE_DEATH; ++i)
runeCost[i] = src->RuneCost[i];
runeCost[RUNE_DEATH] = 0; // calculated later
for(uint32 i = 0; i < MAX_RUNES; ++i)
if(!plr->GetRuneCooldown(i))
runeCost[plr->GetCurrentRune(i)]--;
for(uint32 i = 0; i < RUNE_DEATH; ++i)
if(runeCost[i] > 0)
runeCost[RUNE_DEATH] += runeCost[i];
if(runeCost[RUNE_DEATH] > 0)
return SPELL_FAILED_REAGENTS; // not sure if result code is correct
return 0;
}
void Spell::TakeRunePower()
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
Player *plr = (Player*)m_caster;
if(plr->getClass() != CLASS_DEATH_KNIGHT)
return;
SpellRuneCostEntry const *src = sSpellRuneCostStore.LookupEntry(m_spellInfo->runeCostID);
if(!src || (src->NoRuneCost() && src->NoRunicPowerGain()))
return;
int32 runeCost[NUM_RUNES]; // blood, frost, unholy, death
for(uint32 i = 0; i < RUNE_DEATH; ++i)
runeCost[i] = src->RuneCost[i];
runeCost[RUNE_DEATH] = 0; // calculated later
for(uint32 i = 0; i < MAX_RUNES; ++i)
{
if(!plr->GetRuneCooldown(i))
{
uint8 rune = plr->GetCurrentRune(i);
if(runeCost[rune] > 0)
{
plr->SetRuneCooldown(i, 5); // 5*2=10 sec
runeCost[rune]--;
}
}
}
runeCost[RUNE_DEATH] = runeCost[RUNE_BLOOD] + runeCost[RUNE_FROST] + runeCost[RUNE_UNHOLY];
if(runeCost[RUNE_DEATH] > 0)
{
for(uint32 i = 0; i < MAX_RUNES; ++i)
{
if(!plr->GetRuneCooldown(i) && plr->GetCurrentRune(i) == RUNE_DEATH)
{
plr->SetRuneCooldown(i, 5); // 5*2=10 sec
runeCost[plr->GetCurrentRune(i)]--;
uint8 base = plr->GetBaseRune(i);
plr->SetCurrentRune(i, base);
plr->ConvertRune(i, base);
if(runeCost[RUNE_DEATH] == 0)
break;
}
}
}
float rp = src->runePowerGain;;
rp *= sWorld.getRate(RATE_POWER_RUNICPOWER_INCOME);
rp += plr->GetPower(POWER_RUNIC_POWER);
plr->SetPower(POWER_RUNIC_POWER, (uint32)rp);
}
void Spell::TakeReagents()
{
if(m_IsTriggeredSpell) // reagents used in triggered spell removed by original spell or don't must be removed.
@ -4040,7 +4146,7 @@ uint8 Spell::CanCast(bool strict)
if( form == FORM_CAT || form == FORM_TREE || form == FORM_TRAVEL ||
form == FORM_AQUA || form == FORM_BEAR || form == FORM_DIREBEAR ||
form == FORM_CREATUREBEAR || form == FORM_GHOSTWOLF || form == FORM_FLIGHT ||
form == FORM_FLIGHT_EPIC || form == FORM_MOONKIN )
form == FORM_FLIGHT_EPIC || form == FORM_MOONKIN || form == FORM_METAMORPHOSIS )
return SPELL_FAILED_NOT_SHAPESHIFT;
break;
@ -4431,6 +4537,11 @@ uint8 Spell::CheckPower()
sLog.outError("Spell::CheckMana: Unknown power type '%d'", m_spellInfo->powerType);
return SPELL_FAILED_UNKNOWN;
}
uint8 failReason = CheckRuneCost(m_spellInfo->runeCostID);
if(failReason)
return failReason;
// Check power amount
Powers powerType = Powers(m_spellInfo->powerType);
if(m_caster->GetPower(powerType) < m_powerCost)
@ -4710,6 +4821,29 @@ uint8 Spell::CheckItems()
break;
}
case SPELL_EFFECT_MILLING:
{
if(!m_targets.getItemTarget())
return SPELL_FAILED_CANT_BE_MILLED;
//ensure item is a millable herb
if(!(m_targets.getItemTarget()->GetProto()->BagFamily & BAG_FAMILY_MASK_HERBS) || m_targets.getItemTarget()->GetProto()->Class != ITEM_CLASS_TRADE_GOODS)
return SPELL_FAILED_CANT_BE_MILLED;
//prevent milling in trade slot
if( m_targets.getItemTarget()->GetOwnerGUID() != m_caster->GetGUID() )
return SPELL_FAILED_CANT_BE_MILLED;
//Check for enough skill in inscription
uint32 item_millingskilllevel = m_targets.getItemTarget()->GetProto()->RequiredSkillRank;
if(item_millingskilllevel >p_caster->GetSkillValue(SKILL_INSCRIPTION))
return SPELL_FAILED_LOW_CASTLEVEL;
//make sure the player has the required herbs in inventory
if(m_targets.getItemTarget()->GetCount() < 5)
return SPELL_FAILED_NEED_MORE_ITEMS;
if(!LootTemplates_Milling.HaveLootFor(m_targets.getItemTargetEntry()))
return SPELL_FAILED_CANT_BE_MILLED;
break;
}
case SPELL_EFFECT_WEAPON_DAMAGE:
case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
{

View file

@ -276,6 +276,7 @@ class Spell
void EffectSkinning(uint32 i);
void EffectCharge(uint32 i);
void EffectProspecting(uint32 i);
void EffectMilling(uint32 i);
void EffectSendTaxi(uint32 i);
void EffectSummonCritter(uint32 i);
void EffectKnockBack(uint32 i);
@ -311,6 +312,8 @@ class Spell
void cast(bool skipCheck = false);
void finish(bool ok = true);
void TakePower();
uint8 CheckRuneCost(uint32 runeCostID);
void TakeRunePower();
void TakeReagents();
void TakeCastItem();
void TriggerSpell();

View file

@ -214,7 +214,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
&Spell::EffectNULL, //155 Allows you to equip two-handed axes, maces and swords in one hand, but you attack $49152s1% slower than normal.
&Spell::EffectNULL, //156 Add Socket
&Spell::EffectNULL, //157 create/learn random item/spell for profession
&Spell::EffectNULL, //158 milling
&Spell::EffectMilling, //158 milling
&Spell::EffectNULL //159 allow rename pet once again
};
@ -6114,6 +6114,28 @@ void Spell::EffectProspecting(uint32 /*i*/)
((Player*)m_caster)->SendLoot(itemTarget->GetGUID(), LOOT_PROSPECTING);
}
void Spell::EffectMilling(uint32 /*i*/)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
Player* p_caster = (Player*)m_caster;
if(!itemTarget || !(itemTarget->GetProto()->BagFamily & BAG_FAMILY_MASK_HERBS))
return;
if(itemTarget->GetCount() < 5)
return;
if( sWorld.getConfig(CONFIG_SKILL_MILLING))
{
uint32 SkillValue = p_caster->GetPureSkillValue(SKILL_INSCRIPTION);
uint32 reqSkillValue = itemTarget->GetProto()->RequiredSkillRank;
p_caster->UpdateGatherSkill(SKILL_INSCRIPTION, SkillValue, reqSkillValue);
}
((Player*)m_caster)->SendLoot(itemTarget->GetGUID(), LOOT_MILLING);
}
void Spell::EffectSkill(uint32 /*i*/)
{
sLog.outDebug("WORLD: SkillEFFECT");

View file

@ -2066,6 +2066,7 @@ void Unit::DoAttackDamage (Unit *pVictim, uint32 *damage, CleanDamage *cleanDama
{
case CLASS_WARRIOR:
case CLASS_ROGUE:
case CLASS_DEATH_KNIGHT:
maxLowEnd = 0.91; //If the attacker is a melee class then instead the lower value of 0.91
}

View file

@ -144,6 +144,7 @@ enum ShapeshiftForm
FORM_BERSERKERSTANCE = 0x13,
FORM_TEST = 0x14,
FORM_ZOMBIE = 0x15,
FORM_METAMORPHOSIS = 0x16,
FORM_FLIGHT_EPIC = 0x1B,
FORM_SHADOW = 0x1C,
FORM_FLIGHT = 0x1D,

View file

@ -406,24 +406,30 @@ void World::LoadConfigSettings(bool reload)
rate_values[RATE_HEALTH] = sConfig.GetFloatDefault("Rate.Health", 1);
if(rate_values[RATE_HEALTH] < 0)
{
sLog.outError("Rate.Health (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_HEALTH]);
sLog.outError("Rate.Health (%f) must be > 0. Using 1 instead.",rate_values[RATE_HEALTH]);
rate_values[RATE_HEALTH] = 1;
}
rate_values[RATE_POWER_MANA] = sConfig.GetFloatDefault("Rate.Mana", 1);
if(rate_values[RATE_POWER_MANA] < 0)
{
sLog.outError("Rate.Mana (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_POWER_MANA]);
sLog.outError("Rate.Mana (%f) must be > 0. Using 1 instead.",rate_values[RATE_POWER_MANA]);
rate_values[RATE_POWER_MANA] = 1;
}
rate_values[RATE_POWER_RAGE_INCOME] = sConfig.GetFloatDefault("Rate.Rage.Income", 1);
rate_values[RATE_POWER_RAGE_LOSS] = sConfig.GetFloatDefault("Rate.Rage.Loss", 1);
if(rate_values[RATE_POWER_RAGE_LOSS] < 0)
{
sLog.outError("Rate.Rage.Loss (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_POWER_RAGE_LOSS]);
sLog.outError("Rate.Rage.Loss (%f) must be > 0. Using 1 instead.",rate_values[RATE_POWER_RAGE_LOSS]);
rate_values[RATE_POWER_RAGE_LOSS] = 1;
}
rate_values[RATE_POWER_RUNICPOWER_INCOME] = sConfig.GetFloatDefault("Rate.RunicPower.Income", 1);
rate_values[RATE_POWER_RUNICPOWER_LOSS] = sConfig.GetFloatDefault("Rate.RunicPower.Loss", 1);
if(rate_values[RATE_POWER_RUNICPOWER_LOSS] < 0)
{
sLog.outError("Rate.RunicPower.Loss (%f) must be > 0. Using 1 instead.",rate_values[RATE_POWER_RUNICPOWER_LOSS]);
rate_values[RATE_POWER_RUNICPOWER_LOSS] = 1;
}
rate_values[RATE_POWER_FOCUS] = sConfig.GetFloatDefault("Rate.Focus", 1.0f);
rate_values[RATE_LOYALTY] = sConfig.GetFloatDefault("Rate.Loyalty", 1.0f);
rate_values[RATE_SKILL_DISCOVERY] = sConfig.GetFloatDefault("Rate.Skill.Discovery", 1.0f);
rate_values[RATE_DROP_ITEM_POOR] = sConfig.GetFloatDefault("Rate.Drop.Item.Poor", 1.0f);
rate_values[RATE_DROP_ITEM_NORMAL] = sConfig.GetFloatDefault("Rate.Drop.Item.Normal", 1.0f);
@ -630,10 +636,11 @@ void World::LoadConfigSettings(bool reload)
}
else
m_configs[CONFIG_MAX_PLAYER_LEVEL] = sConfig.GetIntDefault("MaxPlayerLevel", 60);
if(m_configs[CONFIG_MAX_PLAYER_LEVEL] > 255)
if(m_configs[CONFIG_MAX_PLAYER_LEVEL] > 100)
{
sLog.outError("MaxPlayerLevel (%i) must be in range 1..255. Set to 255.",m_configs[CONFIG_MAX_PLAYER_LEVEL]);
m_configs[CONFIG_MAX_PLAYER_LEVEL] = 255;
sLog.outError("MaxPlayerLevel (%i) must be in range 1..100. Set to 100.",m_configs[CONFIG_MAX_PLAYER_LEVEL]);
m_configs[CONFIG_MAX_PLAYER_LEVEL] = 100;
}
m_configs[CONFIG_START_PLAYER_LEVEL] = sConfig.GetIntDefault("StartPlayerLevel", 1);
@ -703,6 +710,7 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_SKILL_CHANCE_SKINNING_STEPS] = sConfig.GetIntDefault("SkillChance.SkinningSteps",75);
m_configs[CONFIG_SKILL_PROSPECTING] = sConfig.GetBoolDefault("SkillChance.Prospecting",false);
m_configs[CONFIG_SKILL_MILLING] = sConfig.GetBoolDefault("SkillChance.Milling",false);
m_configs[CONFIG_SKILL_GAIN_CRAFTING] = sConfig.GetIntDefault("SkillGain.Crafting", 1);
if(m_configs[CONFIG_SKILL_GAIN_CRAFTING] < 0)

View file

@ -168,6 +168,7 @@ enum WorldConfigs
CONFIG_LISTEN_RANGE_SAY,
CONFIG_LISTEN_RANGE_TEXTEMOTE,
CONFIG_LISTEN_RANGE_YELL,
CONFIG_SKILL_MILLING,
CONFIG_VALUE_COUNT
};
@ -178,6 +179,8 @@ enum Rates
RATE_POWER_MANA,
RATE_POWER_RAGE_INCOME,
RATE_POWER_RAGE_LOSS,
RATE_POWER_RUNICPOWER_INCOME,
RATE_POWER_RUNICPOWER_LOSS,
RATE_POWER_FOCUS,
RATE_SKILL_DISCOVERY,
RATE_DROP_ITEM_POOR,
@ -221,7 +224,6 @@ enum Rates
RATE_MINING_AMOUNT,
RATE_MINING_NEXT,
RATE_TALENT,
RATE_LOYALTY,
RATE_CORPSE_DECAY_LOOTED,
RATE_INSTANCE_RESET_TIME,
RATE_TARGET_POS_RECALCULATION_RANGE,

View file

@ -505,7 +505,12 @@ LogColors = ""
# Default: 3600 sec (1 hour)
#
# SkillChance.Prospecting
# For prospecting skillup not possible by default, but can be allowed as custom setting
# For prospecting skillup impossible by default, but can be allowed as custom setting
# Default: 0 - no skilups
# 1 - skilups possible
#
# SkillChance.Milling
# For milling skillup impossible by default, but can be allowed as custom setting
# Default: 0 - no skilups
# 1 - skilups possible
#
@ -555,6 +560,7 @@ MinPetitionSigns = 9
MaxGroupXPDistance = 74
MailDeliveryDelay = 3600
SkillChance.Prospecting = 0
SkillChance.Milling = 0
Event.Announce = 0
BeepAtStart = 1
Motd = "Welcome to the Massive Network Game Object Server."
@ -862,8 +868,9 @@ Visibility.Distance.Grey.Object = 10
# Rate.Mana
# Rate.Rage.Income
# Rate.Rage.Loss
# Rate.RunicPower.Income
# Rate.RunicPower.Loss
# Rate.Focus
# Rate.Loyalty
# Health and power regeneration and rage income from damage.
# Default: 1
#
@ -984,8 +991,9 @@ Rate.Health = 1
Rate.Mana = 1
Rate.Rage.Income = 1
Rate.Rage.Loss = 1
Rate.RunicPower.Income = 1
Rate.RunicPower.Loss = 1
Rate.Focus = 1
Rate.Loyalty = 1
Rate.Skill.Discovery = 1
Rate.Drop.Item.Poor = 1
Rate.Drop.Item.Normal = 1

View file

@ -1194,11 +1194,12 @@ struct SpellRangeEntry
struct SpellRuneCostEntry
{
uint32 ID;
uint32 bloodRuneCost;
uint32 frostRuneCost;
uint32 unholyRuneCost;
uint32 runePowerGain;
uint32 ID; // 0
uint32 RuneCost[3]; // 1-3 (0=blood, 1=frost, 2=unholy)
uint32 runePowerGain; // 4
bool NoRuneCost() const { return RuneCost[0] == 0 && RuneCost[1] == 0 && RuneCost[2] == 0; }
bool NoRunicPowerGain() const { return runePowerGain == 0; }
};
struct SpellShapeshiftEntry