forked from metin2/server
449 lines
9.1 KiB
C++
449 lines
9.1 KiB
C++
#include "stdafx.h"
|
||
#include "mining.h"
|
||
#include "char.h"
|
||
#include "char_manager.h"
|
||
#include "item_manager.h"
|
||
#include "item.h"
|
||
#include "config.h"
|
||
#include "db.h"
|
||
#include "log.h"
|
||
#include "skill.h"
|
||
|
||
namespace mining
|
||
{
|
||
enum
|
||
{
|
||
MAX_ORE = 18,
|
||
MAX_FRACTION_COUNT = 9,
|
||
ORE_COUNT_FOR_REFINE = 100,
|
||
};
|
||
|
||
struct SInfo
|
||
{
|
||
DWORD dwLoadVnum;
|
||
DWORD dwRawOreVnum;
|
||
DWORD dwRefineVnum;
|
||
};
|
||
|
||
SInfo info[MAX_ORE] =
|
||
{
|
||
{ 20047, 50601, 50621 },
|
||
{ 20048, 50602, 50622 },
|
||
{ 20049, 50603, 50623 },
|
||
{ 20050, 50604, 50624 },
|
||
{ 20051, 50605, 50625 },
|
||
{ 20052, 50606, 50626 },
|
||
{ 20053, 50607, 50627 },
|
||
{ 20054, 50608, 50628 },
|
||
{ 20055, 50609, 50629 },
|
||
{ 20056, 50610, 50630 },
|
||
{ 20057, 50611, 50631 },
|
||
{ 20058, 50612, 50632 },
|
||
{ 20059, 50613, 50633 },
|
||
{ 30301, 50614, 50634 },
|
||
{ 30302, 50615, 50635 },
|
||
{ 30303, 50616, 50636 },
|
||
{ 30304, 50617, 50637 },
|
||
{ 30305, 50618, 50638 },
|
||
};
|
||
|
||
int fraction_info[MAX_FRACTION_COUNT][3] =
|
||
{
|
||
{ 20, 1, 10 },
|
||
{ 30, 11, 20 },
|
||
{ 20, 21, 30 },
|
||
{ 15, 31, 40 },
|
||
{ 5, 41, 50 },
|
||
{ 4, 51, 60 },
|
||
{ 3, 61, 70 },
|
||
{ 2, 71, 80 },
|
||
{ 1, 81, 90 },
|
||
};
|
||
|
||
int PickGradeAddPct[10] =
|
||
{
|
||
3, 5, 8, 11, 15, 20, 26, 32, 40, 50
|
||
};
|
||
|
||
int SkillLevelAddPct[SKILL_MAX_LEVEL + 1] =
|
||
{
|
||
0,
|
||
1, 1, 1, 1, // 1 - 4
|
||
2, 2, 2, 2, // 5 - 8
|
||
3, 3, 3, 3, // 9 - 12
|
||
4, 4, 4, 4, // 13 - 16
|
||
5, 5, 5, 5, // 17 - 20
|
||
6, 6, 6, 6, // 21 - 24
|
||
7, 7, 7, 7, // 25 - 28
|
||
8, 8, 8, 8, // 29 - 32
|
||
9, 9, 9, 9, // 33 - 36
|
||
10, 10, 10, // 37 - 39
|
||
11, // 40
|
||
};
|
||
|
||
DWORD GetRawOreFromLoad(DWORD dwLoadVnum)
|
||
{
|
||
for (int i = 0; i < MAX_ORE; ++i)
|
||
{
|
||
if (info[i].dwLoadVnum == dwLoadVnum)
|
||
return info[i].dwRawOreVnum;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
DWORD GetRefineFromRawOre(DWORD dwRawOreVnum)
|
||
{
|
||
for (int i = 0; i < MAX_ORE; ++i)
|
||
{
|
||
if (info[i].dwRawOreVnum == dwRawOreVnum)
|
||
return info[i].dwRefineVnum;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int GetFractionCount()
|
||
{
|
||
int r = Random::get(1, 100);
|
||
|
||
for (int i = 0; i < MAX_FRACTION_COUNT; ++i)
|
||
{
|
||
if (r <= fraction_info[i][0])
|
||
return Random::get(fraction_info[i][1], fraction_info[i][2]);
|
||
else
|
||
r -= fraction_info[i][0];
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
void OreDrop(LPCHARACTER ch, DWORD dwLoadVnum)
|
||
{
|
||
DWORD dwRawOreVnum = GetRawOreFromLoad(dwLoadVnum);
|
||
|
||
int iFractionCount = GetFractionCount();
|
||
|
||
if (iFractionCount == 0)
|
||
{
|
||
sys_err("Wrong ore fraction count");
|
||
return;
|
||
}
|
||
|
||
LPITEM item = ITEM_MANAGER::instance().CreateItem(dwRawOreVnum, GetFractionCount());
|
||
|
||
if (!item)
|
||
{
|
||
sys_err("cannot create item vnum %d", dwRawOreVnum);
|
||
return;
|
||
}
|
||
|
||
PIXEL_POSITION pos;
|
||
pos.x = ch->GetX() + Random::get(-200, 200);
|
||
pos.y = ch->GetY() + Random::get(-200, 200);
|
||
|
||
item->AddToGround(ch->GetMapIndex(), pos);
|
||
item->StartDestroyEvent();
|
||
item->SetOwnership(ch, 15);
|
||
|
||
DBManager::instance().SendMoneyLog(MONEY_LOG_DROP, item->GetVnum(), item->GetCount());
|
||
}
|
||
|
||
int GetOrePct(LPCHARACTER ch)
|
||
{
|
||
int defaultPct = 20;
|
||
int iSkillLevel = ch->GetSkillLevel(SKILL_MINING);
|
||
|
||
LPITEM pick = ch->GetWear(WEAR_WEAPON);
|
||
|
||
if (!pick || pick->GetType() != ITEM_PICK)
|
||
return 0;
|
||
|
||
return defaultPct + SkillLevelAddPct[std::clamp(iSkillLevel, 0, 40)] + PickGradeAddPct[std::clamp(pick->GetRefineLevel(), 0, 9)];
|
||
}
|
||
|
||
EVENTINFO(mining_event_info)
|
||
{
|
||
DWORD pid;
|
||
DWORD vid_load;
|
||
|
||
mining_event_info()
|
||
: pid( 0 )
|
||
, vid_load( 0 )
|
||
{
|
||
}
|
||
};
|
||
|
||
// REFINE_PICK
|
||
bool Pick_Check(CItem& item)
|
||
{
|
||
if (item.GetType() != ITEM_PICK)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
int Pick_GetMaxExp(CItem& pick)
|
||
{
|
||
return pick.GetValue(2);
|
||
}
|
||
|
||
int Pick_GetCurExp(CItem& pick)
|
||
{
|
||
return pick.GetSocket(0);
|
||
}
|
||
|
||
void Pick_IncCurExp(CItem& pick)
|
||
{
|
||
int cur = Pick_GetCurExp(pick);
|
||
pick.SetSocket(0, cur + 1);
|
||
}
|
||
|
||
void Pick_MaxCurExp(CItem& pick)
|
||
{
|
||
int max = Pick_GetMaxExp(pick);
|
||
pick.SetSocket(0, max);
|
||
}
|
||
|
||
bool Pick_Refinable(CItem& item)
|
||
{
|
||
if (Pick_GetCurExp(item) < Pick_GetMaxExp(item))
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
bool Pick_IsPracticeSuccess(CItem& pick)
|
||
{
|
||
return (Random::get(1,pick.GetValue(1))==1);
|
||
}
|
||
|
||
bool Pick_IsRefineSuccess(CItem& pick)
|
||
{
|
||
return (Random::get(1,100) <= pick.GetValue(3));
|
||
}
|
||
|
||
int RealRefinePick(LPCHARACTER ch, LPITEM item)
|
||
{
|
||
if (!ch || !item)
|
||
return 2;
|
||
|
||
LogManager& rkLogMgr = LogManager::instance();
|
||
ITEM_MANAGER& rkItemMgr = ITEM_MANAGER::instance();
|
||
|
||
if (!Pick_Check(*item))
|
||
{
|
||
sys_err("REFINE_PICK_HACK pid(%u) item(%s:%d) type(%d)", ch->GetPlayerID(), item->GetName(), item->GetID(), item->GetType());
|
||
rkLogMgr.RefineLog(ch->GetPlayerID(), item->GetName(), item->GetID(), -1, 1, "PICK_HACK");
|
||
return 2;
|
||
}
|
||
|
||
CItem& rkOldPick = *item;
|
||
|
||
if (!Pick_Refinable(rkOldPick))
|
||
return 2;
|
||
|
||
int iAdv = rkOldPick.GetValue(0) / 10;
|
||
|
||
if (rkOldPick.IsEquipped() == true)
|
||
return 2;
|
||
|
||
if (Pick_IsRefineSuccess(rkOldPick))
|
||
{
|
||
rkLogMgr.RefineLog(ch->GetPlayerID(), rkOldPick.GetName(), rkOldPick.GetID(), iAdv, 1, "PICK");
|
||
|
||
LPITEM pkNewPick = ITEM_MANAGER::instance().CreateItem(rkOldPick.GetRefinedVnum(), 1);
|
||
if (pkNewPick)
|
||
{
|
||
BYTE bCell = rkOldPick.GetCell();
|
||
rkItemMgr.RemoveItem(item, "REMOVE (REFINE PICK)");
|
||
pkNewPick->AddToCharacter(ch, TItemPos(INVENTORY, bCell));
|
||
LogManager::instance().ItemLog(ch, pkNewPick, "REFINE PICK SUCCESS", pkNewPick->GetName());
|
||
return 1;
|
||
}
|
||
|
||
return 2;
|
||
}
|
||
else
|
||
{
|
||
rkLogMgr.RefineLog(ch->GetPlayerID(), rkOldPick.GetName(), rkOldPick.GetID(), iAdv, 0, "PICK");
|
||
|
||
LPITEM pkNewPick = ITEM_MANAGER::instance().CreateItem(rkOldPick.GetValue(4), 1);
|
||
|
||
if (pkNewPick)
|
||
{
|
||
BYTE bCell = rkOldPick.GetCell();
|
||
rkItemMgr.RemoveItem(item, "REMOVE (REFINE PICK)");
|
||
pkNewPick->AddToCharacter(ch, TItemPos(INVENTORY, bCell));
|
||
rkLogMgr.ItemLog(ch, pkNewPick, "REFINE PICK FAIL", pkNewPick->GetName());
|
||
return 0;
|
||
}
|
||
|
||
return 2;
|
||
}
|
||
}
|
||
|
||
void CHEAT_MAX_PICK(LPCHARACTER ch, LPITEM item)
|
||
{
|
||
if (!item)
|
||
return;
|
||
|
||
if (!Pick_Check(*item))
|
||
return;
|
||
|
||
CItem& pick = *item;
|
||
Pick_MaxCurExp(pick);
|
||
|
||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>õ<EFBFBD><C3B5><EFBFBD> <20>ִ<EFBFBD>(%d)<29><> <20>Ǿ<EFBFBD><C7BE><EFBFBD><EFBFBD>ϴ<EFBFBD>."), Pick_GetCurExp(pick));
|
||
}
|
||
|
||
void PracticePick(LPCHARACTER ch, LPITEM item)
|
||
{
|
||
if (!item)
|
||
return;
|
||
|
||
if (!Pick_Check(*item))
|
||
return;
|
||
|
||
CItem& pick = *item;
|
||
if (pick.GetRefinedVnum()<=0)
|
||
return;
|
||
|
||
if (Pick_IsPracticeSuccess(pick))
|
||
{
|
||
|
||
if (Pick_Refinable(pick))
|
||
{
|
||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD>̰<EFBFBD> <20>ִ<EFBFBD> <20><><EFBFBD>õ<EFBFBD><C3B5><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>Ͽ<EFBFBD><CFBF><EFBFBD><EFBFBD>ϴ<EFBFBD>."));
|
||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>۸<EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><EFBFBD>̷<EFBFBD> <20><><EFBFBD><EFBFBD><D7B7>̵<EFBFBD> <20><> <20><> <20>ֽ<EFBFBD><D6BD>ϴ<EFBFBD>."));
|
||
}
|
||
else
|
||
{
|
||
Pick_IncCurExp(pick);
|
||
|
||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>õ<EFBFBD><C3B5><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>Ͽ<EFBFBD><CFBF><EFBFBD><EFBFBD>ϴ<EFBFBD>! (%d/%d)"),
|
||
Pick_GetCurExp(pick), Pick_GetMaxExp(pick));
|
||
|
||
if (Pick_Refinable(pick))
|
||
{
|
||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD>̰<EFBFBD> <20>ִ<EFBFBD> <20><><EFBFBD>õ<EFBFBD><C3B5><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>Ͽ<EFBFBD><CFBF><EFBFBD><EFBFBD>ϴ<EFBFBD>."));
|
||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>۸<EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><EFBFBD>̷<EFBFBD> <20><><EFBFBD><EFBFBD><D7B7>̵<EFBFBD> <20><> <20><> <20>ֽ<EFBFBD><D6BD>ϴ<EFBFBD>."));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// END_OF_REFINE_PICK
|
||
|
||
EVENTFUNC(mining_event)
|
||
{
|
||
mining_event_info* info = dynamic_cast<mining_event_info*>( event->info );
|
||
|
||
if ( info == NULL )
|
||
{
|
||
sys_err( "mining_event_info> <Factor> Null pointer" );
|
||
return 0;
|
||
}
|
||
|
||
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(info->pid);
|
||
LPCHARACTER load = CHARACTER_MANAGER::instance().Find(info->vid_load);
|
||
|
||
if (!ch)
|
||
return 0;
|
||
|
||
ch->mining_take();
|
||
|
||
LPITEM pick = ch->GetWear(WEAR_WEAPON);
|
||
|
||
// REFINE_PICK
|
||
if (!pick || !Pick_Check(*pick))
|
||
{
|
||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD≯<EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20>ʾƼ<CABE> Ķ <20><> <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>."));
|
||
return 0;
|
||
}
|
||
// END_OF_REFINE_PICK
|
||
|
||
if (!load)
|
||
{
|
||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<EFBFBD><EFBFBD><EFBFBD>̻<EFBFBD> ij<><C4B3> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD>."));
|
||
return 0;
|
||
}
|
||
|
||
int iPct = GetOrePct(ch);
|
||
|
||
if (Random::get(1, 100) <= iPct)
|
||
{
|
||
OreDrop(ch, load->GetRaceNum());
|
||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ä<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>Ͽ<EFBFBD><CFBF><EFBFBD><EFBFBD>ϴ<EFBFBD>."));
|
||
}
|
||
else
|
||
{
|
||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ä<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>Ͽ<EFBFBD><CFBF><EFBFBD><EFBFBD>ϴ<EFBFBD>."));
|
||
}
|
||
|
||
PracticePick(ch, pick);
|
||
|
||
return 0;
|
||
}
|
||
|
||
LPEVENT CreateMiningEvent(LPCHARACTER ch, LPCHARACTER load, int count)
|
||
{
|
||
mining_event_info* info = AllocEventInfo<mining_event_info>();
|
||
info->pid = ch->GetPlayerID();
|
||
info->vid_load = load->GetVID();
|
||
|
||
return event_create(mining_event, info, PASSES_PER_SEC(2 * count));
|
||
}
|
||
|
||
bool OreRefine(LPCHARACTER ch, LPCHARACTER npc, LPITEM item, int cost, int pct, LPITEM metinstone_item)
|
||
{
|
||
if (!ch || !npc)
|
||
return false;
|
||
|
||
if (item->GetOwner() != ch)
|
||
{
|
||
sys_err("wrong owner");
|
||
return false;
|
||
}
|
||
|
||
if (item->GetCount() < ORE_COUNT_FOR_REFINE)
|
||
{
|
||
sys_err("not enough count");
|
||
return false;
|
||
}
|
||
|
||
DWORD dwRefinedVnum = GetRefineFromRawOre(item->GetVnum());
|
||
|
||
if (dwRefinedVnum == 0)
|
||
return false;
|
||
|
||
ch->SetRefineNPC(npc);
|
||
item->SetCount(item->GetCount() - ORE_COUNT_FOR_REFINE);
|
||
int iCost = ch->ComputeRefineFee(cost, 1);
|
||
|
||
if (ch->GetGold() < iCost)
|
||
return false;
|
||
|
||
ch->PayRefineFee(iCost);
|
||
|
||
if (metinstone_item)
|
||
ITEM_MANAGER::instance().RemoveItem(metinstone_item, "REMOVE (MELT)");
|
||
|
||
if (Random::get(1, 100) <= pct)
|
||
{
|
||
ch->AutoGiveItem(dwRefinedVnum, 1);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
bool IsVeinOfOre (DWORD vnum)
|
||
{
|
||
for (int i = 0; i < MAX_ORE; i++)
|
||
{
|
||
if (info[i].dwLoadVnum == vnum)
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
}
|
||
|