- CDrop* CWorldServer::GetDrop( CMonster* thismon )
for the moment we will leave the old drops code in place until we are sure it is all working properly
The main function
in serverfunctions .cpp
find function
After entire function add
- // Build Drop the PY way
- CDrop* CWorldServer::GetPYDrop( CMonster* thismon, UINT droptype )
- { //if droptype = 1 then it is a normal drop. if it is 2 then it is a potential side drop.
- Log( MSG_INFO, "PYDrops function selected" );
- Log( MSG_INFO, "monster is %i", thismon->montype );
- if(droptype == 2) // monster is still alive
- {
- // this part of the function reserved for the later addition of side drops
- //return NULL; //temporary bypass for the side drop function
- // kicks it straight back if the monster is not dead
- if(thismon->thisnpc->side != 0) //perhaps we get a side drop??
- {
- if(GServer->RandNumber(0,100) < thismon->thisnpc->sidechance)
- {
- droptype = thismon->thisnpc->side;
- }
- else
- {
- return NULL; //No drop this time
- }
- }
- else
- {
- return NULL; //No drop this time
- }
- }
- CDrop* newdrop = new (nothrow) CDrop;
- if(newdrop==NULL)
- {
- Log(MSG_WARNING, "Error allocing memory [getdrop]" );
- return NULL;
- }
- newdrop->clientid = GetNewClientID( );
- newdrop->posMap = thismon->Position->Map;
- newdrop->pos = RandInCircle( thismon->Position->current, 3 );
- newdrop->droptime = time(NULL);
- newdrop->owner = thismon->MonsterDrop->firsthit;
- newdrop->thisparty = thismon->thisparty;
- ClearItem(newdrop->item);
- CPlayer* thisclient = GServer->GetClientByCID(thismon->MonsterDrop->firsthit);
- // code to modify drop chance for different levels
- //float charm = 0;
- //float droprate = thismon->thisnpc->dropchance;
- float droprate = Config.DROP_RATE;
- float leveldif = (float)thismon->thisnpc->level - (float)thisclient->Stats->Level;
- float dropchance = (droprate + (droprate * 0.01 * leveldif));
- if(dropchance < 10) dropchance = 10; //always a small chance of a drop even when the mob is more than 20 levels beneath your own
- if(thismon->thisnpc->level == 1)
- dropchance = 80;
- if (GServer->RandNumber(0, 100)> dropchance)
- return NULL; // no drop here. not this time anyway.
- CItemType prob[MDropList.size()];
- bool isdrop = false;
- int n = 0;
- int test = 0;
- long int probmax = 0;
- int itemnumber[MDropList.size()];
- int itemtype[MDropList.size()];
- int probability[MDropList.size()];
- int alternate[MDropList.size()][8];
- if( thismon->IsGhost())
- {
- // Stuff to do if the mob is a ghost of any type
- int selection = 1 + rand()%10;
- if( selection <= 5 )
- {
- newdrop->type = 1; //Drop Zuly.
- newdrop->amount = thismon->thisnpc->level * 5 * Config.ZULY_RATE + RandNumber( 1, 10 );
- return newdrop;
- }
- else
- {
- for(int i=0; i<SkillbookList.size( ); i++)
- {
- newdrop->type = 2;
- CMDrops* thisdrop = GServer->SkillbookList.at(i);
- if(thisdrop->level_min <= thismon->thisnpc->level && thisdrop->level_max >= thismon->thisnpc->level)
- {
- itemnumber[n] = thisdrop->itemnum;
- itemtype[n] = thisdrop->itemtype;
- probability[n] = thisdrop->prob;
- probmax += thisdrop->prob;
- n++;
- }
- }
- }
- }
- else // Stuff to do if the mob isn't a ghost
- {
- int dropmode = 0;
- int randv = RandNumber( 1, 100);
- // Each monster has its own rates for zuly and item drops defined in the database
- if(randv > thismon->thisnpc->item) return NULL; // did not qualify to drop anything this time
- if(randv <= thismon->thisnpc->money) // zuly drop instead of item drop
- {
- newdrop->type = 1; //Drop Zuly. Now changes for individual characters if they have items with Zuly Rate stats
- newdrop->amount = thismon->thisnpc->level * 5 * Config.ZULY_RATE + RandNumber( 1, 10 );
- return newdrop;
- }
- // this means it is an item drop
- randv = RandNumber( 1, 100);
- if(randv > 70) // 30% map drop
- {
- dropmode = 1; // map drop selected
- }
- else if(randv > 30) // 40% mob drop
- {
- dropmode = 2; //mob drop selected
- }
- else // 30% level drop
- {
- dropmode = 3; //leveldrop selected
- }
- int randomdrop = GServer->RandNumber(1, 100);
- //Log(MSG_INFO, "Mob type %i. Map = %i. Level = %i", thismon->montype, thismon->Position->Map,thismon->thisnpc->level);
- for(int i=0; i<MDropList.size( ); i++)
- {
- isdrop=false;
- CMDrops* thisdrop = GServer->MDropList.at(i);
- if(thisdrop->mob == thismon->montype && dropmode == 2) // monster drop
- {
- test = GServer->RandNumber(1, 1000);
- if(test < thisdrop->prob)
- {
- isdrop = true;
- }
- }
- if(thisdrop->map == thismon->Position->Map && dropmode == 1) // map drop
- {
- test = GServer->RandNumber(1, 1000);
- if(thismon->thisnpc->level == 1)
- test = GServer->RandNumber(1, 10000); // make it less likely to get map drops from event mobs
- if(test < thisdrop->prob)
- {
- isdrop = true;
- }
- }
- if(thismon->thisnpc->level >= thisdrop->level_min && thismon->thisnpc->level <= thisdrop->level_max && dropmode == 3)
- {
- //Log(MSG_INFO, "Level drop selected. type %i number %i", thisdrop->itemtype, thisdrop->itemnum );
- test = GServer->RandNumber(1, 1000);
- if(test < thisdrop->prob)
- {
- isdrop = true;
- }
- //else
- }
- if(isdrop == true)
- {
- if(droptype != 1) //side drops only. Skip if the item is not a match for side type
- {
- if(itemtype[n] != droptype)continue;
- }
- //droptype 1 is a regular drop
- itemnumber[n] = thisdrop->itemnum;
- itemtype[n] = thisdrop->itemtype;
- //probability[n] = thisdrop->prob;
- alternate[n][0] = 0;
- for(int i=1;i<8;i++)
- {
- alternate[n][i] = thisdrop->alt[i];
- }
- n++;
- }
- }
- }
- int newn = n;
- if(n == 0)
- return NULL;
- int maxitems = n;
- // randomize the item from the list
- n = GServer->RandNumber(0, maxitems);
- newdrop->item.itemnum = itemnumber[n];
- newdrop->item.itemtype = itemtype[n];
- newdrop->type = 2;
- newdrop->item.lifespan = 10 + rand()%80;
- float dmod = 0; //random number from 0 to 100 made up of 4 sub numbers to keep
- //the average value near to 50
- for(int i=0; i<4; i++)
- {
- float r1 = rand()%20;
- dmod += r1;
- }
- newdrop->item.durability = 10 + (int)dmod;
- if( newdrop->item.itemtype == 8 || newdrop->item.itemtype == 9 )
- {
- //This probability is now configurable from WorldServer.conf
- int psocked = rand()%101; //Probability of finding a socketed item
- if( psocked < Config.SlotChance) //default should be around 5% needs to be rare
- {
- newdrop->item.socketed = true;
- }
- else
- {
- newdrop->item.socketed = false;
- }
- }
- else
- {
- newdrop->item.socketed = false;
- }
- //Log( MSG_INFO, "Socket are OK");
- newdrop->item.appraised = false;
- newdrop->item.stats = 0;
- newdrop->item.count = 1;
- //int chamod = 0;
- int chamod = (int)floor(thisclient->Attr->Cha / 30);
- if(chamod <0) chamod = 0;
- int basedrop = 6 + chamod; //Base number of items to be dropped. add CHA to increase this.
- if( newdrop->item.itemtype == 10 || newdrop->item.itemtype == 12 )
- {
- newdrop->item.count = RandNumber(0, basedrop);
- if(thismon->thisnpc->level == 1 && newdrop->item.count > 6) newdrop->item.count = 6; //limit the drop rate of items from level 1 event mobs
- if(newdrop->item.count==0)
- newdrop->item.count = 1;
- if(newdrop->item.itemtype == 10)
- {
- if(newdrop->item.itemnum >=441 && newdrop->item.itemnum <= 888)// skillbooks
- newdrop->item.count = 1;
- }
- if(newdrop->item.itemtype == 11) //gems only get 1
- newdrop->item.count = 1;
- if(newdrop->item.itemtype == 12)
- {
- if(newdrop->item.itemnum > 300 && newdrop->item.itemnum < 360) //bullets get a lot higher count.
- {
- newdrop->item.count *= 10;
- newdrop->item.count += 10;
- }
- }
- }
- else if( newdrop->item.itemtype >1 && newdrop->item.itemtype !=7 && newdrop->item.itemtype < 10)
- {
- // check to see if the item will be refined
- int prefine = rand()%100; //Probability of finding a refined item
- int refinechance = Config.RefineChance;
- if(prefine < refinechance) // default = 5%
- {
- int refinelevel = rand()%101; //which level of refine do we actually get
- if( refinelevel < 5) //default 5%
- newdrop->item.refine = 4 * 16;
- else if( refinelevel < 15 ) //10%
- newdrop->item.refine = 3 * 16;
- else if(refinelevel < 35 ) // 20%
- newdrop->item.refine = 2 * 16;
- else // 65%
- newdrop->item.refine = 16;
- }
- else //99%
- newdrop->item.refine = 0;
- // will the item be a blue?
- bool blue = false;
- int bluechance1 = RandNumber( 1, 100);
- int bluechance2 = Config.BlueChance + chamod;
- Log( MSG_INFO, "Blue chance = %i", bluechance2);
- //This probability is now configurable from WorldServer.conf. CHA also has an effect
- if(bluechance1 < bluechance2) // some percentage of drops will be specials or blues whenever one is available.
- {
- Log( MSG_INFO, "Selected a blue item");
- int p = 1;
- while(alternate[n][p] != 0 && p < 8)
- {
- p++;
- }
- if(p > 1) // blues available for this item
- {
- //Log( MSG_INFO, "blue item available");
- p--;
- int bluenum = RandNumber( 1, p);
- newdrop->item.itemnum = alternate[n][bluenum];
- blue=true;
- }
- }
- // will the items get stats? All blue items will. Uniques count as blues.
- int pstats = rand()%101; //Probability of the item having stats. default = 5%
- if(blue == true)
- {
- pstats = 1;
- }
- int StatChance = Config.StatChance;
- if( pstats < StatChance) // default 5%
- newdrop->item.stats = RandNumber( 1, 300);
- }
- newdrop->item.gem = 0;
- return newdrop;
- }
in Startup.cpp
find function
- bool CWorldServer::LoadDropsData( )
after function, add
- bool CWorldServer::LoadPYDropsData( )
- {
- Log( MSG_INFO, "Loading PYDrops Data" );
- MDropList.clear();
- MYSQL_ROW row;
- MYSQL_RES *result = DB->QStore("SELECT id,type,min_level,max_level,prob,mob,map,alt FROM item_drops");
- if(result==NULL)
- {
- DB->QFree( );
- return false;
- }
- while(row = mysql_fetch_row(result))
- {
- CMDrops* newdrop = new (nothrow) CMDrops;
- assert(newdrop);
- newdrop->itemnum = atoi(row[0]);
- newdrop->itemtype = atoi(row[1]);
- newdrop->level_min = atoi(row[2]);
- newdrop->level_max = atoi(row[3]);
- newdrop->prob = atoi(row[4]);
- newdrop->mob = atoi(row[5]);
- newdrop->map = atoi(row[6]);
- char *tmp;
- if((tmp = strtok( row[7] , "|"))==NULL)
- newdrop->alt[0]=0;
- else
- newdrop->alt[0]=atoi(tmp);
- for(unsigned int i=1;i<8; i++)
- {
- if((tmp = strtok( NULL , "|"))==NULL)
- newdrop->alt[i]=0;
- else
- newdrop->alt[i]=atoi(tmp);
- }
- //if(newdrop->mob != 0)
- //Log( MSG_INFO, "found a non zero mob value %i",newdrop->mob );
- MDropList.push_back( newdrop );
- }
- DB->QFree( );
- Log( MSG_INFO, "PYDrops loaded" );
- return true;
- }
- bool CWorldServer::LoadSkillBookDropsData( )
- {
- Log( MSG_INFO, "Loading Skillbook data" );
- MYSQL_ROW row;
- MYSQL_RES *result = DB->QStore("SELECT id,itemtype,min,max,prob FROM list_skillbooks");
- if(result==NULL)
- {
- DB->QFree( );
- return false;
- }
- int c = 0;
- while(row = mysql_fetch_row(result))
- {
- c++;
- CMDrops* newdrop = new (nothrow) CMDrops;
- assert(newdrop);
- newdrop->itemnum = atoi(row[0]);
- newdrop->itemtype = atoi(row[1]);
- newdrop->level_min = atoi(row[2]);
- newdrop->level_max = atoi(row[3]);
- newdrop->prob = atoi(row[4]);
- SkillbookList.push_back( newdrop );
- }
- DB->QFree( );
- Log( MSG_INFO, "Skillbook Data loaded" );
- return true;
- }
- bool CWorldServer::LoadConfig( )
- {
- Log( MSG_INFO, "Loading database config files" );
- MYSQL_ROW row;
- MYSQL_RES *result = DB->QStore("SELECT exp_rate, drop_rate, zuly_rate, blue_chance, stat_chance, slot_chance, \
- refine_chance FROM list_config");
- if(result==NULL)
- {
- DB->QFree( );
- return false;
- }
- while( row = mysql_fetch_row(result) )
- {
- GServer->Config.EXP_RATE = atoi(row[0]);
- GServer->Config.DROP_RATE = atoi(row[1]);
- GServer->Config.ZULY_RATE = atoi(row[2]);
- GServer->Config.BlueChance = atoi(row[3]);
- GServer->Config.StatChance = atoi(row[4]);
- GServer->Config.SlotChance = atoi(row[5]);
- GServer->Config.RefineChance = atoi(row[6]);
- }
- DB->QFree( );
- Log( MSG_INFO, "Config Data Loaded" );
- return true;
- }
in WorldServe.h find
- CDrop* GetDrop( CMonster* thismon );
after, add
- CDrop* GetPYDrop( CMonster* thismon, UINT droptype );
find
- bool LoadDropsData( );
after, add
- bool LoadPYDropsData( );
- bool LoadSkillBookDropsData( );
- bool LoadConfig( );
in WorldServer.cpp
find
- LoadDropsData( );
REPLACE with
- //LoadDropsData( );
- LoadPYDropsData( );
- LoadSkillBookDropsData( );
- LoadConfig( );
New variables and structures
in Datatype.h find
- UINT dialogid;
after add
- UINT side;
- UINT sidechance;
- UINT zulydrop;
- UINT mapdrop;
- UINT mobdrop;
- UINT leveldrop;
- UINT dropchance;
find
- UINT probmax;
after, add
- UINT prob;
- UINT map;
- UINT mob;
- UINT itemtype;
- UINT itemnum;
- UINT alt[8];
The configuration setup
in Sockets.h find
- int Cfmode
after add
- int BlueChance;
- int StatChance;
- int SlotChance;
- int RefineChance;
[s]Ghost drops list functions (skillbooks but can be expanded to others)[/b]
in worldserver.h
find
- vector<CMDrops*> MDropList; // Drops List
after, add
- vector<CMDrops*> SkillbookList; // Skillbook drop list
we already added the load function for this earlier so we should be good to go once we add the new database tables.
Connect to our new drops system
in Monsterfunctions.cpp
find
- CDrop* CMonster::GetDrop( )
- {
- return GServer->GetDrop( this );
- }
replace with
- CDrop* CMonster::GetDrop( )
- {
- return GServer->GetPYDrop( this, 1 );
- }
for the time being we will only send a droptype value of 1 to the new drop function. 2 is for sidedrops which are not yet complete.
General mopping up
Wtihout this we keep getting calls to all kinds of mob and map drops which cause a bunch of "drop not founded" errors
Since we don't ever need mapdrops or mobdrops again we can just remove the function that gives the errors. We won't actually remove it yet though.
Just bypass it
in ExtraFunctions.cpp
find
- // Get Monster Drop By ID
- CMDrops* CWorldServer::GetDropData( unsigned int id )
- {
- unsigned int A=0,B=0,C=0;
replace with
- // Get Monster Drop By ID
- CMDrops* CWorldServer::GetDropData( unsigned int id )
- {
- return NULL;
- unsigned int A=0,B=0,C=0;
or in other words, just add "return NULL;" where shown
This will prevent all those errors.
first the drops table
In this we now have a completely new system. Items can be set to drop in one of the following modes
Mob drops. The item only drops from the mob specified
Map drops. the item drops from any mob in the specofied map
level drops. The item drops from ANY mob on ANY map if the monster level is btween the values defined in min_level and max_level (inclusive)
example: You can set an apple to drop from any mob in the range 2 to 15 on any map with the following entry
id = 101
type = 10
min_level = 2
max_level = 15
prob = 1000 (range of 1 to 1000 here. 1000 = 100% chance of the item being added to a shortlist of possible drops. The actual item dropped will be selected from this list)
map = 0
mob = 0
alt = 0|0|0|0|0|0|0|0 (default value)
description = "Apple from level range 2 - 15" (this is an optional value that you can set to whatever you like to remind you what the entry means)
the sql
Here are 3 tables to add to your database.
One for item drops. This completely custom but is about as complete as i can make it. Please feel free to mod it as much as you like based on the information above. I also have an editor but after the fiasco of trying to get it to run on Rob's PC I'm a bit reluctant to upload it just yet. PM me if you want me to upload.
The new Config table that i use for configuring the stuff related to the drops. It also contains some server data such as XP rate and Zuly rate which will overwrite data in the .conf file. I intend to use this config DB for all the config values eventually. the only thing i want to see in the ,conf files are the actual server connection stuff.
Once i have developed this a little further it will be possible to instantly change configurations to one of any number of stored sets. A simple admin/GM command will be able to switch them.
The Skillbooks list. This is designed for ghosts to use so that their drops can be controlled seperately to normal mobs.
The reason for the name is because in evo where this was initially designed, ghosts dropped skillbooks almost exclusively (+ stuff like HP water and MP water)
This is an evo based list so it won't be correct at this time. I will fix it up better when i get the chance
We may even decide to lose this function. Not sure at this time. Either way you need this table right now.
This code has been tested as it stands and is running quite well on my test server at work.
I will be uploading it to my server at home in about an hour when I go home from work.
Enjoy