Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


Perseus last won the day on November 16 2021

Perseus had the most liked content!

Community Reputation

98 Good

About Perseus

  • Rank

Recent Profile Visitors

4,144 profile views
  1. Perseus

    [UI] Loot Filter

    I'm not selling the mob drop code, as it is not mine, sorry.
  2. Perseus

    [UI] Loot Filter

    Hello, I built this for for a server but I think it would be useful for the rest of the community too. This feature basically allows players to hide certain dropped items on the ground. They can use this to specifically filter out garbage/low tier drops, and only see rare drops and pick them up. While 'hidden', the items will also not get picked up by Ctrl + A, so they can freely pick up all items that they are interested in without filling their inventory with garbage. I made this feature available through the drop list of a monster on the CO client. You can choose to expose it however you wish. I'll add the CO-client specific changes at the end so you can use it for that if you wish. I've attached a gif of what the feature looks like. Code - GlobalVar.cpp At the top, add - #include "LootFilter.h" a bit further down - CGameApp* g_pGameApp = NULL; LootFilter* g_lootFilter = NULL; Create the following two files in the src directory LootFilter.h #include "stdafx.h" #include <map> class LootFilter { public: LootFilter(); ~LootFilter(); map<int, bool> GetFilteredItems() { return lootFilter; } void AddFilteredItem(unsigned long itemId); void RemoveFilteredItem(unsigned long itemId); bool HasFilteredItem(unsigned long itemId); void PrintAllItems(); private: map<int, bool> lootFilter; }; extern LootFilter* g_lootFilter; LootFilter.cpp #include "stdafx.h" #include <map> #include "LootFilter.h" #include "GameApp.h" #include <string> #include "log.h" LootFilter::LootFilter() { if (!CreateDirectory("user", NULL)) { DWORD dw = GetLastError(); if ( dw != 183 ) { return; } } ifstream lootFilterFile; lootFilterFile.open("user\\lootfilter.txt"); string itemIdReader; if (lootFilterFile.is_open()) { while (getline(lootFilterFile, itemIdReader)) { if ( itemIdReader.length() == 0) { continue; } int itemId = atoi(itemIdReader.c_str()); lootFilter.insert(pair<int, bool>(itemId, TRUE)); } } } void LootFilter::AddFilteredItem(unsigned long itemId) { lootFilter.insert(pair<int, bool>(itemId, TRUE)); ofstream lootFilterFile; char itemIdBuffer[10]; itoa(itemId, itemIdBuffer, 10); lootFilterFile.open("user\\lootfilter.txt", ios_base::app); lootFilterFile << string(itemIdBuffer) << std::endl; lootFilterFile.close(); } void LootFilter::RemoveFilteredItem(unsigned long itemId) { lootFilter.erase(itemId); string line; ifstream in; in.open("user\\lootfilter.txt"); ofstream temp; temp.open("user\\lootfilter_new.txt"); while (getline(in, line)) { char itemIdBuffer[10]; itoa(itemId, itemIdBuffer, 10); if (line.compare(string(itemIdBuffer)) != 0) { temp << line << std::endl; } } in.close(); temp.close(); remove("user\\lootfilter.txt"); rename("user\\lootfilter_new.txt", "user\\lootfilter.txt"); } bool LootFilter::HasFilteredItem(unsigned long itemId) { if ( lootFilter.find(itemId) == lootFilter.end() ) { return false; } return true; } void LootFilter::PrintAllItems() { map<int,bool>::iterator it = lootFilter.begin(); for(it = lootFilter.begin(); it != lootFilter.end(); it++) { g_pGameApp->SysInfo("Filtered item %d %d", it->first, it->second); } } In Main.cpp At the top - #include "LootFilter.h" A bit further down, look for g_pGameApp - g_pGameApp = new CGameApp(); g_lootFilter = new LootFilter(); Scene.h Add this function declaration below the other ones for CSceneItem (after line 217 in CO) CSceneItem* FilterItemsByItemID(unsigned long id, bool shouldHide); Scene.cpp Add this function somewhere in the file - CSceneItem* CGameScene::FilterItemsByItemID(unsigned long id, bool shouldHide) { CSceneItem* pItem; for(int i = 0; i < _nSceneItemCnt; i++) { pItem = &_pSceneItemArray[i]; if (pItem->IsValid()) { CItemRecord* itemData = pItem->GetItemInfo(); if ( itemData ) { if (itemData->lID == id ) { pItem->SetHide(shouldHide); } } } } if ( !shouldHide ) { g_lootFilter->RemoveFilteredItem(id); } else { g_lootFilter->AddFilteredItem(id); } return NULL; } SceneItem.cpp Replace the CSceneItem::Render function with this - void CSceneItem::Render() { if( GetScene()->GetTerrain() ) { if(!GetScene()->GetTerrain()->IsPointVisible((float)_nCurX / 100.0f, (float)_nCurY / 100.0f)) return; } if ( _pItemInfo && g_lootFilter->HasFilteredItem(_pItemInfo->lID) ) { SetHide(TRUE); setIsShowName(false); return; } CSceneNode::Render(); // g_Render.EnableMipmap(FALSE); MPSceneItem::Render(); } In the CSceneItem::setIsShowName function, add this line at the top _IsShowName = v; Make sure to include the LootFilter.h and LootFilter.cpp files in your kop.vcxproj or kop.vcproj files I had .vcxproj, so - kop.vcxproj Look for <ClInclude Include="..\src\GameWG.h" /> and add <ClInclude Include="..\src\LootFilter.h" /> after it. kop.vcxproj.filters Look for <ClCompile Include="..\src\SoundCommon.cpp" /> and add <ClCompile Include="..\src\LootFilter.cpp" /> after it. Look for <ClInclude Include="..\src\SoundManager.h" /> and add <ClInclude Include="..\src\LootFilter.h" /> after it. This should let you compile the given code. For people who are NOT using CO files, if you want to trigger the filter for a particular item, you need to have the item ID, and then do something like this - CGameApp::GetCurScene()->FilterItemsByItemID(itemId, true); If you are using the CO client and have the monster drop UI available, I'll post how to make changes to that UI in a follow-up post below I offer NO support for any issues you may have past basic compilation. It's up to you to figure out how to integrate this if you want it.
  3. Hi, deleted your other thread. Please keep stuff limited to a thread at a time
  4. Perseus

    Lab Parser (Tool)

    Hi, I've been working on a tool that can be used to convert .lab animation files to a format that can be used by all modern 3d animation software like Blender. At the moment, the conversion from .lab to .dae (the format I chose, collada) is functional, and I am working on the reverse conversion so your changes to the animation keyframes and joints can be reflected in the game. I've hit a small snag due to my limited understanding of 3d animation and dummy objects which would take some time for me to research and resolve, so if you are versed with these things and would like to contribute, please leave a message here or send me a DM. The tool can be downloaded here: https://github.com/Perseus/lab-parser/releases/tag/0.1.0. The source code (if you're interested) and usage instructions can be found here: https://github.com/Perseus/lab-parser Converted file example:
  5. I haven't touched this in a bit, but the calculated stats (max atk, hp) have different scaling per class. You can find the scalings and the final attribute calculation in AttrCalculate.lua, code like this - Mxhp_con_rad1[JOB_TYPE_XINSHOU], Mxhp_con_rad2[JOB_TYPE_XINSHOU], Mxhp_lv_rad[JOB_TYPE_XINSHOU] = 3 , 2 , 15 --���hp������Ӱ��ϵ��������ϵ�����ȼ�ϵ���������� Mxsp_sta_rad1[JOB_TYPE_XINSHOU], Mxsp_sta_rad2[JOB_TYPE_XINSHOU], Mxsp_lv_rad[JOB_TYPE_XINSHOU] = 1 , 0 , 3 --���sp������Ӱ��ϵ��������ϵ��������ϵ�����ȼ�ϵ�� Mnatk_str_rad1[JOB_TYPE_XINSHOU], Mnatk_str_rad2[JOB_TYPE_XINSHOU], Mnatk_dex_rad1[JOB_TYPE_XINSHOU], Mnatk_dex_rad2[JOB_TYPE_XINSHOU] = 1.5 , 0.4 , 0 , 0 --��С������������Ӱ��ϵ��������ϵ�� Mxatk_str_rad1[JOB_TYPE_XINSHOU], Mxatk_str_rad2[JOB_TYPE_XINSHOU], Mxatk_dex_rad1[JOB_TYPE_XINSHOU], Mxatk_dex_rad2[JOB_TYPE_XINSHOU] = 1.5 , 0.4 , 0 , 0 --��󹥻�������Ӱ��ϵ��������ϵ�� Def_con_rad1[JOB_TYPE_XINSHOU], Def_con_rad2[JOB_TYPE_XINSHOU] = 0.1 , 0.1 --����������Ӱ��ϵ����רעϵ�� Hit_dex_rad1[JOB_TYPE_XINSHOU], Hit_dex_rad2[JOB_TYPE_XINSHOU] = 0.6 , 0 --���е�����Ӱ��ϵ����רעϵ�����ȼ�ϵ������С������ Flee_agi_rad1[JOB_TYPE_XINSHOU], Flee_agi_rad2[JOB_TYPE_XINSHOU] = 0.6 , 0 --���ܵ�����Ӱ��ϵ��������ϵ�����ȼ�ϵ������С������ Mf_luk_rad[JOB_TYPE_XINSHOU] = 0.39 --�����ʵ�����Ӱ��ϵ��������ϵ�� Crt_luk_rad[JOB_TYPE_XINSHOU] = 0.31 --����������Ӱ��ϵ��������ϵ������С�����ʡ���󱩻��� Hrec_bsmxhp_rad[JOB_TYPE_XINSHOU], Hrec_con_rad[JOB_TYPE_XINSHOU] = 1/200 , 1/8 --hp�ظ�������Ӱ��ϵ�������hpϵ�����ȼ�ϵ����hp��С�ظ��ٶ� Srec_bsmxsp_rad[JOB_TYPE_XINSHOU], Srec_sta_rad[JOB_TYPE_XINSHOU] = 1/100 , 1/12 --sp�ظ�������Ӱ��ϵ�������spϵ�����ȼ�ϵ����sp��С�ظ��ٶ� Aspd_agi_rad[JOB_TYPE_XINSHOU] = 1.1 --����Ƶ�ʵ�����Ӱ��ϵ������������������ϵ������С����Ƶ�� -- Str_updata[JOB_TYPE_XINSHOU] = 0.2 Dex_updata[JOB_TYPE_XINSHOU] = 0.1 Con_updata[JOB_TYPE_XINSHOU] = 0.6 Agi_updata[JOB_TYPE_XINSHOU] = 0.1 Sta_updata[JOB_TYPE_XINSHOU] = 0.1 Luk_updata[JOB_TYPE_XINSHOU] = 0.1 shows the scaling for that job and you can see the final calculations in the `ExAttrCheck` function - local mxhp = math.floor(Con(role)*3 * Mxhp_con_rad1[job] + Mxhp_con_rad2[job] * math.pow( math.floor(Con(role)*3/20 ), 2) +Lv(role) * Mxhp_lv_rad[job] + 40) --[[�������mxhp]]-- local mxsp = math.floor(Sta(role)*3 * Mxsp_sta_rad1[job] + Mxsp_sta_rad2[job] * math.pow( math.floor(Sta(role)*3/20), 2) +Lv(role) * Mxsp_lv_rad[job] + 5) --[[�������mxsp]]-- local mnatk = math.floor( 0+ Str(role) * Mnatk_str_rad1[job] + Dex(role) * Mnatk_dex_rad1[job] + Mnatk_str_rad2[job] * math.pow(math.floor( Str(role)*4/20), 2 ) + Mnatk_dex_rad2[job] * math.pow(math.floor( Dex(role)*4/20), 2 ) ) --[[�������mnatk]]-- local mxatk = math.floor( 0+ Str(role) * Mxatk_str_rad1[job] + Dex(role) * Mxatk_dex_rad1[job] + Mxatk_str_rad2[job] * math.pow(math.floor( Str(role)*4/20), 2 ) + Mxatk_dex_rad2[job] * math.pow(math.floor( Dex(role)*4/20), 2 ) ) --[[�������mxatk]]-- local def = math.floor( Con(role)* 5 * Def_con_rad1[job] + Def_con_rad2[job] * math.floor( math.pow( Con(role) * 3 /20, 2) ) ) --[[�������def]]-- local hit = math.floor( Dex(role) * Hit_dex_rad1[job]) + Lv(role) * 2 + 5 --[[�������hit]]-- local flee = math.floor( Agi(role) * Flee_agi_rad1[job] ) + Lv(role) * 2 + 5 --[[�������flee]]-- local mf = 100 + math.floor( Luk(role)*3 * Mf_luk_rad[job] ) --[[�������mf]]-- local crt =11 + math.floor( Luk(role)*3 * Crt_luk_rad[job] ) --[[�������crt]]-- local hrec = math.max(math.max ( 2 * mxhp * Hrec_bsmxhp_rad[job] + Con(role)*3 * Hrec_con_rad[job] , 1) ,0) --[[�������hrec]]-- local srec = math.max((mxsp * Srec_bsmxsp_rad[job] + Sta(role)*3 * Srec_sta_rad[job])/2 , 1 ) --[[�������srec]]-- local aspd = math.floor ( 100000/ (math.min ( math.floor( 65 + Agi(role) * Aspd_agi_rad[job] ) , 300 ) ) ) --[[�������aspd]]--
  6. I'm not sure how good it would be to host on AWS. They have a basic DDoS protection plan for everyone but their advanced one has a one-year commitment and costs 3k USD. It might be better to look for a dedicated host in the long run. But, if you want to have a go setting everything up in AWS, you can use - A Windows Server instance on EC2. You could go for a t2.small or a t2.medium depending on your usage requirements. For testing purposes a t2.small machine should suffice Their managed DB service (RDS) has MSSQL available on it (I'm using this for hosting the public version of topcms) Combine the two above and you should be good to go
  7. Perseus

    [0.1.0] topCMS

    Just to give an update on what I'm working on apart from fixing bugs and stuff: Payment One of the things on my list was to have an automatic payment system that awarded mall points the moment the payment was done instead of having to go through the painful flow of them donating, sending you an email and then finally getting the points. I plan to integrate the following 2 payment providers initially: PayPal Xsolla Action Log (for admins) Another one was an easily viewable/searchable log of all "important" actions taken by users or admin accounts on the site. I've already kind of laid down the foundation for this (you can see log files in the api folder for certain actions like mall purchases, category/item creations etc.). I plan to improve this and make it easy to keep track of what's going on on the website. Server Database This is nothing novel, but I really like how PKO has done the databases of monsters, items etc. available on the website. It's really useful, so I plan to implement something similar on this as well. This would be a pretty big feature, would contain things like portal opening times etc. as well. Some ideas- Pirate Clubs This has been on my mind for a while, I haven't fully thought out how the feature would work, I remember playing original ToP and joining these clubs but it was a while ago and I don't remember exactly how the implementation was on it. Definitely looking for thoughts/ideas on how this could be approached Media Gallery I'm not really sure how high the demand/utilization of something like this is, but I'd be open to implementing a media gallery for players to share stuff on if the interest is high enough on this. Definitely looking for feedback/suggestions on all of the above and anything else you would like to see.
  8. Perseus

    [0.1.0] topCMS

    Hi, as you know I previously released a new website for Tales of Pirates. I had planned to make updates to that one but due to college and graduation and everything, I ended up not really working on it again. Long story short, here's a new version of the website that I had made, with more features and something that I'll actually be updating regularly according to community response. The features that it currently has: Basic site management ( news, downloads, authors etc.) Ranking (Guild and Player) A bare-bones admin panel that I plan to extend A functional item mall and award center Logging system ( I have the base for this set up but no UI. I'll be adding a UI which will allow you to look through any "critical" actions taken by GMs or players on the website ) The design isn't the best (I'm not a designer) so I'm open to any design feedback that you guys have. Here's a few screenshots of what it looks like: Installation Instructions You're going to need NodeJS for this. You can find it here: https://nodejs.org/en/ You can download the latest LTS version (12.17.0) and install it. Then, you need to open your command prompt (run it as admin), and `cd` into both the `api` and `web` folders and run npm install (For people who aren't well versed with the command prompt) : "cd" is a command to change your current directory in the command prompt. If you have downloaded and extracted the website in the F: drive inside a "topcms" folder for example, the commands you would run after opening a command prompt are: F: cd topcms cd api npm install cd .. cd web npm install (".." is used to go to a directory that contains the current directory (basically go one up from where you currently are )) Next, `cd` into the `api` folder and run npm run install-wizard This should start an installation wizard on `localhost:4000` that you can go through and it'll set the website up for you. Next, to run the website, cd into the `api` folder and run pm2 start dist/server.js and the website should start at port 3000. (You can access it by going to `localhost:3000`) If you want to run it on port 80, go into the `api` folder and look for a `.env`. At the end, write port=80 pm2 is a process manager for node, which is used to run production applications and handles a lot of things that make it easy to run (automatically restarts the app if it crashes etc) Some pm2 basics: run pm2 start dist/server.js from inside the API directory to start your server run pm2 list to see the currently running processes (you should see your server running if you do this) run pm2 kill to stop any processes that are currently running. You need to do this and then "pm2 start" again if you want to restart your server. There are a few more configuration related things that I'll be putting up here but I hoped to get it into your hands so I could iron out any bugs and get this ready to be used by everyone. Please let me know if you run into any issues or if anything needs to be made clear here.
  9. In SQL Configuration Manager, check the `SQL Server Network Configuration` Tab. Is TCP/IP Enabled there? If not, enable it. If it is enabled, double click the TCP/IP protocol and go to the "IP Addresses" tab. Look for the entry which has IP Address as your local IP ( and make sure that's enabled as well.
  10. RE: Moving away from MD5. I've been wanting to remove the "originalPassword" column from the db and using bcrypt to hash the passwords instead. I have not read the AccountServer code extensively, but I'm assuming that most of the user login functionality happens through that. Are you aware of any place where the "originalPassword" field is being used? It's a travesty that passwords are being stored in cleartext in a database.
  11. I feel like he was asking for a way to dupe rather than a way to prevent it.
  12. The crash is due to the music. Convert the music files to .ogg (if i remember correctly) or just delete the entire music folder
  13. If you want to put up a link for the git repo, I'd be happy to contribute.
  14. Perseus

    [1.0.0] topCMS

    @1g0rS1lv4 ^
  15. Perseus

    [1.0.0] topCMS

    I'm working on it
  • Create New...