Files
ROSS/Src/World Server/extrafunctions.cpp
ThomasandClaude Sonnet 5 f053a06920 Fix core RNG collapsing to ~0-1 on Linux due to integer overflow
CWorldServer::RandNumber (and three inlined copies of the same pattern
in worldpackets.cpp's item-refine code) computed "range*rand()" in
integer arithmetic before dividing by (RAND_MAX + 1.0). On Windows
RAND_MAX is 32767 (15-bit), so this never overflowed. On Linux/glibc
RAND_MAX is 2147483647 (2^31-1), so "range*rand()" overflows a 32-bit
UINT for the vast majority of rand() outputs, wrapping to a small
value -- the division by ~2^31 then collapses the result to ~0 or 1
almost always, regardless of the requested range. This made every
percentage-based roll in the game (item drop selection, refine
durability/stats, and anything else using RandNumber) behave as if
called with range 1-2 instead of the real range, which is why item
drops effectively never triggered post-port. Cast to double before
the multiply so it can't overflow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 07:24:14 +02:00

1178 lines
36 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.
depeloped with Main erose/hrose source server + some change from the original eich source
*/
#include "worldserver.h"
// Build Item Head
unsigned CWorldServer::BuildItemHead( CItem thisitem )
{
if (thisitem.count==0) return 0;
return ( ( thisitem.itemnum & 0x7ffffff ) << 5 ) | ( thisitem.itemtype & 0x1f );
}
// Build Item Data
unsigned CWorldServer::BuildItemData( CItem thisitem )
{
if (thisitem.count==0) return 0;
if ( thisitem.itemtype >= 10 && thisitem.itemtype <= 13 )
{
return thisitem.count;
}
else
{
unsigned part1 = (thisitem.refine>>4) << 28;
unsigned part2 = (thisitem.appraised?1:0) << 27;
unsigned part3 = (thisitem.socketed?1:0) << 26;
unsigned part4 = (thisitem.lifespan*10) << 16;
unsigned part5 = thisitem.durability << 9;
unsigned part6 = thisitem.stats;
unsigned part7 = thisitem.gem;
if (!part7==0)part6=0;
return part1 | part2 | part3 | part4 | part5 | part6 | part7;
}
}
// Get Item By Head and Data (Coded by Caali)
CItem CWorldServer::GetItemByHeadAndData( unsigned head, unsigned data )
{
CItem thisitem;
thisitem.Clear ( );
//Get info from ItemHead
thisitem.itemnum = (head >> 5) & 0x7ffffff;
head ^= (thisitem.itemtype & 0x7ffffff) << 5;
thisitem.itemtype = head & 0x1f;
//Get info from ItemData
if( thisitem.itemtype >= 10 && thisitem.itemtype <= 13 ) //Stackable
{
thisitem.refine = 0;
thisitem.appraised = 0;
thisitem.socketed = 0;
thisitem.lifespan = 100;
thisitem.durability = 40;
thisitem.gem = 0;
thisitem.stats = 0;
thisitem.count = (int)data;
}
else //Non-stackable
{
thisitem.refine = (data >> 28) << 4;
data-=(thisitem.refine>>4) << 28;
thisitem.appraised = ((data >> 27)==1)?true:false;
data-=(thisitem.appraised?1:0) << 27;
thisitem.socketed = ((data >> 26)==1)?true:false;
data-=(thisitem.socketed?1:0) << 26;
thisitem.lifespan = (data >> 16) / 10;
data-=(thisitem.lifespan*10) << 16;
thisitem.durability = data >> 9;
data-=thisitem.durability << 9;
thisitem.gem = data;
data-=thisitem.gem;
thisitem.count = 1;
}
return thisitem;
}
//----Build Item Show
unsigned CWorldServer::BuildItemShow(CItem thisitem)
{
if (thisitem.gem == 0)
{
unsigned part1 = thisitem.refine*0x10000;
unsigned part2 = thisitem.itemnum;
return part2|part1;
}
else
{
unsigned part1 = thisitem.refine*0x10000;
unsigned part2 = thisitem.itemnum;
unsigned part3 = (0xd0000)+(((thisitem.gem-320)*0x400));
return part3|part2|part1;
}
}
//----Build Item refine
unsigned CWorldServer::BuildItemRefine(CItem thisitem)
{
if (thisitem.gem == 0)
return ((thisitem.refine)*256);
else
{
return (0xd00+(thisitem.gem-320)*4)+((thisitem.refine)*256);
}
}
// Send a packet too all connected clients
void CWorldServer::SendToAll( CPacket* pak )
{
for(UINT i=0;i<ClientList.size();i++)
{
CPlayer* otherclient = (CPlayer*) ClientList.at( i )->player;
if (otherclient->Session->inGame)
otherclient->client->SendPacketCpy( pak );
}
}
// Send a packet too all X who are visible
// -- CLIENT --
void CWorldServer::SendToVisible( CPacket* pak, CPlayer* thisclient, bool dothisclient )
{
for(unsigned j=0; j<thisclient->VisiblePlayers.size(); j++)
{
thisclient->VisiblePlayers.at( j )->client->SendPacketCpy( pak );
}
if(dothisclient)
thisclient->client->SendPacketCpy( pak );
}
void CWorldServer::SendToVisible( CPacket* pak, CPlayer* thisclient, CPlayer* xotherclient )
{
for(unsigned j=0; j<thisclient->VisiblePlayers.size(); j++)
{
CPlayer* otherclient = thisclient->VisiblePlayers.at( j );
if(otherclient==xotherclient)
continue;
otherclient->client->SendPacketCpy( pak );
}
}
// -- MONSTER --
void CWorldServer::SendToVisible( CPacket* pak, CMonster* thismon, CDrop* thisdrop )
{
CMap* map = MapList.Index[thismon->Position->Map];
for(UINT i=0;i<map->PlayerList.size();i++)
{
CPlayer* otherclient = map->PlayerList.at(i);
if( IsVisible(otherclient, thismon) )
otherclient->client->SendPacketCpy( pak );
if(thisdrop!=NULL)
{
otherclient->VisibleDrops.push_back( thisdrop );
}
}
}
// -- CHARACTER --
void CWorldServer::SendToVisible( CPacket* pak, CCharacter* character, CDrop* thisdrop )
{
if(character->IsPlayer( ))
{
SendToVisible( pak, (CPlayer*)character );
}
else
if(character->IsMonster( ))
{
SendToVisible( pak, (CMonster*)character, thisdrop );
}
}
// -- DROP --
void CWorldServer::SendToVisible( CPacket* pak, CDrop* thisdrop )
{
CMap* map = MapList.Index[thisdrop->posMap];
for(UINT i=0;i<map->PlayerList.size();i++)
{
CPlayer* otherclient = map->PlayerList.at(i);
if (otherclient->client == NULL) continue;
if (!otherclient->client->isActive) continue;
if( IsVisible(otherclient, thisdrop) )
otherclient->client->SendPacketCpy( pak );
}
}
// Send a packet too all clients on the specified map
void CWorldServer::SendToMap( CPacket* pak, int mapid )
{
CMap* map = MapList.Index[mapid];
for(UINT i=0;i<map->PlayerList.size();i++)
{
CPlayer* otherclient = map->PlayerList.at(i);
if( otherclient->Session->inGame )
otherclient->client->SendPacketCpy( pak );
}
}
// -----------------------------------------------------------------------------------------
// Check if an object is visible to a client
// -----------------------------------------------------------------------------------------
// -- CLIENT --
bool CWorldServer::IsVisible( CPlayer* thisclient, CPlayer* otherclient )
{
for(unsigned j=0; j<thisclient->VisiblePlayers.size(); j++)
{
if (otherclient==thisclient->VisiblePlayers.at(j))
return true;
}
return false;
}
// -- MOB --
bool CWorldServer::IsVisible( CPlayer* thisclient, CMonster* thismon )
{
for(unsigned j=0; j<thisclient->VisibleMonsters.size(); j++)
{
if (thismon->clientid == thisclient->VisibleMonsters.at(j)) return true;
}
return false;
}
// -- DROP --
bool CWorldServer::IsVisible( CPlayer* thisclient, CDrop* thisdrop )
{
for(unsigned j=0; j<thisclient->VisibleDrops.size(); j++)
{
if (thisdrop == thisclient->VisibleDrops.at(j)) return true;
}
return false;
}
// -- NPC --
bool CWorldServer::IsVisible( CPlayer* thisclient, CNPC* thisnpc )
{
for(unsigned j=0; j<thisclient->VisibleNPCs.size(); j++){
if (thisnpc==thisclient->VisibleNPCs.at(j)) return true;
}
return false;
}
// This function gets a new clientID for a npc, monster or mob
unsigned CWorldServer::GetNewClientID( ) {
unsigned i = ClientIDIndex; // Set i (the index value) equal to the current Index Pointer value
for (unsigned j=1; j<0xffff; j++) { // Outer loop will limit the entire function to no more than 65535 iterations
if ( ClientIDList[i] == 0 ) { // Found a free ID at position i
ClientIDList[i] = 1; // Set the value at this position to 1 to indicate it is in use
ClientIDIndex = i + 1; // Set the pointer to the next element of the Array
if (ClientIDIndex >= 0xffff) // If ClientIDIndex is greater than or equal to 65535 then wrap it around to 1
ClientIDIndex = 1;
return i; // Return the index value of the element that we just found
}
i++; // Increment i
if (i >= 0xffff) // If i is greater than or equal to 65535 then wrap it around to 1
i = 1;
}
return 0; // After 65535 iterations we failed to find a free ID so return 0
}
// This function will free our clientID
void CWorldServer::ClearClientID( unsigned int id ) {
ClientIDList[id] = 0; // Clear the value at the position pointed at by the released ID
}
// Search a drop by ID
CDrop* CWorldServer::GetDropByID( UINT id, UINT map )
{
if(map!=0)
return MapList.Index[map]->GetDropInMap( id );
for(unsigned j=0; j<MapList.Map.size(); j++)
{
CDrop* thisdrop = MapList.Map.at(j)->GetDropInMap( id );
if(thisdrop != NULL)
return thisdrop;
}
return NULL;
}
// Search a Monster by ID
CMonster* CWorldServer::GetMonsterByID( UINT id, UINT map )
{
if(map!=0)
return MapList.Index[map]->GetMonsterInMap( id );
for(UINT i=0;i<MapList.Map.size();i++)
{
CMonster* thismon = MapList.Map.at(i)->GetMonsterInMap( id );
if(thismon!=NULL)
return thismon;
}
return NULL;
}
// Search a Client by Username
CPlayer* CWorldServer::GetClientByUserName( char *username )
{
for(UINT i=0;i<ClientList.size();i++)
{
CPlayer* thisclient = (CPlayer*) ClientList.at(i)->player;
if (strcmp(thisclient->Session->username,username)==0)
return thisclient;
}
return NULL;
}
// Get Client By ID
CPlayer* CWorldServer::GetClientByID( UINT id, UINT map )
{
if(map!=0)
return MapList.Index[map]->GetPlayerInMap( id );
for(UINT i=0;i<ClientList.size();i++)
{
CPlayer* thisclient = (CPlayer*) ClientList.at(i)->player;
if (thisclient->clientid==id)
return thisclient;
}
return NULL;
}
// Search a Client by CharID
CPlayer* CWorldServer::GetClientByCID( UINT id, UINT map )
{
if(map!=0)
return MapList.Index[map]->GetCharIDInMap( id );
for(UINT i=0;i<ClientList.size();i++)
{
CPlayer* thisclient = (CPlayer*) ClientList.at(i)->player;
if (thisclient->CharInfo->charid==id)
return thisclient;
}
return NULL;
}
// Seach a client by Charname
CPlayer* CWorldServer::GetClientByCharName( char* name )
{
for(UINT i=0;i<ClientList.size();i++)
{
CPlayer* thisclient = (CPlayer*) ClientList.at(i)->player;
if (strncmp(thisclient->CharInfo->charname,name, 16)==0)
return thisclient;
}
return NULL;
}
// Get Spawn Area by ID
CSpawnArea* CWorldServer::GetSpawnArea( UINT id, UINT map )
{
if(map!=0)
{
for(unsigned j=0; j<MapList.Index[map]->MonsterSpawnList.size(); j++)
{
CSpawnArea* thisspawn = MapList.Index[map]->MonsterSpawnList.at(j);
if (thisspawn->id==id)
return thisspawn;
}
}
else
{
for(map=0;map<MapList.Map.size();map++)
{
for(unsigned j=0; j<MapList.Index[map]->MonsterSpawnList.size(); j++)
{
CSpawnArea* thisspawn = MapList.Index[map]->MonsterSpawnList.at(j);
if (thisspawn->id==id)
return thisspawn;
}
}
}
return NULL;
}
#ifdef USEIFO
CMobGroup* CWorldServer::GetMobGroup(UINT id, UINT map )
{
if (map != 0)
{
for (unsigned j = 0; j < MapList.Index[map]->MobGroupList.size(); j++)
{
CMobGroup* thisgroup = MapList.Index[map]->MobGroupList.at(j);
if (thisgroup->id == id)
return thisgroup;
}
}
else
{
for (map = 0; map < MapList.Map.size(); map++)
{
for (unsigned j = 0; j < MapList.Index[map]->MobGroupList.size(); j++)
{
CMobGroup* thisgroup = MapList.Index[map]->MobGroupList.at(j);
if (thisgroup->id == id)
return thisgroup;
}
}
}
return NULL;
}
#endif
// delete a spawn
bool CWorldServer::DeleteSpawn( CSpawnArea* spawn )
{
if(spawn==NULL)
return false;
for(UINT i=0;i<MapList.Index[spawn->map]->MonsterSpawnList.size();i++)
{
if(MapList.Index[spawn->map]->MonsterSpawnList.at(i)==spawn)
{
MapList.Index[spawn->map]->MonsterSpawnList.erase( MapList.Index[spawn->map]->MonsterSpawnList.begin()+i );
delete spawn;
return true;
}
}
return false;
}
// Search NPC by ID
CNPC* CWorldServer::GetNPCByID( UINT id, UINT map )
{
if(map!=0)
return MapList.Index[map]->GetNPCInMap( id );
for(unsigned j=0; j<MapList.Map.size(); j++)
{
CNPC* thisnpc = MapList.Map.at( j )->GetNPCInMap( id );
if (thisnpc!=0)
return thisnpc;
}
return NULL;
}
// Get Telegate by ID
CTeleGate* CWorldServer::GetTeleGateByID( unsigned int id )
{
for(unsigned j=0; j<TeleGateList.size(); j++)
{
CTeleGate* thisgate = TeleGateList.at(j);
if (thisgate->id == id) return thisgate;
}
// Hmm, shit, couldent find it
return NULL;
}
// Get Triangle Area
float CWorldServer::AreaOfTriangle( fPoint p1, fPoint p2, fPoint p3 )
{
return abs((int)((p2.x * p1.y - p1.x * p2.y) + (p3.x * p2.y - p2.x * p3.y) + (p1.x * p3.y - p2.x * p1.y))) / 2;
}
// Get a random point in triangle area
fPoint CWorldServer::RandInTriangle( fPoint p1, fPoint p2, fPoint p3 )
{
fPoint thispoint;
float a = 1.0;
float b = 1.0;
while (a+b>1) { a=(float)(rand()*1.0/RAND_MAX); b=(float)(rand()*1.0/RAND_MAX); }
float c = 1 - a - b;
thispoint.x = (p1.x*a)+(p2.x*b)+(p3.x*c);
thispoint.y = (p1.y*a)+(p2.y*b)+(p3.y*c);
return thispoint;
}
// Get random point in poly
fPoint CWorldServer::RandInPoly( fPoint p[], int pcount )
{
int tnum = 0;
float tval = 0;
float totalarea = 0;
float* areas = new float[pcount-1];
for(int i=0; i<pcount-2; i++) {
totalarea += AreaOfTriangle(p[0], p[i+1], p[i+2]);
areas[i+1] = totalarea;
}
tval = rand() * totalarea / RAND_MAX;
for (tnum=1; tnum<pcount-1; tnum++) { if(tval<=areas[tnum]&&tval>areas[tnum-1]) break; }
return RandInTriangle(p[0], p[tnum], p[tnum+1]);
}
// Get Random point in circle
fPoint CWorldServer::RandInCircle( fPoint center, float radius )
{
fPoint thispoint;
float angle = (float)(rand() * ( pi * 2 ) / RAND_MAX);
float dist = (float)sqrt( rand() * 1.0 / RAND_MAX ) * radius;
thispoint.x = cos(angle) * dist + center.x;
thispoint.y = sin(angle) * dist + center.y;
return thispoint;
}
// Check if a Monster is in a Circle around a Client with a specific range
bool CWorldServer::IsMonInCircle( CPlayer* thisclient, CMonster* thismon, float radius )
{
if ( thisclient->Position->Map!=thismon->Position->Map ) return false;
float dx = ( thisclient->Position->current.x - thismon->Position->current.x );
float dy = ( thisclient->Position->current.y - thismon->Position->current.y );
if ( sqrt( (dx * dx) + (dy * dy) ) <= radius )
{
return true;
}
else
{
return false;
}
}
// Check if a Monster is in a Circle around a point with a specific radius
bool CWorldServer::IsMonInCircle( fPoint center, fPoint position, float radius )
{
float dx = ( center.x - position.x );
float dy = ( center.y - position.y );
if ( sqrt( (dx * dx) + (dy * dy) ) <= radius )
{
return true;
}
else
{
return false;
}
}
// Check if a Player is in a Circle around a Client with a specific range
bool CWorldServer::IsPlayerInCircle( CPlayer* thisclient, CPlayer* otherclient, float radius )
{
if ( thisclient->Position->Map!=otherclient->Position->Map ) return false;
float dx = ( thisclient->Position->current.x - otherclient->Position->current.x );
float dy = ( thisclient->Position->current.y - otherclient->Position->current.y );
if ( sqrt( (dx * dx) + (dy * dy) ) <= radius )
{
return true;
}
else
{
return false;
}
}
// Search Respawn by ID
CRespawnPoint* CWorldServer::GetRespawnByID( unsigned id )
{
for(UINT i=0;i<MapList.Map.size();i++)
{
CMap* thismap = MapList.Map.at(i);
for(UINT j=0;j<thismap->RespawnList.size();j++)
{
CRespawnPoint* thisrespawn = thismap->RespawnList.at(j);
if(thisrespawn->id==id)
return thisrespawn;
}
}
return NULL;
}
// Search Respawn by Map
CRespawnPoint* CWorldServer::GetRespawnByMap( int map )
{
for(UINT j=0;j<MapList.Index[map]->RespawnList.size();j++)
{
if(MapList.Index[map]->RespawnList.at(j)->destMap==map)
return MapList.Index[map]->RespawnList.at(j);
}
return NULL;
}
// Search Skill By ID
CSkills* CWorldServer::GetSkillByID( unsigned int id )
{
unsigned int A=0,B=0,C=0;
for(A=0,B=SkillList.size()-1;A<=B;)
{
if(A==B)
{
CSkills* thisskill = (CSkills*) SkillList.at( A );
if( id = thisskill->id )// lol this function aint working correct
{
//Log(MSG_INFO,"skill returned id = %i", id );
return thisskill;
}
else
{
Log(MSG_WARNING,"SKILL NOT FOUND! %i", id );
return NULL;
}
}
C = (A+B)/2;
CSkills* thisskill = (CSkills*) SkillList.at( C );
if(thisskill->id == id)
{
//Log(MSG_INFO,"skill returned id = %i. C = %i", id,C );
return thisskill;
}
if(thisskill->id > id)
{
B=C-1;
}
else
{
A=C+1;
}
}
Log( MSG_WARNING,"SKILL NOT FOUNDED! %i", id );
return NULL;
}
// Search Status By ID
CStatus* CWorldServer::GetStatusByID( unsigned int id )
{
unsigned int A=0,B=0,C=0;
for(A=0,B=StatusList.size()-1;A<=B;)
{
if(A==B)
{
CStatus* thisstatus = (CStatus*) StatusList.at( A );
if( id = thisstatus->id )
return thisstatus;
else
{
Log(MSG_WARNING,"STATUS NOT FOUND! %i", id );
return NULL;
}
}
C = (A+B)/2;
CStatus* thisstatus = (CStatus*) StatusList.at( C );
if(thisstatus->id == id)
return thisstatus;
if(thisstatus->id > id)
B=C-1;
else
A=C+1;
}
Log( MSG_WARNING,"STATUS NOT FOUND! %i", id );
return NULL;
}
// Get Monster Drop By ID
CMDrops* CWorldServer::GetDropData( unsigned int id )
{
return NULL;
unsigned int A=0,B=0,C=0;
for(A=0,B=MDropList.size()-1;A<=B;)
{
if(A==B)
{
CMDrops* thismdrop = (CMDrops*) MDropList.at( A );
if( id == thismdrop->id ){return thismdrop;}
else
{
Log(MSG_WARNING,"DROP NOT FOUNDED! %i", id );
return 0;
}
}
C = (A+B)/2;
CMDrops* thismdrop = (CMDrops*) MDropList.at( C );
if(thismdrop->id == id){return thismdrop;}
if(thismdrop->id > id){B=C-1;}
else{A=C+1;}
}
Log(MSG_WARNING,"DROP NOT FOUNDED! %i", id );
return NULL;
}
// Get NPC Data by ID
CNPCData* CWorldServer::GetNPCDataByID( unsigned int id )
{
unsigned int A=0,B=0,C=0;
for(A=0,B=NPCData.size()-1;A<=B;)
{
if(A==B)
{
CNPCData* thisnpc = (CNPCData*) NPCData.at( A );
if( id == thisnpc->id ){return thisnpc;}
else
{
Log(MSG_WARNING,"NPC NOT FOUNDED! %i", id );
return 0;
}
}
C = (A+B)/2;
CNPCData* thisnpc = (CNPCData*) NPCData.at( C );
if(thisnpc->id == id){return thisnpc;}
if(thisnpc->id > id){B=C-1;}
else{A=C+1;}
}
Log(MSG_WARNING,"NPC NOT FOUNDED! %i", id );
return NULL;
}
// Get Consumible Item Info
CUseInfo* CWorldServer::GetUseItemInfo(CPlayer* thisclient, unsigned int slot )
{
if(thisclient->items[slot].count<1)
return NULL;
CUseInfo* useitem = new (nothrow) CUseInfo;
if(useitem==NULL)
{
Log(MSG_WARNING, "Error allocing memory GetUseItemInfo");
return useitem;
}
useitem->itemnum = 0;
useitem->itemtype = 0;
useitem->usescript = 0;
useitem->usetype = 0;
useitem->usevalue = 0;
unsigned int type = 0;
useitem->itemnum = thisclient->items[slot].itemnum;
useitem->itemtype = thisclient->items[slot].itemtype;
type = UseList.Index[useitem->itemnum]->type;
switch(type)
{
case 311://Medicine
case 312://Food
{
useitem->usescript = 1;
useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[0];
useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1];
}
break;
case 313://Magic Item [scrolls/summons/fireworks/etc]
{
// scrolls [maxhp,attackpower up, ... ]
if((useitem->itemnum >= 301 && useitem->itemnum <= 323)|| (useitem->itemnum >= 961 && useitem->itemnum <= 964) ||(useitem->itemnum >= 331 && useitem->itemnum <= 340))
{
useitem->usescript = 7;
useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[1];
useitem->usevalue = 0;
}
else//Return Scrolls
if(useitem->itemnum > 349 && useitem->itemnum < 367)
{
if( thisclient->Stats->MP < 33 ){delete useitem;}
thisclient->Stats->MP -= 32;
useitem->usescript = 2;
switch( useitem->itemnum )
{
//Adventure plain [credits to tomiz]
case 350:
useitem->usetype = 22;
useitem->usevalue = 51105310;
break;
//Zant
case 351:
useitem->usetype = 1;
useitem->usevalue = 52405192;
break;
//Junon
case 352:
useitem->usetype = 2;
useitem->usevalue = 55095283;
break;
//Eucar
case 353:
useitem->usetype = 51;
useitem->usevalue = 53665099;
break;
//Xita
case 354:
useitem->usetype = 61;
useitem->usevalue = 54344607;
break;
//Goblin cave
case 360:
useitem->usetype = 31;
useitem->usevalue = 55185444;
break;
//Desert of dead
case 361:
useitem->usetype = 29;
useitem->usevalue = 50825013;
break;
//El Verloon
case 362:
useitem->usetype = 24;
useitem->usevalue = 55275377;
break;
//George of Silence
case 363:
useitem->usetype = 28;
useitem->usevalue = 54674783;
break;
//Shady Jungle
case 364:
useitem->usetype = 62;
useitem->usevalue = 57515196;
break;
//Sunshine Beach
case 366:
useitem->usetype = 37;
useitem->usevalue = 50055382;
break;
default:
break;
}
}
else //Charm Scrolls
if( (useitem->itemnum>380 && useitem->itemnum<384) )
{
useitem->usescript = 3;
useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[1];
thisclient->CharInfo->stamina -= 5000;
}
else //Dance Scroll
if( (useitem->itemnum>370 && useitem->itemnum<377) ||
(useitem->itemnum>389 && useitem->itemnum<398) )
{
useitem->usescript = 12; // should not be treated the same as firecrackers. this is a skill
useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[1];
}
else // Firecrackers
if( useitem->itemnum == 913 || (useitem->itemnum >= 921 && useitem->itemnum <= 929) ||
(useitem->itemnum >= 991 && useitem->itemnum <= 995) )
{
useitem->usescript = 13;
useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[1];
//useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1];
}
else // Emotions
if( (useitem->itemnum>970 && useitem->itemnum<979) )
{
useitem->usescript = 4;
useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[0];
useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1];
}
else // Summons
if(useitem->itemnum > 400 && useitem->itemnum < 440)
{
if( thisclient->CharInfo->stamina < 100 )
return NULL;
thisclient->CharInfo->stamina -= 100;
useitem->usescript = 5;
useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[1];
CSkills* skill = GetSkillByID(useitem->usetype);
if(skill == NULL)
{
Log(MSG_INFO,"Useitem returnd a NULL skill");
return NULL;
}
useitem->usevalue = skill->svalue1;
}
else // Summons 2. Special summons such as goodie bags
if(useitem->itemnum > 908 && useitem->itemnum < 918)
{
if( thisclient->CharInfo->stamina < 100 )
return NULL;
thisclient->CharInfo->stamina -= 100;
useitem->usescript = 5; // testing at 11 for a bit. Gives no owner to the summons.
useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[1];
CSkills* skill = GetSkillByID(useitem->usetype);
if(skill == NULL)
{
Log(MSG_INFO,"Useitem returnd a NULL skill");
return NULL;
}
useitem->usevalue = skill->svalue1;
}
else // Snowball
if( useitem->itemnum == 326 )
{
useitem->usescript = 6;
useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[0];
useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1];
}
else // fairy scroll
if( useitem->itemnum == 4860 ) // random number. please adjust to the correct id for your scroll
{
useitem->usescript = 14; // new category for fairy scroll
useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[0];
useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1];
}
else
{
Log( MSG_WARNING, "Unknown Item %i - Type %i",useitem->itemnum,type);
delete useitem;
return NULL;
}
}
break;
case 314://Skill Book
useitem->usescript = 10;
useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[0];
useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1];
break;
case 315://Repair Tool
delete useitem;
return NULL;
break;
case 316://Quest Scroll
delete useitem;
return NULL;
break;
case 317://Engine Fuel
delete useitem;
return NULL;
break;
case 320://Automatic Consumption
delete useitem;
return NULL;
break;
case 321://Time Coupon / shop coupon / storage expansion coupon
useitem->usetype = UseList.Index[useitem->itemnum]->useeffect[0];
useitem->usevalue = UseList.Index[useitem->itemnum]->useeffect[1];
switch( useitem->usetype )
{
case 96: // shop coupons
delete useitem;
thisclient->Shop->ShopType = UseList.Index[useitem->itemnum]->quality;
break;
case 94: // free storage coupon
break;
case 95: // storage expansion coupon
break;
case 133: // Exp medals
delete useitem;
thisclient->Stats->ItemXPRate = 2;
if (thisclient->Stats->ItemXPTime == 0) // don't reset timer if it already has time on it
{
thisclient->Stats->ItemXPTime = clock( ); // set start time for clock countdown
}
thisclient->Stats->ItemXPTimer += useitem->usevalue * 600000; //convert to seconds and add to time remaining
break;
case 138: // Exp nulifier
delete useitem;
thisclient->Stats->ItemXPRate = -1; // set to -1 for XP nulification
thisclient->Stats->ItemXPTime = clock( ); // set start time for clock countdown. This overwrites any other XP timer
thisclient->Stats->ItemXPTimer += useitem->usevalue * 600000; //convert to seconds and add to time remaining
break;
default:
break;
}
/*switch( useitem->itemnum )
{
case 954: //shop coupon A
{
delete useitem;
thisclient->Shop->ShopType = 1;
}
case 955: //shop coupon B
{
delete useitem;
thisclient->Shop->ShopType = 2;
}
case 956: //shop coupon C
{
delete useitem;
thisclient->Shop->ShopType = 3;
}
}
*/
return NULL;
break;
case 322: // gift boxes and stuff
Log( MSG_WARNING, "Tried to use a gift box");
break;
default:
Log( MSG_WARNING, "Unknown use item type: %i",type);
return NULL;
break;
}
useitem->itemtype -= 1;
return useitem;
}
// Look if a skill target is friendly
bool CWorldServer::isSkillTargetFriendly( CSkills* thisskill )
{
switch(thisskill->target)
{ //Non-Hostile Target
case tYourself://yourself
case tPartyMember://party member
case tClanMember://clan member
case tAlly://ally
case tAllMembers://all members
case tAllCharacters: // All characters
case tFaintedAlly://Fainted
return true;
break; //Hostile Target
case tMonster: //monster
case tHostileCharacter: //hostile character
case tHostileTeamMember: //hostile team member
case tHostileMonster: //hostile monster
return false;
break;
default:
Log(MSG_WARNING,"Unknown skill target %i. Skill id = %i", thisskill->target, thisskill->id);
break;
}
return false;
}
// calc distance
float CWorldServer::distance( fPoint pos1, fPoint pos2 )
{
float dist = 0;
float dx = pos1.x - pos2.x;
float dy = pos1.y - pos2.y;
dist = sqrt( (dx*dx) + (dy*dy) );
return dist;
}
// Search client by userid
CPlayer* CWorldServer::GetClientByUserID( UINT userid )
{
for(UINT i=0;i<ClientList.size();i++)
{
CPlayer* thisclient = (CPlayer*)ClientList.at(i)->player;
if(thisclient->Session->userid==userid)
return thisclient;
};
return NULL;
}
// Check if a slot is valid (to prevent crash)
bool CWorldServer::CheckInventorySlot( CPlayer* thisclient, int slot )
{
if(slot>=MAX_INVENTORY || slot<0)
{
Log(MSG_HACK, "Invalid Inventory slot %i from %s", slot, thisclient->Session->username );
return false;
}
return true;
}
// Return a pseudo random number
UINT CWorldServer::RandNumber( UINT init, UINT range, UINT seed )
{
if(seed!=0)
srand(seed);
if(range>RAND_MAX)
{
UINT max = (UINT)GServer->round(sqrt((double)range));
// NOTE: multiply in double first -- on Linux RAND_MAX is 2^31-1 (vs.
// Windows' 15-bit 32767), so "max*rand()" done in integer arithmetic
// before dividing overflows UINT for nearly every rand() result,
// collapsing the output to ~0-1 regardless of range/max.
UINT num1 = init+int((double)max*rand()/(RAND_MAX + 1.0));
UINT num2 = init+int((double)max*rand()/(RAND_MAX + 1.0));
UINT num3 = init+int((double)max*rand()/(RAND_MAX + 1.0));
UINT res = (num1 * num2)+num3;
return res>range?range:res;
}
else
{
UINT ranum = init+int((double)range*rand()/(RAND_MAX + 1.0));
return ranum;
}
}
// Check if is a valid item
bool CWorldServer::IsValidItem(UINT type, UINT id )
{
if(type==0 || id==0 )
return false;
if(type<10)
{
if(id>4999)
return false;
if(EquipList[type].Index[id]->id==0)
return false;
}
else
{
switch(type)
{
case 10:
if(id>1999)
return false;
if(UseList.Index[id]->id==0)
return false;
break;
case 11:
if(id>3999)
return false;
if(JemList.Index[id]->id==0)
return false;
break;
case 12:
if(id>999)
return false;
if(NaturalList.Index[id]->id==00)
return false;
break;
case 14:
if(id>999)
return false;
if(PatList.Index[id]->id==00)
return false;
break;
default:
return false;
}
}
return true;
}
int CWorldServer::GetIntValue( const char* s , void* var )
{
char *tmp = strtok( (char*)var, (const char*)s );
return atoi(tmp==NULL?0:tmp);
}
UINT CWorldServer::GetUIntValue( const char* s , void* var )
{
char *tmp = strtok( (char*)var, (const char*)s );
return atoi(tmp==NULL?0:tmp);
}
char* CWorldServer::GetStrValue( const char* s , void* var )
{
char *tmp = strtok( (char*)var, (const char*)s );
return tmp;
}
bool CWorldServer::AddParty( CParty* thisparty )
{
PartyList.push_back( thisparty );
return true;
}
bool CWorldServer::RemoveParty( CParty* thisparty )
{
for(UINT i=0;i<PartyList.size( );i++)
{
if(PartyList.at(i)==thisparty)
{
PartyList.erase(PartyList.begin( )+i );
return true;
}
}
return false;
}
UINT CWorldServer::GetMatIDfromRecipe (UINT CraftLevel, UINT matType)
{
int ThisCount = 0;
for(int i=0;i<GServer->NaturalList.Data.size();i++)
{
if(GServer->NaturalList.Index[i]->type == matType)
{
ThisCount++;
if(ThisCount == CraftLevel)
{
return GServer->NaturalList.Index[i]->id;
}
}
}
return 0;
}
// return fairy range for waiting time random
UINT CWorldServer::GetFairyRange( UINT part )
{
UINT Range1[] = { 5, 5, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
UINT Range2[] = { 10, 7, 7, 6, 6, 6, 6, 5, 4, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1 };
if (part == 0) return Range1[GServer->FairyList.size()];
if (part == 1) return Range2[GServer->FairyList.size()];
}