POSIX clock() sums CPU time across every thread in the process, unlike Windows' clock() which tracks wall-clock milliseconds. With several worker threads running, respawn/spawn/AI timers based on clock() raced far ahead of real elapsed time. Switch spawn-timer bookkeeping to time(NULL)/time_t (seconds) throughout the world server, and add a portable clock() shim in sockets.h (backed by CLOCK_MONOTONIC, millisecond resolution) for remaining call sites so CLOCKS_PER_SEC==1000 still holds like it did on Windows. Also: fix MapDelay/VisualDelay/WorldDelay being passed to usleep() (us) without converting from their configured milliseconds; fix the shutdown timer likewise; stop leaking the temporary actor used to run an NPC's AIP script; fix CleanDrops() skipping the element that shifts into position after an erase. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1307 lines
48 KiB
C++
1307 lines
48 KiB
C++
/*
|
|
Rose Online Server Emulator
|
|
Copyright (C) 2006,2007 OSRose Team http://www.osrose.net
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
developed with Main erose/hrose source server + some change from the original eich source
|
|
*/
|
|
#include "worldserver.h"
|
|
bool CWorldServer::LoadSTBData( )
|
|
{
|
|
Log( MSG_LOAD, "STB Data " );
|
|
STBStoreData( "3DData/STB/LIST_NPC.STB", &STB_NPC );
|
|
STBStoreData( "3DData/STB/LIST_SKILL.STB", &STB_SKILL );
|
|
STBStoreData( "3DData/STB/LIST_STATUS.STB", &STB_STATUS );
|
|
STBStoreData( "3DData/STB/LIST_QUEST.STB", &STB_QUEST );
|
|
STBStoreData( "3DData/STB/LIST_FACEITEM.STB", &STB_ITEM[0] );
|
|
STBStoreData( "3DData/STB/LIST_CAP.STB", &STB_ITEM[1] );
|
|
STBStoreData( "3DData/STB/LIST_BODY.STB", &STB_ITEM[2] );
|
|
STBStoreData( "3DData/STB/LIST_ARMS.STB", &STB_ITEM[3] );
|
|
STBStoreData( "3DData/STB/LIST_FOOT.STB", &STB_ITEM[4] );
|
|
STBStoreData( "3DData/STB/LIST_BACK.STB", &STB_ITEM[5] );
|
|
STBStoreData( "3DData/STB/LIST_JEWEL.STB", &STB_ITEM[6] );
|
|
STBStoreData( "3DData/STB/LIST_WEAPON.STB", &STB_ITEM[7] );
|
|
STBStoreData( "3DData/STB/LIST_SUBWPN.STB", &STB_ITEM[8] );
|
|
STBStoreData( "3DData/STB/LIST_USEITEM.STB", &STB_ITEM[9] );
|
|
STBStoreData( "3DData/STB/LIST_JEMITEM.STB", &STB_ITEM[10] );
|
|
STBStoreData( "3DData/STB/LIST_NATURAL.STB", &STB_ITEM[11] );
|
|
STBStoreData( "3DData/STB/LIST_QUESTITEM.STB", &STB_ITEM[12] );
|
|
STBStoreData( "3DData/STB/LIST_PAT.STB", &STB_ITEM[13] );
|
|
STBStoreData( "3DData/STB/LIST_PRODUCT.STB", &STB_PRODUCT );
|
|
STBStoreData( "3DData/STB/LIST_BREAK.STB", &STB_BREAK );
|
|
STBStoreData( "3DData/STB/LIST_SELL.STB", &STB_SELL );
|
|
STBStoreData( "3DData/STB/LIST_ZONE.STB", &STB_ZONE );
|
|
//STBStoreData( "3DData/STB/ITEM_DROP.STB", &STB_DROP );
|
|
STBStoreData("3DData/STB/LIST_GRADE.STB", &ListGrade);
|
|
STBStoreData("3DData/STB/LIST_UPGRADE.STB", &ListUpGrade);
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadNPCData( )
|
|
{
|
|
Log( MSG_INFO, "Loading NPC Data" );
|
|
for (unsigned int i = 0; i<STB_NPC.rowcount;i++)
|
|
{
|
|
CNPCData* newnpc = new (nothrow) CNPCData;
|
|
if(newnpc==NULL)
|
|
{
|
|
Log( MSG_ERROR, "Error allocing memory" );
|
|
continue;
|
|
}
|
|
newnpc->id = i;
|
|
newnpc->life = STB_NPC.rows[i][1];
|
|
newnpc->stance = mRUNNING;
|
|
newnpc->wspeed = STB_NPC.rows[i][2];
|
|
newnpc->rspeed = STB_NPC.rows[i][3];
|
|
newnpc->dspeed = STB_NPC.rows[i][4];
|
|
newnpc->weapon = STB_NPC.rows[i][5];
|
|
newnpc->subweapon = STB_NPC.rows[i][6];
|
|
newnpc->level = STB_NPC.rows[i][7];
|
|
newnpc->hp = STB_NPC.rows[i][8];
|
|
newnpc->atkpower = STB_NPC.rows[i][9];
|
|
//reduce attack power by 10% to improve combat
|
|
newnpc->atkpower = (int)(newnpc->atkpower * 0.9);
|
|
newnpc->hitrate = STB_NPC.rows[i][10];
|
|
newnpc->defense = STB_NPC.rows[i][11];
|
|
newnpc->magicdefense = STB_NPC.rows[i][12];
|
|
newnpc->dodge = STB_NPC.rows[i][13];
|
|
//reduce dodge by 20% to improve combat
|
|
newnpc->dodge = (int)(newnpc->dodge * 0.8);
|
|
newnpc->atkspeed = STB_NPC.rows[i][14];
|
|
newnpc->magicattack = STB_NPC.rows[i][15];
|
|
newnpc->AI = STB_NPC.rows[i][16];
|
|
newnpc->exp = STB_NPC.rows[i][17];
|
|
newnpc->dropid = STB_NPC.rows[i][18];
|
|
newnpc->money = STB_NPC.rows[i][19];
|
|
newnpc->item = STB_NPC.rows[i][20];
|
|
newnpc->tab1 = STB_NPC.rows[i][21];
|
|
newnpc->tab2 = STB_NPC.rows[i][22];
|
|
newnpc->tab3 = STB_NPC.rows[i][23];
|
|
newnpc->specialtab = STB_NPC.rows[i][24];
|
|
newnpc->atkdistance = STB_NPC.rows[i][26]/100;
|
|
newnpc->aggresive = STB_NPC.rows[i][27];
|
|
newnpc->die_quest = STB_NPC.rows[i][41]; //this contains the hash of the quest trigger.We can use it to run quest triggers directly
|
|
newnpc->shp = STB_NPC.rows[i][42]; //PY: What the heck. This column of the STB defines the height of a flying monster such as a bee
|
|
newnpc->dialogid = 0;
|
|
//Log( MSG_DEBUG, "NPC %i lvl %i with AI %i", newnpc->id, newnpc->level, newnpc->AI);
|
|
NPCData.push_back( newnpc );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadStatusData( )
|
|
{
|
|
Log( MSG_INFO, "Loading Stats Data " );
|
|
for (unsigned int i = 0; i < STB_STATUS.rowcount; i++)
|
|
{
|
|
CStatus* newstatus = new (nothrow) CStatus;
|
|
if (newstatus == NULL)
|
|
{
|
|
Log(MSG_WARNING, "\nError allocing memory: status_data");
|
|
return false;
|
|
}
|
|
newstatus->id = i;
|
|
newstatus->type = STB_STATUS.rows[i][1]; // Unk
|
|
newstatus->duplication = STB_STATUS.rows[i][2]; // Are results stackable?
|
|
newstatus->dir = STB_STATUS.rows[i][3]; // 0: Up 1: Down 2: ?
|
|
newstatus->repeat = STB_STATUS.rows[i][4]; // 1: Effect over time 2: Once only (eg. Increase max Stat) 3: Status Effect (taunt, fainted etc.)
|
|
newstatus->ability[0] = STB_STATUS.rows[i][5]; // Status # to call?
|
|
newstatus->amount[0] = STB_STATUS.rows[i][6]; // Amount
|
|
newstatus->ability[1] = STB_STATUS.rows[i][7]; // Second Status
|
|
newstatus->amount[1] = STB_STATUS.rows[i][8]; // Second amount
|
|
newstatus->decrease = STB_STATUS.rows[i][17]; // 0: Increase 1: Decrease
|
|
|
|
StatusList.push_back(newstatus);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadSkillData( )
|
|
{
|
|
Log( MSG_INFO, "Loading Skills data " );
|
|
for (unsigned int i = 0; i<STB_SKILL.rowcount;i++)
|
|
{
|
|
CSkills* newskill = new (nothrow) CSkills;
|
|
if(newskill==NULL)
|
|
{
|
|
Log(MSG_WARNING, "\nError allocing memory: skills_data" );
|
|
return false;
|
|
}
|
|
char *tmp;
|
|
newskill->id=i;
|
|
newskill->skillid = STB_SKILL.rows[i][1]; // Skillid. Should be the same for all levels of a given skill
|
|
newskill->level = STB_SKILL.rows[i][2]; // Skills Level
|
|
newskill->sp = STB_SKILL.rows[i][3]; // Cost to get skill
|
|
newskill->type = STB_SKILL.rows[i][4]; //
|
|
newskill->skilltype = STB_SKILL.rows[i][5]; // Type of skill
|
|
newskill->range = STB_SKILL.rows[i][6]/100; // Range of skill
|
|
newskill->target = STB_SKILL.rows[i][7]; // Skill Target
|
|
newskill->aoerange = STB_SKILL.rows[i][8]/100; // AOE Range
|
|
newskill->atkpower = STB_SKILL.rows[i][9]; // Attack Power
|
|
newskill->status[0] = STB_SKILL.rows[i][11]; // status
|
|
newskill->status[1] = STB_SKILL.rows[i][12]; // status
|
|
newskill->success = STB_SKILL.rows[i][13]; // Success Rate
|
|
if(newskill->success == 0)
|
|
newskill->success = 100; //success rate is stored as NULL where it is 100%. eg dual scratch
|
|
newskill->duration = STB_SKILL.rows[i][14]; // Duration
|
|
newskill->costtype[0] = STB_SKILL.rows[i][16]; //not all costs are in MP
|
|
newskill->costamount[0] = STB_SKILL.rows[i][17]; //some are in HP or Stamina
|
|
newskill->mp = STB_SKILL.rows[i][17];
|
|
newskill->costtype[1] = STB_SKILL.rows[i][18]; // All driveby skills have 2 costs
|
|
newskill->costamount[1] = STB_SKILL.rows[i][19]; // The second is normally cart gauge
|
|
newskill->cooldown = STB_SKILL.rows[i][20]; // The time it takes to be able to use the skill again.
|
|
newskill->buff[0] = STB_SKILL.rows[i][21]; // Stat
|
|
//if(newskill->id == 1403)
|
|
// Log(MSG_DEBUG,"Skill 1403 buff[0] value is %i",newskill->buff[0]);
|
|
newskill->value1[0] = STB_SKILL.rows[i][22]; // Int Value
|
|
newskill->value2[0] = STB_SKILL.rows[i][23]; // % Value
|
|
newskill->buff[1] = STB_SKILL.rows[i][24]; // Stat
|
|
//if(newskill->id == 1403)
|
|
// Log(MSG_DEBUG,"Skill 1403 buff[1] value is %i",newskill->buff[1]);
|
|
newskill->value1[1] = STB_SKILL.rows[i][25]; // Int Value
|
|
newskill->value2[1] = STB_SKILL.rows[i][26]; // % Value
|
|
//newskill->success=STB_SKILL.rows[i][69]; //Dunno what this field is really but it sure isn't success rate <_<
|
|
newskill->weapon[0] = STB_SKILL.rows[i][30]; // Item Type must be equipped (Only 1)
|
|
//if(newskill->weapon[0] != 0 && newskill->id < 1500)
|
|
// Log(MSG_DEBUG,"Skill %i weapon 1 id is %i",newskill->id,newskill->weapon[0]);
|
|
newskill->weapon[1] = STB_SKILL.rows[i][31]; // Item Type must be equipped
|
|
//if(newskill->weapon[1] != 0 && newskill->id < 1500)
|
|
// Log(MSG_DEBUG,"Skill %i weapon 2 id is %i",newskill->id,newskill->weapon[1]);
|
|
newskill->weapon[2] = STB_SKILL.rows[i][32]; // Item Type must be equipped
|
|
//if(newskill->weapon[2] != 0 && newskill->id < 1500)
|
|
// Log(MSG_DEBUG,"Skill %i weapon 3 id is %i",newskill->id,newskill->weapon[2]);
|
|
newskill->weapon[3] = STB_SKILL.rows[i][33]; // Item Type must be equipped
|
|
//if(newskill->weapon[3] != 0 && newskill->id < 1500)
|
|
// Log(MSG_DEBUG,"Skill %i weapon 4 id is %i",newskill->id,newskill->weapon[3]);
|
|
newskill->weapon[4] = STB_SKILL.rows[i][34]; // Item Type must be equipped
|
|
//if(newskill->weapon[4] != 0 && newskill->id < 1500)
|
|
// Log(MSG_DEBUG,"Skill %i weapon 5 id is %i",newskill->id,newskill->weapon[4]);
|
|
newskill->c_class[0] = STB_SKILL.rows[i][35]; // Required Class (Only One)
|
|
newskill->c_class[1] = STB_SKILL.rows[i][36]; // Required Class
|
|
newskill->c_class[2] = STB_SKILL.rows[i][37]; // Required Class
|
|
newskill->c_class[3] = STB_SKILL.rows[i][38]; // Required Class
|
|
newskill->c_class[4] = 0;
|
|
newskill->rskill[0] = STB_SKILL.rows[i][39]; // Required Skill
|
|
newskill->rskill[1] = STB_SKILL.rows[i][41]; // Required Skill
|
|
newskill->rskill[2] = STB_SKILL.rows[i][43]; // Required Skill
|
|
newskill->lskill[0] = STB_SKILL.rows[i][40]; // Required Skill Level
|
|
newskill->lskill[1] = STB_SKILL.rows[i][42]; // Required Skill Level
|
|
newskill->lskill[2] = STB_SKILL.rows[i][44]; // Required Skill Level
|
|
|
|
|
|
|
|
//WTF are these for???
|
|
//newskill->buff[2]=0;STB_SKILL.rows[i][11];//for damage support
|
|
//newskill->value1[2]=0;STB_SKILL.rows[i][9];//for damage support
|
|
//newskill->value2[2]=0;
|
|
|
|
newskill->req[0] = STB_SKILL.rows[i][45]; //the requirement type (usually 31 = level)
|
|
newskill->clevel = STB_SKILL.rows[i][46]; //requirement amount
|
|
newskill->reqam[0] = STB_SKILL.rows[i][46]; //this need not always be level but is usually
|
|
newskill->req[1] = STB_SKILL.rows[i][47]; //the requirement type (in most cases blank)
|
|
newskill->reqam[1] = STB_SKILL.rows[i][48]; //requirement amount
|
|
newskill->zuly = (STB_SKILL.rows[i][85] * 100); // Required zuly
|
|
if ((STB_SKILL.rows[i][8])>0)
|
|
{
|
|
newskill->aoe = 1;
|
|
newskill->aoeradius = STB_SKILL.rows[i][8]/100;
|
|
}
|
|
else
|
|
{
|
|
newskill->aoe = 0;
|
|
newskill->aoeradius = 0;
|
|
}
|
|
if ((STB_SKILL.rows[i][28])>0)
|
|
{
|
|
newskill->script = 1;
|
|
newskill->svalue1 = STB_SKILL.rows[i][28];
|
|
}
|
|
else
|
|
{
|
|
newskill->script=0;
|
|
newskill->svalue1=0;
|
|
}
|
|
newskill->nbuffs = 0;
|
|
if(newskill->buff[0] != 0 || newskill->status[0] != 0)
|
|
newskill->nbuffs += 1;
|
|
if(newskill->buff[1]!= 0 || newskill->status[1] != 0)
|
|
newskill->nbuffs += 2;
|
|
|
|
|
|
SkillList.push_back( newskill );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadTeleGateData( )
|
|
{
|
|
Log( MSG_INFO, "Loading Telegates data " );
|
|
MYSQL_ROW row;
|
|
MYSQL_RES *result = DB->QStore("SELECT id, srcx, srcy, srcmap, destx, desty, destmap FROM list_telegates");
|
|
if(result==NULL) return false;
|
|
while( row = mysql_fetch_row(result) )
|
|
{
|
|
CTeleGate* thisgate = new (nothrow) CTeleGate;
|
|
if(thisgate==NULL)
|
|
{
|
|
Log(MSG_ERROR, "Error allocing memory " );
|
|
DB->QFree( );
|
|
return false;
|
|
}
|
|
thisgate->id = atoi(row[0]);
|
|
thisgate->src.x = (float)atof(row[1]);
|
|
thisgate->src.y = (float)atof(row[2]);
|
|
thisgate->srcMap = atoi(row[3]);
|
|
thisgate->dest.x = (float)atof(row[4]);
|
|
thisgate->dest.y = (float)atof(row[5]);
|
|
thisgate->destMap = atoi(row[6]);
|
|
TeleGateList.push_back( thisgate );
|
|
}
|
|
DB->QFree( );
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadRespawnData( )
|
|
{
|
|
Log( MSG_INFO, "Loading RespawnZones data " );
|
|
MYSQL_ROW row;
|
|
MYSQL_RES *result = DB->QStore("SELECT id,x,y,map,radius,type FROM list_respawnzones");
|
|
if(result==NULL) return false;
|
|
while( row = mysql_fetch_row(result) )
|
|
{
|
|
CRespawnPoint* thisrespawnpoint = new (nothrow) CRespawnPoint;
|
|
if(thisrespawnpoint==NULL)
|
|
{
|
|
Log(MSG_ERROR, "Error allocing memory" );
|
|
DB->QFree( );
|
|
return false;
|
|
}
|
|
thisrespawnpoint->id = atoi(row[0]);
|
|
thisrespawnpoint->dest.x = (float)atof(row[1]);
|
|
thisrespawnpoint->dest.y = (float)atof(row[2]);
|
|
thisrespawnpoint->destMap = atoi(row[3]);
|
|
thisrespawnpoint->radius = atoi(row[4]);
|
|
thisrespawnpoint->masterdest = (atoi(row[5]) == 1);
|
|
MapList.Index[thisrespawnpoint->destMap]->RespawnList.push_back( thisrespawnpoint );
|
|
}
|
|
DB->QFree( );
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CWorldServer::LoadMobGroups()
|
|
{
|
|
if(GServer->Config.SpawnType == 0)
|
|
{
|
|
Log(MSG_INFO, "Loading MobGroups data " );
|
|
vector<CMobGroup*> mobGroups;
|
|
MYSQL_ROW row;
|
|
bool flag = true;
|
|
char* tmp = NULL;
|
|
MYSQL_RES *result = DB->QStore("SELECT id, map, x, y, `range`, respawntime, `limit`, tacticalpoints, moblist FROM list_mobgroups");
|
|
if (result == NULL) return false;
|
|
while (row = mysql_fetch_row(result))
|
|
{
|
|
CMobGroup* thisgroup = new (nothrow) CMobGroup;
|
|
if (thisgroup == NULL) {
|
|
Log(MSG_ERROR, "Error allocating memory");
|
|
DB->QFree();
|
|
return false;
|
|
}
|
|
thisgroup->id = atoi(row[0]);
|
|
thisgroup->map = atoi(row[1]);
|
|
thisgroup->point.x = atof(row[2]);
|
|
thisgroup->point.y = atof(row[3]);
|
|
thisgroup->range = atoi(row[4]);
|
|
thisgroup->respawntime = atoi(row[5]);
|
|
thisgroup->limit = atoi(row[6]);
|
|
thisgroup->tacticalpoints = atoi(row[7]);
|
|
char* mobList = row[8];
|
|
|
|
thisgroup->lastRespawnTime = time(NULL);
|
|
thisgroup->active = 0;
|
|
thisgroup->basicKills = 0;
|
|
thisgroup->curTac = 0;
|
|
thisgroup->curBasic = 0;
|
|
|
|
thisgroup->basicMobs.clear();
|
|
thisgroup->tacMobs.clear();
|
|
|
|
// Fill in basic/tac mobs
|
|
tmp = strtok(mobList, ",|");
|
|
while (tmp != NULL) {
|
|
int mobId = atoi(tmp);
|
|
tmp = strtok(NULL, ",|");
|
|
if (tmp == NULL) {
|
|
Log(MSG_ERROR, "MobGroup %i is invalid", thisgroup->id);
|
|
flag = false;
|
|
break;
|
|
}
|
|
int amount = atoi(tmp);
|
|
tmp = strtok(NULL, ",|");
|
|
if (tmp == NULL) {
|
|
Log(MSG_ERROR, "MobGroup %i is invalid", thisgroup->id);
|
|
flag = false;
|
|
break;
|
|
}
|
|
int tactical = atoi(tmp);
|
|
CMob *thismob = new (nothrow) CMob;
|
|
if (thismob == NULL) {
|
|
Log(MSG_ERROR, "Error allocating memory");
|
|
DB->QFree();
|
|
return false;
|
|
}
|
|
thismob->amount = amount;
|
|
thismob->tactical = (unsigned int)tactical/2;
|
|
thismob->mobId = mobId;
|
|
thismob->thisnpc = GetNPCDataByID( thismob->mobId );
|
|
thismob->mapdrop = GetDropData( thisgroup->map ); //hmm. We don't even use these drops anymore
|
|
thismob->mobdrop = GetDropData( thismob->thisnpc->dropid );
|
|
if (thismob->thisnpc == NULL) {
|
|
Log(MSG_WARNING, "Invalid mobId - Mob %i will not be added", atoi(row[0]));
|
|
delete thismob;
|
|
continue;
|
|
}
|
|
if (thismob->tactical)
|
|
thisgroup->tacMobs.push_back(thismob);
|
|
else
|
|
thisgroup->basicMobs.push_back(thismob);
|
|
tmp = strtok(NULL, ",|");
|
|
}
|
|
if (!flag) {
|
|
delete thisgroup;
|
|
continue;
|
|
}
|
|
MapList.Index[thisgroup->map]->MobGroupList.push_back(thisgroup);
|
|
mobGroups.push_back(thisgroup);
|
|
}
|
|
DB->QFree( );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadMonsterSpawn( )
|
|
{
|
|
Log( MSG_INFO, "Loading SpawnZones data" );
|
|
//clear the respawns data first
|
|
|
|
for(int i=0;i<MapList.Map.size();i++)
|
|
{
|
|
MapList.Index[i]->MonsterSpawnList.clear();
|
|
}
|
|
MYSQL_ROW row;
|
|
//MYSQL_RES *result = DB->QStore("SELECT id,map,montype,min,max,respawntime,points, spawntype, triggermontype, triggerammount, agressive, areatrigger, lim, spawnk FROM list_spawnareas");
|
|
MYSQL_RES *result = DB->QStore("SELECT id, map, montype, min, max, respawntime, x, y, radius, spawntype, triggermontype, triggerammount, agressive, areatrigger, lim, spawnk, aip, refvar, refval FROM list_special_spawns");
|
|
|
|
if(result == NULL)
|
|
{
|
|
DB->QFree( );
|
|
return false;
|
|
}
|
|
Log( MSG_INFO, "Loaded database data" );
|
|
while(row = mysql_fetch_row(result))
|
|
{
|
|
bool flag = true;
|
|
char* tmp;
|
|
CSpawnArea* thisspawn = new (nothrow) CSpawnArea;
|
|
if(thisspawn==NULL)
|
|
{
|
|
Log(MSG_ERROR, "Error allocing memory" );
|
|
DB->QFree( );
|
|
return false;
|
|
}
|
|
thisspawn->id = atoi(row[0]);
|
|
if(thisspawn->id > GServer->maxSpawnId)
|
|
GServer->maxSpawnId = thisspawn->id;
|
|
thisspawn->map = atoi(row[1]);
|
|
thisspawn->montype = atoi(row[2]);
|
|
thisspawn->min = 0;//atoi(row[3]);
|
|
thisspawn->max = atoi(row[4]);
|
|
thisspawn->respawntime = atoi(row[5]);
|
|
thisspawn->point.x = atoi(row[6]);
|
|
if(thisspawn->map == 3)
|
|
thisspawn->point.x -= 100; //for some reason some JP map has an X offset.
|
|
thisspawn->point.y = atoi(row[7]);
|
|
thisspawn->radius = atoi(row[8]);
|
|
if (thisspawn->respawntime == 0)
|
|
thisspawn->respawntime=5;
|
|
thisspawn->amon = 0;
|
|
thisspawn->lastRespawnTime = time(NULL);
|
|
thisspawn->type = atoi(row[9]);
|
|
thisspawn->triggertype = atoi(row[10]);
|
|
thisspawn->triggeramount = atoi(row[11]);
|
|
thisspawn->aggressive = atoi(row[12]);
|
|
thisspawn->typeoftrigger = atoi(row[13]);
|
|
thisspawn->limit = atoi(row[14]);
|
|
thisspawn->spawnkey = atoi(row[15]);
|
|
thisspawn->AI = atoi(row[16]);
|
|
thisspawn->RefVar = atoi(row[17]);
|
|
thisspawn->RefVal = atoi(row[18]);
|
|
thisspawn->triggercount = 0;
|
|
//if(thisspawn->id >= 10000) //tower defense spawn
|
|
// thisspawn->triggercount = 10000; //disables the spawn until it is reset to 0
|
|
//if(flag)
|
|
//{
|
|
thisspawn->thisnpc = GetNPCDataByID( thisspawn->montype );
|
|
if(thisspawn->thisnpc == NULL)
|
|
{
|
|
Log( MSG_WARNING, "Invalid montype - Spawn %i will not be added", thisspawn->id );
|
|
delete thisspawn;
|
|
continue;
|
|
}
|
|
MapList.Index[thisspawn->map]->MonsterSpawnList.push_back( thisspawn );
|
|
//}
|
|
}
|
|
DB->QFree( );
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadNPCs( )
|
|
{
|
|
Log( MSG_INFO, "Loading NPC spawn " );
|
|
MYSQL_ROW row;
|
|
MYSQL_RES *result = DB->QStore("SELECT type,map,dir,x,y,dialogid FROM list_npcs");
|
|
if(result == NULL) return false;
|
|
while(row = mysql_fetch_row(result))
|
|
{
|
|
CNPC* thisnpc = new (nothrow) CNPC;
|
|
if(thisnpc == NULL)
|
|
{
|
|
Log(MSG_ERROR, "Error allocing memory" );
|
|
DB->QFree( );
|
|
return false;
|
|
}
|
|
thisnpc->clientid = GetNewClientID();
|
|
thisnpc->npctype = atoi(row[0]);
|
|
thisnpc->posMap = atoi(row[1]);
|
|
thisnpc->dir = (float)atof(row[2]);
|
|
thisnpc->pos.x = (float)atof(row[3]);
|
|
thisnpc->pos.y = (float)atof(row[4]);
|
|
thisnpc->thisnpc = GetNPCDataByID( thisnpc->npctype );
|
|
thisnpc->thisnpc->_EntityType = ENTITY_NPC;
|
|
if( thisnpc->thisnpc == NULL)
|
|
{
|
|
|
|
delete thisnpc;
|
|
continue;
|
|
}
|
|
thisnpc->thisnpc->dialogid = atoi(row[5]); // This is global to NPC type
|
|
//Log( MSG_INFO, "creating npc type %i",thisnpc->npctype );
|
|
MapList.Index[thisnpc->posMap]->AddNPC( thisnpc );
|
|
thisnpc->thisnpc->eventid = 0;
|
|
}
|
|
DB->QFree( );
|
|
return true;
|
|
}
|
|
|
|
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::LoadNewDrops( )
|
|
{
|
|
Log( MSG_INFO, "Loading New Drops Data" );
|
|
MDropList.clear();
|
|
MYSQL_ROW row;
|
|
MYSQL_RES *result = DB->QStore("SELECT * FROM new_drops");
|
|
if(result==NULL)
|
|
{
|
|
DB->QFree( );
|
|
return false;
|
|
}
|
|
while(row = mysql_fetch_row(result))
|
|
{
|
|
CMDrops* newdrop = new (nothrow) CMDrops;
|
|
assert(newdrop);
|
|
|
|
UINT readIndex = 1;
|
|
newdrop->Dropid = atoi(row[0]);
|
|
for(int i=0;i<50;i++)
|
|
{
|
|
newdrop->item[i] = atoi(row[readIndex]);
|
|
readIndex++;
|
|
newdrop->chance[i] = atoi(row[readIndex]);
|
|
readIndex++;
|
|
if(i == 0)
|
|
{
|
|
newdrop->RunningTotal[i] = newdrop->chance[i];
|
|
}
|
|
else
|
|
{
|
|
newdrop->RunningTotal[i] = newdrop->RunningTotal[i-1] + newdrop->chance[i];
|
|
}
|
|
//if(newdrop->Dropid == 26)
|
|
// Log( MSG_INFO, "item %i chance %i total %i",newdrop->item[i],newdrop->chance[i],newdrop->RunningTotal[i] );
|
|
|
|
}
|
|
newdrop->TotalChance = newdrop->RunningTotal[49];
|
|
MDropList.push_back( newdrop );
|
|
}
|
|
DB->QFree( );
|
|
Log( MSG_INFO, "New Drops 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::LoadLTB( )
|
|
{
|
|
Log(MSG_INFO, "Loading LTB strings");
|
|
MYSQL_ROW row;
|
|
MYSQL_RES *result = DB->QStore("SELECT id,npc,ltbstring,questnpc,questltb FROM ltb");
|
|
if(result==NULL)
|
|
{
|
|
DB->QFree( );
|
|
return false;
|
|
}
|
|
int index = 0;
|
|
while( row = mysql_fetch_row(result) )
|
|
{
|
|
strcpy(GServer->Ltbstring[atoi(row[0])].NPCname,row[1]);
|
|
strcpy(GServer->Ltbstring[atoi(row[0])].LTBstring,row[2]);
|
|
strcpy(GServer->Ltbstring[atoi(row[0])].QuestNpcName,row[3]);
|
|
strcpy(GServer->Ltbstring[atoi(row[0])].QuestLTB,row[4]);
|
|
}
|
|
DB->QFree( );
|
|
Log( MSG_INFO, "LTB Data Loaded" );
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadConfig( )
|
|
{
|
|
Log( MSG_INFO, "Loading database config" );
|
|
MYSQL_ROW row;
|
|
MYSQL_RES *result = DB->QStore("SELECT exp_rate, drop_rate, zuly_rate, blue_chance, stat_chance, slot_chance, refine_chance, rare_refine, kill_on_fail, spawntype, player_damage, monster_damage, player_acc, monster_acc, pvp_acc, skill_damage, dropmultiple, attkmult, skillmult, dropdelay, basedroprate, ktpointrate, movespeedmod, maxlevel FROM list_config");
|
|
if(result==NULL)
|
|
{
|
|
DB->QFree( );
|
|
return false;
|
|
}
|
|
if (mysql_num_rows(result) == 0)
|
|
{
|
|
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]);
|
|
GServer->Config.Rare_Refine = atoi(row[7]);
|
|
GServer->Config.KillOnFail = atoi(row[8]);
|
|
GServer->Config.SpawnType = atoi(row[9]);
|
|
GServer->Config.PlayerDmg = atoi(row[10]);
|
|
GServer->Config.MonsterDmg = atoi(row[11]);
|
|
// Not implemented in server yet - Drakia
|
|
//GServer->Config.PlayerAcc = atoi(row[12]);
|
|
//GServer->Config.MonsterAcc = atoi(row[13]);
|
|
//GServer->Config.PvpAcc = atoi(row[14]);
|
|
//GServer->Config.SkillDmg = atoi(row[15]);
|
|
GServer->Config.DropMultiple = atoi(row[16]);
|
|
GServer->Config.AttkMult = atoi(row[17]);
|
|
GServer->Config.SkillMult = atoi(row[18]);
|
|
GServer->Config.DropDelay = atoi(row[19]);
|
|
GServer->Config.BASE_DROP_CHANCE = atoi(row[20]);
|
|
GServer->Config.KTPointRate = atoi(row[21]);
|
|
GServer->MOVE_SPEED_MOD = atoi(row[22]);
|
|
GServer->Config.MaxLevel = atoi(row[23]);
|
|
}
|
|
DB->QFree( );
|
|
Log( MSG_INFO, "Config Data Loaded" );
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CWorldServer::LoadMonsters( )
|
|
{
|
|
Log( MSG_INFO, "Monsters Spawn" );
|
|
// Do our monster spawnin
|
|
|
|
//old method
|
|
/*for(UINT i=0;i<MapList.Map.size();i++)
|
|
{
|
|
CMap* thismap = MapList.Map.at(i);
|
|
for(UINT j=0;j<thismap->MonsterSpawnList.size();j++)
|
|
{
|
|
CSpawnArea* thisspawn = thismap->MonsterSpawnList.at(j);
|
|
thisspawn->mapdrop = GetDropData( thisspawn->map );
|
|
thisspawn->mobdrop = GetDropData( thisspawn->thisnpc->dropid );
|
|
for(UINT k=0;k<thisspawn->max;k++)
|
|
{
|
|
fPoint position = RandInPoly( thisspawn->points, thisspawn->pcount );
|
|
thismap->AddMonster( thisspawn->montype, position, 0, thisspawn->mobdrop, thisspawn->mapdrop, thisspawn->id );
|
|
}
|
|
}
|
|
}*/
|
|
|
|
//PY method
|
|
if(GServer->Config.SpawnType == 1)
|
|
{
|
|
Log( MSG_INFO,"PY method");
|
|
for(UINT i=0;i<MapList.Map.size();i++)
|
|
{
|
|
CMap* thismap = MapList.Map.at(i);
|
|
for(UINT j=0;j<thismap->MonsterSpawnList.size();j++)
|
|
{
|
|
CSpawnArea* thisspawn = thismap->MonsterSpawnList.at(j);
|
|
if(thisspawn->id >= 10000)continue; //don't spawn the tower def monsters
|
|
//thisspawn->mapdrop = GetDropData( thisspawn->map );
|
|
//thisspawn->mobdrop = GetDropData( thisspawn->thisnpc->dropid );
|
|
for(UINT k=0;k<thisspawn->max;k++)
|
|
{
|
|
if( thisspawn->type == 1 )
|
|
{
|
|
fPoint position = RandInCircle( thisspawn->point, thisspawn->radius );
|
|
//fPoint position = RandInPoly( thisspawn->points, thisspawn->pcount );
|
|
thismap->AddMonster( thisspawn->montype, position, 0, thisspawn->id, thisspawn->aggressive, false, thisspawn->limit);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//Log( MSG_INFO, "Finished Monsters Spawns" );
|
|
return true;
|
|
}
|
|
|
|
//IFO method
|
|
if(GServer->Config.SpawnType == 0)
|
|
{
|
|
Log(MSG_LOAD,"IFO method");
|
|
for (UINT i = 0; i < MapList.Map.size(); i++)
|
|
{
|
|
CMap *thismap = MapList.Map.at(i);
|
|
for (UINT j = 0; j < thismap->MobGroupList.size(); j++)
|
|
{
|
|
CMobGroup* thisgroup = thismap->MobGroupList.at(j);
|
|
// Load some basic mobs onto map
|
|
for (UINT k = 0; k < thisgroup->limit; k++)
|
|
{
|
|
CMob* thismob = thisgroup->basicMobs.at(thisgroup->curBasic);
|
|
thisgroup->curBasic++;
|
|
if (thisgroup->curBasic >= thisgroup->basicMobs.size()) thisgroup->curBasic = 0;
|
|
for (UINT l = 0; l < thismob->amount; l++)
|
|
{
|
|
fPoint position = RandInCircle( thisgroup->point, thisgroup->range );
|
|
thismap->AddMonster( thismob->mobId, position, 0, thisgroup->id,1 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
bool CWorldServer::LoadUpgrade( )//for succes rate
|
|
{
|
|
Log( MSG_INFO, "Loading Refine Data " );
|
|
FILE* fh = NULL;
|
|
fh = fopen("data/refine_data.csv", "r");
|
|
if(fh==NULL)
|
|
{
|
|
Log(MSG_ERROR, "\nError loading file data/refine_data.csv" );
|
|
return false;
|
|
}
|
|
char line[50];
|
|
fgets( line, 50, fh );// this is the column name
|
|
while(!feof(fh))
|
|
{
|
|
memset( &line, '\0', 50 );
|
|
fgets( line, 50, fh );
|
|
upgrade[GetUIntValue(",",&line)] = GetUIntValue(",");
|
|
}
|
|
fclose(fh);
|
|
return true;
|
|
} */
|
|
|
|
bool CWorldServer::LoadUpgrade( )//Load success rates for Bindrune and Talismans from STB
|
|
{
|
|
Log( MSG_INFO, "Loading Upgrades data " );
|
|
for (unsigned int i = 0; i<ListUpGrade.rowcount;i++)
|
|
{
|
|
upgrade[i].BindRune_Level = ListUpGrade.rows[i][0];
|
|
upgrade[i].BindRune_Success = ListUpGrade.rows[i][1];
|
|
upgrade[i].Talisman_Level = ListUpGrade.rows[i][2];
|
|
upgrade[i].Talisman_Success = ListUpGrade.rows[i][3];
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::CleanConnectedList( )
|
|
{
|
|
Log( MSG_LOAD, "Cleaning Connected Clients " );
|
|
DB->QExecute("UPDATE accounts set online=false");
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadEquip( )
|
|
{
|
|
Log( MSG_INFO, "Loading Equip Data " );
|
|
for(int j=0;j<9;j++)
|
|
{
|
|
EquipList[j+1].STBMax = STB_ITEM[j].rowcount;
|
|
for(unsigned int i=0;i<STB_ITEM[j].rowcount;i++)
|
|
{
|
|
CEquip* newequip = new (nothrow) CEquip;
|
|
if(newequip==NULL)
|
|
{
|
|
Log(MSG_WARNING, "\nError allocing memory: equip" );
|
|
return false;
|
|
}
|
|
newequip->id = i;
|
|
newequip->itemtype = (j+1);
|
|
newequip->equiptype = (j+1);
|
|
//get back to this later. It's a pain in the ass reading a string out of the STB
|
|
//strcpy(newequip->name, STB_ITEM[j].rows[i][0]);
|
|
newequip->type = STB_ITEM[j].rows[i][4];
|
|
newequip->price = STB_ITEM[j].rows[i][5];
|
|
newequip->pricerate = STB_ITEM[j].rows[i][6];
|
|
newequip->weight = STB_ITEM[j].rows[i][7];
|
|
newequip->quality = STB_ITEM[j].rows[i][8];
|
|
newequip->level = STB_ITEM[j].rows[i][13];
|
|
newequip->material = STB_ITEM[j].rows[i][14];
|
|
newequip->unionpoint = STB_ITEM[j].rows[i][15];
|
|
newequip->durability = STB_ITEM[j].rows[i][29];
|
|
newequip->defense = STB_ITEM[j].rows[i][31];
|
|
newequip->magicresistence = STB_ITEM[j].rows[i][32];
|
|
newequip->attackdistance = STB_ITEM[j].rows[i][33];//Speed of travel/Range
|
|
if(newequip->equiptype == SHOE)
|
|
{
|
|
newequip->movespeed = STB_ITEM[j].rows[i][33];
|
|
}
|
|
else
|
|
{
|
|
newequip->movespeed = 0;
|
|
}
|
|
|
|
if (STB_ITEM[j].fieldcount>35)
|
|
{
|
|
newequip->attackpower = STB_ITEM[j].rows[i][35];
|
|
newequip->attackspeed = STB_ITEM[j].rows[i][36];
|
|
newequip->magicattack = STB_ITEM[j].rows[i][37]; //new. Staffs and wands are designated as magic weapons.
|
|
if(newequip->magicattack > 1)newequip->magicattack = 0; //all the blue named spears have weird numbers here. Maybe why NA had a massive damage glitch with them.
|
|
}
|
|
else
|
|
{
|
|
newequip->attackpower = 0;
|
|
newequip->attackspeed = 0;
|
|
newequip->magicattack = 0;
|
|
}
|
|
|
|
|
|
for(int k=0;k<3;k++)
|
|
newequip->occupation[k] = STB_ITEM[j].rows[i][(16+k)];
|
|
for(int k=0;k<2;k++)
|
|
newequip->condition1[k] = STB_ITEM[j].rows[i][(19+k)];
|
|
for(int k=0;k<2;k++)
|
|
newequip->condition2[k] = STB_ITEM[j].rows[i][(21+k)];
|
|
for(int k=0;k<2;k++)
|
|
newequip->stat1[k] = STB_ITEM[j].rows[i][(24+k)];
|
|
for(int k=0;k<2;k++)
|
|
newequip->stat2[k] = STB_ITEM[j].rows[i][(27+k)];
|
|
|
|
if (STB_ITEM[j].fieldcount>47)
|
|
{
|
|
newequip->refinetype = STB_ITEM[j].rows[i][45]; //new this is the recipe for refining the item
|
|
newequip->itemgrade = STB_ITEM[j].rows[i][46];
|
|
newequip->raretype = STB_ITEM[j].rows[i][47];
|
|
}
|
|
else
|
|
{
|
|
newequip->refinetype = 0;
|
|
newequip->itemgrade = 0;
|
|
newequip->raretype = 0;
|
|
}
|
|
|
|
if(newequip->id > 4999)
|
|
{
|
|
//Log(MSG_WARNING,"Equipment type %i ID %i is outside of accepted range",newequip->equiptype, newequip->id );
|
|
//item not added
|
|
//return false;
|
|
}
|
|
else
|
|
{
|
|
EquipList[newequip->equiptype].Data.push_back( newequip );
|
|
EquipList[newequip->equiptype].Index[newequip->id] = newequip; // Index to read more quickly the data
|
|
}
|
|
//delete newequip;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadJemItem( )//STB_ITEM[10]
|
|
{
|
|
Log( MSG_INFO, "Loading Jem Data " );
|
|
EquipList[11].STBMax = STB_ITEM[10].rowcount;
|
|
for(unsigned int i=0;i<STB_ITEM[10].rowcount;i++)
|
|
{
|
|
CJemData* thisjem = new (nothrow) CJemData;
|
|
if(thisjem == NULL)
|
|
{
|
|
Log(MSG_WARNING, "\nError allocing memory: jemitem" );
|
|
continue;
|
|
}
|
|
thisjem->id = i;
|
|
thisjem->itemtype = 11;
|
|
thisjem->type = STB_ITEM[10].rows[i][4];
|
|
thisjem->price = STB_ITEM[10].rows[i][5];
|
|
thisjem->pricerate = STB_ITEM[10].rows[i][6];
|
|
thisjem->weight = STB_ITEM[10].rows[i][7];
|
|
thisjem->quality = STB_ITEM[10].rows[i][8];
|
|
thisjem->material = STB_ITEM[10].rows[i][14];
|
|
thisjem->unionpoint = STB_ITEM[10].rows[i][15];
|
|
thisjem->stat1[0] = STB_ITEM[10].rows[i][16];
|
|
thisjem->stat1[1] = STB_ITEM[10].rows[i][17];
|
|
thisjem->stat2[0] = STB_ITEM[10].rows[i][18];
|
|
thisjem->stat2[1] = STB_ITEM[10].rows[i][19];
|
|
if(thisjem->id > 999)
|
|
{
|
|
Log(MSG_WARNING,"Gem ID %i outside of accepted range",thisjem->id );
|
|
return false;
|
|
}
|
|
JemList.Data.push_back( thisjem );
|
|
JemList.Index[thisjem->id] = thisjem;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadNaturalItem( )//STB_ITEM[11]
|
|
{
|
|
Log( MSG_INFO, "Loading Natural Data " );
|
|
for(unsigned int i=0;i<STB_ITEM[11].rowcount;i++)
|
|
{
|
|
CNaturalData* thisnatural = new (nothrow) CNaturalData;
|
|
if(thisnatural==NULL)
|
|
{
|
|
Log(MSG_WARNING, "\nError allocing memory: natural" );
|
|
return false;
|
|
}
|
|
thisnatural->id = i;
|
|
thisnatural->itemtype = 12;
|
|
thisnatural->type = STB_ITEM[11].rows[i][4];
|
|
thisnatural->price = STB_ITEM[11].rows[i][5];
|
|
thisnatural->pricerate = STB_ITEM[11].rows[i][6];
|
|
thisnatural->weight = STB_ITEM[11].rows[i][7];
|
|
thisnatural->quality = STB_ITEM[11].rows[i][8];
|
|
thisnatural->unionpoint = STB_ITEM[11].rows[i][15];
|
|
thisnatural->pricevalue = STB_ITEM[11].rows[i][16];
|
|
if(thisnatural->id > 999)
|
|
{
|
|
Log(MSG_WARNING,"Natural item ID %i outside of accepted range",thisnatural->id );
|
|
return false;
|
|
}
|
|
NaturalList.Data.push_back( thisnatural );
|
|
NaturalList.Index[thisnatural->id] = thisnatural;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadPatItem( )
|
|
{
|
|
Log( MSG_INFO, "Loading PAT Data " );
|
|
EquipList[14].STBMax = STB_ITEM[13].rowcount;
|
|
for(unsigned int i=0;i<STB_ITEM[13].rowcount;i++)
|
|
{
|
|
CPatData* newpat = new (nothrow) CPatData;
|
|
if(newpat==NULL)
|
|
{
|
|
Log(MSG_WARNING, "Error allocing memory: pat\n" );
|
|
return false;
|
|
}
|
|
newpat->id = i;
|
|
newpat->itemtype = 14;
|
|
newpat->type = STB_ITEM[13].rows[i][4];
|
|
newpat->price = STB_ITEM[13].rows[i][5];
|
|
newpat->pricerate = STB_ITEM[13].rows[i][6];
|
|
newpat->weight = STB_ITEM[13].rows[i][7];
|
|
newpat->quality = STB_ITEM[13].rows[i][8];
|
|
newpat->material = STB_ITEM[13].rows[i][14];
|
|
newpat->unionpoint = STB_ITEM[13].rows[i][15];
|
|
newpat->partversion = STB_ITEM[13].rows[i][17];
|
|
newpat->level = STB_ITEM[13].rows[i][22]; // extra field added rev 70
|
|
newpat->condition[1] = STB_ITEM[13].rows[i][24]; // extra field added rev 70
|
|
newpat->modifier[1] = STB_ITEM[13].rows[i][25]; // extra field added rev 70
|
|
newpat->condition[2] = STB_ITEM[13].rows[i][27]; // extra field added rev 70
|
|
newpat->modifier[2] = STB_ITEM[13].rows[i][28]; // extra field added rev 70
|
|
newpat->durability = STB_ITEM[13].rows[i][29];
|
|
newpat->maxfuel = STB_ITEM[13].rows[i][31];
|
|
newpat->fuelcons = STB_ITEM[13].rows[i][32];
|
|
newpat->speed = STB_ITEM[13].rows[i][33];
|
|
newpat->attackdistance = STB_ITEM[13].rows[i][35];
|
|
newpat->attackpower = STB_ITEM[13].rows[i][36];
|
|
newpat->attackspeed = STB_ITEM[13].rows[i][37];
|
|
if(newpat->id > 999)
|
|
{
|
|
Log(MSG_WARNING,"PAT item ID %i outside of accepted range",newpat->id );
|
|
return false;
|
|
}
|
|
PatList.Data.push_back( newpat );
|
|
PatList.Index[newpat->id] = newpat;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadBreak( ) //STB_BREAK
|
|
{
|
|
Log( MSG_INFO, "Bypassing Break Data till we actually add some");
|
|
return true;
|
|
unsigned int STBcol = 3;
|
|
unsigned int Ind = 1;
|
|
for(unsigned int i=0;i<STB_BREAK.rowcount;i++)
|
|
{
|
|
//Log( MSG_INFO, "STB row %i",i);
|
|
CBreakData* newbreak = new (nothrow) CBreakData;
|
|
if(newbreak==NULL)
|
|
{
|
|
Log(MSG_WARNING, "Error allocing memory: break\n" );
|
|
return false;
|
|
}
|
|
newbreak->id = i;
|
|
newbreak->ItemId = STB_BREAK.rows[i][2];
|
|
for(unsigned int j=1;j<=20;j++)
|
|
{
|
|
if(i > 700)
|
|
Log( MSG_INFO, "STB row %i item %i",i,j);
|
|
for(unsigned int k=1;k<=4;k++)
|
|
{
|
|
newbreak->Mat[Ind]=STB_BREAK.rows[i][STBcol];
|
|
newbreak->Min[Ind]=STB_BREAK.rows[i][STBcol+3];
|
|
newbreak->Max[Ind]=STB_BREAK.rows[i][STBcol+4];
|
|
newbreak->Chance[Ind]=STB_BREAK.rows[i][STBcol+5];
|
|
}
|
|
STBcol += 4;
|
|
}
|
|
newbreak->MinDis=STB_BREAK.rows[i][STBcol];
|
|
newbreak->MaxDis=STB_BREAK.rows[i][STBcol+1];
|
|
newbreak->ItemCount=STB_BREAK.rows[i][STBcol+2];
|
|
if(newbreak->id > 5000)
|
|
{
|
|
Log(MSG_WARNING,"Break item ID %i outside of accepted range",newbreak->id );
|
|
return false;
|
|
}
|
|
BreakList.Data.push_back( newbreak );
|
|
BreakList.Index[newbreak->id] = newbreak;
|
|
}
|
|
Log( MSG_INFO, "Loaded Break Data ");
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadProductItem( )//STB_PRODUCT
|
|
{
|
|
Log( MSG_INFO, "Loading Product Data " );
|
|
for(unsigned int i=0;i<STB_PRODUCT.rowcount;i++)
|
|
{
|
|
CProductData* newproduct = new (nothrow) CProductData;
|
|
if(newproduct==NULL)
|
|
{
|
|
Log(MSG_WARNING, "Error allocing memory: product\n" );
|
|
return false;
|
|
}
|
|
newproduct->id = i;
|
|
newproduct->matType=STB_PRODUCT.rows[i][1];
|
|
newproduct->item[0]=STB_PRODUCT.rows[i][2];
|
|
newproduct->itemtype[0]=(int)(newproduct->item[0] / 1000);
|
|
newproduct->itemid[0]=newproduct->item[0] - (newproduct->itemtype[0] * 1000);
|
|
newproduct->amount[0]=STB_PRODUCT.rows[i][3];
|
|
newproduct->item[1]=STB_PRODUCT.rows[i][4];
|
|
newproduct->itemtype[1]=(int)(newproduct->item[1] / 1000);
|
|
newproduct->itemid[1]=newproduct->item[1] - (newproduct->itemtype[1] * 1000);
|
|
newproduct->amount[1]=STB_PRODUCT.rows[i][5];
|
|
newproduct->item[2]=STB_PRODUCT.rows[i][6];
|
|
newproduct->itemtype[2]=(int)(newproduct->item[2] / 1000);
|
|
newproduct->itemid[2]=newproduct->item[2] - (newproduct->itemtype[2] * 1000);
|
|
newproduct->amount[2]=STB_PRODUCT.rows[i][7];
|
|
newproduct->item[3]=STB_PRODUCT.rows[i][8];
|
|
newproduct->itemtype[3]=(int)(newproduct->item[3] / 1000);
|
|
newproduct->itemid[3]=newproduct->item[3] - (newproduct->itemtype[3] * 1000);
|
|
newproduct->amount[3]=STB_PRODUCT.rows[i][9];
|
|
if(newproduct->id > 1999)
|
|
{
|
|
Log(MSG_WARNING,"Product ID %i outside of accepted range",newproduct->id );
|
|
return false;
|
|
}
|
|
ProductList.Data.push_back( newproduct );
|
|
ProductList.Index[newproduct->id] = newproduct;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadSellData( )//STB_SELL
|
|
{
|
|
Log( MSG_INFO, "Loading Sell Data" );
|
|
//Log(MSG_WARNING,"STB rowcount: %i",STB_SELL.rowcount );
|
|
for(unsigned int i=0;i<STB_SELL.rowcount;i++)
|
|
{
|
|
//Log( MSG_INFO, "Sell Data row %i",i );
|
|
CCSellData* newsell = new (nothrow) CCSellData;
|
|
if(newsell == NULL)
|
|
{
|
|
Log(MSG_WARNING, "\nError Allocing memory: sell" );
|
|
return false;
|
|
}
|
|
newsell->id = i;
|
|
for(unsigned int j=2;j<STB_SELL.fieldcount;j++)
|
|
{
|
|
newsell->item[j-2] = STB_SELL.rows[i][j];
|
|
}
|
|
if(newsell->id > 999)
|
|
{
|
|
Log(MSG_WARNING,"List_Sell ID %i outside of accepted range",newsell->id );
|
|
return false;
|
|
}
|
|
SellList.Data.push_back( newsell );
|
|
SellList.Index[newsell->id] = newsell;
|
|
}
|
|
Log( MSG_INFO, "Loaded Sell Data" );
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadConsItem( )
|
|
{
|
|
Log( MSG_INFO, "Loading Consumable Data " );
|
|
for(unsigned int i=0;i<STB_ITEM[9].rowcount;i++)
|
|
{
|
|
CUseData* newuse = new (nothrow) CUseData;
|
|
if(newuse==NULL)
|
|
{
|
|
Log(MSG_WARNING, "\nError allocing memory: use" );
|
|
return false;
|
|
}
|
|
newuse->id = i;
|
|
newuse->itemtype = 10;
|
|
newuse->restriction = STB_ITEM[9].rows[i][3];
|
|
newuse->type = STB_ITEM[9].rows[i][4];
|
|
newuse->price = STB_ITEM[9].rows[i][5];
|
|
newuse->pricerate = STB_ITEM[9].rows[i][6];
|
|
newuse->weight = STB_ITEM[9].rows[i][7];
|
|
newuse->quality = STB_ITEM[9].rows[i][8];
|
|
newuse->unionpoint = STB_ITEM[9].rows[i][15];
|
|
newuse->pricevalue = STB_ITEM[9].rows[i][16];
|
|
newuse->usecondition[0]= STB_ITEM[9].rows[i][17];
|
|
newuse->usecondition[1]= STB_ITEM[9].rows[i][18];
|
|
newuse->useeffect[0]= STB_ITEM[9].rows[i][19];
|
|
newuse->useeffect[1]= STB_ITEM[9].rows[i][20];
|
|
//if(newuse->id == 285)
|
|
// Log(MSG_WARNING,"Usitem useeffect[1] = %i",newuse->useeffect[1] );
|
|
if(newuse->id > 1999)
|
|
{
|
|
Log(MSG_WARNING,"Usitem ID %i outside of accepted range",newuse->id );
|
|
return false;
|
|
}
|
|
UseList.Data.push_back( newuse );
|
|
UseList.Index[newuse->id] = newuse;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadWayPoints()
|
|
{
|
|
Log( MSG_INFO, "Loading Waypoints" );
|
|
MYSQL_ROW row;
|
|
MYSQL_RES *result = DB->QStore("SELECT mapid,wpnum,mapx,mapy,wptype FROM list_waypoints");
|
|
if(result==NULL)
|
|
{
|
|
DB->QFree( );
|
|
return false;
|
|
}
|
|
if (mysql_num_rows(result) == 0)
|
|
{
|
|
DB->QFree( );
|
|
Log( MSG_INFO, "list_waypoints currently contains no data" );
|
|
return true;
|
|
}
|
|
// first clear the waypoint list
|
|
for(int i=0;i<100;i++)
|
|
{
|
|
for(int j=0;j<100;j++)
|
|
{
|
|
GServer->WPList[i][j].pos.x = 0;
|
|
GServer->WPList[i][j].pos.y = 0;
|
|
GServer->WPList[i][j].WPType = 0;
|
|
}
|
|
}
|
|
while( row = mysql_fetch_row(result) )
|
|
{
|
|
int thismap = atoi(row[0]);
|
|
int thisindex = atoi(row[1]);
|
|
GServer->WPList[thismap][thisindex].pos.x = atof(row[2]);
|
|
GServer->WPList[thismap][thisindex].pos.y = atof(row[3]);
|
|
GServer->WPList[thismap][thisindex].WPType = atoi(row[4]);
|
|
}
|
|
DB->QFree( );
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadZoneData( )
|
|
{
|
|
Log( MSG_INFO, "Loading Zone Data " );
|
|
|
|
for(unsigned int i=0;i<STB_ZONE.rowcount;i++)
|
|
{
|
|
CMap* newzone = new (nothrow) CMap( );
|
|
if(newzone==NULL)
|
|
{
|
|
Log(MSG_WARNING, "Error allocing memory: CMap\n" );
|
|
return false;
|
|
}
|
|
newzone->id = i;
|
|
newzone->dungeon = (STB_ZONE.rows[i][4] == 1);
|
|
newzone->dayperiod = STB_ZONE.rows[i][13];
|
|
newzone->morningtime = STB_ZONE.rows[i][14];
|
|
newzone->daytime = STB_ZONE.rows[i][15];
|
|
newzone->eveningtime = STB_ZONE.rows[i][16];
|
|
newzone->nighttime = STB_ZONE.rows[i][17];
|
|
newzone->allowpvp = STB_ZONE.rows[i][18];
|
|
newzone->allowpat = STB_ZONE.rows[i][30]==0?true:false;
|
|
if (i==20||i==22)
|
|
newzone->ghost = 0;
|
|
else
|
|
newzone->ghost = 1;
|
|
newzone->MapTime = 0;
|
|
//TD stuff
|
|
newzone->TDNextMon = 0;
|
|
newzone->TDNextSpawn = 0;
|
|
newzone->lastTDSpawnTime = time(NULL);
|
|
newzone->LastUpdate = clock( );
|
|
newzone->CurrentTime = 0;
|
|
newzone->TDMobDelay = 1500;
|
|
for(unsigned int j=0;j<=100;j++)
|
|
{
|
|
newzone->TDMobList[j] = 0;
|
|
}
|
|
//end TD stuff
|
|
newzone->MonsterSpawnList.clear();
|
|
if(GServer->Config.SpawnType == 0)
|
|
newzone->MobGroupList.clear();
|
|
newzone->crystalhealth = 100;
|
|
newzone->mapXPRate = 2; //always 2 as long as the crystal is alive
|
|
MapList.Map.push_back(newzone);
|
|
MapList.Index[newzone->id] = newzone;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldServer::LoadItemStats( )
|
|
{
|
|
Log( MSG_INFO, "Loading Item Stats " );
|
|
for(unsigned int i=0;i<STB_ITEM[10].rowcount;i++)
|
|
{
|
|
StatsList[i].stat[0] = STB_ITEM[10].rows[i][16];
|
|
StatsList[i].value[0] = STB_ITEM[10].rows[i][17];
|
|
StatsList[i].stat[1] = STB_ITEM[10].rows[i][18];
|
|
StatsList[i].value[1] = STB_ITEM[10].rows[i][19];
|
|
}
|
|
return true;
|
|
}
|
|
//LMA: Loading grades bonuses (% and +)
|
|
bool CWorldServer::LoadGrades( )
|
|
{
|
|
Log( MSG_INFO, "Loading Grades - STB " );
|
|
for(unsigned int i=0;i<ListGrade.rowcount;i++)
|
|
{
|
|
//CGrade* tempGrade[1]=new (nothrow) CGrade;
|
|
//GradeList[i]=tempGrade[0];
|
|
GradeList[i].atk_addbonus=ListGrade.rows[i][0];
|
|
GradeList[i].atk_percent=ListGrade.rows[i][1];
|
|
GradeList[i].acc_addbonus=ListGrade.rows[i][2];
|
|
GradeList[i].acc_percent=ListGrade.rows[i][3];
|
|
GradeList[i].def_addbonus=ListGrade.rows[i][4];
|
|
GradeList[i].def_percent=ListGrade.rows[i][5];
|
|
GradeList[i].mdef_addbonus=ListGrade.rows[i][6];
|
|
GradeList[i].mdef_percent=ListGrade.rows[i][7];
|
|
GradeList[i].dodge_addbonus=ListGrade.rows[i][8];
|
|
GradeList[i].dodge_percent=ListGrade.rows[i][9];
|
|
}
|
|
|
|
Log( MSG_LOAD, "Grades Loaded - STB" );
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|