added source folders
This commit is contained in:
1 parent
f23521f720
commit
cfdc19df45
145 files changed
+54574
No files matched your search
@@ -0,0 +1,946 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
// Add Player buffs
|
||||
bool CWorldServer::AddBuffs( CSkills* thisskill, CCharacter* character, int Evalue )
|
||||
{
|
||||
//Log(MSG_INFO, "called addbuffs." );
|
||||
bool bflag = false;
|
||||
for(unsigned int i=0;i<2;i++)
|
||||
{
|
||||
if(thisskill->status[i] != 0) // sets status flags in magicstatus array
|
||||
{
|
||||
bflag = CheckABuffs( thisskill, character, Evalue, i );
|
||||
}
|
||||
else // some skills are instant effects without status changes e.g. healing.
|
||||
{
|
||||
if(thisskill->buff[i] != 0)
|
||||
{
|
||||
bflag = InstantBuff( thisskill, character, Evalue, i );
|
||||
}
|
||||
}
|
||||
}
|
||||
return bflag;
|
||||
}
|
||||
|
||||
// handle instant effect skills without status changes
|
||||
bool CWorldServer::InstantBuff( CSkills* thisskill, CCharacter* character, int Evalue, int i )
|
||||
{
|
||||
//Log( MSG_INFO, "Instant skill cast: Status (should be zero):%i buff: %i", thisskill->status[i], thisskill->buff[i] );
|
||||
switch( thisskill->buff[i])
|
||||
{
|
||||
case 16: // HP
|
||||
{
|
||||
if(thisskill->duration <= 0 )//is heal
|
||||
{
|
||||
if(character->Stats->HP <= 0) //character is dead
|
||||
return true;
|
||||
character->Stats->HP += (long int)thisskill->value1[i]; //apparently there is no buff here. // + Evalue - 8;
|
||||
if(character->Stats->HP > character->Stats->MaxHP )
|
||||
character->Stats->HP = character->Stats->MaxHP;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 17: // MP
|
||||
{
|
||||
if(thisskill->duration <= 0 )// recover MP
|
||||
{
|
||||
character->Stats->MP += (long int)thisskill->value1[i];
|
||||
if(character->Stats->MP > character->Stats->MaxMP)
|
||||
character->Stats->MP = character->Stats->MaxMP;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 76: // Stamina
|
||||
{
|
||||
if(character->IsPlayer()) //monsters do not have stamina it would seem
|
||||
{
|
||||
if(thisskill->duration <= 0 )// recover Stamina
|
||||
{
|
||||
CPlayer* thisclient = GServer->GetClientByID(character->clientid);
|
||||
if(thisclient == NULL)return false;
|
||||
thisclient->CharInfo->stamina += (long int)thisskill->value1[i];
|
||||
if(thisclient->CharInfo->stamina > 5000)
|
||||
thisclient->CharInfo->stamina = 5000;
|
||||
//Log(MSG_INFO,"Character %i Stamina is now %i", character->clientid, thisclient->CharInfo->stamina);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add DeBuffs
|
||||
bool CWorldServer::CheckABuffs( CSkills* thisskill, CCharacter* character, int Evalue ,int i)
|
||||
{
|
||||
//Log( MSG_INFO, "checkabuffs status: %i buff: %i",( thisskill->status[i], thisskill->buff[i] ));
|
||||
bool bflag = false;
|
||||
if(thisskill->status[i] != 0)
|
||||
{
|
||||
switch( thisskill->status[i])
|
||||
{
|
||||
case 7: case 8: case 9: case 10: case 11: //poisoned
|
||||
{
|
||||
//Log( MSG_INFO, "checkabuffs: Poisoned Status detected %i", thisskill->status[i] );
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, 0xff, character->Status->Poisoned, character->Status->Poisoned,true, true );
|
||||
if(BuffValue.Position != 0xff)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
if(j>14)
|
||||
{
|
||||
character->Status->Poisoned = j;
|
||||
character->MagicStatus[j].Buff = 1; // needs some value or else it will not be counted in GetBuffValue. trying it with 1
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = 0; // this might cause headaches later
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
//Log( MSG_INFO, "Poisoned Status applied");
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 30: // muted
|
||||
{
|
||||
//Log( MSG_INFO, "checkabuffs: Mute Status detected %i", thisskill->status[i] );
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, 0xff, character->Status->Muted, character->Status->Muted, true, true );
|
||||
if(BuffValue.Position != 0xff)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
if(j>14)
|
||||
{
|
||||
character->Status->Muted = j;
|
||||
character->Status->CanCastSkill = false;
|
||||
character->MagicStatus[j].Buff = 1; // needs some value or else it will not be counted in GetBuffValue. trying it with 1
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = 0; // this might cause headaches later
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
//Log( MSG_INFO, "Muted Status applied");
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 31: // sleep
|
||||
{
|
||||
//Log( MSG_INFO, "checkabuffs: Sleep Status detected %i", thisskill->status[i] );
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, 0xff, character->Status->Sleep, character->Status->Sleep, true, true );
|
||||
Log( MSG_INFO, "Sleep Status attempted. position = %i", BuffValue.Position);
|
||||
if(BuffValue.Position != 0xff)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
if(j>14)
|
||||
{
|
||||
character->Status->Sleep = j;
|
||||
character->Status->CanAttack = false;
|
||||
character->Status->CanMove = false;
|
||||
character->Status->CanCastSkill = false;
|
||||
character->MagicStatus[j].Buff = 1; // needs some value or else it will not be counted in GetBuffValue. trying it with 1
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = 0; // this might cause headaches later
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
//Log( MSG_INFO, "Sleep Status applied");
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 32: // faint
|
||||
{
|
||||
//Log( MSG_INFO, "checkabuffs: Fainted Status detected %i", thisskill->status[i] );
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i, 0xff, character->Status->Faint, character->Status->Faint, true, true );
|
||||
//Log( MSG_INFO, "Stun Status attempted. position = %i", BuffValue.Position);
|
||||
if(BuffValue.Position != 0xff)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
if(j>14)
|
||||
{
|
||||
character->Status->Faint = j;
|
||||
character->Status->CanAttack = false;
|
||||
character->MagicStatus[j].Buff = 1; // needs some value or else it will not be counted in GetBuffValue. trying it with 1
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = 0; // this might cause headaches later
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
//Log( MSG_INFO, "Fainted Status applied");
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 39: // Dispell. removes all buff and debuff effects
|
||||
{
|
||||
Log( MSG_INFO, "checkabuffs: Dispell Status detected %i", thisskill->status[i] );
|
||||
// some dispell skills have duration of 2 seconds. Others have zero.
|
||||
// for now we will treat them all as instants so we don't need a buff slot
|
||||
float randv = GServer->RandNumber(0, 100);
|
||||
if(randv <= thisskill->successrate)
|
||||
{
|
||||
for(int j=0;j<30;j++)
|
||||
{ // Clean Buffs
|
||||
character->MagicStatus[j].Duration = 0;
|
||||
}
|
||||
Log( MSG_INFO, "Dispell Status applied successfully");
|
||||
// the rest will be taken care of in RefreshBuffs()
|
||||
}
|
||||
bflag = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 54: //A_GMExtra_Damage:
|
||||
case 36: //A_Extra_Damage:
|
||||
{
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i,
|
||||
character->Status->ExtraDamage_up,
|
||||
character->Status->ExtraDamage_down,
|
||||
character->Stats->ExtraDamage, true );
|
||||
if(BuffValue.Position != 0xff)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
character->Stats->ExtraDamage = thisskill->atkpower;
|
||||
if(j<15)
|
||||
character->Status->ExtraDamage_up = j;
|
||||
else
|
||||
character->Status->ExtraDamage_down = j;
|
||||
character->MagicStatus[j].Buff = thisskill->buff[i];
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = thisskill->atkpower;
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
bflag = true;
|
||||
Log( MSG_INFO, "Extra Damage Buff. Value %i Status %i",character->MagicStatus[j].Value,character->MagicStatus[j].Status);
|
||||
}
|
||||
}
|
||||
case 35: // shield damage
|
||||
{
|
||||
// uses skill power not buff value. Add later
|
||||
}
|
||||
break;
|
||||
case 18: // attack power up
|
||||
case 19: // attack power down
|
||||
case 48: // attack power up
|
||||
{
|
||||
//Log( MSG_INFO, "checkabuffs: Attack buff detected %i", thisskill->buff[i] );
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i,
|
||||
character->Status->Attack_up,
|
||||
character->Status->Attack_down,
|
||||
character->Stats->Attack_Power, true );
|
||||
if(BuffValue.NewValue!=0)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
character->Stats->Attack_Power = BuffValue.NewValue;
|
||||
if(j<15)
|
||||
character->Status->Attack_up = j;
|
||||
else
|
||||
character->Status->Attack_down = j;
|
||||
character->MagicStatus[j].Buff = thisskill->buff[i];
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = BuffValue.Value;
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 20: // def up
|
||||
case 21: // def down
|
||||
case 49: // def up
|
||||
{
|
||||
//Log( MSG_INFO, "checkabuffs: Defense buff detected %i", thisskill->buff[i] );
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i,
|
||||
character->Status->Defense_up,
|
||||
character->Status->Defense_down,
|
||||
character->Stats->Defense, true );
|
||||
if(BuffValue.NewValue!=0)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
character->Stats->Defense = BuffValue.NewValue;
|
||||
if(j<15)
|
||||
character->Status->Defense_up = j;
|
||||
else
|
||||
character->Status->Defense_down = j;
|
||||
character->MagicStatus[j].Buff = thisskill->buff[i];
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = BuffValue.Value;
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 24: //accuracy up
|
||||
case 25: //accuracy down
|
||||
case 51: //attack accuracy up.
|
||||
{
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i,
|
||||
character->Status->Accury_up,
|
||||
character->Status->Accury_down,
|
||||
character->Stats->Accury, true );
|
||||
if(BuffValue.NewValue!=0)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
character->Stats->Accury = BuffValue.NewValue;
|
||||
if(j<15)
|
||||
character->Status->Accury_up = j;
|
||||
else
|
||||
character->Status->Accury_down = j;
|
||||
character->MagicStatus[j].Buff = thisskill->buff[i];
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = BuffValue.Value;
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 22: // macic resistance up
|
||||
case 23: // magic resistance down
|
||||
case 50: // magic resistance up
|
||||
{
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i,
|
||||
character->Status->Magic_Defense_up,
|
||||
character->Status->Magic_Defense_down,
|
||||
character->Stats->Magic_Defense, true );
|
||||
if(BuffValue.NewValue!=0)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
character->Stats->Magic_Defense = BuffValue.NewValue;
|
||||
if(j<15)
|
||||
character->Status->Magic_Defense_up = j;
|
||||
else
|
||||
character->Status->Magic_Defense_down = j;
|
||||
character->MagicStatus[j].Buff = thisskill->buff[i];
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = BuffValue.Value;
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 28: //dodge up
|
||||
case 29: //dodge down
|
||||
case 53: //dodge rate up
|
||||
{
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i,
|
||||
character->Status->Dodge_up,
|
||||
character->Status->Dodge_down,
|
||||
character->Stats->Dodge, true );
|
||||
if(BuffValue.NewValue!=0)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
character->Stats->Dodge = BuffValue.NewValue;
|
||||
if(j<15)
|
||||
character->Status->Dodge_up = j;
|
||||
else
|
||||
character->Status->Dodge_down = j;
|
||||
character->MagicStatus[j].Buff = thisskill->buff[i];
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = BuffValue.Value;
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 14: //dash
|
||||
case 15: //slow
|
||||
case 46: //movement speed increased
|
||||
{
|
||||
//Log( MSG_INFO, "checkabuffs: move speed buff detected %i", thisskill->buff[i] );
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i,
|
||||
character->Status->Dash_up,
|
||||
character->Status->Dash_down,
|
||||
character->Stats->Move_Speed, true );
|
||||
if(BuffValue.NewValue != 0)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
character->Stats->Move_Speed = BuffValue.NewValue;
|
||||
if(j<15)
|
||||
character->Status->Dash_up = j;
|
||||
else
|
||||
character->Status->Dash_down = j;
|
||||
//Log( MSG_INFO, "Move speed buff position %i. new move speed %i. buff value %i", j,BuffValue.NewValue,BuffValue.Value );
|
||||
character->MagicStatus[j].Buff = thisskill->buff[i];
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = BuffValue.Value;
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 16: // haste attack
|
||||
case 17: // slow attack
|
||||
case 47: // attack speed up
|
||||
{
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i,
|
||||
character->Status->Haste_up,
|
||||
character->Status->Haste_down,
|
||||
(UINT)character->Stats->Attack_Speed, true );
|
||||
if(BuffValue.NewValue!=0)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
character->Stats->Attack_Speed = BuffValue.NewValue;
|
||||
if(j<15)
|
||||
character->Status->Haste_up = j;
|
||||
else
|
||||
character->Status->Haste_down = j;
|
||||
character->MagicStatus[j].Buff = thisskill->buff[i];
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = BuffValue.Value;
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 26: // crit up
|
||||
case 27: // crit down
|
||||
case 52: // crit up
|
||||
{
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i,
|
||||
character->Status->Critical_up,
|
||||
character->Status->Critical_down,
|
||||
character->Stats->Critical, true );
|
||||
if(BuffValue.NewValue != 0)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
character->Stats->Critical = BuffValue.NewValue;
|
||||
if(j<15)
|
||||
character->Status->Critical_up = j;
|
||||
else
|
||||
character->Status->Critical_down = j;
|
||||
character->MagicStatus[j].Buff = thisskill->buff[i];
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = BuffValue.Value;
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 12: // max HP up
|
||||
case 44: // max HP up
|
||||
{
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i,
|
||||
character->Status->HP_up,
|
||||
character->Status->HP_down,
|
||||
character->Stats->MaxHP, true );
|
||||
if(BuffValue.NewValue!=0)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
character->Stats->MaxHP = BuffValue.NewValue;
|
||||
if(j<15)
|
||||
character->Status->HP_up = j;
|
||||
else
|
||||
character->Status->HP_down = j;
|
||||
character->MagicStatus[j].Buff = thisskill->buff[i];
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = BuffValue.Value;
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 13: // max MP up
|
||||
case 45: // max MP up
|
||||
{
|
||||
CBValue BuffValue = GetBuffValue( thisskill, character, Evalue, i,
|
||||
character->Status->MP_up,
|
||||
character->Status->MP_down,
|
||||
character->Stats->MaxMP, true );
|
||||
if(BuffValue.NewValue!=0)
|
||||
{
|
||||
UINT j = BuffValue.Position;
|
||||
character->Stats->MaxMP = BuffValue.NewValue;
|
||||
if(j<15)
|
||||
character->Status->MP_up = j;
|
||||
else
|
||||
character->Status->MP_down = j;
|
||||
character->MagicStatus[j].Buff = thisskill->buff[i];
|
||||
character->MagicStatus[j].BuffTime = clock();
|
||||
character->MagicStatus[j].Duration = thisskill->duration;
|
||||
character->MagicStatus[j].Value = BuffValue.Value;
|
||||
character->MagicStatus[j].Status = thisskill->status[i];
|
||||
bflag = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log(MSG_WARNING,"Status %i unknown. Possibly not coded yet",thisskill->status[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bflag;
|
||||
}
|
||||
|
||||
// Get the new buffs values (character)
|
||||
CBValue CWorldServer::GetBuffValue( CSkills* thisskill, CCharacter* character, UINT Evalue, UINT i, UINT up, UINT down, UINT CurrentValue, bool Buff, bool Status )
|
||||
{
|
||||
CBValue NewValue;
|
||||
NewValue.Position = 0xff;
|
||||
NewValue.NewValue = 0;
|
||||
NewValue.Value = 0;
|
||||
INT Value = 0;
|
||||
UINT NormalValue = 0;
|
||||
UINT UpValue = 0;
|
||||
UINT DownValue = 0;
|
||||
bool BuffFlag = false;
|
||||
// not currently detecting debuffs properly so let's bring in some new code :)
|
||||
//PY buff mod based on thiskill->status[i]
|
||||
switch(thisskill->status[i])
|
||||
{
|
||||
case 12: case 13: case 14: case 16: case 18: case 20: case 22: case 24: case 26: case 28: case 35: case 36: case 53:
|
||||
case 54:
|
||||
Buff = true;
|
||||
break;
|
||||
case 7: case 8: case 9: case 10: case 15: case 17: case 19: case 21: case 23: case 25: case 27: case 29:
|
||||
case 30: case 31: case 32: case 59: case 60:
|
||||
Buff = false;
|
||||
break;
|
||||
default:
|
||||
Buff = true;
|
||||
break;
|
||||
}
|
||||
Log(MSG_INFO, "Current Buff boolean status %i Skill status = %i up: %i down: %i", Buff,thisskill->status[i],up,down);
|
||||
//PY end
|
||||
if(Buff) //skill is a buff
|
||||
{
|
||||
//find whether character already has buffs for this stat
|
||||
for(UINT z=0;z<15;z++) // 0 to 14 are upward
|
||||
{
|
||||
if(character->MagicStatus[z].Status == thisskill->status[i])
|
||||
{
|
||||
NewValue.Position = z;
|
||||
//Log(MSG_INFO, "Existing Buff position = %i",z);
|
||||
NormalValue = CurrentValue - character->MagicStatus[z].Value;
|
||||
UpValue = character->MagicStatus[z].Value;
|
||||
BuffFlag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(BuffFlag == false) // no active buffs so find a new slot
|
||||
{
|
||||
for(UINT z=0;z<15;z++) // 0 to 14 are upward
|
||||
{
|
||||
if(character->MagicStatus[z].Status == 0)
|
||||
{
|
||||
NewValue.Position = z;
|
||||
//Log(MSG_INFO, "New Buff position = %i",z);
|
||||
NormalValue = CurrentValue;
|
||||
BuffFlag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//find whether character already has debuffs for this stat
|
||||
for(UINT z=15;z<32;z++) // 15 to 32 are downward
|
||||
{
|
||||
if(character->MagicStatus[z].Status == thisskill->status[i])
|
||||
{
|
||||
NewValue.Position = z;
|
||||
//Log(MSG_INFO, "Existing De-Buff position = %i",z);
|
||||
NormalValue = CurrentValue + character->MagicStatus[z].Value;
|
||||
DownValue = character->MagicStatus[z].Value;
|
||||
BuffFlag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(BuffFlag == false) // no active debuffs so find a new slot
|
||||
{
|
||||
for(UINT z=15;z<32;z++) // 15 to 32 are downward
|
||||
{
|
||||
if(character->MagicStatus[z].Status == 0)
|
||||
{
|
||||
NewValue.Position = z;
|
||||
//Log(MSG_INFO, "New De-Buff position = %i",z);
|
||||
NormalValue = CurrentValue;
|
||||
BuffFlag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(BuffFlag == false) // no available spaces found
|
||||
return NewValue;
|
||||
|
||||
|
||||
|
||||
|
||||
//if(up != 0xff) //currently have a buff. up is its location
|
||||
//{
|
||||
// UpValue = character->MagicStatus[up].Value;
|
||||
//Log(MSG_INFO, "Current active buff detected in position %i with value %i", NewValue.Position,UpValue);
|
||||
// NormalValue = CurrentValue - character->MagicStatus[up].Value;
|
||||
// if(Buff)
|
||||
// NewValue.Position = up;
|
||||
//}
|
||||
|
||||
//else // don't currently have a buff
|
||||
if(NewValue.Position == 0xff)
|
||||
{
|
||||
//Log(MSG_INFO, "No buff currently active");
|
||||
if(Buff) //is this skill a buff?
|
||||
{
|
||||
for(UINT z=0;z<15;z++) // 0 to 14 are upward
|
||||
{
|
||||
if(character->MagicStatus[z].Status == 0)
|
||||
{
|
||||
NewValue.Position = z; //z is the new index in the magicstatus array where the buff will be placed
|
||||
//Log(MSG_INFO, "Buff position = %i",z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(NewValue.Position == 0xff) //no spaces found ??
|
||||
return NewValue;
|
||||
NormalValue = CurrentValue; // no buffs currently active so set normalvalue to currentvalue
|
||||
}
|
||||
else //debuff
|
||||
{
|
||||
for(UINT z=15;z<30;z++) // 15 to 29 are downward
|
||||
{
|
||||
if(character->MagicStatus[z].Status == 0)
|
||||
{
|
||||
NewValue.Position = z; //z is the new index in the magicstatus array where the buff will be placed
|
||||
Log(MSG_INFO, "DeBuff position = %i",z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(NewValue.Position == 0xff) //no spaces found ??
|
||||
return NewValue;
|
||||
NormalValue = CurrentValue; // no buffs currently active so set normalvalue to currentvalue
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (!Status) // status boolean set false as default so it will run this code in pretty much all cases
|
||||
{
|
||||
Log(MSG_INFO, "Status set to false so checking for debuffs");
|
||||
if( down != 0xff) //do we have a debuff in place already? down is its location
|
||||
{
|
||||
DownValue = character->MagicStatus[down].Value;
|
||||
Log(MSG_INFO, "Current active de-buff detected in position %i with value %i", i,DownValue);
|
||||
if( NormalValue!=0)
|
||||
NormalValue += character->MagicStatus[down].Value;
|
||||
else
|
||||
NormalValue = CurrentValue + character->MagicStatus[down].Value;
|
||||
if(!Buff)
|
||||
NewValue.Position = down;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(MSG_INFO, "No de-buff currently active");
|
||||
if(!Buff) //is this a debuff?
|
||||
{
|
||||
Log(MSG_INFO, "Buff is false so adding a new de-buff");
|
||||
for(UINT z=15;z<30;z++) // 15 to 29 are downward
|
||||
{
|
||||
if(character->MagicStatus[z].Status == thisskill->status[i])
|
||||
{
|
||||
NewValue.Position = z; //z is the index in the magicstatus array where the de-buff will be placed
|
||||
Log(MSG_INFO, "#1 found status De-Buff in position = %i. refreshing it.",z);
|
||||
break;
|
||||
}
|
||||
if(character->MagicStatus[z].Status == 0)
|
||||
{
|
||||
NewValue.Position = z; //z is the new index in the magicstatus array where the de-buff will be placed
|
||||
Log(MSG_INFO, "#1 De-Buff position = %i",z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(NewValue.Position == 0xff)
|
||||
return NewValue;
|
||||
NormalValue = CurrentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( down != 0xff ) //Status = true and we currently have a debuff in effect??
|
||||
{
|
||||
Log(MSG_INFO, "Current active debuff detected in position %i with value %i", DownValue, character->MagicStatus[down].Value);
|
||||
NormalValue = CurrentValue + character->MagicStatus[down].Value;
|
||||
if(!Buff)
|
||||
NewValue.Position = down;
|
||||
}
|
||||
else //status set true and no current debuff. Used for skills with only a Status effect but no buff/debuff value
|
||||
{
|
||||
for(UINT z=15;z<30;z++) //check magicstatus array debuff area for spaces
|
||||
{
|
||||
if(character->MagicStatus[z].Status == 0)
|
||||
{
|
||||
NewValue.Position = z; //z is the new index in the magicstatus array where the de-buff will be placed
|
||||
Log(MSG_INFO, "#2 De-Buff position = %i",z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(NewValue.Position == 0xff)
|
||||
return NewValue;
|
||||
NormalValue = CurrentValue;
|
||||
}
|
||||
*/
|
||||
|
||||
//Log(MSG_INFO, "Newvalue.position = %i",NewValue.Position);
|
||||
if( thisskill->value2[i] > 0 )
|
||||
{
|
||||
Value = NormalValue * thisskill->value2[i] / 100;
|
||||
//Log(MSG_INFO, "Percentage buff = %i",Value);
|
||||
}
|
||||
if( thisskill->value1[i] > 0 )
|
||||
{
|
||||
Value += thisskill->value1[i];
|
||||
if(thisskill->buff[i] == A_DASH || thisskill->buff[i] == MOV_SPEED)
|
||||
{
|
||||
//Log(MSG_DEBUG,"Made it into the DASH calculations. Evalue = %i",Evalue);
|
||||
if( Evalue <= 60 )
|
||||
{
|
||||
int temp1 = Evalue - 60;
|
||||
//Log(MSG_DEBUG,"temp1 = %i",temp1);
|
||||
float temp2 = (20.0 / 45.0);
|
||||
//Log(MSG_DEBUG,"temp2 = %f",temp2);
|
||||
int temp = (int)(temp1 * temp2);
|
||||
//Log(MSG_DEBUG,"Result of calculation for INT < 60 = %i",temp);
|
||||
//int temp = (int)((Evalue - 60) * (20.0 / 45.0));
|
||||
//Log(MSG_DEBUG,"Result of calculation for INT < 60 = %i",temp);
|
||||
Value += temp;
|
||||
}
|
||||
if( Evalue > 60 && Evalue <= 150)
|
||||
{
|
||||
int temp1 = Evalue - 60;
|
||||
float temp2 = (17.0 / 45.0);
|
||||
int temp = (int)(temp1 * temp2);
|
||||
Value += temp;
|
||||
}
|
||||
if( Evalue > 150 && Evalue <= 195)
|
||||
{
|
||||
int temp1 = Evalue - 150;
|
||||
float temp2 = (18.0 / 45.0);
|
||||
int temp = (int)(temp1 * temp2);
|
||||
Value += 34 + temp;
|
||||
}
|
||||
if( Evalue > 195 && Evalue <= 240)
|
||||
{
|
||||
int temp1 = Evalue - 195;
|
||||
float temp2 = (19.0 / 45.0);
|
||||
int temp = (int)(temp1 * temp2);
|
||||
Value += 52 + temp;
|
||||
}
|
||||
if( Evalue > 240 && Evalue <= 285)
|
||||
{
|
||||
int temp1 = Evalue - 240;
|
||||
float temp2 = (17.0 / 45.0);
|
||||
int temp = (int)(temp1 * temp2);
|
||||
Value += 71 + temp;
|
||||
}
|
||||
if( Evalue > 285 && Evalue <= 330)
|
||||
{
|
||||
int temp1 = Evalue - 285;
|
||||
float temp2 = (18.0 / 45.0);
|
||||
int temp = (int)(temp1 * temp2);
|
||||
Value += 88 + temp;
|
||||
}
|
||||
if( Evalue > 330 && Evalue <= 375)
|
||||
{
|
||||
int temp1 = Evalue - 330;
|
||||
float temp2 = (17.0 / 45.0);
|
||||
int temp = (int)(temp1 * temp2);
|
||||
Value += 106 + temp;
|
||||
}
|
||||
if( Evalue > 375 && Evalue <= 385)
|
||||
{
|
||||
int temp1 = Evalue - 420;
|
||||
float temp2 = (19.0 / 45.0);
|
||||
int temp = (int)(temp1 * temp2);
|
||||
Value += 123 + temp;
|
||||
}
|
||||
}
|
||||
// Value += (Evalue - 15) / 10;
|
||||
else
|
||||
Value += (Evalue-15)/32;
|
||||
//Log(MSG_INFO, "Number buff = %i",Value);
|
||||
}
|
||||
if(Buff)
|
||||
{
|
||||
if( Value + CurrentValue - DownValue >= CurrentValue )
|
||||
{
|
||||
CurrentValue = NormalValue - DownValue + Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentValue = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentValue = NormalValue + UpValue - Value;
|
||||
}
|
||||
NewValue.NewValue = CurrentValue;
|
||||
NewValue.Value = Value;
|
||||
return NewValue;
|
||||
}
|
||||
|
||||
// Build Buffs to Show
|
||||
unsigned int CWorldServer::BuildBuffs( CCharacter* character )
|
||||
{
|
||||
BYTE buff1 = 0;
|
||||
BYTE buff2 = 0;
|
||||
BYTE buff3 = 0;
|
||||
BYTE buff4 = 0;
|
||||
//Build Debuffs and Buffs
|
||||
//Up
|
||||
if(character->Status->Attack_up != 0xff)//A_ATTACK:
|
||||
buff2 += ATTACK_UP;
|
||||
if(character->Status->Defense_up != 0xff)//A_DEFENSE:
|
||||
buff2 += DEFENSE_UP;
|
||||
if(character->Status->Magic_Defense_up != 0xff)//A_MDEFENSE_UP
|
||||
buff2 += MDEFENSE_UP;
|
||||
if(character->Status->Accury_up != 0xff)//A_ACCUR:
|
||||
buff3 += HITRATE_UP;
|
||||
if(character->Status->Dash_up != 0xff)//A_DASH:
|
||||
buff1 += DASH_UP;
|
||||
if(character->Status->Haste_up != 0xff)//A_HASTE:
|
||||
buff2 += HASTE_UP;
|
||||
if(character->Status->HP_up != 0xff)//A_HP:
|
||||
buff1 += HP_UP;
|
||||
if(character->Status->MP_up != 0xff)//A_MP:
|
||||
buff1 += MP_UP;
|
||||
if(character->Status->Critical_up != 0xff)//A_CRITICAL:
|
||||
buff3 += CRITICAL_UP;
|
||||
if(character->Status->Dodge_up != 0xff)//A_DODGE:
|
||||
buff1 += DODGE_UP;
|
||||
if(character->Status->ExtraDamage_up != 0xff)//A_Extra_Damage:
|
||||
buff4 += DAMAGE_UP;
|
||||
//Down
|
||||
if(character->Status->Attack_down != 0xff) // A_ATTACK:
|
||||
buff2 += ATTACK_DOWN;
|
||||
if(character->Status->Defense_down != 0xff)//A_DEFENSE:
|
||||
buff2 += DEFENSE_DOWN;
|
||||
if(character->Status->Magic_Defense_down != 0xff)
|
||||
buff2 += MDEFENSE_DOWN;
|
||||
if(character->Status->Accury_down != 0xff)//A_ACCUR:
|
||||
buff3 += HITRATE_DOWN;
|
||||
if(character->Status->Dash_down != 0xff)//A_DASH:
|
||||
buff1 += DASH_DOWN;
|
||||
if(character->Status->Haste_down != 0xff)//A_HASTE:
|
||||
buff2 += HASTE_DOWN;
|
||||
if(character->Status->HP_down != 0xff)//A_HP:
|
||||
buff1 += 0;
|
||||
if(character->Status->MP_down != 0xff)//A_MP:
|
||||
buff1 += 0;
|
||||
if(character->Status->Critical_down != 0xff)//A_CRITICAL:
|
||||
buff3 += CRITICAL_DOWN;
|
||||
if(character->IsSummon( ))
|
||||
buff4 += SUMMON;
|
||||
if(character->Status->Stuned != 0xff)//A_STUN
|
||||
buff4+= STUN;
|
||||
if(character->Status->Poisoned != 0xff)//A_POISON
|
||||
buff1+= POISONED;
|
||||
if(character->Status->Muted != 0xff)//A_MUTE
|
||||
buff3+= MUTED;
|
||||
return (buff1 * 0x01) + (buff2 * 0x100 ) + (buff3 * 0x10000) + (buff4 * 0x1000000);
|
||||
}
|
||||
|
||||
// Build Buffs to Show
|
||||
unsigned int CWorldServer::BuildUpBuffs( CCharacter* character )
|
||||
{
|
||||
BYTE buff1 = 0;
|
||||
BYTE buff2 = 0;
|
||||
BYTE buff3 = 0;
|
||||
BYTE buff4 = 0;
|
||||
// Buffs
|
||||
//Up
|
||||
if(character->Status->Attack_up != 0xff)//A_ATTACK:
|
||||
buff2 += ATTACK_UP;
|
||||
if(character->Status->Defense_up != 0xff)//A_DEFENSE:
|
||||
buff2 += DEFENSE_UP;
|
||||
if(character->Status->Magic_Defense_up != 0xff)//A_MDEFENSE_UP
|
||||
buff2 += MDEFENSE_UP;
|
||||
if(character->Status->Accury_up != 0xff)//A_ACCUR:
|
||||
buff3 += HITRATE_UP;
|
||||
if(character->Status->Dash_up != 0xff)//A_DASH:
|
||||
buff1 += DASH_UP;
|
||||
if(character->Status->Haste_up != 0xff)//A_HASTE:
|
||||
buff2 += HASTE_UP;
|
||||
if(character->Status->HP_up != 0xff)//A_HP:
|
||||
buff1 += HP_UP;
|
||||
if(character->Status->MP_up != 0xff)//A_MP:
|
||||
buff1 += MP_UP;
|
||||
if(character->Status->Critical_up != 0xff)//A_CRITICAL:
|
||||
buff3 += CRITICAL_UP;
|
||||
if(character->Status->Dodge_up != 0xff)//A_DODGE:
|
||||
buff1 += DODGE_UP;
|
||||
if(character->Status->ExtraDamage_up != 0xff)//A_Extra_Damage:
|
||||
buff4 += DAMAGE_UP;
|
||||
return (buff1 * 0x01) + (buff2 * 0x100 ) + (buff3 * 0x10000) + (buff4 * 0x1000000);
|
||||
}
|
||||
// Build Buffs to Show
|
||||
unsigned int CWorldServer::BuildDeBuffs( CCharacter* character )
|
||||
{
|
||||
BYTE buff1 = 0;
|
||||
BYTE buff2 = 0;
|
||||
BYTE buff3 = 0;
|
||||
BYTE buff4 = 0;
|
||||
//Build Debuffs
|
||||
//Down
|
||||
if(character->Status->Attack_down != 0xff) // A_ATTACK:
|
||||
buff2 += ATTACK_DOWN;
|
||||
if(character->Status->Defense_down != 0xff)//A_DEFENSE:
|
||||
buff2 += DEFENSE_DOWN;
|
||||
if(character->Status->Magic_Defense_down != 0xff)
|
||||
buff2 += MDEFENSE_DOWN;
|
||||
if(character->Status->Accury_down != 0xff)//A_ACCUR:
|
||||
buff3 += HITRATE_DOWN;
|
||||
if(character->Status->Dash_down != 0xff)//A_DASH:
|
||||
buff1 += DASH_DOWN;
|
||||
if(character->Status->Haste_down != 0xff)//A_HASTE:
|
||||
buff2 += HASTE_DOWN;
|
||||
if(character->Status->HP_down != 0xff)//A_HP:
|
||||
buff1 += 0;
|
||||
if(character->Status->MP_down != 0xff)//A_MP:
|
||||
buff1 += 0;
|
||||
if(character->Status->Critical_down != 0xff)//A_CRITICAL:
|
||||
buff3 += CRITICAL_DOWN;
|
||||
if(character->IsSummon( ))
|
||||
buff4 += SUMMON;
|
||||
if(character->Status->Stuned != 0xff)//A_STUN
|
||||
buff4+= STUN;
|
||||
if(character->Status->Poisoned != 0xff)//A_POISON
|
||||
buff1+= POISONED;
|
||||
if(character->Status->Muted != 0xff)//A_MUTE
|
||||
buff3+= MUTED;
|
||||
return (buff1 * 0x01) + (buff2 * 0x100 ) + (buff3 * 0x10000) + (buff4 * 0x1000000);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,847 @@
|
||||
/*
|
||||
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 "character.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
void CCharacter::DoAttack( )
|
||||
{
|
||||
//if(Status->Sleep <= 0xff)
|
||||
//{
|
||||
// ClearBattle( Battle );
|
||||
// return; // can't take any action while we are asleep can we?
|
||||
//}
|
||||
//Log(MSG_INFO,"skill type %i selected. Chartype = %i",Battle->atktype, CharType);
|
||||
if(Battle->iscasting == 1)
|
||||
{
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
//Log(MSG_DEBUG,"Iscasting detected as true. Reset to false.");
|
||||
if(CanAttack())
|
||||
{
|
||||
if(IsPlayer())
|
||||
{
|
||||
Log(MSG_DEBUG,"Should reach here once on normal attack. iscasting state = %i",Battle->iscasting);
|
||||
}
|
||||
ClearBattle( Battle );
|
||||
|
||||
StartAction(Enemy, NORMAL_ATTACK, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(!CanAttack())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(IsSummon())
|
||||
{
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL || (Battle->atktype != SKILL_AOE && Battle->atktype != BUFF_AOE))
|
||||
{
|
||||
//Log(MSG_DEBUG,"No Enemy found");
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(this == Enemy) //summoned monster is attacking itself. It has been observed to happen
|
||||
{
|
||||
//Log(MSG_INFO,"Clearing Battle for this summon");
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(Enemy->IsSummon())
|
||||
{
|
||||
CMonster* thisMonster = reinterpret_cast<CMonster*>(this);
|
||||
if(thisMonster == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
CMonster* otherMonster = reinterpret_cast<CMonster*>(Enemy);
|
||||
if(otherMonster == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(thisMonster->owner == otherMonster->owner)
|
||||
{
|
||||
//Log(MSG_INFO,"Clearing Battle for this summon");
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
//Log(MSG_INFO,"DoAttack Battle Attack type = %i",Battle->atktype);
|
||||
switch(Battle->atktype)
|
||||
{
|
||||
case NORMAL_ATTACK://normal attack
|
||||
{
|
||||
//if(!Status->CanAttack)
|
||||
// return; //can't attack right now.
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(Enemy == this)
|
||||
{
|
||||
//Log(MSG_INFO,"WTF?? I AM trying to attack myself");
|
||||
ClearBattle( Battle );
|
||||
}
|
||||
if(IsTargetReached( Enemy ))
|
||||
{
|
||||
//if(IsMonster())
|
||||
// Log(MSG_INFO,"Monster is trying to hit the player");
|
||||
NormalAttack( Enemy );
|
||||
if (Enemy->IsMonster()) // do monster AI script when monster is attacked.
|
||||
{
|
||||
CMonster* monster = GServer->GetMonsterByID(Enemy->clientid, Enemy->Position->Map);
|
||||
if(monster == NULL)return;
|
||||
//monster->DoAi(monster->thisnpc->AI, 3);
|
||||
monster->DoAi(monster->monAI, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SKILL_ATTACK://skill attack
|
||||
{
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
//ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(IsTargetReached( Enemy, skill ))
|
||||
{
|
||||
SkillAttack( Enemy, skill );
|
||||
if (Enemy->IsMonster())
|
||||
{
|
||||
CMonster* monster = GServer->GetMonsterByID(Enemy->clientid, Enemy->Position->Map);
|
||||
if(monster == NULL)return;
|
||||
monster->DoAi(monster->monAI, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SKILL_BUFF://buffs
|
||||
{
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL)
|
||||
{
|
||||
//Log(MSG_DEBUG,"this target is NULL");
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
//Log(MSG_DEBUG,"this skill is NULL");
|
||||
return;
|
||||
}
|
||||
if(IsTargetReached( Enemy, skill ))
|
||||
{
|
||||
//Log(MSG_DEBUG,"Skill successfully cast");
|
||||
BuffSkill( Enemy, skill );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SKILL_AOE:
|
||||
//case SKILL_SELF: //this is impossible. it can never be set
|
||||
case AOE_TARGET:
|
||||
{
|
||||
//Log(MSG_DEBUG,"Called skill type %i",Battle->atktype);
|
||||
CCharacter* Enemy = NULL;
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(Battle->atktype == AOE_TARGET)
|
||||
{
|
||||
float distance = GServer->distance( Position->current, Position->destiny );
|
||||
if(distance <= skill->range)
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
AoeSkill( skill, Enemy );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Position->skilltargetpos = Position->current;
|
||||
AoeSkill( skill, NULL );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BUFF_SELF:
|
||||
{
|
||||
// return; // can't cast skills right now
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
//ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
BuffSkill( this, skill );
|
||||
}
|
||||
break;
|
||||
case BUFF_AOE:
|
||||
{
|
||||
//Log(MSG_INFO,"BUFF AOE selected");
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
//ClearBattle( Battle );
|
||||
|
||||
return;
|
||||
}
|
||||
AoeBuff( skill );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// do normal attack
|
||||
void CCharacter::NormalAttack( CCharacter* Enemy )
|
||||
{
|
||||
Enemy->OnBeAttacked( this );
|
||||
Position->destiny = Position->current;
|
||||
// PY code
|
||||
float mod = 0;
|
||||
if(IsPlayer())
|
||||
{
|
||||
mod = 23 + (Stats->Level / 1.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
mod = 23 + (Stats->Level / 4);
|
||||
|
||||
}
|
||||
float attack = 0;
|
||||
if(Stats->magicattack == 1)
|
||||
{
|
||||
attack = (float) Stats->Attack_Power / Enemy->Stats->Magic_Defense;
|
||||
attack *= mod;
|
||||
}
|
||||
else
|
||||
{
|
||||
attack = (float) Stats->Attack_Power / Enemy->Stats->Defense;
|
||||
attack *= mod;
|
||||
}
|
||||
//PY end
|
||||
if(attack < 5) attack = 5;
|
||||
float d_attack = attack / 100;
|
||||
mod = GServer->RandNumber( 0, 10 ) * d_attack;
|
||||
attack += mod;
|
||||
long int hitpower = (long int)floor(attack + GServer->RandNumber(0, 10));
|
||||
if(IsPlayer( )) //temp fix to find balance btw monster and player
|
||||
{
|
||||
hitpower = (long int)floor(attack * (GServer->Config.PlayerDmg/100.00));
|
||||
hitpower+=((hitpower*(Stats->ExtraDamage))/100);
|
||||
}
|
||||
if(IsMonster( )) //temp fix to find balance btw monster and player
|
||||
hitpower = (long int)floor(attack * (GServer->Config.MonsterDmg/100.00));
|
||||
bool critical = false;
|
||||
if(hitpower <= 5)
|
||||
{
|
||||
hitpower = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(GServer->RandNumber(0,300)<Stats->Critical)
|
||||
{
|
||||
hitpower = (long int)floor(hitpower * 1.5);
|
||||
critical = true;
|
||||
}
|
||||
}
|
||||
// dodge
|
||||
unsigned int hitvalue = (unsigned int)floor((double)(Stats->Accury * 100 / Enemy->Stats->Dodge));
|
||||
if(hitvalue>100) hitvalue = 100;
|
||||
if(GServer->RandNumber( 0, 100 )>hitvalue)
|
||||
hitpower = 0; // dodged
|
||||
if (hitpower > 0x7ff)//2047 packet size limit.
|
||||
{
|
||||
hitpower=0x7ff;
|
||||
}
|
||||
|
||||
//maybe this little line here will fix my ghosty problem
|
||||
//they are definitely attacking. But no animation so maybe atktarget is empty
|
||||
Battle->atktarget = Battle->target;
|
||||
//Well it does make the damage show up at teh client but the animation is still dodgey
|
||||
|
||||
if(IsMonster())
|
||||
{
|
||||
//Log(MSG_INFO,"Monster hits player for %i damage. Attack target = %i",hitpower,Battle->atktarget);
|
||||
}
|
||||
//Log( MSG_INFO, "hitpower %i. Attack %f ", hitpower,attack );
|
||||
if(!Enemy->IsSummon( ) && Enemy->IsMonster( ))
|
||||
{
|
||||
Enemy->AddDamage( this, hitpower );
|
||||
Enemy->damagecounter += hitpower;// is for AI
|
||||
}
|
||||
Enemy->Stats->HP -= hitpower;
|
||||
if(IsPlayer())
|
||||
Log(MSG_DEBUG,"Player attack did damage %i",hitpower);
|
||||
if(IsMonster())
|
||||
Log(MSG_DEBUG,"Monster attack did damage %i",hitpower);
|
||||
|
||||
|
||||
|
||||
BEGINPACKET( pak, 0x799 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Battle->atktarget );
|
||||
|
||||
if(Enemy->IsDead())
|
||||
{
|
||||
CDrop* thisdrop = NULL;
|
||||
ADDWORD ( pak, (hitpower | ( critical?0xb000:0x8000 ) ));
|
||||
if(!Enemy->IsSummon( ) && !Enemy->IsPlayer( ))
|
||||
{
|
||||
thisdrop = Enemy->GetDrop( );
|
||||
if(thisdrop != NULL)
|
||||
{
|
||||
ADDFLOAT ( pak, thisdrop->pos.x*100 );
|
||||
ADDFLOAT ( pak, thisdrop->pos.y*100 );
|
||||
if(thisdrop->type == 1)
|
||||
{
|
||||
ADDWORD( pak, 0xccdf );
|
||||
ADDDWORD( pak, thisdrop->amount );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, GServer->BuildItemHead( thisdrop->item ) );
|
||||
ADDDWORD ( pak, GServer->BuildItemData( thisdrop->item ) );
|
||||
}
|
||||
ADDWORD ( pak, thisdrop->clientid );
|
||||
ADDWORD ( pak, thisdrop->owner );
|
||||
CMap* map = GServer->MapList.Index[thisdrop->posMap];
|
||||
map->AddDrop( thisdrop );
|
||||
}
|
||||
}
|
||||
GServer->SendToVisible( &pak, Enemy, thisdrop );
|
||||
OnEnemyDie( Enemy );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, (hitpower|(hitpower>0?(critical?0x4000:0):0)));
|
||||
GServer->SendToVisible( &pak, Enemy );
|
||||
}
|
||||
ReduceABC( );
|
||||
Battle->lastAtkTime = clock( );
|
||||
}
|
||||
|
||||
// do skill attack
|
||||
bool CCharacter::SkillAttack( CCharacter* Enemy, CSkills* skill )
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
//if(Battle->castTime == 0)
|
||||
//{
|
||||
BEGINPACKET( pak, 0x7bb );
|
||||
ADDWORD ( pak, clientid );
|
||||
GServer->SendToVisible( &pak, this );
|
||||
// Battle->castTime = clock();
|
||||
// return true;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// clock_t etime = clock() - Battle->castTime;
|
||||
// if(etime < SKILL_DELAY)
|
||||
// return true;
|
||||
//}
|
||||
//Battle->castTime = 0;
|
||||
UseAtkSkill( Enemy, skill );
|
||||
Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100));
|
||||
if(Stats->MP < 0)
|
||||
Stats->MP = 0;
|
||||
//Battle->atktype = NORMAL_ATTACK;
|
||||
//Battle->skilltarget = 0;
|
||||
//Battle->atktarget = Battle->target;
|
||||
//Battle->skillid = 0;
|
||||
GServer->DoSkillScript( this, skill );
|
||||
Battle->lastAtkTime = clock( );
|
||||
if(Enemy->IsDead())
|
||||
ClearBattle(Battle);
|
||||
Battle->iscasting = 1;
|
||||
//Log(MSG_DEBUG,"Cast a skill. Iscasting set to true");
|
||||
return true;
|
||||
}
|
||||
|
||||
// do buff skill
|
||||
bool CCharacter::BuffSkill( CCharacter* Target, CSkills* skill )
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
//if(Battle->castTime == 0)
|
||||
//{
|
||||
BEGINPACKET( pak, 0x7bb );
|
||||
ADDWORD ( pak, clientid );
|
||||
GServer->SendToVisible( &pak, (CCharacter*)this );
|
||||
// Battle->castTime = clock();
|
||||
// return true;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// clock_t etime = clock() - Battle->castTime;
|
||||
// if(etime < SKILL_DELAY)
|
||||
// return true;
|
||||
//}
|
||||
//Battle->castTime = 0;
|
||||
//Log(MSG_DEBUG,"applied buff skill %i",skill->id);
|
||||
UseBuffSkill( Target, skill );
|
||||
Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100));
|
||||
if(Stats->MP < 0) Stats->MP = 0;
|
||||
GServer->DoSkillScript( this, skill ); //summons a monster. Why do we do this for all skills?
|
||||
if(!IsMonster())
|
||||
{
|
||||
ClearBattle( Battle ); // clear battle for players when they use buff skills
|
||||
//Battle->bufftarget = 0;
|
||||
//Battle->skilltarget = 0;
|
||||
//Battle->skillid = 0;
|
||||
//Battle->atktype = NORMAL_ATTACK;
|
||||
}
|
||||
else //Monsters need to be reset to normal attack and clear skill attacks.
|
||||
{
|
||||
//Battle->atktarget = Battle->target;
|
||||
//Battle->bufftarget = 0;
|
||||
//Battle->skilltarget = 0;
|
||||
//Battle->skillid = 0;
|
||||
//Battle->atktype = NORMAL_ATTACK;
|
||||
//StartAction(Target, NORMAL_ATTACK);
|
||||
}
|
||||
Battle->lastAtkTime = clock( );
|
||||
Battle->iscasting = 1;
|
||||
return true;
|
||||
}
|
||||
//arnold
|
||||
// do AoE skill
|
||||
bool CCharacter::AoeSkill( CSkills* skill, CCharacter* Enemy )
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
//Log(MSG_INFO,"Doing AOE Skill");
|
||||
//if(Battle->castTime == 0)
|
||||
//{
|
||||
BEGINPACKET( pak, 0x7bb );
|
||||
ADDWORD ( pak, clientid );
|
||||
GServer->SendToVisible( &pak, (CCharacter*)this );
|
||||
// Battle->castTime = clock();
|
||||
// return true;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// clock_t etime = clock() - Battle->castTime;
|
||||
// if(etime < SKILL_DELAY)
|
||||
// return true;
|
||||
//}
|
||||
//Battle->castTime = 0;
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
|
||||
//PY replacement code
|
||||
if(IsPlayer() || IsSummon())
|
||||
{
|
||||
for(UINT i=0;i<map->MonsterList.size();i++)
|
||||
{
|
||||
CMonster* monster = map->MonsterList.at(i);
|
||||
if(monster == NULL) continue;
|
||||
if(monster->clientid == clientid) continue;
|
||||
if(monster->IsSummon( ) && (map->allowpvp == 0 || monster->owner == clientid)) continue;
|
||||
if(GServer->IsMonInCircle( Position->skilltargetpos, monster->Position->current,(float)skill->aoeradius + 1))
|
||||
UseAtkSkill( (CCharacter*) monster, skill );
|
||||
}
|
||||
}
|
||||
if(IsMonster() && !IsSummon())
|
||||
{
|
||||
for(UINT i=0;i<map->PlayerList.size();i++)
|
||||
{
|
||||
CPlayer* player = map->PlayerList.at(i);
|
||||
if(player == NULL)continue;
|
||||
if(player->clientid == clientid) continue;
|
||||
if(GServer->IsMonInCircle( Position->skilltargetpos,player->Position->current,(float)skill->aoeradius+1))
|
||||
UseAtkSkill( (CCharacter*) player, skill );
|
||||
}
|
||||
}
|
||||
//PY end
|
||||
|
||||
// this code appears to me to be completely wrong replaced with code above
|
||||
/*
|
||||
for(UINT i=0;i<map->MonsterList.size();i++)
|
||||
{
|
||||
CMonster* monster = map->MonsterList.at(i);
|
||||
if(monster->clientid == clientid) continue;
|
||||
if(IsSummon( ) || IsPlayer( ))
|
||||
{
|
||||
if(monster->IsSummon( ) && (map->allowpvp==0 || monster->owner == clientid)) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!monster->IsSummon( )) continue;
|
||||
}
|
||||
if(GServer->IsMonInCircle( Position->skilltargetpos, monster->Position->current,(float)skill->aoeradius + 1))
|
||||
UseAtkSkill( (CCharacter*) monster, skill );
|
||||
}
|
||||
if(map->allowpvp!=0 || (IsMonster( ) && !IsSummon( )))
|
||||
{
|
||||
for(UINT i=0;i<map->PlayerList.size();i++)
|
||||
{
|
||||
CPlayer* player = map->PlayerList.at(i);
|
||||
if(player->clientid==clientid) continue;
|
||||
if(GServer->IsMonInCircle( Position->skilltargetpos,player->Position->current,(float)skill->aoeradius+1))
|
||||
UseAtkSkill( (CCharacter*) player, skill );
|
||||
}
|
||||
}*/
|
||||
Battle->iscasting = 1;
|
||||
//Log(MSG_DEBUG,"Cast a skill. Iscasting set to true");
|
||||
if(Enemy != NULL)
|
||||
{
|
||||
if(Enemy->IsDead( ))
|
||||
ClearBattle( Battle );
|
||||
}
|
||||
else
|
||||
ClearBattle( Battle );
|
||||
Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100));
|
||||
if(Stats->MP < 0) Stats->MP = 0;
|
||||
Battle->lastAtkTime = clock( );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCharacter::AoeBuff( CSkills* skill )
|
||||
{
|
||||
//Log(MSG_INFO,"BUFF AOE called");
|
||||
Position->destiny = Position->current;
|
||||
BEGINPACKET( pak, 0x7bb );
|
||||
ADDWORD ( pak, clientid );
|
||||
GServer->SendToVisible( &pak, (CCharacter*)this );
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
|
||||
if(CharType != 2) //player || Summon || NPC. Anything that is not a monster
|
||||
{
|
||||
for(UINT i=0;i<map->PlayerList.size();i++)
|
||||
{
|
||||
CPlayer* player = map->PlayerList.at(i);
|
||||
if(player == NULL)continue;
|
||||
switch(skill->target)
|
||||
{
|
||||
case 1: //tPartyMember
|
||||
if(player->Party->party == GetParty())
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough party member");
|
||||
if (player == this) //it is me so I had better cast it anyway ^-^
|
||||
{
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
else if(player->Party->party != NULL) // Don't cast the skill on players who are not in any party at all.
|
||||
{
|
||||
if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1))
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2: //tClanMember
|
||||
if (player->Clan == GetClan( ) && (player->Clan != 0 || player == this))
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough clan member");
|
||||
if(GServer->IsMonInCircle( Position->current, player->Position->current, (float)skill->aoeradius + 1 ) )
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
break;
|
||||
case 3: //tAlly
|
||||
case 7: //tAllCharacters
|
||||
case 8: //tAllMembers. all characters
|
||||
{
|
||||
Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough allied Player");
|
||||
if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1))
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
break;
|
||||
case 5: //tHostileCharacter
|
||||
{
|
||||
if (CharType == 2)
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough hostile Player");
|
||||
if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1))
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (CharType == 2) //monster
|
||||
{
|
||||
for(UINT i=0;i<map->MonsterList.size();i++)
|
||||
{
|
||||
CMonster* monster = map->MonsterList.at(i);
|
||||
if(monster == NULL)continue;
|
||||
switch(skill->target)
|
||||
{
|
||||
case tPartyMember: // party
|
||||
break;
|
||||
case 2: //tClanMember
|
||||
break;
|
||||
case 3: //tAlly
|
||||
case 7: //tAllCharacters
|
||||
case 8: //tAllMembers. all characters
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough monster");
|
||||
if(GServer->IsMonInCircle( Position->current,monster->Position->current,(float)skill->aoeradius + 1))
|
||||
UseBuffSkill( (CCharacter*)monster, skill );
|
||||
}
|
||||
break;
|
||||
case tHostileCharacter:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100));
|
||||
if(Stats->MP < 0) Stats->MP = 0;
|
||||
//Battle->atktarget = Battle->target;
|
||||
//Battle->bufftarget = 0;
|
||||
//Battle->skilltarget = 0;
|
||||
//Battle->skillid = 0;
|
||||
//Battle->atktype = NORMAL_ATTACK;
|
||||
ClearBattle( Battle );
|
||||
Battle->lastAtkTime = clock( );
|
||||
Battle->iscasting = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// use skill attack
|
||||
void CCharacter::UseAtkSkill( CCharacter* Enemy, CSkills* skill, bool deBuff)
|
||||
{
|
||||
long int skillpower = skill->atkpower;
|
||||
UINT weapontype = 0;
|
||||
float mod = 0;
|
||||
float attack = 0;
|
||||
if(IsPlayer())
|
||||
{
|
||||
mod = 20 + (Stats->Level / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
mod = 15 + (Stats->Level / 2);
|
||||
}
|
||||
if(skill->skilltype == 6 || skill->skilltype == 7) //magical damage
|
||||
{
|
||||
attack = skillpower / (Enemy->Stats->Magic_Defense / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
attack = skillpower / (Enemy->Stats->Defense / 2);
|
||||
}
|
||||
attack *= mod;
|
||||
attack += (Stats->Attack_Power / 2); // let's add our AP to the skillpower to make it worthwhile to cast skills.
|
||||
|
||||
skillpower = (int)attack;
|
||||
bool bflag = false;
|
||||
Enemy->OnBeAttacked( this );
|
||||
if(skillpower <= 0) skillpower = 0;
|
||||
if(IsPlayer())
|
||||
{
|
||||
skillpower += (int)(skillpower * (Stats->ExtraDamage))/100;
|
||||
}
|
||||
if (skill->atkpower == 0)
|
||||
{
|
||||
skillpower = 0; // If the skill has a 0 atk power, it isn't meant to damage
|
||||
Log( MSG_INFO, "This skill does no damage" );
|
||||
//ClearBattle( Battle );
|
||||
return; // probably need to add some alternative code here for legitimate skills that have no attack power.
|
||||
}
|
||||
if(skillpower >2046)skillpower = 2046;
|
||||
//Log(MSG_DEBUG,"Cast a skill. damage is %i",skillpower);
|
||||
if(!Enemy->IsSummon( ) && Enemy->IsMonster( ))
|
||||
{
|
||||
Enemy->AddDamage( this, skillpower );
|
||||
Enemy->damagecounter += skillpower;// is for AI
|
||||
}
|
||||
Enemy->Stats->HP -= skillpower;
|
||||
if(IsPlayer())
|
||||
Log(MSG_DEBUG,"Player Spell did damage %i",skillpower);
|
||||
if(IsMonster())
|
||||
Log(MSG_DEBUG,"Monster spell did damage %i",skillpower);
|
||||
BEGINPACKET( pak, 0x7b6 );
|
||||
ADDWORD ( pak, Enemy->clientid );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDDWORD ( pak, 0x000007f8 );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
if (Enemy->Stats->HP<1)
|
||||
{
|
||||
ADDWORD ( pak, skillpower + 0x8000 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, skillpower + 0x2000 );
|
||||
}
|
||||
if(Enemy->IsDead())
|
||||
{
|
||||
//Log(MSG_DEBUG,"Enemy died");
|
||||
CDrop* thisdrop = NULL;
|
||||
ADDDWORD ( pak, 16 );
|
||||
if(!Enemy->IsSummon( ) && !Enemy->IsPlayer( ))
|
||||
{
|
||||
thisdrop = Enemy->GetDrop( );
|
||||
if(thisdrop != NULL)
|
||||
{
|
||||
ADDFLOAT ( pak, thisdrop->pos.x*100 );
|
||||
ADDFLOAT ( pak, thisdrop->pos.y*100 );
|
||||
if(thisdrop->type==1)
|
||||
{
|
||||
ADDWORD( pak, 0xccdf );
|
||||
ADDDWORD( pak, thisdrop->amount );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, GServer->BuildItemHead( thisdrop->item ) );
|
||||
ADDDWORD ( pak, GServer->BuildItemData( thisdrop->item ) );
|
||||
}
|
||||
ADDWORD ( pak, thisdrop->clientid );
|
||||
ADDWORD ( pak, thisdrop->owner );
|
||||
CMap* map = GServer->MapList.Index[thisdrop->posMap];
|
||||
map->AddDrop( thisdrop );
|
||||
}
|
||||
}
|
||||
GServer->SendToVisible( &pak, Enemy, thisdrop );
|
||||
OnEnemyDie( Enemy );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDDWORD ( pak, 4 );
|
||||
GServer->SendToVisible( &pak, Enemy );
|
||||
bflag = GServer->AddBuffs( skill, Enemy, GetInt( ) );
|
||||
|
||||
//Send (de)buff information to the whole world
|
||||
if(skill->nbuffs > 0 && bflag)
|
||||
{
|
||||
BEGINPACKET( pak, 0x7b5 );
|
||||
ADDWORD ( pak, Battle->skilltarget );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Battle->skillid );
|
||||
ADDWORD ( pak, GetInt( ) );
|
||||
ADDBYTE ( pak, skill->nbuffs );
|
||||
GServer->SendToVisible( &pak, Enemy );
|
||||
}
|
||||
}
|
||||
if (deBuff) return;
|
||||
//Send skill animation to the world
|
||||
RESETPACKET( pak, 0x7b9);
|
||||
ADDWORD ( pak, clientid);
|
||||
ADDWORD ( pak, Battle->skillid);
|
||||
ADDWORD ( pak, 1);
|
||||
GServer->SendToVisible( &pak, this );
|
||||
Battle->bufftarget = 0;
|
||||
Battle->skilltarget = 0;
|
||||
Battle->skillid = 0;
|
||||
//Battle->atktype = NORMAL_ATTACK;
|
||||
}
|
||||
|
||||
// use buff skill
|
||||
void CCharacter::UseBuffSkill( CCharacter* Target, CSkills* skill )
|
||||
{
|
||||
bool bflag = false;
|
||||
bflag = GServer->AddBuffs( skill, Target, GetInt( ) );
|
||||
if(skill->nbuffs > 0 && bflag == true)
|
||||
{
|
||||
BEGINPACKET( pak, 0x7b5 );
|
||||
ADDWORD ( pak, Target->clientid );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Battle->skillid );
|
||||
ADDWORD ( pak, GetInt( )/4 );
|
||||
ADDBYTE ( pak, skill->nbuffs );
|
||||
GServer->SendToVisible( &pak, Target );
|
||||
//Log(MSG_DEBUG,"Buff Skill. buffs applied 1");
|
||||
}
|
||||
|
||||
BEGINPACKET( pak, 0x7b9);
|
||||
ADDWORD ( pak, clientid);
|
||||
ADDWORD ( pak, Battle->skillid);
|
||||
ADDWORD ( pak, 1);
|
||||
GServer->SendToVisible( &pak, this );
|
||||
//Log(MSG_DEBUG,"Buff Skill. buffs applied 2 nbuffs = %i",skill->nbuffs);
|
||||
//Log(MSG_INFO,"Target HP = %i",Target->Stats->HP);
|
||||
}
|
||||
|
||||
// Use a skill
|
||||
bool CCharacter::UseSkill( CSkills* skill, CCharacter *Target )
|
||||
{
|
||||
if (skill->atkpower > 0)
|
||||
{
|
||||
Log(MSG_INFO, "Need to do %i%s %s in range %i to target %i",
|
||||
( skill->atkpower ),
|
||||
( (skill->range > 0) ? " AOE" : "" ),
|
||||
( (GServer->isSkillTargetFriendly(skill)) ? "healing" : "damage" ),
|
||||
( skill->range ),
|
||||
( skill->target ) );
|
||||
}
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Log(MSG_INFO, "Status ID: %i", skill->status[i]);
|
||||
if (skill->status[i] == 0) continue;
|
||||
CStatus* status = GServer->GetStatusByID(skill->status[i]);
|
||||
if (status == NULL) continue;
|
||||
/************************
|
||||
We'll probably need to use status->decrease (Figure out whether buf is up/down
|
||||
status->repeat will tell us whether it's a one-time (Stat Boost [2]), repeat
|
||||
(Recovery, continueing damage [1]) or Special (Status Effect [3]).
|
||||
************************/
|
||||
if (status->repeat == 1) // Continuous
|
||||
{
|
||||
Log(MSG_INFO, "Need to take stat %i and %s it by %i%s over %i seconds", skill->buff[i],
|
||||
((status->decrease) ? "decrease" : "increase"),
|
||||
((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]),
|
||||
((skill->value1[i] != 0) ? "" : "%"), skill->duration);
|
||||
}
|
||||
else if (status->repeat == 2) // Once (Stat Boost)
|
||||
{
|
||||
Log(MSG_INFO, "Need to take stat %i and %s it by %i%s for %i seconds", skill->buff[i],
|
||||
((status->decrease) ? "decrease" : "increase"),
|
||||
((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]),
|
||||
((skill->value1[i] != 0) ? "" : "%"), skill->duration);
|
||||
|
||||
} else if (status->repeat == 3) // Status Effect (Poison,etc)
|
||||
{
|
||||
Log(MSG_INFO, "Need to give user status effect %i for %i seconds", skill->buff[i],
|
||||
((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]),
|
||||
( skill->duration ) );
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
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 "character.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
// return true if this character is monster
|
||||
bool CCharacter::IsMonster( )
|
||||
{
|
||||
if(CharType == 2 || CharType == 3)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if this character is player
|
||||
bool CCharacter::IsPlayer( )
|
||||
{
|
||||
if(CharType == 1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//return true if this character is an NPC
|
||||
bool CCharacter::IsNPC( )
|
||||
{
|
||||
if(CharType == 4)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if HP is lower to 1
|
||||
bool CCharacter::IsDead( )
|
||||
{
|
||||
if(Stats->HP <= 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if this character is summon
|
||||
bool CCharacter::IsSummon( )
|
||||
{
|
||||
if(this == NULL) return false;
|
||||
if(CharType == 3)// this line is erroring. No idea why
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Virtual function [return item drop]
|
||||
CDrop* CCharacter::GetDrop( )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// start action [attack]
|
||||
void CCharacter::StartAction( CCharacter* Target, BYTE action, UINT skillid)
|
||||
{
|
||||
|
||||
BEGINPACKET( pak, 0 );
|
||||
switch(action)
|
||||
{
|
||||
case NORMAL_ATTACK:
|
||||
{
|
||||
//Log( MSG_INFO, "case NORMAL_ATTACK");
|
||||
if(Target == NULL)return;
|
||||
RESETPACKET( pak, 0x798 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Target->clientid );
|
||||
ADDWORD ( pak, Stats->Move_Speed );
|
||||
ADDFLOAT ( pak, Target->Position->current.x * 100 );
|
||||
ADDFLOAT ( pak, Target->Position->current.y * 100 );
|
||||
Battle->target = Target->clientid;
|
||||
Battle->atktarget = Target->clientid;
|
||||
Battle->atktype = action;
|
||||
Battle->skilltarget = 0;
|
||||
Battle->bufftarget = 0;
|
||||
Battle->skillid = 0;
|
||||
Position->destiny = Target->Position->current;
|
||||
Position->lastMoveTime = clock();
|
||||
}
|
||||
break;
|
||||
case SKILL_ATTACK: //Log( MSG_INFO, "case SKILL_ATTACK");
|
||||
case SKILL_BUFF:
|
||||
{
|
||||
if(Target == NULL)return;
|
||||
RESETPACKET( pak, 0x7b3 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Target->clientid );
|
||||
ADDWORD ( pak, skillid );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
ADDFLOAT ( pak, Target->Position->current.x * 100 );
|
||||
ADDFLOAT ( pak, Target->Position->current.y * 100 );
|
||||
Battle->target = Target->clientid;
|
||||
|
||||
if(action == SKILL_ATTACK)
|
||||
{
|
||||
//Log( MSG_INFO, "case SKILL_ATTACK");
|
||||
Battle->skilltarget = Target->clientid;
|
||||
Battle->bufftarget = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Log( MSG_INFO, "case SKILL_BUFF");
|
||||
Battle->bufftarget = Target->clientid;
|
||||
Battle->skilltarget = 0;
|
||||
}
|
||||
|
||||
Battle->atktype = action;
|
||||
Position->destiny = Target->Position->current;
|
||||
Battle->skillid = skillid;
|
||||
Position->lastMoveTime = clock();
|
||||
}
|
||||
break;
|
||||
case SKILL_AOE://Log( MSG_INFO, "case SKILL_AOE");
|
||||
case BUFF_SELF://Log( MSG_INFO, "case BUFF_SELF");
|
||||
case BUFF_AOE:
|
||||
{
|
||||
//Log( MSG_INFO, "case BUFF_AOE");
|
||||
RESETPACKET( pak, 0x7b2);
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, skillid );
|
||||
Battle->atktype = action;
|
||||
Battle->skillid = skillid;
|
||||
//Log( MSG_INFO, "BUFF_AOE packet sent");
|
||||
}
|
||||
break;
|
||||
case AOE_TARGET:
|
||||
{
|
||||
//Log( MSG_INFO, "case AOE_TARGET");
|
||||
//Target is NULL for this function when cast by a player
|
||||
//Target is NOT NULL when it comes from a monster
|
||||
if(Target != NULL) //monster attack
|
||||
{
|
||||
Position->skilltargetpos.x = Target->Position->current.x;
|
||||
Position->skilltargetpos.y = Target->Position->current.y;
|
||||
}
|
||||
RESETPACKET( pak, 0x7b4 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, skillid );
|
||||
ADDFLOAT ( pak, Position->skilltargetpos.x * 100 );
|
||||
ADDFLOAT ( pak, Position->skilltargetpos.y * 100 );
|
||||
Battle->atktype = action;
|
||||
Battle->skillid = skillid;
|
||||
Battle->skilltarget = 0;
|
||||
Battle->bufftarget = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
GServer->SendToVisible( &pak, this );
|
||||
Battle->contatk = true;
|
||||
|
||||
//Log(MSG_INFO,"Battle Attacktype = %i",Battle->atktype);
|
||||
return;
|
||||
}
|
||||
|
||||
bool CCharacter::IsOnBattle( )
|
||||
{
|
||||
if(Battle->atktype == 0)
|
||||
{
|
||||
if(Battle->iscasting == 1)
|
||||
{
|
||||
clock_t etime = clock() - Battle->lastAtkTime;
|
||||
if( etime < CLOCKS_PER_SEC * 100 / Stats->Attack_Speed )
|
||||
{
|
||||
//Log(MSG_DEBUG,"Iscasting is true while NOT in battle. Reset to false.");
|
||||
Battle->iscasting = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
switch(Battle->atktype)
|
||||
{
|
||||
case NORMAL_ATTACK:
|
||||
{
|
||||
if(Battle->atktarget != 0) return true;
|
||||
}
|
||||
break;
|
||||
case SKILL_ATTACK:
|
||||
case AOE_TARGET:
|
||||
{
|
||||
if(Battle->skilltarget != 0 && Battle->skillid != 0) return true;
|
||||
}
|
||||
break;
|
||||
case SKILL_BUFF:
|
||||
{
|
||||
if(Battle->bufftarget != 0 && Battle->skillid != 0) return true;
|
||||
}
|
||||
break;
|
||||
case SKILL_AOE:
|
||||
//case SKILL_SELF: //no such skill type
|
||||
case BUFF_SELF:
|
||||
case BUFF_AOE:
|
||||
{
|
||||
if(Battle->skillid != 0) return true;
|
||||
}
|
||||
break;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CCharacter::CanAttack( )
|
||||
{
|
||||
|
||||
clock_t etime = clock() - Battle->lastAtkTime;
|
||||
if (Stats->Attack_Speed < 1)
|
||||
{
|
||||
Log( MSG_INFO, "weapon with no attackspeed.");
|
||||
return false;
|
||||
}
|
||||
if(Battle->iscasting == 1)
|
||||
{
|
||||
if( etime < CLOCKS_PER_SEC * 25 / Stats->Attack_Speed ) //should make the delay 4 times longer
|
||||
{
|
||||
//if(IsPlayer())
|
||||
// Log(MSG_INFO,"Cannot use magic attack yet. Last attack time = %i",Battle->lastAtkTime);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Battle->iscasting = 0;
|
||||
//if(IsPlayer())
|
||||
// Log(MSG_INFO,"OK Now we can use a new (magic or normal) attack.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else //normal attack
|
||||
{
|
||||
if( etime < CLOCKS_PER_SEC * 100 / Stats->Attack_Speed )
|
||||
{
|
||||
//if(IsPlayer())
|
||||
// Log(MSG_INFO,"Cannot attack yet. Last attack time = %i",Battle->lastAtkTime);
|
||||
return false;
|
||||
}
|
||||
//if(IsPlayer())
|
||||
// Log(MSG_INFO,"OK Now we can use a normal attack");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// return true if target is reached
|
||||
bool CCharacter::IsTargetReached( CCharacter* Enemy, CSkills* skill )
|
||||
{
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
if(map == NULL)return false;
|
||||
float distance = GServer->distance( Position->current, Enemy->Position->current );
|
||||
if(skill == NULL) //it's a normal attack
|
||||
{
|
||||
if(distance <= Stats->Attack_Distance)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Position->destiny = Enemy->Position->current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(distance <= skill->range)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Position->destiny = Enemy->Position->current;
|
||||
}
|
||||
}
|
||||
//Log(MSG_INFO,"Monster cannot attack. Too far away");
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if the character is moving
|
||||
bool CCharacter::IsMoving( )
|
||||
{
|
||||
if(Position->current.x == Position->destiny.x && Position->current.y == Position->destiny.y )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// VIRTUAL [add player damage]
|
||||
void CCharacter::AddDamage( CCharacter* enemy, long int hitpower)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// return character target
|
||||
CCharacter* CCharacter::GetCharTarget( )
|
||||
{
|
||||
CCharacter* thistarget = GServer->MapList.Index[Position->Map]->GetCharInMap( Battle->target );
|
||||
if (thistarget != NULL)
|
||||
return thistarget;
|
||||
//if I don't currently have a target then return the character who just hit me
|
||||
return GServer->MapList.Index[Position->Map]->GetCharInMap( Battle->hitby );
|
||||
}
|
||||
|
||||
// VIRTUAL [reduce ammon/bullets/cannons]
|
||||
void CCharacter::ReduceABC( )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// VIRTUAL [get extra buff value]
|
||||
int CCharacter::GetEVBuff( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// VIRTUAL [return intelligence]
|
||||
unsigned int CCharacter::GetInt( )
|
||||
{
|
||||
return 15;
|
||||
}
|
||||
|
||||
|
||||
// Check For Debuffs
|
||||
void CCharacter::RefreshBuff( )
|
||||
{
|
||||
bool bflag = false;
|
||||
for( UINT i=0;i<30;i++)
|
||||
{
|
||||
if(MagicStatus[i].Buff == 0) continue;
|
||||
clock_t etime = clock() - MagicStatus[i].BuffTime;
|
||||
if( etime >= MagicStatus[i].Duration * CLOCKS_PER_SEC )
|
||||
{
|
||||
switch(MagicStatus[i].Status)
|
||||
{
|
||||
case 18: // attack power up
|
||||
case 19: // attack power down
|
||||
case 48: // attack power up
|
||||
if(i<15)
|
||||
Status->Attack_up = 0xff;
|
||||
else
|
||||
Status->Attack_down = 0xff;
|
||||
Stats->Attack_Power = GetAttackPower( );
|
||||
break;
|
||||
case 20: // def up
|
||||
case 21: // def down
|
||||
case 49: // def up
|
||||
if(i<15)
|
||||
Status->Defense_up= 0xff;
|
||||
else
|
||||
Status->Defense_down = 0xff;
|
||||
Stats->Defense = GetDefense( );
|
||||
break;
|
||||
case 24: //accuracy up
|
||||
case 25: //accuracy down
|
||||
case 51: //attack accuracy up.
|
||||
if(i<15)
|
||||
Status->Accury_up= 0xff;
|
||||
else
|
||||
Status->Accury_down = 0xff;
|
||||
Stats->Accury = GetAccury( );
|
||||
break;
|
||||
case 22: // macic resistance up
|
||||
case 23: // magic resistance down
|
||||
case 50: // magic resistance up
|
||||
if(i<15)
|
||||
Status->Magic_Defense_up = 0xff;
|
||||
else
|
||||
Status->Magic_Defense_down = 0xff;
|
||||
Stats->Magic_Defense = GetMagicDefense( );
|
||||
break;
|
||||
case 28: //dodge up
|
||||
case 29: //dodge down
|
||||
case 53: //dodge rate up
|
||||
if(i<15)
|
||||
Status->Dodge_up = 0xff;
|
||||
else
|
||||
Status->Dodge_down = 0xff;
|
||||
Stats->Dodge = GetDodge( );
|
||||
break;
|
||||
case 14: //dash
|
||||
case 15: //slow
|
||||
case 46: //movement speed increased
|
||||
if(i<15)
|
||||
Status->Dash_up = 0xff;
|
||||
else
|
||||
Status->Dash_down = 0xff;
|
||||
Stats->Move_Speed = GetMoveSpeed( );
|
||||
break;
|
||||
case 16: // haste attack
|
||||
case 17: // slow attack
|
||||
case 47: // attack speed up
|
||||
if(i<15)
|
||||
Status->Haste_up = 0xff;
|
||||
else
|
||||
Status->Haste_down = 0xff;
|
||||
Stats->Attack_Speed = GetAttackSpeed( );
|
||||
break;
|
||||
case 26: // crit up
|
||||
case 27: // crit down
|
||||
case 52: // crit up
|
||||
if(i<15)
|
||||
Status->Critical_up = 0xff;
|
||||
else
|
||||
Status->Critical_down = 0xff;
|
||||
Stats->Critical = GetCritical( );
|
||||
break;
|
||||
case 12: // max HP up
|
||||
case 44: // max HP up
|
||||
if(i<15)
|
||||
Status->HP_up = 0xff;
|
||||
else
|
||||
Status->HP_down = 0xff;
|
||||
Stats->MaxHP = GetMaxHP( );
|
||||
break;
|
||||
case 13: // max MP up
|
||||
case 45: // max MP up
|
||||
if(i<15)
|
||||
Status->MP_up = 0xff;
|
||||
else
|
||||
Status->MP_down = 0xff;
|
||||
Stats->MaxMP = GetMaxMP( );
|
||||
break;
|
||||
case 32: // faint
|
||||
Status->Stuned = 0xff;
|
||||
Status->CanAttack = true;
|
||||
//printf("removing stun\n");
|
||||
break;
|
||||
case 7: case 8: case 9: case 10: case 11: //poisoned
|
||||
Status->Poisoned = 0xff;
|
||||
//printf("removing poison\n");
|
||||
break;
|
||||
case 30: // muted
|
||||
Status->Muted = 0xff;
|
||||
Status->CanCastSkill = true;
|
||||
break;
|
||||
case 31: // sleep May need to be fixed later to accomodate multiple status effects.
|
||||
Status->Sleep = 0xff;
|
||||
Status->CanMove = true;
|
||||
Status->CanCastSkill = true;
|
||||
Status->CanAttack = true;
|
||||
break;
|
||||
case 54: //A_GMExtra_Damage:
|
||||
case 36: //A_Extra_Damage:
|
||||
if(i<15)
|
||||
{
|
||||
Status->ExtraDamage_up = 0xff;
|
||||
Stats->ExtraDamage = 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
Status->ExtraDamage_down = 0xff;
|
||||
Stats->ExtraDamage = 0xff;
|
||||
}
|
||||
|
||||
break;
|
||||
case 56: // Taunt
|
||||
Status->Taunt = 0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
MagicStatus[i].Status = 0;
|
||||
MagicStatus[i].Buff = 0;
|
||||
MagicStatus[i].BuffTime = 0;
|
||||
MagicStatus[i].Duration = 0;
|
||||
MagicStatus[i].Value = 0;
|
||||
bflag = true;
|
||||
}
|
||||
else if (MagicStatus[i].Status >= 7 && MagicStatus[i].Status <= 11 && etime > 1*CLOCKS_PER_SEC) //Do poison dmg every 1.5 seconds
|
||||
{
|
||||
Stats->HP -= MagicStatus[i].Status; //Actually take 7, 8, 9, 10 or 11 from the health. Based on the Status itself
|
||||
MagicStatus[i].BuffTime += 1*CLOCKS_PER_SEC;
|
||||
MagicStatus[i].Duration -= 1;
|
||||
//printf("did %i poison dmg to the player, still %i seconds and %i HP remain \n", MagicStatus[i].Status, MagicStatus[i].Duration, Stats->HP);
|
||||
|
||||
//A bunch of messy code to send dmg packet
|
||||
BEGINPACKET( pak, 0x7b6 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, 0 );
|
||||
ADDDWORD ( pak, 0x000007f8 );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
ADDDWORD ( pak, MagicStatus[i].Status );
|
||||
|
||||
//If Enemy is killed
|
||||
if( IsDead())
|
||||
{
|
||||
//printf("char died\n");
|
||||
CDrop* thisdrop = NULL;
|
||||
ADDDWORD ( pak, 16 );
|
||||
if( !IsSummon( ) && !IsPlayer( ))
|
||||
{
|
||||
thisdrop = GetDrop( );
|
||||
if( thisdrop!=NULL)
|
||||
{
|
||||
ADDFLOAT ( pak, thisdrop->pos.x*100 );
|
||||
ADDFLOAT ( pak, thisdrop->pos.y*100 );
|
||||
if( thisdrop->type==1)
|
||||
{
|
||||
ADDDWORD( pak, 0xccccccdf );
|
||||
ADDDWORD( pak, thisdrop->amount );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDDWORD ( pak, GServer->BuildItemHead( thisdrop->item ) );
|
||||
ADDDWORD ( pak, GServer->BuildItemData( thisdrop->item ) );
|
||||
}
|
||||
ADDWORD ( pak, thisdrop->clientid );
|
||||
ADDWORD ( pak, thisdrop->owner );
|
||||
CMap* map = GServer->MapList.Index[thisdrop->posMap];
|
||||
map->AddDrop( thisdrop );
|
||||
}
|
||||
}
|
||||
GServer->SendToVisible( &pak, this, thisdrop );
|
||||
}
|
||||
|
||||
//If enemy is still alive
|
||||
else
|
||||
{
|
||||
ADDDWORD ( pak, 4 );
|
||||
GServer->SendToVisible( &pak, this );
|
||||
}
|
||||
}
|
||||
}
|
||||
if(bflag)
|
||||
{
|
||||
BEGINPACKET( pak,0x7b7 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDDWORD ( pak, GServer->BuildBuffs( this ) );
|
||||
GServer->SendToVisible( &pak, this );
|
||||
}
|
||||
}
|
||||
|
||||
// VIRTUAL [return party pointer]
|
||||
CParty* CCharacter::GetParty( )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// VIRTUAL [return clan pointer]
|
||||
CLAN* CCharacter::GetClan( )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CCharacter::IsAttacking( )
|
||||
{
|
||||
if(IsOnBattle( ))
|
||||
{
|
||||
switch(Battle->atktype)
|
||||
{
|
||||
case SKILL_BUFF:
|
||||
case BUFF_SELF:
|
||||
case BUFF_AOE:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
// Map Process
|
||||
PVOID MapProcess( PVOID TS )
|
||||
{
|
||||
bool only_npc = false;
|
||||
while(GServer->ServerOnline)
|
||||
{
|
||||
pthread_mutex_lock( &GServer->PlayerMutex );
|
||||
pthread_mutex_lock( &GServer->MapMutex );
|
||||
for(UINT i=0;i<GServer->MapList.Map.size();i++)
|
||||
{
|
||||
CMap* map = GServer->MapList.Map.at(i);
|
||||
if( map->PlayerList.size()<1 )
|
||||
only_npc = true;
|
||||
else
|
||||
only_npc = false;
|
||||
//continue;
|
||||
|
||||
if(!only_npc)
|
||||
{
|
||||
// Player update //------------------------
|
||||
for(UINT j=0;j<map->PlayerList.size();j++)
|
||||
{
|
||||
CPlayer* player = map->PlayerList.at(j);
|
||||
if(!player->Session->inGame && player->Session->Respawned == 1) //only set in pakUserDied on receiving respawn packet 0x753
|
||||
{
|
||||
Log( MSG_INFO, "ingame: = %i respawned: = %i",player->Session->inGame,player->Session->Respawned);
|
||||
if(!player->RespawnPlayer(player)) //so we respawn the player
|
||||
{
|
||||
Log( MSG_INFO, "Player not respawned correctly");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log( MSG_INFO, "Player Respawned" );
|
||||
}
|
||||
player->Session->inGame = true;
|
||||
player->Session->Respawned = 2; //testing toggle
|
||||
}
|
||||
if(player->IsDead( )) continue;
|
||||
if(player->UpdateValues( )) //checks if player is on a cart. otherwise returns true
|
||||
{
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "Running UpdatePosition" );}
|
||||
player->UpdatePosition( );
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "ran UpdatePosition" );}
|
||||
}
|
||||
|
||||
if(player->IsOnBattle( ))
|
||||
{
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "Player is in battle so call DoAttack" );}
|
||||
player->DoAttack( );
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "ran DoAttack" );}
|
||||
}
|
||||
player->RefreshBuff( );
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "ran refreshbuff" );}
|
||||
player->PlayerHeal( );
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "ran playerheal" );}
|
||||
player->Regeneration( );
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "ran regeneration" );}
|
||||
player->CheckPlayerLevelUP( );
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "ran Check Levelup" );}
|
||||
if(3600000 < (UINT)GServer->round((clock( ) - player->Session->LogTime))) //1 hour since login
|
||||
{
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "updating logtime stuff" );}
|
||||
player->Session->logtime += 1;
|
||||
player->Session->TotalLogTime ++;
|
||||
GServer->DB->QExecute("UPDATE accounts SET logtime = %i, totlogtime = %i WHERE id = %i", player->Session->logtime, player->Session->TotalLogTime, player->Session->userid);
|
||||
player->Session->LogTime = clock();
|
||||
}
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "Checking autosave" );}
|
||||
if( GServer->Config.AUTOSAVE == 1 )
|
||||
{
|
||||
clock_t etime = clock() - player->lastSaveTime;
|
||||
if( etime >= GServer->Config.SAVETIME*1000 )
|
||||
{
|
||||
player->savedata( );
|
||||
player->lastSaveTime = clock();
|
||||
}
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "saved player data" );}
|
||||
}
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "Checking ItemXPTimer" );}
|
||||
if(player->Stats->ItemXPTimer > 0) // does the player have a timed XPRate buff?
|
||||
{
|
||||
clock_t etime = clock() - player->Stats->ItemXPTime;
|
||||
if(etime >= player->Stats->ItemXPTimer) // turn off timed XPRate buff when time exceeds timer
|
||||
{
|
||||
player->Stats->ItemXPTime = 0;
|
||||
player->Stats->ItemXPRate = 1;
|
||||
}
|
||||
//if(player->Session->Respawned == 2){Log( MSG_INFO, "set XPRates and stuff" );}
|
||||
}
|
||||
player->Session->Respawned = 0; //resetting respawned status
|
||||
}
|
||||
// Monster update //------------------------
|
||||
pthread_mutex_lock( &map->MonsterMutex );
|
||||
//spawn TD monsters
|
||||
unsigned nextmon = map->TDMobList[map->TDNextSpawn];
|
||||
if(nextmon != 0)
|
||||
{
|
||||
//Log(MSG_DEBUG,"Spawning TD mobs");
|
||||
clock_t etime = clock() - map->lastTDSpawnTime;
|
||||
if(etime >= map->TDMobDelay) // check if elapsed time is greater than spawn delay
|
||||
{
|
||||
//spawn the next monster on the list
|
||||
//Log(MSG_DEBUG,"Spawning TD mob of type %i at position %i in the list for map %i",nextmon, map->TDNextSpawn, map->id);
|
||||
map->AddMonster(nextmon, GServer->WPList[map->id][1].pos, 0, 0, 0, 1, 0, 999 ); //spawn a TD mob with no AI
|
||||
//Log(MSG_DEBUG,"Spawned mob in addmonster");
|
||||
map->TDMobList[map->TDNextSpawn] = 0; //clear the spawn
|
||||
//Log(MSG_DEBUG,"cleared the spawn");
|
||||
map->TDNextSpawn++; //move the pointer
|
||||
//Log(MSG_DEBUG,"moved the pointer");
|
||||
if(map->TDNextSpawn > 100)map->TDNextSpawn = 0;
|
||||
map->lastTDSpawnTime = clock();
|
||||
}
|
||||
}
|
||||
//TD spawn end
|
||||
|
||||
for(UINT j=0;j<map->MonsterList.size();j++)
|
||||
{
|
||||
CMonster* monster = map->MonsterList.at(j);
|
||||
|
||||
//UINT thistimer = monster->AItimer;
|
||||
clock_t etime = clock() - monster->lastAiUpdate;
|
||||
//if(etime < monster->AItimer) continue; //not time to run AI for this monster yet
|
||||
if(monster->hitcount == 0xFF)//this is a delay for new monster spawns this might olso fix invisible monsters(if they attack directly on spawning the client dosn't get the attack packet(its not in it's visible list yet))
|
||||
{
|
||||
monster->hitcount = 0;
|
||||
//monster->DoAi(monster->thisnpc->AI, 0);
|
||||
monster->DoAi(monster->monAI, 0);
|
||||
monster->lastAiUpdate=clock();
|
||||
}
|
||||
|
||||
//still need this for special spawns
|
||||
if(!map->IsNight( ) && monster->Status->nightonly)// if day, delete all night time monsters
|
||||
{
|
||||
//Log( MSG_INFO, "Night Only monster deleted. Type %i", monster->montype);
|
||||
map->DeleteMonster( monster, true, j );
|
||||
continue;
|
||||
}
|
||||
if(map->IsNight() && monster->Status->dayonly)
|
||||
{
|
||||
//Log( MSG_INFO, "Day Only monster deleted. Type %i", monster->montype);
|
||||
map->DeleteMonster( monster, true, j );
|
||||
continue;
|
||||
}
|
||||
//Do TD stuff
|
||||
if(monster->monAI == 999 && etime >= monster->AItimer) //It's a TD mob
|
||||
{
|
||||
monster->UpdatePosition( );
|
||||
float distance = GServer->distance( monster->Position->current, monster->Position->destiny );
|
||||
//Log( MSG_INFO, "Found a TD monster %f from target. Waypoint type = %i stance %i speed %i", distance,GServer->WPList[map->id][monster->NextWayPoint].WPType,monster->Stats->stance, monster->Stats->Move_Speed);
|
||||
//Log( MSG_INFO, "monster location X: %f Y: %f",monster->Position->current.x, monster->Position->current.y);
|
||||
//Log( MSG_INFO, "WP location X: %f Y: %f ", GServer->WPList[map->id][monster->NextWayPoint].pos.x,GServer->WPList[map->id][monster->NextWayPoint].pos.y);
|
||||
if(distance < 1) //monster has reached it's destination
|
||||
{
|
||||
switch(GServer->WPList[map->id][monster->NextWayPoint].WPType)
|
||||
{
|
||||
case 1: //Start waypoint
|
||||
case 2: //regular waypoint. Increment waypoint counter. Set detiny to next waypoint
|
||||
{
|
||||
//Log( MSG_INFO, "Monster reached waypoint %i type: %i", monster->NextWayPoint,GServer->WPList[map->id][monster->NextWayPoint].WPType);
|
||||
monster->NextWayPoint++;
|
||||
monster->Position->destiny.x = GServer->WPList[map->id][monster->NextWayPoint].pos.x;
|
||||
monster->Position->destiny.y = GServer->WPList[map->id][monster->NextWayPoint].pos.y;
|
||||
BEGINPACKET( pak, 0x797 );
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
ADDWORD ( pak, monster->Stats->Move_Speed ); //speed
|
||||
ADDFLOAT ( pak, monster->Position->destiny.x * 100 );
|
||||
ADDFLOAT ( pak, monster->Position->destiny.y * 100 );
|
||||
ADDWORD ( pak, 0xcdcd );
|
||||
ADDBYTE ( pak, monster->Stats->stance ); //should be walking
|
||||
GServer->SendToVisible(&pak, monster);
|
||||
}
|
||||
break;
|
||||
case 3: //Final waypoint. do stuff. suicide
|
||||
{
|
||||
//Log( MSG_INFO, "Reached final waypoint");
|
||||
//monster->Stats->HP = 0;
|
||||
map->DeleteMonster( monster, true, j );
|
||||
//Log( MSG_INFO, "monster successfully deleted");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log( MSG_INFO, "WPType %i not recognized",GServer->WPList[map->id][monster->NextWayPoint].WPType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//send move packet again to try to synchronise the monster movement
|
||||
BEGINPACKET( pak, 0x797 );
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
ADDWORD ( pak, monster->Stats->Move_Speed ); //speed
|
||||
ADDFLOAT ( pak, monster->Position->destiny.x * 100 );
|
||||
ADDFLOAT ( pak, monster->Position->destiny.y * 100 );
|
||||
ADDWORD ( pak, 0xcdcd );
|
||||
ADDBYTE ( pak, monster->Stats->stance ); //should be walking
|
||||
GServer->SendToVisible(&pak, monster);
|
||||
}
|
||||
monster->lastAiUpdate = clock();
|
||||
}
|
||||
//end TD stuff
|
||||
|
||||
if(!monster->PlayerInRange( )) continue;
|
||||
//if(!monster->UpdateValues( )) continue;
|
||||
monster->UpdatePosition( );
|
||||
if(monster->IsOnBattle( ))
|
||||
{
|
||||
monster->DoAttack( ); // why was this commented? Monsters were not attacking
|
||||
//monster->DoAi(monster->thisnpc->AI, 2); //don't use thisnpc->AI any more.
|
||||
if(etime >= monster->AItimer)
|
||||
{
|
||||
monster->DoAi(monster->monAI, 2);
|
||||
monster->lastAiUpdate = clock();
|
||||
}
|
||||
//Log(MSG_INFO,"Monster type: %i current HP: %i",monster->montype, monster->Stats->HP);
|
||||
}
|
||||
else
|
||||
{
|
||||
//monster->DoAi(monster->thisnpc->AI, 1);
|
||||
if(etime >= monster->AItimer)
|
||||
{
|
||||
monster->DoAi(monster->monAI, 1);
|
||||
monster->lastAiUpdate = clock();
|
||||
}
|
||||
}
|
||||
monster->RefreshBuff( );
|
||||
if (monster->IsSummon())
|
||||
{
|
||||
monster->SummonUpdate(monster,map, j);
|
||||
continue;
|
||||
}
|
||||
if(monster->IsDead())
|
||||
{
|
||||
if(clock() - monster->DeathDelayTimer > GServer->Config.DeathDelay)
|
||||
{
|
||||
//Log(MSG_DEBUG,"Found dead monster montype %i",monster->montype);
|
||||
monster->OnDie( ); //all this does is give exp
|
||||
//Log(MSG_DEBUG,"back from giving exp");
|
||||
monster->DoAi(monster->monAI, 5);
|
||||
//Log(MSG_DEBUG,"ran AI");
|
||||
map->DeleteMonster( monster, true, j );
|
||||
//Log(MSG_DEBUG,"deleted monster");
|
||||
continue;
|
||||
}
|
||||
/*else
|
||||
{
|
||||
Log(MSG_DEBUG,"Dead monster found. waiting for death delay timer");
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
if(only_npc)
|
||||
pthread_mutex_lock( &map->MonsterMutex );
|
||||
|
||||
//AIP for NPCs
|
||||
for(UINT j=0;j<map->NPCList.size();j++)
|
||||
{
|
||||
CNPC* npc = map->NPCList.at(j);
|
||||
if(npc->thisnpc->AI != 0)
|
||||
{
|
||||
CAip* script = NULL;
|
||||
for(unsigned k=0; k < GServer->AipList.size(); k++)
|
||||
{
|
||||
if (GServer->AipList.at(k)->AInumber == npc->thisnpc->AI)
|
||||
{
|
||||
script = GServer->AipList.at(k);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(script == NULL)
|
||||
{
|
||||
//Log( MSG_WARNING, "Invalid AI script for AI %i", npc->thisnpc->AI );
|
||||
continue;
|
||||
}
|
||||
UINT thistimer = script->minTime * 1000; //seems to be set in seconds in AIP
|
||||
if(thistimer<(UINT)GServer->round((clock( ) - npc->lastAiUpdate))) //check AIP conditions when the timer calls for it
|
||||
{
|
||||
CNPCData* thisnpc = GServer->GetNPCDataByID( npc->npctype );
|
||||
if(thisnpc == NULL)
|
||||
{
|
||||
Log( MSG_WARNING, "Invalid montype %i", npc->npctype );
|
||||
continue;
|
||||
}
|
||||
CMonster* monster = new (nothrow) CMonster( npc->pos, npc->npctype, map->id, 0, 0 );
|
||||
monster->thisnpc = thisnpc;
|
||||
monster->CharType = 4;
|
||||
int AIP = monster->thisnpc->AI;
|
||||
int lma_previous_eventID = npc->thisnpc->eventid;
|
||||
monster->DoAi(monster->thisnpc->AI, 1);
|
||||
//check if eventID changed, if we do it in AIP conditions / actions, it just fails...
|
||||
if (lma_previous_eventID != npc->thisnpc->eventid)
|
||||
{
|
||||
//Log(MSG_WARNING,"Event ID not the same NPC %i from %i to %i in map %i, npc->thisnpc->eventid=%i !", npc->npctype, lma_previous_eventID, monster->thisnpc->eventid, map->id, npc->thisnpc->eventid);
|
||||
npc->thisnpc->eventid = monster->thisnpc->eventid;
|
||||
BEGINPACKET( pak, 0x790 );
|
||||
ADDWORD ( pak, npc->clientid );
|
||||
ADDWORD ( pak, npc->thisnpc->eventid );
|
||||
GServer->SendToMap(&pak, map->id);
|
||||
}
|
||||
|
||||
|
||||
if(AIP == GServer->Config.AIWatch)
|
||||
Log(MSG_DEBUG,"NPC AI number %i successfully run",monster->thisnpc->AI);
|
||||
if(monster->IsOnBattle())
|
||||
{
|
||||
//Log(MSG_DEBUG,"NPC %i is registered as in battle",monster->thisnpc->id);
|
||||
monster->DoAttack( );
|
||||
}
|
||||
//map->DeleteMonster(monster);
|
||||
//delete monster;
|
||||
npc->lastAiUpdate = clock();
|
||||
if(AIP == GServer->Config.AIWatch)
|
||||
Log(MSG_DEBUG,"LastAiUpdate clock reset");
|
||||
}
|
||||
}
|
||||
|
||||
//LMA: Sometimes another NPC does the job for you.
|
||||
if(npc->thisnpc->eventid != GServer->ObjVar[npc->npctype][0])
|
||||
{
|
||||
int new_event_id = GServer->ObjVar[npc->npctype][0];
|
||||
//Log(MSG_WARNING,"(2)Event ID not the same NPC %i from %i to %i in map %i, npc->thisnpc->eventid=%i !",npc->npctype,npc->thisnpc->eventid,new_event_id,map->id,npc->thisnpc->eventid);
|
||||
npc->thisnpc->eventid = new_event_id;
|
||||
//LMA: We have to change the event ID here since we didn't send the clientID :(
|
||||
BEGINPACKET( pak, 0x790 );
|
||||
ADDWORD ( pak, npc->clientid );
|
||||
ADDWORD ( pak, npc->thisnpc->eventid );
|
||||
GServer->SendToMap(&pak,map->id);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock( &map->MonsterMutex );
|
||||
}
|
||||
pthread_mutex_unlock( &GServer->MapMutex );
|
||||
pthread_mutex_unlock( &GServer->PlayerMutex );
|
||||
#ifdef _WIN32
|
||||
Sleep(GServer->Config.MapDelay);
|
||||
#else
|
||||
usleep(GServer->Config.MapDelay);
|
||||
#endif
|
||||
}
|
||||
pthread_exit( NULL );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Visibility Process
|
||||
PVOID VisibilityProcess(PVOID TS)
|
||||
{
|
||||
while(GServer->ServerOnline)
|
||||
{
|
||||
pthread_mutex_lock( &GServer->PlayerMutex );
|
||||
pthread_mutex_lock( &GServer->MapMutex );
|
||||
for(UINT i=0;i<GServer->MapList.Map.size();i++)
|
||||
{
|
||||
CMap* map = GServer->MapList.Map.at(i);
|
||||
map->CleanDrops( );//moved for test
|
||||
map->RespawnMonster( );//moved for test
|
||||
if( map->PlayerList.size()<1 )
|
||||
continue;
|
||||
for(UINT j=0;j<map->PlayerList.size();j++)
|
||||
{
|
||||
CPlayer* player = map->PlayerList.at(j);
|
||||
if(!player->Session->inGame) continue;
|
||||
if(!player->VisiblityList()) Log(MSG_WARNING, "Visibility False: %u", player->clientid );
|
||||
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock( &GServer->MapMutex );
|
||||
pthread_mutex_unlock( &GServer->PlayerMutex );
|
||||
#ifdef _WIN32
|
||||
Sleep(GServer->Config.VisualDelay);
|
||||
#else
|
||||
usleep(GServer->Config.VisualDelay);
|
||||
#endif
|
||||
}
|
||||
pthread_exit(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// World Process
|
||||
PVOID WorldProcess( PVOID TS )
|
||||
{
|
||||
while( GServer->ServerOnline )
|
||||
{
|
||||
pthread_mutex_lock( &GServer->MapMutex );
|
||||
for(UINT i=0;i<GServer->MapList.Map.size();i++)
|
||||
{
|
||||
CMap* map = GServer->MapList.Map.at(i);
|
||||
if( map->PlayerList.size()<1 )
|
||||
continue;
|
||||
map->UpdateTime( );
|
||||
pthread_mutex_lock( &map->DropMutex );
|
||||
//map->CleanDrops( );
|
||||
pthread_mutex_unlock( &map->DropMutex );
|
||||
pthread_mutex_lock( &map->MonsterMutex );
|
||||
//map->RespawnMonster( );
|
||||
pthread_mutex_unlock( &map->MonsterMutex );
|
||||
}
|
||||
pthread_mutex_unlock( &GServer->MapMutex );
|
||||
GServer->RefreshFairy( );
|
||||
#ifdef _WIN32
|
||||
Sleep(GServer->Config.WorldDelay);
|
||||
#else
|
||||
usleep(GServer->Config.WorldDelay);
|
||||
#endif
|
||||
}
|
||||
pthread_exit(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Shutdown Server Process
|
||||
void ShutdownServer(PVOID TS)
|
||||
{
|
||||
int minutes = (int)TS;
|
||||
#ifdef _WIN32
|
||||
Sleep(minutes*60000);
|
||||
#else
|
||||
usleep(minutes*60000);
|
||||
#endif
|
||||
Log( MSG_INFO, "Saving User Information... " );
|
||||
GServer->DisconnectAll();
|
||||
Log( MSG_INFO, "Waiting Process to ShutDown... " );
|
||||
GServer->ServerOnline = false;
|
||||
int status,res;
|
||||
res = pthread_join( GServer->WorldThread[0], (PVOID*)&status );
|
||||
if(res)
|
||||
{
|
||||
Log( MSG_WARNING, "World thread can't be joined" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Log( MSG_INFO, "World thread closed." );
|
||||
}
|
||||
res = pthread_join( GServer->WorldThread[1], (PVOID*)&status );
|
||||
if(res)
|
||||
{
|
||||
Log( MSG_WARNING, "Visibility thread can't be joined" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Log( MSG_INFO, "Visibility thread closed." );
|
||||
}
|
||||
res = pthread_join( GServer->MapThread[0], (PVOID*)&status );
|
||||
if(res)
|
||||
{
|
||||
Log( MSG_WARNING, "Map thread can't be joined" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Log( MSG_INFO, "Map thread closed." );
|
||||
}
|
||||
Log( MSG_INFO, "All Threads Closed." );
|
||||
GServer->isActive = false;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
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 "worldmonster.h"
|
||||
|
||||
// update attack values and destiny position
|
||||
bool CMonster::UpdateValues( )
|
||||
{
|
||||
return true; //bypass for AIP
|
||||
/*if(IsSummon( ))
|
||||
{
|
||||
CPlayer* thisclient = GetOwner( );
|
||||
if(thisclient!=NULL)if(thisclient->Stats->HP>0)
|
||||
{
|
||||
if(!IsOnBattle( ) && thisclient->IsAttacking( ))
|
||||
{
|
||||
Battle->target = thisclient->Battle->target;
|
||||
Battle->atktarget = Battle->target;
|
||||
Battle->atktype = NORMAL_ATTACK;
|
||||
Battle->contatk = true;
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy!=NULL)
|
||||
StartAction( Enemy, NORMAL_ATTACK );
|
||||
}
|
||||
else
|
||||
if(!IsOnBattle( ))
|
||||
{
|
||||
Position->source = thisclient->Position->current;
|
||||
float distance = GServer->distance( Position->destiny , thisclient->Position->current );
|
||||
if((distance>15 && !IsOnBattle()) || distance>50)
|
||||
Move( );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UnspawnMonster( );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(!IsMoving( ) && !IsOnBattle( ) && CanMove( ))
|
||||
{
|
||||
clock_t etime = clock() - Position->lastMoveTime;
|
||||
if(etime > 20*CLOCKS_PER_SEC)
|
||||
Move( );
|
||||
}
|
||||
if(!IsSummon( ))
|
||||
{
|
||||
CPlayer* player = GetNearPlayer( 10 );
|
||||
if(player!=NULL)
|
||||
OnEnemyOnSight( (CCharacter*)player );
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy!=NULL)
|
||||
{
|
||||
//need a skill timer here. It casts all teh time otherwise.
|
||||
int randv = GServer->RandNumber(1, 100);
|
||||
if(randv < 50 || GServer->Config.MobSkill == 0)//normal attack
|
||||
{
|
||||
StartAction( Enemy, NORMAL_ATTACK );
|
||||
}
|
||||
else //skill attack
|
||||
{
|
||||
CSkills* skill = GServer->GetSkillByID(GServer->Config.MobSkill);
|
||||
switch (skill->type)
|
||||
{
|
||||
case 6:
|
||||
Log( MSG_DEBUG, "Monster used an attack skill. id = %i",GServer->Config.MobSkill);
|
||||
StartAction( Enemy, SKILL_ATTACK, GServer->Config.MobSkill);
|
||||
break;
|
||||
case 17:
|
||||
Log( MSG_DEBUG, "Monster used an AOE skill. id = %i",GServer->Config.MobSkill);
|
||||
StartAction( Enemy, SKILL_AOE, GServer->Config.MobSkill);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
if(IsMonster( ))
|
||||
{}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Spawn a monster
|
||||
void CMonster::SpawnMonster( CPlayer* player, CMonster* thismon )
|
||||
{
|
||||
BEGINPACKET( pak, 0x792 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDFLOAT ( pak, Position->current.x*100 );
|
||||
ADDFLOAT ( pak, Position->current.y*100 );
|
||||
ADDFLOAT ( pak, Position->destiny.x*100 );
|
||||
ADDFLOAT ( pak, Position->destiny.y*100 );
|
||||
/*
|
||||
if(IsDead( ))
|
||||
{
|
||||
ADDWORD ( pak, 0x0003 );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
}
|
||||
else if(IsOnBattle( ))
|
||||
{
|
||||
ADDWORD ( pak, 0x0002 );
|
||||
ADDWORD ( pak, Battle->target );
|
||||
}
|
||||
else if(IsMoving( ))
|
||||
{
|
||||
ADDWORD ( pak, 0x0001 );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
}
|
||||
*/
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
ADDDWORD ( pak, 0x00000000 );
|
||||
|
||||
ADDDWORD ( pak, Stats->HP );
|
||||
if(thismon->owner != player->clientid)
|
||||
{
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
if(IsSummon( ) && map->allowpvp!=0) {ADDDWORD( pak, 0x00000064 );} //Hostil
|
||||
else if (IsSummon( ) && map->allowpvp==0) {ADDDWORD ( pak, 0x00000000 );}//Friendly
|
||||
else {ADDDWORD( pak, 0x00000064 );} //Hostil
|
||||
}
|
||||
else {ADDDWORD( pak, 0x00000000 );}//Friendly
|
||||
ADDDWORD( pak, GServer->BuildBuffs( this ) );
|
||||
ADDWORD ( pak, montype );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
if(IsSummon( ))
|
||||
{
|
||||
ADDWORD( pak, owner );
|
||||
ADDWORD( pak, 0x0000 ); //id del skill (si es summon de skill)
|
||||
}
|
||||
player->client->SendPacket( &pak );
|
||||
}
|
||||
|
||||
// UnSpawn a Monster Why the fuck do we need this??????????
|
||||
bool CMonster::UnspawnMonster( )
|
||||
{
|
||||
Log( MSG_INFO, "Unspawning monster %i ",this->montype);
|
||||
GServer->MapList.Index[Position->Map]->DeleteMonster( this, true );
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move a mob (random place)
|
||||
void CMonster::Move( )
|
||||
{
|
||||
Position->destiny = GServer->RandInCircle( Position->source, 5 );
|
||||
Position->lastMoveTime = clock();
|
||||
//ClearBattle( Battle );
|
||||
BEGINPACKET( pak, 0x797 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, 0x0000 );//???
|
||||
ADDWORD ( pak, Stats->Move_Speed ); //speed
|
||||
ADDFLOAT ( pak, Position->destiny.x*100 );
|
||||
ADDFLOAT ( pak, Position->destiny.y*100 );
|
||||
ADDWORD ( pak, 0xcdcd );
|
||||
//ADDBYTE ( pak, 0x01 ); //replaced for AIP
|
||||
ADDBYTE ( pak, Stats->stance );
|
||||
GServer->SendToVisible(&pak, this);
|
||||
}
|
||||
|
||||
// Move a mob (specified point)
|
||||
void CMonster::MoveTo( fPoint nPos, bool randcircle )
|
||||
{
|
||||
if(randcircle) Position->destiny = GServer->RandInCircle( nPos, 5 );
|
||||
else Position->destiny = nPos;
|
||||
Position->lastMoveTime = clock();
|
||||
BEGINPACKET( pak, 0x797 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, 0x0000 ); // ???
|
||||
ADDWORD ( pak, Stats->Move_Speed );
|
||||
ADDFLOAT ( pak, Position->destiny.x*100 );
|
||||
ADDFLOAT ( pak, Position->destiny.y*100 );
|
||||
ADDWORD ( pak, 0xcdcd );
|
||||
//ADDBYTE ( pak, 0x01 ); // replaced for AIP
|
||||
ADDBYTE ( pak, Stats->stance);
|
||||
GServer->SendToVisible(&pak, this );
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
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 "worldmonster.h"
|
||||
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
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 "worldmonster.h"
|
||||
|
||||
// called when a monster is attacked [attack/use atk skill/use buff/run/summon]//Enemy is the one attacking?
|
||||
bool CMonster::OnBeAttacked( CCharacter* Enemy )
|
||||
{
|
||||
|
||||
Battle->hitby = Enemy->clientid;
|
||||
if(!IsOnBattle( ))
|
||||
{
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
map->UpdateSpawnCounter( montype, Status->spawnid, 3); // update the counters
|
||||
if(thisnpc->aggresive == 0) //prevent monsters with zero aggresion fighting back.
|
||||
return true;
|
||||
//StartAction( Enemy, NORMAL_ATTACK, 0 ); // temporary removal for testing
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// called when a monster die [give exp/give extra drop]
|
||||
bool CMonster::OnDie( )
|
||||
{
|
||||
/* Don't need this now. AIP takes care of it.
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
if(map->ghost!=0)
|
||||
{
|
||||
if((map->IsNight( ) || map->ghost==2) && !IsGhost( ) && !IsGhostSeed( ) && !IsSummon( ))
|
||||
{
|
||||
UINT gs = GServer->RandNumber( 0, 100 );
|
||||
if(gs<30) // 30% / 70%
|
||||
{ // spawn a ghost seed [for now will be 701 [level 1 ghost seed] ]
|
||||
map->AddMonster( 701, Position->current, 0, Position->respawn, 0 );
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
GServer->GiveExp( this );
|
||||
return true;
|
||||
}
|
||||
// called when we create a new monster [attack players?]
|
||||
bool CMonster::OnSpawn( bool Attack )
|
||||
{
|
||||
if(IsGhost( ) && Attack )
|
||||
{
|
||||
CPlayer* player = GetNearPlayer( );
|
||||
if(player == NULL) // no players or too far
|
||||
return true;
|
||||
StartAction( (CCharacter*)player, NORMAL_ATTACK, 0 );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// [not]called when a monster is almost dead [run/use only skills/call a healer? :S xD]
|
||||
bool CMonster::OnAlmostDie( )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// called when a enemy is on sight
|
||||
bool CMonster::OnEnemyOnSight( CCharacter* Enemy )
|
||||
{
|
||||
//return true //enable for AI stuff
|
||||
clock_t etime = clock() - lastSighCheck;
|
||||
if(etime<5000) return true;
|
||||
if(!IsOnBattle( ))
|
||||
{
|
||||
if(thisnpc->aggresive>1)
|
||||
{
|
||||
UINT aggro = GServer->RandNumber(2,15);
|
||||
if(thisnpc->aggresive>=aggro && !IsGhostSeed( ))
|
||||
{
|
||||
StartAction( (CCharacter*) Enemy, NORMAL_ATTACK, 0 );
|
||||
}
|
||||
else
|
||||
if(IsGhostSeed( ) || thisnpc->aggresive>5)
|
||||
MoveTo( Enemy->Position->current, true );
|
||||
}
|
||||
lastSighCheck = clock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// called when enemy die
|
||||
bool CMonster::OnEnemyDie( CCharacter* Enemy )
|
||||
{
|
||||
Position->destiny = Position->source;
|
||||
ClearBattle( Battle );
|
||||
MoveTo( Position->source );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMonster::OnFar( )
|
||||
{
|
||||
if(!IsSummon())return true;
|
||||
Position->destiny = Position->source;
|
||||
ClearBattle( Battle );
|
||||
MoveTo( Position->source );
|
||||
return true;
|
||||
}
|
||||
/*bool CMonster::Guardiantree(CMonster* monster,CMap* map)
|
||||
{
|
||||
return true;
|
||||
if(Stats->HP>9000)monster->hitcount=0;
|
||||
switch (monster->hitcount)
|
||||
{
|
||||
case 0:
|
||||
if(Stats->HP<9000)
|
||||
{
|
||||
for(unsigned char i=0;i<5;i++){
|
||||
fPoint position = GServer->RandInCircle( monster->Position->current, 5 );
|
||||
CMonster* newmonster=map->AddMonster( 210, position, 0, 0, 10 );
|
||||
newmonster->hitcount=0;
|
||||
}
|
||||
monster->hitcount=1;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if(Stats->HP<8000)
|
||||
{
|
||||
/* for(unsigned char i=0;i<5;i++){
|
||||
fPoint position = GServer->RandInCircle( monster->Position->current, 5 );
|
||||
CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true );
|
||||
newmonster->hitcount=0;
|
||||
}*/
|
||||
/*monster->hitcount=2;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if(Stats->HP<7000)
|
||||
{
|
||||
for(unsigned char i=0;i<5;i++){
|
||||
fPoint position = GServer->RandInCircle( monster->Position->current, 5 );
|
||||
CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true );
|
||||
newmonster->hitcount=0;
|
||||
}
|
||||
monster->hitcount=3;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if(Stats->HP<6000)
|
||||
{
|
||||
/* for(unsigned char i=0;i<5;i++){
|
||||
fPoint position = GServer->RandInCircle( monster->Position->current, 5 );
|
||||
CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true );
|
||||
newmonster->hitcount=0;
|
||||
}*/
|
||||
/*monster->hitcount=4;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if(Stats->HP<5000)
|
||||
{
|
||||
for(unsigned char i=0;i<5;i++)
|
||||
{
|
||||
fPoint position = GServer->RandInCircle( monster->Position->current, 5 );
|
||||
CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true );
|
||||
newmonster->hitcount=0;
|
||||
}
|
||||
monster->hitcount=5;
|
||||
CDrop* thisdrop = new CDrop;
|
||||
assert(thisdrop);
|
||||
thisdrop->clientid = GServer->GetNewClientID();
|
||||
thisdrop->type = 2; // ITEM
|
||||
thisdrop->pos = GServer->RandInCircle( monster->Position->current, 3 );
|
||||
thisdrop->posMap = monster->Position->Map;
|
||||
thisdrop->droptime = time(NULL);
|
||||
thisdrop->amount = 1;
|
||||
thisdrop->item.count = thisdrop->amount;
|
||||
thisdrop->owner = 0;
|
||||
thisdrop->item.itemtype=10;
|
||||
thisdrop->item.itemnum=rand()%4+10;
|
||||
map->AddDrop( thisdrop );
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if(Stats->HP<4000)
|
||||
{
|
||||
/* for(unsigned char i=0;i<5;i++){
|
||||
fPoint position = GServer->RandInCircle( monster->Position->current, 5 );
|
||||
CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true );
|
||||
newmonster->hitcount=0;
|
||||
}*/
|
||||
/*monster->hitcount=6;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if(Stats->HP<3000)
|
||||
{
|
||||
for(unsigned char i=0;i<5;i++){
|
||||
fPoint position = GServer->RandInCircle( monster->Position->current, 5 );
|
||||
CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true );
|
||||
newmonster->hitcount=0;
|
||||
}
|
||||
monster->hitcount=7;
|
||||
CDrop* thisdrop = new CDrop;
|
||||
assert(thisdrop);
|
||||
thisdrop->clientid = GServer->GetNewClientID();
|
||||
thisdrop->type = 2; // ITEM
|
||||
thisdrop->pos = GServer->RandInCircle( monster->Position->current, 3 );
|
||||
thisdrop->posMap = monster->Position->Map;
|
||||
thisdrop->droptime = time(NULL);
|
||||
thisdrop->amount = 1;
|
||||
thisdrop->item.count = thisdrop->amount;
|
||||
thisdrop->owner = 0;
|
||||
thisdrop->item.itemtype=10;
|
||||
thisdrop->item.itemnum=rand()%4+29;
|
||||
map->AddDrop( thisdrop );
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
if(Stats->HP<2000)
|
||||
{
|
||||
/* for(unsigned char i=0;i<5;i++){
|
||||
fPoint position = GServer->RandInCircle( monster->Position->current, 5 );
|
||||
CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true );
|
||||
newmonster->hitcount=0;
|
||||
}*/
|
||||
/*monster->hitcount=8;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
monster->hitcount=9;
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
/*bool CMonster::Scarab(CMonster* monster,CMap* map)
|
||||
{
|
||||
return true;
|
||||
fPoint position;
|
||||
CMonster* othermon;
|
||||
CMonster* newmonster;
|
||||
if (monster->hitcount>5)monster->hitcount=0;
|
||||
switch (monster->hitcount)
|
||||
{
|
||||
case 0:
|
||||
position = GServer->RandInCircle( monster->Position->current, 1 );
|
||||
newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true );
|
||||
newmonster->hitcount=1;
|
||||
GServer->ClearClientID( monster->clientid );
|
||||
for(UINT i=0;i<map->MonsterList.size();i++)
|
||||
{
|
||||
othermon = map->MonsterList.at(i);
|
||||
if(othermon==monster)
|
||||
{
|
||||
map->MonsterList.erase( map->MonsterList.begin()+i );
|
||||
delete monster;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
position = GServer->RandInCircle( monster->Position->current, 1 );
|
||||
newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true );
|
||||
newmonster->hitcount=2;
|
||||
GServer->ClearClientID( monster->clientid );
|
||||
for(UINT i=0;i<map->MonsterList.size();i++)
|
||||
{
|
||||
othermon = map->MonsterList.at(i);
|
||||
if(othermon==monster)
|
||||
{
|
||||
map->MonsterList.erase( map->MonsterList.begin()+i );
|
||||
delete monster;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
CDrop* thisdrop = new CDrop;
|
||||
assert(thisdrop);
|
||||
thisdrop->clientid = GServer->GetNewClientID();
|
||||
thisdrop->type = 2; // ITEM
|
||||
thisdrop->pos = GServer->RandInCircle( monster->Position->current, 3 );
|
||||
thisdrop->posMap = monster->Position->Map;
|
||||
thisdrop->droptime = time(NULL);
|
||||
thisdrop->amount = rand()%3+1;
|
||||
thisdrop->item.count = thisdrop->amount;
|
||||
thisdrop->owner = 0;
|
||||
thisdrop->item.itemtype=12;
|
||||
thisdrop->item.itemnum=rand()%10+11;
|
||||
map->AddDrop( thisdrop );
|
||||
GServer->ClearClientID( monster->clientid );
|
||||
for(UINT i=0;i<map->MonsterList.size();i++)
|
||||
{
|
||||
othermon = map->MonsterList.at(i);
|
||||
if(othermon==monster)
|
||||
{
|
||||
map->MonsterList.erase( map->MonsterList.begin()+i );
|
||||
delete monster;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}*/
|
||||
bool CMonster::SummonUpdate(CMonster* monster, CMap* map, UINT j)
|
||||
{
|
||||
BEGINPACKET( pak, 0x799 );
|
||||
clock_t etime = clock() - lastDegenTime;
|
||||
CPlayer* ownerclient = monster->GetOwner( );
|
||||
if( etime >= 4 * CLOCKS_PER_SEC && Stats->HP > 0 )
|
||||
{
|
||||
Stats->HP -= 4;
|
||||
lastDegenTime = clock();
|
||||
if (Stats->HP < 1||ownerclient == NULL||ownerclient->IsDead())//orphan check
|
||||
{
|
||||
Log( MSG_INFO, "Deleting orphan summon %i (summonupdate)",monster->montype);
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDDWORD ( pak, 0x8005 );
|
||||
GServer->SendToVisible( &pak, monster );
|
||||
map->DeleteMonster( monster, true, j );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
if (montype > 800 && montype < 811)
|
||||
{
|
||||
/*
|
||||
CPlayer* ownerclient = monster->GetOwner( );
|
||||
float distance = 0x9;
|
||||
for(UINT i=0;i<GServer->MapList.Index[Position->Map]->PlayerList.size();i++)
|
||||
{
|
||||
CPlayer* thisclient = GServer->MapList.Index[Position->Map]->PlayerList.at(i);
|
||||
float tempdist = GServer->distance( Position->current, thisclient->Position->current );
|
||||
if(tempdist<distance)
|
||||
{
|
||||
thisclient->Stats->HP += ((((montype-800)*5)+((ownerclient->Stats->Level)/10))+10);
|
||||
thisclient->Stats->MP += (((montype-800)*5)+((ownerclient->Stats->Level)/25));
|
||||
BEGINPACKET( pak, 0x7b2);
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDWORD ( pak, 0x0b9d );
|
||||
ADDBYTE ( pak, 0x06 );
|
||||
thisclient->client->SendPacket(&pak);
|
||||
|
||||
RESETPACKET( pak, 0x7b5);
|
||||
ADDWORD ( pak, thisclient->clientid );
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDWORD ( pak, 0x0b9d+0x3000+((ownerclient->Stats->Level%4)*0x4000) );
|
||||
ADDBYTE ( pak, ownerclient->Stats->Level/4 );
|
||||
thisclient->client->SendPacket(&pak);
|
||||
|
||||
RESETPACKET( pak, 0x7b9);
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDWORD ( pak, 0x0b9d );
|
||||
thisclient->client->SendPacket(&pak);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
// SET MONSTER HEALTH
|
||||
//hopefully this will 100% control the health of summons from the server end
|
||||
BEGINPACKET( pak1, 0x79f );
|
||||
ADDWORD( pak1, clientid );
|
||||
ADDWORD( pak1, Stats->HP );
|
||||
ADDWORD( pak1, 0 );
|
||||
//thisclient->client->SendPacket( &pak );
|
||||
GServer->SendToVisible( &pak1, monster );
|
||||
}
|
||||
else
|
||||
if (Stats->HP < 1)
|
||||
{
|
||||
Log( MSG_INFO, "Deleting dead summon %i (summonupdate)",monster->montype);
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDDWORD ( pak, 0x8005 );
|
||||
GServer->SendToVisible( &pak, monster );
|
||||
map->DeleteMonster( monster, true, j );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMonster::DoAi(int ainumber,char type)//ainumber is monster->AI type is add=0 idle=1 attacking=2 attacked=3 after killing target=4 hp<1=5
|
||||
{
|
||||
CAip* script = NULL;
|
||||
int AIWatch = GServer->Config.AIWatch;
|
||||
int aiindex = (ainumber*0x10000)+(type*0x100);
|
||||
if(type == 5)
|
||||
{
|
||||
//Log(MSG_INFO,"Monster died. Activating AI type 5");
|
||||
}
|
||||
for(unsigned j=0; j < GServer->AipList.size(); j++)
|
||||
{
|
||||
if (GServer->AipList.at(j)->AipID == aiindex)
|
||||
{
|
||||
script = GServer->AipList.at(j);
|
||||
|
||||
//if(ainumber == AIWatch)Log(MSG_DEBUG, "Record count = %i",script->recordcount[type]);
|
||||
if(ainumber == AIWatch)Log(MSG_DEBUG, "aiCondition type: %i Index: %i condition count %i", type, aiindex, script->ConditionCount);
|
||||
int success = AI_SUCCESS; //needs to be AI_SUCCESS otherwise would not perform conditionless actions
|
||||
int thisaction = 0;
|
||||
for (dword i = 0; i < script->ConditionCount; i++)
|
||||
{
|
||||
int command = script->Conditions[i]->opcode;
|
||||
if (command > 30 || command < 0) continue;
|
||||
success = (*GServer->aiCondFunc[command])(GServer, this, script->Conditions[i]->data);
|
||||
if(ainumber == AIWatch)Log(MSG_DEBUG, "aiCondition %03u returned %d", command, success);
|
||||
if (success == AI_FAILURE)
|
||||
break;
|
||||
}
|
||||
if (success == AI_SUCCESS)
|
||||
{
|
||||
for (dword i = 0; i < script->ActionCount; i++)
|
||||
{
|
||||
int command = script->Actions[i]->opcode;
|
||||
if (command > 38 || command < 0) continue;
|
||||
success = (*GServer->aiActFunc[command])(GServer, this, script->Actions[i]->data);
|
||||
if(ainumber == AIWatch)Log(MSG_DEBUG, "aiAction: %03u returned %d", command, success);
|
||||
}
|
||||
if(success == AI_SUCCESS)
|
||||
return; //automatically return after performing the first successful action
|
||||
}
|
||||
aiindex++;
|
||||
}
|
||||
else if(GServer->AipList.at(j)->AipID > aiindex)return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
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 "worldmonster.h"
|
||||
|
||||
// Get Mob Attack Power
|
||||
unsigned int CMonster::GetAttackPower( )
|
||||
{
|
||||
unsigned int attack;
|
||||
attack = thisnpc->atkpower;
|
||||
if(thisnpc->weapon != 0)
|
||||
attack += GServer->EquipList[8].Index[thisnpc->weapon]->attackpower;
|
||||
if(Status->Attack_up!=0xff)
|
||||
attack += MagicStatus[Status->Attack_up].Value;
|
||||
if(Status->Attack_down!=0xff)
|
||||
attack -= MagicStatus[Status->Attack_down].Value;
|
||||
return attack;
|
||||
}
|
||||
|
||||
// Get Monster Defense
|
||||
unsigned int CMonster::GetDefense( )
|
||||
{
|
||||
unsigned int defense;
|
||||
defense = thisnpc->defense;
|
||||
if(thisnpc->subweapon!=0)
|
||||
defense += GServer->EquipList[SUBWEAPON].Index[thisnpc->subweapon]->defense;
|
||||
if(Status->Defense_up!=0xff)
|
||||
defense += MagicStatus[Status->Defense_up].Value;
|
||||
if(Status->Defense_down!=0xff)
|
||||
defense -= MagicStatus[Status->Defense_down].Value;
|
||||
if(defense == 0)defense = 1; //hopefully stops some division by zero errors.
|
||||
return defense;
|
||||
}
|
||||
|
||||
// Get Monster atk speed
|
||||
unsigned int CMonster::GetAttackSpeed( )
|
||||
{
|
||||
unsigned int aspeed = (unsigned int)thisnpc->atkspeed;
|
||||
if(aspeed == 0) aspeed = 1; // prevent a /0 when hunting dragon egg
|
||||
if(Status->Haste_up!=0xff)
|
||||
aspeed += MagicStatus[Status->Haste_up].Value;
|
||||
if(Status->Haste_down!=0xff)
|
||||
aspeed -= MagicStatus[Status->Haste_down].Value;
|
||||
return aspeed;
|
||||
}
|
||||
|
||||
// Get Monster Movement speed
|
||||
unsigned int CMonster::GetMoveSpeed( )
|
||||
{
|
||||
unsigned int mspeed = 0;
|
||||
if (Stats->stance == 0) //mWalking. All other stances produce running animation at client
|
||||
{
|
||||
mspeed = thisnpc->wspeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
mspeed = thisnpc->rspeed;
|
||||
}
|
||||
if(Status->Dash_up!=0xff)
|
||||
mspeed += MagicStatus[Status->Dash_up].Value;
|
||||
if(Status->Dash_down!=0xff)
|
||||
mspeed -= MagicStatus[Status->Dash_down].Value;
|
||||
return mspeed;
|
||||
}
|
||||
|
||||
// return dodge
|
||||
unsigned int CMonster::GetDodge( )
|
||||
{
|
||||
UINT dodge = 0;
|
||||
dodge = thisnpc->dodge;
|
||||
if(dodge == 0) dodge = thisnpc->defense; // prevent a /0 for hawk skill
|
||||
if(Status->Dodge_up!=0xff)
|
||||
dodge += MagicStatus[Status->Dodge_up].Value;
|
||||
if(Status->Dodge_down!=0xff)
|
||||
dodge -= MagicStatus[Status->Dodge_down].Value;
|
||||
if(dodge == 0)dodge = 1; //hopefully stops some division by zero errors.
|
||||
return dodge;
|
||||
}
|
||||
|
||||
// return accury
|
||||
unsigned int CMonster::GetAccury( )
|
||||
{
|
||||
UINT hitrate = 0;
|
||||
hitrate = thisnpc->hitrate;
|
||||
if(Status->Accury_up!=0xff)
|
||||
hitrate += MagicStatus[Status->Accury_up].Value;
|
||||
if(Status->Accury_down!=0xff)
|
||||
hitrate -= MagicStatus[Status->Accury_down].Value;
|
||||
return hitrate;
|
||||
}
|
||||
|
||||
// return magic defense
|
||||
unsigned int CMonster::GetMagicDefense( )
|
||||
{
|
||||
UINT mdef = 0;
|
||||
mdef = thisnpc->magicdefense;
|
||||
if(Status->Magic_Defense_up!=0xff)
|
||||
mdef += MagicStatus[Status->Magic_Defense_up].Value;
|
||||
if(Status->Magic_Defense_down!=0xff)
|
||||
mdef -= MagicStatus[Status->Magic_Defense_down].Value;
|
||||
if(mdef == 0)mdef = 1; //hopefully stops some division by zero errors.
|
||||
return mdef;
|
||||
}
|
||||
|
||||
// return magic attack
|
||||
unsigned int CMonster::GetMagicAttack( )
|
||||
{
|
||||
return thisnpc->magicattack;
|
||||
}
|
||||
|
||||
|
||||
// return max hp
|
||||
unsigned int CMonster::GetMaxHP( )
|
||||
{
|
||||
UINT MaxHP = 0;
|
||||
MaxHP = thisnpc->hp;
|
||||
if(owner == 0)
|
||||
MaxHP *= thisnpc->level;
|
||||
//if(MaxHP == 0)
|
||||
//MaxHP = thisnpc->shp; // Bullshit. npc->shp is the flying height of a monster such as a bee. Dumb ass!!
|
||||
return MaxHP;
|
||||
}
|
||||
|
||||
unsigned int CMonster::GetCritical( )
|
||||
{
|
||||
return 60; //60 = 20% of 300 our critical probability
|
||||
}
|
||||
|
||||
float CMonster::GetAttackDistance( )
|
||||
{
|
||||
return thisnpc->atkdistance;
|
||||
}
|
||||
|
||||
unsigned int CMonster::GetXPRate( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int CMonster::GetItemDropRate( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int CMonster::GetItemDropCountRate( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int CMonster::GetZulyDropRate( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// set monster stats values
|
||||
bool CMonster::SetStats( )
|
||||
{
|
||||
if(thisnpc == NULL) return false;
|
||||
Stats->Level = thisnpc->level;
|
||||
Stats->Attack_Power = GetAttackPower( );
|
||||
Stats->Defense = GetDefense( );
|
||||
Stats->xprate = GetXPRate( );
|
||||
Stats->itemdroprate = GetItemDropRate( );
|
||||
Stats->zulydroprate = GetZulyDropRate( );
|
||||
Stats->Attack_Speed = GetAttackSpeed( );
|
||||
Stats->Move_Speed = GetMoveSpeed( );
|
||||
Stats->Dodge = GetDodge( );
|
||||
Stats->Accury = GetAccury( );
|
||||
Stats->Critical = GetCritical( );
|
||||
Stats->Magic_Defense = GetMagicDefense( );
|
||||
Stats->magicattack = GetMagicAttack( );
|
||||
Stats->Attack_Distance = GetAttackDistance( );
|
||||
for(int i=0;i<20;i++)
|
||||
{
|
||||
AIVar[i] = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
// Party Actions [invite/leave/kick]
|
||||
bool CWorldServer::pakPartyActions( CPlayer* thisclient, CPacket* P )
|
||||
{
|
||||
unsigned int action = GETBYTE((*P),0);
|
||||
CMap* map = MapList.Index[thisclient->Position->Map];
|
||||
if(map == NULL)return true;
|
||||
switch(action)
|
||||
{
|
||||
case 0x00://Invita a new party
|
||||
case 0x01://invite a existent party
|
||||
{
|
||||
UINT clientid = GETWORD((*P),1);
|
||||
if(thisclient->Party->party!=NULL)
|
||||
{
|
||||
if(thisclient->Party->party->Members.size()>=thisclient->Party->party->Capacity)
|
||||
{
|
||||
SendSysMsg( thisclient, "Party is full" );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
CPlayer* otherclient = map->GetPlayerInMap( clientid );// have to be in same map
|
||||
if(otherclient == NULL)
|
||||
{
|
||||
BEGINPACKET( pak, 0x7d1 );
|
||||
ADDBYTE ( pak, 0x00 );//No encontro el ID
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
thisclient->client->SendPacketCpy( &pak );
|
||||
return true;
|
||||
}
|
||||
if(otherclient->Party->party != NULL)
|
||||
{
|
||||
BEGINPACKET( pak, 0x7d1 );
|
||||
ADDBYTE ( pak, 0x01 );//No puede ser solicitado (ya tiene party)
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
thisclient->client->SendPacketCpy( &pak );
|
||||
return true;
|
||||
}
|
||||
if(abs(otherclient->Stats->Level-thisclient->Stats->Level)>(Config.Partygap+1))
|
||||
{
|
||||
BEGINPACKET( pak, 0x7d1 );
|
||||
ADDBYTE ( pak, 0x07 );//Level inapropiado
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
thisclient->client->SendPacketCpy( &pak );
|
||||
return true;
|
||||
}
|
||||
BEGINPACKET( pak, 0x7d0 );
|
||||
ADDBYTE ( pak, action );
|
||||
ADDWORD ( pak, thisclient->clientid );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
otherclient->client->SendPacketCpy( &pak );
|
||||
}
|
||||
break;
|
||||
case 0x02://Leave Party
|
||||
{
|
||||
if(thisclient->Party->party == NULL)
|
||||
return true;
|
||||
CParty* party = thisclient->Party->party;
|
||||
if(party == NULL)
|
||||
return true;
|
||||
|
||||
bool pflag = false;
|
||||
if(!party->RemovePlayer( thisclient )) //if this player is not in this party
|
||||
return true;
|
||||
if(party->Members.size()>1)
|
||||
{
|
||||
for(UINT i=0;i<party->Members.size();i++)
|
||||
{
|
||||
CPlayer* thismember = party->Members.at(i);
|
||||
if(thismember == NULL)
|
||||
return true;
|
||||
BEGINPACKET( pak, 0x7d2 );
|
||||
ADDWORD ( pak, 0xff00 );
|
||||
ADDDWORD ( pak, thisclient->CharInfo->charid );
|
||||
if(!pflag)
|
||||
{
|
||||
ADDDWORD( pak, thismember->CharInfo->charid );
|
||||
if(thisclient->Party->IsMaster)
|
||||
thismember->Party->IsMaster = true;
|
||||
pflag = true;
|
||||
}
|
||||
thismember->client->SendPacketCpy( &pak );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(UINT i=0;i<party->Members.size();i++)
|
||||
{
|
||||
CPlayer* thismember = party->Members.at(i);
|
||||
if(thismember == NULL)
|
||||
return true;
|
||||
BEGINPACKET( pak, 0x7d1 );
|
||||
ADDBYTE ( pak, 0x05 );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
thismember->client->SendPacket( &pak );
|
||||
thismember->Party->party = NULL;
|
||||
thismember->Party->IsMaster = true;
|
||||
}
|
||||
RemoveParty( party );
|
||||
delete party;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x03: // Give leader
|
||||
{
|
||||
if(thisclient->Party->party == NULL)
|
||||
return true;
|
||||
unsigned int charid = GETDWORD((*P),1);
|
||||
if( !thisclient->Party->IsMaster || thisclient->CharInfo->charid == charid )
|
||||
return true;
|
||||
CPlayer* otherclient = GetClientByCID( charid );
|
||||
if(otherclient == NULL)
|
||||
return true;
|
||||
BEGINPACKET(pak, 0x07d1)
|
||||
ADDBYTE(pak, 8);
|
||||
ADDWORD(pak, otherclient->clientid);
|
||||
ADDWORD(pak, 0);
|
||||
otherclient->Party->IsMaster = true;
|
||||
thisclient->Party->IsMaster = false;
|
||||
thisclient->Party->party->SendToMembers( &pak );
|
||||
}
|
||||
break;
|
||||
case 0x81: //Kick from party
|
||||
{
|
||||
unsigned int charid = GETDWORD((*P),1);
|
||||
if(thisclient->Party->party==NULL)
|
||||
return true;
|
||||
if(!thisclient->Party->IsMaster || thisclient->CharInfo->charid==charid)
|
||||
return true;
|
||||
CParty* party = thisclient->Party->party;
|
||||
if(party == NULL)
|
||||
return true;
|
||||
CPlayer* thismember = party->GetMemberByCharID( charid );
|
||||
if(thismember == NULL)
|
||||
return true;
|
||||
|
||||
BEGINPACKET( pak, 0x7d1 ); // Kicked from party message
|
||||
ADDBYTE ( pak, 0x80 );
|
||||
ADDDWORD ( pak, thismember->CharInfo->charid );
|
||||
thismember->client->SendPacketCpy( &pak );
|
||||
RESETPACKET( pak, 0x7d2 );
|
||||
ADDWORD ( pak, 0xff00 );
|
||||
ADDDWORD ( pak, thismember->CharInfo->charid );
|
||||
bool pflag = false;
|
||||
if(!party->RemovePlayer( thismember )) //if this player is not in this party
|
||||
return true;
|
||||
if(party->Members.size()>1)
|
||||
{
|
||||
for(UINT i=0;i<party->Members.size();i++)
|
||||
{
|
||||
CPlayer* othermember = party->Members.at(i);
|
||||
if(othermember == NULL)
|
||||
return true;
|
||||
if(!pflag)
|
||||
{
|
||||
ADDDWORD( pak, othermember->CharInfo->charid );
|
||||
if(thismember->Party->IsMaster)
|
||||
othermember->Party->IsMaster = true;
|
||||
pflag = true;
|
||||
}
|
||||
othermember->client->SendPacketCpy( &pak );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(UINT i=0;i<party->Members.size();i++)
|
||||
{
|
||||
CPlayer* othermember = party->Members.at(i);
|
||||
if(othermember == NULL)
|
||||
return true;
|
||||
RESETPACKET( pak, 0x7d1 );
|
||||
ADDBYTE ( pak, 0x05 );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
othermember->client->SendPacketCpy( &pak );
|
||||
othermember->Party->party = NULL;
|
||||
othermember->Party->IsMaster = true;
|
||||
}
|
||||
RemoveParty( party );
|
||||
delete party;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log(MSG_WARNING,"Party unknown action: %i", action);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Party Manager
|
||||
bool CWorldServer::pakPartyManager( CPlayer* thisclient, CPacket* P )
|
||||
{
|
||||
unsigned int action = GETBYTE((*P),0);
|
||||
switch(action)
|
||||
{
|
||||
case 0x02://Acepto
|
||||
{
|
||||
if(thisclient->Party->party!=NULL)// have party
|
||||
return true;
|
||||
unsigned int clientid = GETWORD((*P),1);
|
||||
CPlayer* otherclient = GetClientByID( clientid, thisclient->Position->Map );
|
||||
|
||||
if(otherclient == NULL)
|
||||
{
|
||||
BEGINPACKET( pak, 0x7d1 );
|
||||
ADDBYTE ( pak, 0x00 );//No encontro el ID
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
thisclient->client->SendPacketCpy( &pak );
|
||||
return true;
|
||||
}
|
||||
CParty* party = otherclient->Party->party;
|
||||
if(party != NULL)
|
||||
{
|
||||
if(party->Members.size()>=party->Capacity)
|
||||
{
|
||||
SendSysMsg( thisclient, "Party is Full" );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(abs(otherclient->Stats->Level-thisclient->Stats->Level)>(Config.Partygap+1))
|
||||
{
|
||||
BEGINPACKET( pak, 0x7d1 );
|
||||
ADDBYTE ( pak, 0x07 );//Level inapropiado
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
thisclient->client->SendPacketCpy( &pak );
|
||||
return true;
|
||||
}
|
||||
BEGINPACKET( pak, 0x7d1 );
|
||||
ADDBYTE ( pak, 0x02 );//Acepto Party
|
||||
ADDWORD ( pak, otherclient->clientid );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
otherclient->client->SendPacketCpy( &pak );
|
||||
if( party == NULL )
|
||||
{ // new party
|
||||
CParty* thisparty = new CParty;
|
||||
thisparty->counted = false;
|
||||
thisparty->Pending_Exp = 0;
|
||||
thisparty->AddPlayer( otherclient );
|
||||
AddParty( thisparty );
|
||||
otherclient->Party->IsMaster = true;
|
||||
party = thisparty;
|
||||
}
|
||||
//Send Party Level and Party Exp
|
||||
RESETPACKET( pak, 0x7d4 ); //
|
||||
ADDBYTE ( pak, party->PartyLevel );
|
||||
ADDDWORD ( pak, party->Exp );
|
||||
thisclient->client->SendPacketCpy( &pak );
|
||||
thisclient->Party->IsMaster = false;
|
||||
// Send New Party Member info to other players
|
||||
RESETPACKET( pak, 0x7d2 );
|
||||
ADDBYTE ( pak, party->Option );
|
||||
ADDBYTE ( pak, 0x01 );
|
||||
ADDDWORD ( pak, thisclient->CharInfo->charid );
|
||||
ADDWORD ( pak, thisclient->clientid );
|
||||
ADDWORD ( pak, thisclient->Stats->MaxHP );
|
||||
ADDWORD ( pak, thisclient->Stats->HP );
|
||||
ADDDWORD ( pak, 0x00000000 );
|
||||
ADDDWORD ( pak, 0x0000000f );
|
||||
ADDWORD ( pak, 0x1388 );
|
||||
ADDSTRING ( pak, thisclient->CharInfo->charname );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
party->SendToMembers( &pak );
|
||||
|
||||
// Send To New Party Member the members List
|
||||
RESETPACKET( pak, 0x7d2 );
|
||||
ADDBYTE ( pak, party->Option );
|
||||
ADDBYTE ( pak, party->Members.size() );
|
||||
for(int i=0;i<party->Members.size();i++)
|
||||
{
|
||||
CPlayer* member= party->Members.at(i);
|
||||
if(member == NULL)
|
||||
continue;
|
||||
ADDDWORD ( pak, member->CharInfo->charid );
|
||||
ADDWORD ( pak, member->clientid );
|
||||
ADDWORD ( pak, member->Stats->MaxHP );
|
||||
ADDWORD ( pak, member->Stats->HP );
|
||||
ADDDWORD ( pak, 0x00000000 );
|
||||
ADDDWORD ( pak, 0x0000000f );
|
||||
ADDWORD ( pak, 0x1388 );
|
||||
ADDSTRING ( pak, member->CharInfo->charname );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
}
|
||||
thisclient->client->SendPacketCpy( &pak );
|
||||
party->AddPlayer( thisclient );
|
||||
}
|
||||
break;
|
||||
case 0x04://No acepto
|
||||
{
|
||||
unsigned int clientid = GETWORD((*P),1);
|
||||
CPlayer* otherclient = GetClientByID( clientid, thisclient->Position->Map );
|
||||
if(otherclient == NULL)
|
||||
{
|
||||
BEGINPACKET( pak, 0x7d1 );
|
||||
ADDBYTE ( pak, 0x00 );//No encontro el ID
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
thisclient->client->SendPacketCpy( &pak );
|
||||
return true;
|
||||
}
|
||||
BEGINPACKET( pak, 0x7d1 );
|
||||
ADDBYTE ( pak, 0x04 );//No acepto
|
||||
ADDWORD ( pak, thisclient->clientid );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
otherclient->client->SendPacketCpy( &pak );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log(MSG_WARNING,"Party Manager unknown action: %i", action);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Party Chat
|
||||
bool CWorldServer::pakPartyChat( CPlayer* thisclient, CPacket* P )
|
||||
{
|
||||
CParty* party = thisclient->Party->party;
|
||||
if(party == NULL)
|
||||
return true;
|
||||
BEGINPACKET( pak, 0x786 );
|
||||
ADDWORD ( pak, thisclient->clientid );
|
||||
ADDSTRING ( pak, P->Buffer );
|
||||
ADDBYTE ( pak, 0 );
|
||||
party->SendToMembers( &pak );
|
||||
return true;
|
||||
}
|
||||
|
||||
// Change Party Options
|
||||
bool CWorldServer::pakPartyOption( CPlayer* thisclient, CPacket* P )
|
||||
{
|
||||
if(!thisclient->Party->IsMaster)
|
||||
return true;
|
||||
CParty* party = thisclient->Party->party;
|
||||
if(party == NULL)
|
||||
return true;
|
||||
party->Option = GETBYTE((*P),0);
|
||||
BEGINPACKET( pak, 0x7d7 );
|
||||
ADDBYTE ( pak, party->Option );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
party->SendToMembers( &pak );
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int CWorldServer::GetMaxPartyExp( unsigned int partylevel )
|
||||
{
|
||||
unsigned int exp = 540;
|
||||
exp += 100 * partylevel + ( partylevel-2*10 );
|
||||
return exp;
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
#include "main.h"
|
||||
|
||||
dword GetRewardValue(dword function, dword amount, CWorldClient* client, word nDupCNT){
|
||||
switch(function){
|
||||
case 0:
|
||||
{
|
||||
dword tempVal = amount + 0x1E;
|
||||
tempVal *= client->GetStat(sCharm) + 0x0a;
|
||||
tempVal *= (100 & 0xFFFF);//World Rate
|
||||
tempVal *= 0x14;//Fame + 0x14
|
||||
tempVal = ((tempVal / (client->stats_base[sLevel] + 0x46)) / 0x7530) + amount;
|
||||
return tempVal;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
dword tempVal = (client->stats_base[sLevel] + 3) * amount;
|
||||
tempVal *= (client->GetStat(sCharm) >> 1) + client->stats_base[sLevel] + 0x28;
|
||||
tempVal *= (100 & 0xFFFF);//World Rate
|
||||
return tempVal / 0x2710;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
return (amount * nDupCNT);
|
||||
break;
|
||||
case 3:
|
||||
case 5:
|
||||
{
|
||||
dword tempVal = amount + 0x14;
|
||||
tempVal *= client->GetStat(sCharm) + 0x0a;
|
||||
tempVal *= (100 & 0xFFFF);//World Rate
|
||||
tempVal *= 0x14;//Fame + 0x14
|
||||
tempVal = ((tempVal / (client->stats_base[sLevel] + 0x46)) / 0x7530) + amount;
|
||||
return tempVal;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
dword tempVal = amount + 0x2;
|
||||
tempVal *= (client->GetStat(sCharm) + client->stats_base[sLevel] + 0x28);
|
||||
tempVal *= 0x28;//Fame + 0x28
|
||||
tempVal *= (100 & 0xFFFF);//World Rate
|
||||
return tempVal / 0x222E0;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
{
|
||||
dword tempVal = amount + 0x14;
|
||||
tempVal *= client->GetStat(sCharm) + client->stats_base[sLevel];
|
||||
tempVal *= 0x14;//Fame + 0x14
|
||||
tempVal *= (100 & 0xFFFF);//World Rate
|
||||
return (tempVal / 0x2DC6C0) + amount;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
|
||||
//Update Quest
|
||||
QUESTREWD(000){
|
||||
GETREWDDATA(000);
|
||||
|
||||
switch(data->btOp){//0 remove, 1 start, 4 select
|
||||
case 0:
|
||||
{
|
||||
for(dword i = 0; i < 10; i++){
|
||||
if(client->quest.quests[i].QuestID != data->iQuestSN) continue;
|
||||
|
||||
memset(&client->quest.quests[i], 0, sizeof(SQuest));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
for(dword i = 0; i < 10; i++){
|
||||
if(client->quest.quests[i].QuestID == 0) continue;
|
||||
memset(&client->quest.quests[i], 0, sizeof(SQuest));
|
||||
client->quest.quests[i].QuestID = data->iQuestSN;
|
||||
client->quest.quests[i].Time = clock_t();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
client->ActiveQuest = data->iQuestSN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Update Quest Items
|
||||
QUESTREWD(001){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Set Quest Variable
|
||||
QUESTREWD(002){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Update Stats
|
||||
QUESTREWD(003){
|
||||
GETREWDDATA(003);
|
||||
for(dword i = 0; i < data->iDataCnt; i++){
|
||||
qword address = i * 0x0C;
|
||||
address += (qword)data;
|
||||
address += 4;
|
||||
STR_ABIL_DATA* curAbil = (STR_ABIL_DATA*)address;
|
||||
if(!OperateValues(curAbil->btOp, &client->stats_base[curAbil->iType], curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Set Quest Variable
|
||||
QUESTREWD(004){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Give Reward
|
||||
QUESTREWD(005){
|
||||
GETREWDDATA(005);
|
||||
|
||||
switch(data->btTarget){
|
||||
case 0://EXP
|
||||
{
|
||||
client->basic.exp += GetRewardValue(data->btEquation, data->iValue, client, 0);
|
||||
CPacket pakout(0x79b);
|
||||
pakout.Add<dword>(client->basic.exp);
|
||||
pakout.Add<dword>(client->stats_base[sStamina]);
|
||||
pakout.Add<word>(0);
|
||||
server->SendPacket(client, &pakout);
|
||||
}
|
||||
break;
|
||||
case 1://Zuly
|
||||
client->inv.money += GetRewardValue(data->btEquation, data->iValue, client, 1);//dunno nDupCount for this one!
|
||||
CPacket pakout(0x71D);
|
||||
pakout.Add<qword>(client->inv.money);
|
||||
server->SendPacket(client, &pakout);
|
||||
break;
|
||||
case 2://Item
|
||||
{
|
||||
CItem nItem;
|
||||
nItem.type = data->iItemSN / 1000;
|
||||
nItem.id = data->iItemSN - (nItem.type * 1000);
|
||||
if(nItem.IsStackable()){
|
||||
nItem.count = GetRewardValue(data->btEquation, data->iValue, client, 0);
|
||||
}else{
|
||||
nItem.itemdata = 0;
|
||||
nItem.durability = 40;
|
||||
nItem.lifespan = 1000;
|
||||
nItem.appraised = 1;
|
||||
}
|
||||
client->AddItem(&nItem, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Restore HP/MP
|
||||
QUESTREWD(006){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Teleport
|
||||
QUESTREWD(007){
|
||||
GETREWDDATA(007);
|
||||
client->MoveMap(data->iZoneSN, (float)data->iX, (float)data->iY);
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Spawn Monster
|
||||
QUESTREWD(008){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Execute Quest Trigger
|
||||
QUESTREWD(009){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Reset Stats
|
||||
QUESTREWD(010){
|
||||
client->stats_base[sStatPoints] = 0;
|
||||
|
||||
client->stats_base[sStrength] = 15;
|
||||
client->stats_base[sDexterity] = 15;
|
||||
client->stats_base[sIntelligence] = 15;
|
||||
client->stats_base[sConcentration] = 15;
|
||||
client->stats_base[sCharm] = 10;
|
||||
client->stats_base[sSensibility] = 10;
|
||||
|
||||
for(int i = 0; i <= client->stats_base[sLevel]; i++)
|
||||
client->stats_base[sStatPoints] += (i + 9);
|
||||
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Update Quest Var
|
||||
QUESTREWD(011){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//NPC Speak
|
||||
QUESTREWD(012){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Unknown
|
||||
QUESTREWD(013){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Learn Skill
|
||||
QUESTREWD(014){
|
||||
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Set Quest Flag
|
||||
QUESTREWD(015){
|
||||
GETREWDDATA(015);
|
||||
client->quest.SetFlag(data->nSN, (data->btOp == 1)?true:false);
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Unknown
|
||||
QUESTREWD(016){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Reset All Quest Flags
|
||||
QUESTREWD(017){
|
||||
memset(&client->quest.flags, 0, 244);
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Send Announcement
|
||||
QUESTREWD(018){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Execute Quest Trigger in Other Map
|
||||
QUESTREWD(019){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//PvP Status
|
||||
QUESTREWD(020){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Set Respawn Position
|
||||
QUESTREWD(021){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Unknown
|
||||
QUESTREWD(022){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Raise Clan Grade
|
||||
QUESTREWD(023){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Money
|
||||
QUESTREWD(024){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Points
|
||||
QUESTREWD(025){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Skill
|
||||
QUESTREWD(026){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Contribution
|
||||
QUESTREWD(027){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Teleportation
|
||||
QUESTREWD(028){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
#include "main.h"
|
||||
|
||||
int OperateValues(byte btOp, int* value1PTR, int value2){
|
||||
int value1 = *value1PTR;
|
||||
switch(btOp){
|
||||
case 0:
|
||||
return (value1 == value2);
|
||||
case 1:
|
||||
return (value1 <= value2);
|
||||
case 2:
|
||||
return (value1 >= value2);
|
||||
case 3:
|
||||
return (value1 > value2);
|
||||
case 4:
|
||||
return (value1 < value2);
|
||||
case 5:
|
||||
(*value1PTR) = value2;
|
||||
return 1;
|
||||
case 6:
|
||||
(*value1PTR) = value1 + value2;
|
||||
return 1;
|
||||
case 7:
|
||||
(*value1PTR) = value1 - value2;
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Check Quest
|
||||
QUESTCOND(000){
|
||||
GETCONDDATA(000);
|
||||
for(dword i = 0; i < 10; i++){
|
||||
if(client->quest.quests[i].QuestID == data->iQuestSN)
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//Check Quest Variable
|
||||
QUESTCOND(001){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Quest Variable
|
||||
QUESTCOND(002){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Stats
|
||||
QUESTCOND(003){
|
||||
GETCONDDATA(003);
|
||||
for(int i = 0; i < data->iDataCnt; i++){
|
||||
qword address = i * 0x0C;
|
||||
address += (qword)data;
|
||||
address += 4;
|
||||
STR_ABIL_DATA* curAbil = (STR_ABIL_DATA*)address;
|
||||
if(!OperateValues(curAbil->btOp, &client->stats_base[curAbil->iType], curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Items
|
||||
QUESTCOND(004){
|
||||
GETCONDDATA(004);
|
||||
for(int i = 0; i < data->iDataCnt; i++){
|
||||
//QWord for 64bit safe pointers.
|
||||
qword address = i * 0x10;
|
||||
address += (qword)data;
|
||||
address += 4;
|
||||
STR_ITEM_DATA* curItem = (STR_ITEM_DATA*)address;
|
||||
if(curItem->uiItemSN == 0 && curItem->iWhere != 0){
|
||||
int itemHead = client->inv.items[curItem->iWhere].itemhead;
|
||||
if(!OperateValues(curItem->btOp, &itemHead, curItem->uiItemSN))
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
Log(MSG_INFO,"uiItemSN: %u iWhere: %u iRequestCnt: %u btOp: %u", curItem->uiItemSN, curItem->iWhere, curItem->iRequestCnt, curItem->btOp);
|
||||
}
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Party
|
||||
QUESTCOND(005){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Near Point
|
||||
QUESTCOND(006){
|
||||
GETCONDDATA(006);
|
||||
client->UpdatePosition();
|
||||
if(data->iZoneSN != 0){
|
||||
if(client->basic.map != data->iZoneSN)
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
float dx = client->basic.pos.x - (float)data->iX;
|
||||
float dy = client->basic.pos.y - (float)data->iY;
|
||||
float distance = sqrt((dx*dx) + (dy*dy));
|
||||
return (distance < data->iRadius)?QUEST_SUCCESS:QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//World Time
|
||||
QUESTCOND(007){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Quest Time
|
||||
QUESTCOND(008){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Skill
|
||||
QUESTCOND(009){
|
||||
GETCONDDATA(009);
|
||||
int checkVal = (data->btOp)?QUEST_SUCCESS:QUEST_FAILURE;
|
||||
for(dword i = 0; i < 90; i++){
|
||||
if(client->skill.Skills[i] == data->iSkillSN1)
|
||||
return (data->btOp)?QUEST_SUCCESS:QUEST_FAILURE;
|
||||
}
|
||||
return (data->btOp)?QUEST_FAILURE:QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Unknown
|
||||
QUESTCOND(010){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Quest Variable
|
||||
QUESTCOND(011){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Execute Trigger in Zone
|
||||
QUESTCOND(012){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Near NPC
|
||||
QUESTCOND(013){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Quest Flag
|
||||
QUESTCOND(014){
|
||||
GETCONDDATA(014);
|
||||
bool dataOp = (data->btOp)?true:false;
|
||||
return (client->quest.GetFlag(data->nSN) == dataOp)?QUEST_SUCCESS:QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//Unknown
|
||||
QUESTCOND(015){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Zone Time
|
||||
QUESTCOND(016){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//NPC Variables
|
||||
QUESTCOND(017){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Time on Date
|
||||
QUESTCOND(018){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Time on Day
|
||||
QUESTCOND(019){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Unknown
|
||||
QUESTCOND(020){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Unknown
|
||||
QUESTCOND(021){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Server/Channel
|
||||
QUESTCOND(022){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//In Clan
|
||||
QUESTCOND(023){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Position
|
||||
QUESTCOND(024){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Contribution
|
||||
QUESTCOND(025){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Grade
|
||||
QUESTCOND(026){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Points
|
||||
QUESTCOND(027){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Funds
|
||||
QUESTCOND(028){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Members
|
||||
QUESTCOND(029){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Skills
|
||||
QUESTCOND(030){
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
#define QUESTCOND(cond) int QUEST_COND_ ## cond ## (class CWorldServer* server, class CPlayer* client, byte* raw)
|
||||
typedef int (*fpQuestCond)(class CWorldServer*, class CPlayer*, byte*);
|
||||
#define GETCONDDATA(cond) STR_COND_ ## cond ## * data = (STR_COND_ ## cond ## *)raw;
|
||||
|
||||
QUESTCOND(000);QUESTCOND(001);QUESTCOND(002);
|
||||
QUESTCOND(003);QUESTCOND(004);QUESTCOND(005);
|
||||
QUESTCOND(006);QUESTCOND(007);QUESTCOND(008);
|
||||
QUESTCOND(009);QUESTCOND(010);QUESTCOND(011);
|
||||
QUESTCOND(012);QUESTCOND(013);QUESTCOND(014);
|
||||
QUESTCOND(015);QUESTCOND(016);QUESTCOND(017);
|
||||
QUESTCOND(018);QUESTCOND(019);QUESTCOND(020);
|
||||
QUESTCOND(021);QUESTCOND(022);QUESTCOND(023);
|
||||
QUESTCOND(024);QUESTCOND(025);QUESTCOND(026);
|
||||
QUESTCOND(027);QUESTCOND(028);QUESTCOND(029);
|
||||
QUESTCOND(030);
|
||||
|
||||
struct STR_COND_000 {
|
||||
int iQuestSN;
|
||||
};
|
||||
|
||||
struct STR_COND_001 {
|
||||
int iDataCnt;
|
||||
STR_QUEST_DATA* CheckData;
|
||||
};
|
||||
|
||||
struct STR_COND_002 {
|
||||
int iDataCnt;
|
||||
STR_QUEST_DATA* CheckData;
|
||||
};
|
||||
|
||||
struct STR_COND_003 {
|
||||
int iDataCnt;
|
||||
STR_ABIL_DATA* CheckData;
|
||||
};
|
||||
|
||||
struct STR_COND_004 {
|
||||
int iDataCnt;
|
||||
STR_ITEM_DATA* CheckData;
|
||||
};
|
||||
|
||||
struct STR_COND_005 {
|
||||
byte btIsLeader;
|
||||
int iLevel;
|
||||
byte btReversed;
|
||||
};
|
||||
|
||||
struct STR_COND_006 {
|
||||
int iZoneSN;
|
||||
int iX;
|
||||
int iY;
|
||||
int iZ;
|
||||
int iRadius;
|
||||
};
|
||||
|
||||
struct STR_COND_007 {
|
||||
dword ulTime;
|
||||
dword ulEndTime;
|
||||
};
|
||||
|
||||
struct STR_COND_008 {
|
||||
dword ulTime;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_COND_009 {
|
||||
int iSkillSN1;
|
||||
int iSkillSN2;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_COND_010 {
|
||||
byte btLowPcnt;
|
||||
byte btHighPcnt;
|
||||
};
|
||||
|
||||
struct STR_COND_011 {
|
||||
union {
|
||||
byte btWho;
|
||||
word usWho;
|
||||
};
|
||||
word nVarNo;
|
||||
int iValue;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_COND_012 {
|
||||
union {
|
||||
word iZone;
|
||||
dword ulZone;
|
||||
};
|
||||
int iX;
|
||||
int iY;
|
||||
union {
|
||||
int iEventID;
|
||||
dword m_HashEventOBJ;
|
||||
};
|
||||
};
|
||||
|
||||
struct STR_COND_013 {
|
||||
int iNpcNo;
|
||||
};
|
||||
|
||||
struct STR_COND_014 {
|
||||
word nSN;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_COND_015 {
|
||||
word nNumber1;
|
||||
word nNumber2;
|
||||
};
|
||||
|
||||
struct STR_COND_016 {
|
||||
union {
|
||||
byte btWho;
|
||||
dword ulWho;
|
||||
};
|
||||
dword ulTime;
|
||||
dword ulEndTime;
|
||||
};
|
||||
|
||||
struct STR_COND_017 {
|
||||
STR_NPCVAR NpcVar1;
|
||||
STR_NPCVAR NpcVar2;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_COND_018 {
|
||||
byte btDate;
|
||||
byte btHour1;
|
||||
byte btMin1;
|
||||
byte btHour2;
|
||||
byte btMin2;
|
||||
};
|
||||
|
||||
struct STR_COND_019 {
|
||||
byte btWeekDay;
|
||||
byte btHour1;
|
||||
byte btMin1;
|
||||
byte btHour2;
|
||||
byte btMin2;
|
||||
};
|
||||
|
||||
struct STR_COND_020 {
|
||||
int iNo1;
|
||||
int iNo2;
|
||||
};
|
||||
|
||||
struct STR_COND_021 {
|
||||
union {
|
||||
byte btSelObjType;
|
||||
dword ulSelObjType;
|
||||
};
|
||||
int iRadius;
|
||||
};
|
||||
|
||||
struct STR_COND_022 {
|
||||
word nX;
|
||||
word nY;
|
||||
};
|
||||
|
||||
struct STR_COND_023 {
|
||||
byte btReg;
|
||||
};
|
||||
|
||||
struct STR_COND_024 {
|
||||
word nPOS;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_COND_025 {
|
||||
word nCONT;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_COND_026 {
|
||||
word nGRD;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_COND_027 {
|
||||
word nPOINT;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_COND_028 {
|
||||
word iMONEY;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_COND_029 {
|
||||
word nMemberCNT;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_COND_030 {
|
||||
word nSkill1;
|
||||
word nSkill2;
|
||||
byte btOP;
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include "worldserver.h"
|
||||
class CQuestTrigger {
|
||||
public:
|
||||
struct SQuestDatum {
|
||||
int size;
|
||||
int opcode;
|
||||
byte* data;
|
||||
};
|
||||
|
||||
string TriggerName;
|
||||
dword TriggerHash;
|
||||
|
||||
byte CheckNext;
|
||||
|
||||
dword ConditionCount;
|
||||
SQuestDatum** Conditions;
|
||||
|
||||
dword ActionCount;
|
||||
SQuestDatum** Actions;
|
||||
private:
|
||||
};
|
||||
|
||||
struct STR_NPCVAR {
|
||||
dword iNpcNo;
|
||||
word nVarNo;
|
||||
word unused;
|
||||
};
|
||||
|
||||
struct STR_ITEM_DATA {
|
||||
dword uiItemSN;
|
||||
dword iWhere;
|
||||
dword iRequestCnt;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_QUEST_DATA {
|
||||
union {
|
||||
dword iType;
|
||||
struct {
|
||||
word m_wVarNO;
|
||||
word m_wVarTYPE;
|
||||
};
|
||||
};
|
||||
word nValue;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_ABIL_DATA {
|
||||
dword iType;
|
||||
int iValue;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
#define QUEST_FAILURE 0
|
||||
#define QUEST_SUCCESS 1
|
||||
|
||||
int OperateValues(byte btOp, int* value1PTR, int value2);
|
||||
|
||||
#include "QuestConditions.h"
|
||||
#include "quest/QuestActions.h"
|
||||
@@ -0,0 +1,898 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
// from Paul_T
|
||||
bool CWorldServer::SendPM( CPlayer* thisclient, const char* Format, ... )
|
||||
{
|
||||
char buf[512];
|
||||
va_list ap;
|
||||
va_start( ap, Format );
|
||||
vsprintf( buf, Format, ap );
|
||||
BEGINPACKET( pak, 0x0784 );
|
||||
ADDSTRING( pak, "Server" );
|
||||
ADDBYTE( pak, 0 );
|
||||
ADDSTRING( pak, buf );
|
||||
ADDBYTE( pak, 0 );
|
||||
thisclient->client->SendPacket(&pak);
|
||||
va_end( ap );
|
||||
return true;
|
||||
}
|
||||
|
||||
// from Paul_T
|
||||
bool CWorldServer::SendGlobalMSG( CPlayer* thisclient, const char* Format, ... )
|
||||
{
|
||||
char buf[512];
|
||||
va_list ap;
|
||||
va_start( ap, Format );
|
||||
vsprintf( buf, Format, ap );
|
||||
BEGINPACKET( pak, 0x0784 );
|
||||
ADDSTRING( pak, thisclient->CharInfo->charname );
|
||||
ADDBYTE( pak, 0 );
|
||||
ADDSTRING( pak, buf );
|
||||
ADDBYTE( pak, 0 );
|
||||
SendToAll(&pak);
|
||||
va_end( ap );
|
||||
return true;
|
||||
}
|
||||
|
||||
// NPC Announce to the server
|
||||
bool CWorldServer::NPCAnnounce( const char msg[200], const char npc[50])
|
||||
{
|
||||
BEGINPACKET( pak, 0x702 );
|
||||
ADDSTRING( pak, npc );
|
||||
ADDSTRING( pak, "> " );
|
||||
ADDSTRING( pak, msg);
|
||||
ADDBYTE( pak, 0x00);
|
||||
SendToAll( &pak );
|
||||
return true;
|
||||
}
|
||||
|
||||
// NPC Whisper to a character
|
||||
bool CWorldServer::NPCWhisper( CPlayer* thisclient, CMonster* thismon, const char msg[200], const char npc[50] )
|
||||
{
|
||||
BEGINPACKET( pak, 0x0784 );
|
||||
ADDSTRING( pak, npc );
|
||||
ADDBYTE( pak, 0 );
|
||||
ADDSTRING( pak, msg );
|
||||
ADDBYTE( pak, 0 );
|
||||
thisclient->client->SendPacket(&pak);
|
||||
return true;
|
||||
}
|
||||
|
||||
// NPC Shout to the current map
|
||||
bool CWorldServer::NPCShout( int map, const char msg[200], const char npc[50] )
|
||||
{
|
||||
BEGINPACKET(pak, 0x0785);
|
||||
ADDSTRING ( pak, npc );
|
||||
ADDBYTE ( pak, 0 );
|
||||
ADDSTRING ( pak, msg );
|
||||
ADDBYTE ( pak, 0 );
|
||||
SendToMap ( &pak, map );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWorldServer::DoSkillScript( CCharacter* character, CSkills* thisskill )
|
||||
{
|
||||
try
|
||||
{
|
||||
if(thisskill == 0)
|
||||
return false;
|
||||
if(thisskill->script == 0)
|
||||
return false;
|
||||
if(thisskill->svalue1 == 0)
|
||||
return false;
|
||||
fPoint position = RandInCircle( character->Position->current, 1 );
|
||||
CMap* map = MapList.Index[character->Position->Map];
|
||||
map->AddMonster( thisskill->svalue1, position, character->clientid,0,1 );
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Log(MSG_WARNING, "Error in CWorldServer::DoSkillScript( CCharacter* character, CSkills* thisskill )(serverFunctions.cpp)" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Build Drop. New experimental method.
|
||||
CDrop* CWorldServer::GetNewDrop( CMonster* thismon )
|
||||
{
|
||||
//Log(MSG_DEBUG,"Monster %i killed. FInding a drop",thismon->montype);
|
||||
CPlayer* thisclient = GServer->GetClientByCID(thismon->MonsterDrop->firsthit);
|
||||
if(thisclient == NULL)return NULL;
|
||||
if( thisclient->Session->codedebug )
|
||||
SendPM(thisclient, "Drops function called after killing monster type %i",thismon->montype);
|
||||
|
||||
int randv;
|
||||
int i;
|
||||
CDrop* newdrop = new (nothrow) CDrop;
|
||||
|
||||
if(newdrop == NULL)
|
||||
{
|
||||
Log(MSG_WARNING, "newDrop is NULL [getdrop]" );
|
||||
return NULL;
|
||||
}
|
||||
CMDrops* thisdrop = NULL;
|
||||
CMDrops* testdrop = 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);
|
||||
|
||||
|
||||
|
||||
int droprate = 80; // base drop percentage for monsters your own level
|
||||
int DropFloor = Config.BASE_DROP_CHANCE;
|
||||
int leveldif = (int)((float)thismon->thisnpc->level - (float)thisclient->Stats->Level);
|
||||
float mod = droprate * 0.01 * leveldif;
|
||||
int dropchance = int(droprate + mod);
|
||||
if(dropchance < DropFloor) dropchance = DropFloor; //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( thisclient->Session->codedebug )
|
||||
// SendPM(thisclient, "Drop chance (based on level diference) = %i", dropchance );
|
||||
int droprand = GServer->RandNumber(0,100);
|
||||
if (droprand > dropchance)
|
||||
{
|
||||
if( thisclient->Session->codedebug )
|
||||
SendPM(thisclient, "Drop rate check failed with %i. No drop this time",droprand);
|
||||
return NULL; // no drop here. not this time anyway.
|
||||
}
|
||||
|
||||
|
||||
// Each monster has its own rates for zuly and item drops defined in the stb
|
||||
// first check for Zuly drops
|
||||
randv = RandNumber( 1, 100);
|
||||
//if( thisclient->Session->codedebug )
|
||||
//{
|
||||
// SendPM(thisclient, "randv = %i. money = %i. zulyrate = %i. ", randv,thismon->thisnpc->money,thisclient->Stats->zulydroprate );
|
||||
//}
|
||||
if(randv <= (thismon->thisnpc->money + thisclient->Stats->zulydroprate)) // zuly drop
|
||||
{
|
||||
newdrop->type = 1; //zuly
|
||||
newdrop->amount = thismon->thisnpc->level * 5 * (Config.ZULY_RATE + thisclient->Stats->zulydroprate) + RandNumber( 1, 10 );
|
||||
if( thisclient->Session->codedebug )
|
||||
{
|
||||
SendPM(thisclient, "Zuly drop selected " );
|
||||
}
|
||||
return newdrop;
|
||||
}
|
||||
//now check for item drops
|
||||
//if( thisclient->Session->codedebug )
|
||||
//{
|
||||
// SendPM(thisclient, "randv = %i. Item = %i. Itemrate = %i", randv,thismon->thisnpc->item,thisclient->Stats->itemdroprate );
|
||||
//}
|
||||
if(randv <= (thismon->thisnpc->item + thisclient->Stats->itemdroprate + thismon->thisnpc->money)) // Item drop
|
||||
{
|
||||
//OK got an item. Now we find which one
|
||||
//Map drop or mob drop? map = 50% mob = 50%. Maybe should be configurable or modified by monster data?
|
||||
int randv2 = RandNumber( 1, 100);
|
||||
if(randv2 <= 50) //map drop
|
||||
{
|
||||
for(i=0;i<GServer->MDropList.size();i++)
|
||||
{
|
||||
testdrop = GServer->MDropList.at(i);
|
||||
if(testdrop == NULL) return NULL;
|
||||
if(testdrop->Dropid == thismon->Position->Map)
|
||||
{
|
||||
thisdrop = testdrop;
|
||||
}
|
||||
}
|
||||
|
||||
if( thisclient->Session->codedebug )
|
||||
{
|
||||
SendPM(thisclient, "Map drop selected for map %i. ", thismon->Position->Map );
|
||||
}
|
||||
}
|
||||
else // mob drop
|
||||
{
|
||||
for(i=0;i<GServer->MDropList.size();i++)
|
||||
{
|
||||
testdrop = GServer->MDropList.at(i);
|
||||
if(testdrop == NULL) return NULL;
|
||||
if(testdrop->Dropid == thismon->thisnpc->dropid)
|
||||
{
|
||||
thisdrop = testdrop;
|
||||
}
|
||||
}
|
||||
if( thisclient->Session->codedebug )
|
||||
{
|
||||
SendPM(thisclient, "Mob drop selected for dropid %i. ", thismon->thisnpc->dropid );
|
||||
}
|
||||
}
|
||||
if(thisdrop == NULL)
|
||||
{
|
||||
Log(MSG_WARNING, "Thisdrop is NULL [getdrop]" );
|
||||
return NULL;
|
||||
}
|
||||
newdrop->type = 2; //Item
|
||||
dword randitem = RandNumber( 1, thisdrop->TotalChance );
|
||||
UINT itemindex = 0;
|
||||
//if( thisclient->Session->codedebug )
|
||||
//{
|
||||
// SendPM(thisclient, "random chance %i", randitem);
|
||||
//}
|
||||
|
||||
i=0;
|
||||
do
|
||||
{
|
||||
//if( thisclient->Session->codedebug )
|
||||
//{
|
||||
// SendPM(thisclient, "Index: %i Item id: %i chance: %i", i, thisdrop->item[i], thisdrop->RunningTotal[i]);
|
||||
//}
|
||||
i++;
|
||||
}
|
||||
while (randitem > thisdrop->RunningTotal[i]);
|
||||
itemindex = i;
|
||||
|
||||
if(thisdrop->item[itemindex] == 0)
|
||||
{
|
||||
Log(MSG_DEBUG, "item at itemindex %i does not exist",itemindex );
|
||||
return NULL; //no item listed for this dropid
|
||||
}
|
||||
//ok we found an item. Now we process it.
|
||||
//if( thisclient->Session->codedebug )
|
||||
//{
|
||||
// SendPM(thisclient, "Found an item to drop itemindex: %i id: %i. ", itemindex, thisdrop->item[itemindex] );
|
||||
//}
|
||||
if(thisdrop->item[itemindex] < 100000)
|
||||
{
|
||||
newdrop->item.itemtype = thisdrop->item[itemindex] / 1000;
|
||||
newdrop->item.itemnum = thisdrop->item[itemindex] % 1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
newdrop->item.itemtype = thisdrop->item[itemindex] / 1000000;
|
||||
newdrop->item.itemnum = thisdrop->item[itemindex] % 1000000;
|
||||
}
|
||||
//if( thisclient->Session->codedebug )
|
||||
//{
|
||||
// SendPM(thisclient, "Item type = %i. item number = %i", newdrop->item.itemtype,newdrop->item.itemnum );
|
||||
//}
|
||||
newdrop->item.lifespan = 10 + rand()%80;
|
||||
switch( newdrop->item.itemtype )
|
||||
{
|
||||
case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9:
|
||||
newdrop->item.type = GServer->EquipList[newdrop->item.itemtype].Index[newdrop->item.itemnum]->type;
|
||||
newdrop->item.raretype = GServer->EquipList[newdrop->item.itemtype].Index[newdrop->item.itemnum]->raretype;
|
||||
break;
|
||||
case 10:
|
||||
newdrop->item.type = GServer->UseList.Index[newdrop->item.itemnum]->type;
|
||||
newdrop->item.raretype = 0;
|
||||
break;
|
||||
case 11:
|
||||
newdrop->item.type = GServer->JemList.Index[newdrop->item.itemnum]->type;
|
||||
newdrop->item.raretype = 0;
|
||||
break;
|
||||
case 12:
|
||||
newdrop->item.type = GServer->NaturalList.Index[newdrop->item.itemnum]->type;
|
||||
newdrop->item.raretype = 0;
|
||||
break;
|
||||
case 14:
|
||||
newdrop->item.type = GServer->PatList.Index[newdrop->item.itemnum]->type;
|
||||
newdrop->item.raretype = 0;
|
||||
break;
|
||||
default:
|
||||
Log(MSG_DEBUG, "itemtype %i does not exist",newdrop->item.itemtype );
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
float dmod = 0; //random number from 0 to 100 made up of 5 sub numbers to keep
|
||||
//the average value near to 50
|
||||
for(i=0; i<5; i++)
|
||||
{
|
||||
float r1 = rand()%20;
|
||||
dmod += r1;
|
||||
}
|
||||
newdrop->item.durability = 10 + (int)dmod;
|
||||
newdrop->item.appraised = true;
|
||||
newdrop->item.stats = 0;
|
||||
newdrop->item.count = 1;
|
||||
|
||||
if( newdrop->item.itemtype == 8 || newdrop->item.itemtype == 9 )
|
||||
{
|
||||
//This probability is now configurable
|
||||
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;
|
||||
}
|
||||
|
||||
float TmpMod = thisclient->Attr->Cha / 30;
|
||||
int chamod = (int)floor(TmpMod);
|
||||
if(chamod < 0) chamod = 0;
|
||||
int basedrop = thisclient->Stats->itemdroprate; //Base number of items to be dropped.
|
||||
int maxdrop = basedrop + Config.DropMultiple;
|
||||
if(newdrop->item.itemtype == 11) //gems only get 1
|
||||
newdrop->item.count = 1;
|
||||
if( newdrop->item.itemtype == 10 || newdrop->item.itemtype == 12 )
|
||||
{
|
||||
newdrop->item.count = RandNumber(basedrop, maxdrop);
|
||||
if(thismon->thisnpc->level == 1)
|
||||
newdrop->item.count = 1; //limit the drop rate of items from level 1 event mobs
|
||||
if(newdrop->item.count == 0)
|
||||
newdrop->item.count = 1;
|
||||
if(newdrop->item.type == 314) //skillbooks
|
||||
newdrop->item.count = 1;
|
||||
if(newdrop->item.itemtype == 12)
|
||||
{
|
||||
newdrop->item.count = RandNumber(1, 6) + RandNumber(0, thisclient->Stats->itemdropcountrate); //basic type 12 items
|
||||
//some item types have their own rules
|
||||
if(newdrop->item.type == 428) //special skill items such as mana jams and scrolls
|
||||
newdrop->item.count = RandNumber(1, 3);
|
||||
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 //95%
|
||||
newdrop->item.refine = 0;
|
||||
|
||||
int StatChance = Config.StatChance;
|
||||
if(newdrop->item.raretype > 0) // it's a blue item
|
||||
{
|
||||
newdrop->item.durability += 10; //make blues a little more durable
|
||||
newdrop->item.appraised = false; //blues need to be appraised
|
||||
newdrop->item.stats = RandNumber(0, 29) * 10 + 1; // stats can only be 1,11,21,,,,,291
|
||||
}
|
||||
else // not a blue item
|
||||
{
|
||||
int pstats = RandNumber(1, 100);
|
||||
if( pstats < StatChance) // default 5%
|
||||
newdrop->item.stats = RandNumber(0, 29) * 10 + 1; // stats can only be 1,11,21,,,,,291
|
||||
}
|
||||
}
|
||||
newdrop->item.gem = 0;
|
||||
if(newdrop->item.count == 0)
|
||||
newdrop->item.count = 1; //just in case this is causing bugged items.
|
||||
if( thisclient->Session->codedebug )
|
||||
{
|
||||
SendPM(thisclient, "Dropped item type: %i ID: %i count %i", newdrop->item.itemtype, newdrop->item.itemnum,newdrop->item.count);
|
||||
}
|
||||
return newdrop;
|
||||
|
||||
}
|
||||
return NULL;
|
||||
// did not qualify to drop anything this time
|
||||
}
|
||||
|
||||
// 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.
|
||||
//type 2 removed a it is now handled by AIP
|
||||
//Log( MSG_INFO, "PYDrops function selected" );
|
||||
//Log( MSG_INFO, "monster is %i", thismon->montype );
|
||||
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);
|
||||
if(thisclient == NULL)return NULL;
|
||||
|
||||
// code to modify drop chance for different levels
|
||||
//float charm = 0;
|
||||
//float droprate = thismon->thisnpc->dropchance;
|
||||
int droprate = Config.DROP_RATE;
|
||||
int DropFloor = Config.BASE_DROP_CHANCE;
|
||||
int leveldif = (int)((float)thismon->thisnpc->level - (float)thisclient->Stats->Level);
|
||||
float mod = droprate * 0.01 * leveldif;
|
||||
int dropchance = int(droprate + mod);
|
||||
if(dropchance < DropFloor) dropchance = DropFloor; //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( thisclient->Session->codedebug )
|
||||
SendPM(thisclient, "Drop rate (based on level diference) = %i", dropchance );
|
||||
int droprand = GServer->RandNumber(0,100);
|
||||
if (droprand > dropchance)
|
||||
{
|
||||
if( thisclient->Session->codedebug )
|
||||
SendPM(thisclient, "Drop rate check failed with %i. No drop this time",droprand);
|
||||
return NULL; // no drop here. not this time anyway.
|
||||
}
|
||||
int DropListSize = MDropList.size();
|
||||
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 * 10 * 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 stb
|
||||
// We also add a small amount based on the player's ItemDropRate and ZulyDroprate.
|
||||
dropchance = thismon->thisnpc->item + thismon->thisnpc->money + thisclient->Stats->itemdroprate + thisclient->Stats->zulydroprate;
|
||||
if( thisclient->Session->codedebug )
|
||||
{
|
||||
SendPM(thisclient, "randv = %i. total drop chance = %i. money = %i. zulyrate = %i. Item = %i. Itemrate = %i", randv,dropchance,thismon->thisnpc->money,thisclient->Stats->zulydroprate,thismon->thisnpc->item,thisclient->Stats->itemdroprate );
|
||||
}
|
||||
if(randv > dropchance)return NULL; // did not qualify to drop anything this time
|
||||
if(randv <= (thismon->thisnpc->money + thisclient->Stats->zulydroprate)) // zuly drop instead of item drop
|
||||
{
|
||||
newdrop->type = 1;
|
||||
newdrop->amount = thismon->thisnpc->level * 5 * Config.ZULY_RATE + RandNumber( 1, 10 );
|
||||
return newdrop;
|
||||
}
|
||||
|
||||
int randomdrop = GServer->RandNumber(1, 100);
|
||||
for(int i=0; i<MDropList.size( ); i++)
|
||||
{
|
||||
isdrop = true; // start out true then eliminate drops later
|
||||
CMDrops* thisdrop = GServer->MDropList.at(i);
|
||||
if(thisdrop->mob != 0 && thisdrop->mob != thismon->montype)
|
||||
isdrop = false;
|
||||
if(thisdrop->map != 0 && thisdrop->map != thismon->Position->Map)
|
||||
isdrop = false;
|
||||
if(thisdrop->level_max > thisdrop->level_min && (thismon->thisnpc->level < thisdrop->level_min || thismon->thisnpc->level > thisdrop->level_max))
|
||||
isdrop = false;
|
||||
|
||||
test = GServer->RandNumber(1, 10000);
|
||||
if(thisdrop->prob < test)
|
||||
isdrop = false;
|
||||
if(isdrop == true)
|
||||
{
|
||||
itemnumber[n] = thisdrop->itemnum;
|
||||
itemtype[n] = thisdrop->itemtype;
|
||||
alternate[n][0] = 0;
|
||||
for(int j=1;i<8;i++)
|
||||
{
|
||||
alternate[n][j] = thisdrop->alt[j];
|
||||
}
|
||||
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;
|
||||
if( thisclient->Session->codedebug )
|
||||
{
|
||||
for(int j=0;j<maxitems;j++)
|
||||
{
|
||||
SendPM(thisclient,"Shortlist item %i, type = %i. id = %i",j, itemtype[j], itemnumber[j]);
|
||||
}
|
||||
SendPM(thisclient, "Selected item %i. type = %i. id = %i ",n, newdrop->item.itemtype,newdrop->item.itemnum);
|
||||
}
|
||||
|
||||
newdrop->item.lifespan = 10 + rand()%80;
|
||||
float dmod = 0; //random number from 0 to 100 made up of 5 sub numbers to keep
|
||||
//the average value near to 50
|
||||
for(int i=0; i<5; 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 = thisclient->Stats->itemdroprate; //Base number of items to be dropped.
|
||||
int maxdrop = basedrop + Config.DropMultiple + chamod;
|
||||
if(newdrop->item.itemtype == 11) //gems only get 1
|
||||
newdrop->item.count = 1;
|
||||
if( newdrop->item.itemtype == 10 || newdrop->item.itemtype == 12 )
|
||||
{
|
||||
newdrop->item.count = RandNumber(basedrop, maxdrop);
|
||||
if(thismon->thisnpc->level == 1)
|
||||
newdrop->item.count = 1 + chamod; //limit the drop rate of items from level 1 event mobs
|
||||
if(newdrop->item.count == 0)
|
||||
newdrop->item.count = 1;
|
||||
if(newdrop->item.type == 314) //skillbooks
|
||||
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 == 12)
|
||||
{
|
||||
newdrop->item.count = RandNumber(0, 6) + RandNumber(0, thisclient->Stats->itemdropcountrate); //basic type 12 items
|
||||
//some item types have their own rules
|
||||
if(newdrop->item.type == 428) //special skill items such as mana jams and scrolls
|
||||
newdrop->item.count = RandNumber(0, 3);
|
||||
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;
|
||||
if( thisclient->Session->codedebug )
|
||||
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.
|
||||
{
|
||||
if( thisclient->Session->codedebug )
|
||||
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;
|
||||
}
|
||||
}
|
||||
//is the item a blue or other special or unique type?
|
||||
if(newdrop->item.itemtype < 10 || newdrop->item.itemtype == 14) //equipable or PAT
|
||||
{
|
||||
if(GServer->EquipList[newdrop->item.itemtype].Index[newdrop->item.itemnum]->raretype != 0)
|
||||
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 = 0;
|
||||
}
|
||||
int StatChance = Config.StatChance;
|
||||
if( pstats < StatChance) // default 5%
|
||||
newdrop->item.stats = RandNumber( 1, 300);
|
||||
}
|
||||
newdrop->item.gem = 0;
|
||||
//newdrop->droptime = clock(); //moved to AddDrop function
|
||||
*/
|
||||
return newdrop;
|
||||
|
||||
}
|
||||
|
||||
|
||||
UINT CWorldServer::GetColorExp( UINT playerlevel,UINT moblevel, UINT exp )
|
||||
{
|
||||
/*
|
||||
purple = +16 - 200%
|
||||
red = +10 - 150%
|
||||
orange = +4 - 125%
|
||||
yellow = same - 100%
|
||||
green = -4 - 80%
|
||||
blue = -9 - 50%
|
||||
white = -15 - 20%
|
||||
*/
|
||||
int dif = moblevel - playerlevel;
|
||||
//Log(MSG_INFO,"Level dif = %i XP factor is %i",dif,factor);
|
||||
if(dif > 15) //purple
|
||||
exp = (UINT)floor(exp * 2.0);
|
||||
else if(dif > 10) //red
|
||||
exp = (UINT)floor(exp * 1.5);
|
||||
else if(dif > 4) //orange
|
||||
exp = (UINT)floor(exp * 1.25);
|
||||
else if(dif >= 0) //yellow
|
||||
exp = (UINT)floor(exp * 1.0);
|
||||
else if(dif > -4) //green
|
||||
exp = (UINT)floor(exp * 0.8);
|
||||
else if(dif > -9) //blue
|
||||
exp = (UINT)floor(exp * 0.5);
|
||||
if(dif <= -9) //white
|
||||
exp = (UINT)floor(exp * 0.2);
|
||||
|
||||
if(playerlevel < 10) // lower the rate for the first 10 levels
|
||||
exp = (UINT)floor(exp * 0.5);
|
||||
return exp;
|
||||
}
|
||||
|
||||
bool CWorldServer::SendSysMsg( CPlayer* thisclient, string message )
|
||||
{
|
||||
BEGINPACKET( pak, 0x7df );
|
||||
ADDBYTE ( pak, 0xf1 );
|
||||
ADDSTRING ( pak, message.c_str( ));
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
thisclient->client->SendPacket( &pak );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// teleport to specificated map and position
|
||||
bool CWorldServer::TeleportTo ( CPlayer* thisclient, int map, fPoint position )
|
||||
{
|
||||
if(map > 70)
|
||||
return true;
|
||||
MapList.Index[map]->TeleportPlayer( thisclient, position, false );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Learn Skill
|
||||
bool CWorldServer::LearnSkill( CPlayer* thisclient, UINT skill, bool cost )
|
||||
{
|
||||
/*
|
||||
0 - ya aprendido
|
||||
1 - aprendio
|
||||
2 - no job
|
||||
3 - falta otro skill
|
||||
4 - no status points (lvl)
|
||||
5 - no se puede subir de lvl el skill
|
||||
6 - numero incorrecto de skill
|
||||
7 - no tienes suficientes sp
|
||||
*/
|
||||
BEGINPACKET(pak1, 0 );
|
||||
BEGINPACKET(pak2, 0 );
|
||||
BEGINPACKET(pak3, 0 );
|
||||
//int b=1;
|
||||
CSkills* thisskill = SkillList.at( skill);
|
||||
char skilltype = thisskill->type;
|
||||
//int skillpoints = thisskill->sp;
|
||||
if(thisclient->Session->codedebug) //Debug code
|
||||
{
|
||||
char buffer[200];
|
||||
sprintf ( buffer, "Attempting to learn a new skill. id = %i skill type = %i Cost to learn = %i", thisskill->id,thisskill->type,thisskill->sp );
|
||||
SendPM(thisclient, buffer);
|
||||
}
|
||||
int newslot = thisclient->GetNewSkillSlot(skilltype);
|
||||
//CSkills* thisskill = 0;
|
||||
if (newslot != 0xffff)
|
||||
{
|
||||
switch(skilltype)
|
||||
{
|
||||
case 0:
|
||||
thisclient->bskills[newslot]= skill;
|
||||
if (cost)
|
||||
thisclient->CharInfo->SkillPoints -= thisskill->sp;
|
||||
RESETPACKET(pak1, 0x7b0);
|
||||
ADDBYTE(pak1, 1);
|
||||
ADDBYTE(pak1, newslot);
|
||||
ADDWORD(pak1, skill);
|
||||
ADDWORD(pak1, thisclient->CharInfo->SkillPoints);
|
||||
thisclient->client->SendPacket(&pak1);
|
||||
thisclient->SetStats();
|
||||
return true;
|
||||
case 1:
|
||||
thisclient->askill[newslot]= skill;
|
||||
if (cost)
|
||||
thisclient->CharInfo->SkillPoints-=thisskill->sp;
|
||||
RESETPACKET(pak2, 0x7b0);
|
||||
ADDBYTE(pak2, 1);
|
||||
ADDBYTE(pak2, 30+newslot);
|
||||
ADDWORD(pak2, skill);
|
||||
ADDWORD(pak2, thisclient->CharInfo->SkillPoints);
|
||||
thisclient->client->SendPacket(&pak2);
|
||||
thisclient->SetStats();
|
||||
return true;
|
||||
case 2:
|
||||
thisclient->pskill[newslot]= skill;
|
||||
if (cost)
|
||||
thisclient->CharInfo->SkillPoints-=thisskill->sp;
|
||||
RESETPACKET(pak3, 0x7b0);
|
||||
ADDBYTE(pak3, 1);
|
||||
ADDBYTE(pak3, 60+newslot);
|
||||
ADDWORD(pak3, skill);
|
||||
ADDWORD(pak3, thisclient->CharInfo->SkillPoints);
|
||||
thisclient->client->SendPacket(&pak3);
|
||||
thisclient->SetStats();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//add / remove a Fairy
|
||||
void CWorldServer::DoFairyStuff(CPlayer* targetclient, int action)
|
||||
{
|
||||
BEGINPACKET( pak, 0x7dc );
|
||||
ADDBYTE( pak, action );
|
||||
ADDWORD( pak, targetclient->clientid);
|
||||
SendToVisible( &pak, targetclient );
|
||||
}
|
||||
|
||||
void CWorldServer::DoFairyFree( int fairy )
|
||||
{
|
||||
GServer->FairyList.at(fairy)->assigned = false;
|
||||
GServer->FairyList.at(fairy)->ListIndex = 0;
|
||||
|
||||
if(GServer->Config.FairyTestMode == 0)
|
||||
GServer->FairyList.at(fairy)->WaitTime = GServer->Config.FairyWait * (rand()% GServer->GetFairyRange(1)+ GServer->GetFairyRange(0));
|
||||
|
||||
GServer->FairyList.at(fairy)->LastTime = clock();
|
||||
}
|
||||
|
||||
void CWorldServer::RefreshFairy( )
|
||||
{
|
||||
//fairy: The wait timer should be possibibly bigger when there is few people online.
|
||||
if (Config.FairyMode == 1 && ClientList.size() > 2) //if fairy mode on and a few people online
|
||||
{
|
||||
for (int i=0; i<Config.FairyMax; i++) // check all fairies
|
||||
{
|
||||
if ( FairyList.at(i)->LastTime <= ( clock() - (FairyList.at(i)->WaitTime * 60 * CLOCKS_PER_SEC) ) && !FairyList.at(i)->assigned ) // if fairy hour is now
|
||||
{
|
||||
if(ClientList.size() > 2) //stop solo players from getting fairied and screwing up their stats during testing.
|
||||
{
|
||||
int value = rand()%(int)(ClientList.size()-1) + 1; //choose random value in clientlist
|
||||
CPlayer* targetclient = (CPlayer*) ClientList.at(value)->player;
|
||||
if(targetclient == NULL)
|
||||
return;
|
||||
FairyList.at(i)->ListIndex = value;
|
||||
FairyList.at(i)->LastTime = clock();
|
||||
FairyList.at(i)->assigned = true;
|
||||
FairyList.at(i)->scroll = false; // Scroll value set to false. this is NOT a scroll buff fairy
|
||||
targetclient->Fairy = true;
|
||||
targetclient->FairyListIndex = i; // FairyList index number of our actual fairy
|
||||
DoFairyStuff(targetclient, 1); // spawn fairy to target
|
||||
targetclient->SetStats();
|
||||
}
|
||||
}
|
||||
if ( (FairyList.at(i)->LastTime + (Config.FairyStay*60*CLOCKS_PER_SEC)) <= clock() && FairyList.at(i)->assigned) // id time for our fairy to go away
|
||||
{
|
||||
CPlayer* oldclient = (CPlayer*) ClientList.at(FairyList.at(i)->ListIndex)->player;
|
||||
if(FairyList.at(i)->scroll == true) //this is a scroll fairy
|
||||
{
|
||||
GServer->Config.ScrollFairy --; //reduce count of scroll fairies by one
|
||||
if(GServer->Config.ScrollFairy < 0)GServer->Config.ScrollFairy = 0; //Make sure we don't go negative
|
||||
GServer->Config.FairyMax = (int)ceil((float)GServer->ClientList.size() / 50.0) + GServer->Config.ScrollFairy; //recalculate the maximum number of fairies
|
||||
FairyList.at(i)->scroll = false; //reset scroll value
|
||||
}
|
||||
DoFairyFree(i);
|
||||
oldclient->Fairy = false;
|
||||
oldclient->FairyListIndex = 0;
|
||||
DoFairyStuff(oldclient, 0); // unspawn fairy
|
||||
oldclient->SetStats();
|
||||
}
|
||||
}
|
||||
}
|
||||
// this close fairies after their time if GM de activate fairy mode when some fairies are assigned.
|
||||
if (Config.FairyMode == 0 && ClientList.size() > 1){ // if serevr mode off and someone online
|
||||
for (int i=0; i<Config.FairyMax; i++)
|
||||
{
|
||||
if ( (FairyList.at(i)->LastTime + (Config.FairyStay*60*CLOCKS_PER_SEC)) <= clock() && FairyList.at(i)->assigned)
|
||||
{
|
||||
CPlayer* oldclient = (CPlayer*) ClientList.at(FairyList.at(i)->ListIndex)->player;
|
||||
DoFairyFree(i);
|
||||
oldclient->Fairy = 0;
|
||||
oldclient->FairyListIndex = 0;
|
||||
DoFairyStuff(oldclient, 0);
|
||||
oldclient->SetStats();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
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"
|
||||
/*
|
||||
/ from signal.h
|
||||
* NOTE: SIGINT is produced when the user presses Ctrl-C.
|
||||
* SIGILL has not been tested.
|
||||
* SIGFPE doesn't seem to work?
|
||||
* SIGSEGV does not catch writing to a NULL pointer (that shuts down
|
||||
* your app; can you say "segmentation violation core dump"?).
|
||||
* SIGTERM comes from what kind of termination request exactly?
|
||||
* SIGBREAK is indeed produced by pressing Ctrl-Break.
|
||||
* SIGABRT is produced by calling abort.
|
||||
*/
|
||||
|
||||
void StartSignal( )
|
||||
{
|
||||
signal( SIGINT, HandleSignal );
|
||||
signal( SIGILL, HandleSignal );
|
||||
signal( SIGFPE, HandleSignal );
|
||||
signal( SIGSEGV, HandleSignal );
|
||||
signal( SIGTERM, HandleSignal );
|
||||
signal( SIGBREAK, HandleSignal );
|
||||
signal( SIGABRT, HandleSignal );
|
||||
}
|
||||
|
||||
void StopSignal( )
|
||||
{
|
||||
signal( SIGINT, 0 );
|
||||
signal( SIGILL, 0 );
|
||||
signal( SIGFPE, 0 );
|
||||
signal( SIGSEGV, 0 );
|
||||
signal( SIGTERM, 0 );
|
||||
signal( SIGBREAK, 0 );
|
||||
signal( SIGABRT, 0 );
|
||||
}
|
||||
|
||||
void HandleSignal( int num )
|
||||
{
|
||||
switch(num)
|
||||
{
|
||||
case SIGINT:/* Interactive attention */
|
||||
Log( MSG_ERROR, "Signal received: SIGINT" );
|
||||
#ifdef _WIN32
|
||||
Sleep(1000);
|
||||
#else
|
||||
usleep(1000);
|
||||
#endif
|
||||
delete GServer;
|
||||
raise(num);
|
||||
break;
|
||||
case SIGILL:/* Illegal instruction */
|
||||
Log( MSG_ERROR, "Signal received: SIGILL, Server will be closed, Trying to save..." );
|
||||
GServer->DisconnectAll();
|
||||
#ifdef _WIN32
|
||||
Sleep(1000);
|
||||
#else
|
||||
usleep(1000);
|
||||
#endif
|
||||
delete GServer;
|
||||
raise(num);
|
||||
break;
|
||||
case SIGFPE:/* Floating point error */
|
||||
Log( MSG_ERROR, "Signal received: SIGFPE, Server will be closed, Trying to save..." );
|
||||
GServer->DisconnectAll();
|
||||
delete GServer;
|
||||
raise(num);
|
||||
break;
|
||||
case SIGSEGV:/* Segmentation violation */
|
||||
Log( MSG_ERROR, "Signal received: SIGSEGV, Server will be closed, Trying to save..." );
|
||||
GServer->DisconnectAll();
|
||||
#ifdef _WIN32
|
||||
Sleep(1000);
|
||||
#else
|
||||
usleep(1000);
|
||||
#endif
|
||||
delete GServer;
|
||||
raise(num);
|
||||
break;
|
||||
|
||||
case SIGTERM:/* Termination request */
|
||||
Log( MSG_INFO, "Signal received: SIGTERM, Server will be closed" );
|
||||
pthread_create( &GServer->WorldThread[SHUTDOWN_THREAD], NULL, (void *(__cdecl *)(void *))ShutdownServer, (PVOID)0);
|
||||
break;
|
||||
#ifdef _WIN32
|
||||
case SIGBREAK:/* Control-break */
|
||||
Log( MSG_INFO, "Signal received: SIGBREAK, Server will be closed" );
|
||||
pthread_create( &GServer->WorldThread[SHUTDOWN_THREAD], NULL, (void *(__cdecl *)(void *))ShutdownServer, (PVOID)0);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
cout << "Unknown Signal Number: " << num << endl;
|
||||
raise(num);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Spawn a monster
|
||||
void CMonster::SpawnMonster( CPlayer* player, CMonster* thismon )
|
||||
{
|
||||
BEGINPACKET( pak, 0x792 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDFLOAT ( pak, Position->current.x*100 );
|
||||
ADDFLOAT ( pak, Position->current.y*100 );
|
||||
ADDFLOAT ( pak, Position->destiny.x*100 );
|
||||
ADDFLOAT ( pak, Position->destiny.y*100 );
|
||||
|
||||
if(IsDead( ))
|
||||
{
|
||||
ADDWORD ( pak, 0x0003 );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
}
|
||||
else if(IsOnBattle( ))
|
||||
{
|
||||
ADDWORD ( pak, 0x0002 );
|
||||
ADDWORD ( pak, Battle->target );
|
||||
}
|
||||
else if(IsMoving( ))
|
||||
{
|
||||
ADDWORD ( pak, 0x0001 );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
}
|
||||
|
||||
ADDBYTE ( pak, thisnpc->stance );
|
||||
ADDWORD ( pak, Stats->HP );
|
||||
|
||||
ADDDWORD ( pak, Stats->HP );
|
||||
if(thismon->owner != player->clientid)
|
||||
{
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
if(IsSummon( ) && map->allowpvp!=0) {ADDWORD( pak, 0x0064 );} //Hostil
|
||||
else if (IsSummon( ) && map->allowpvp==0) {ADDWORD ( pak, 0x0000 );}//Friendly
|
||||
else {ADDWORD( pak, 0x0064 );} //Hostil
|
||||
}
|
||||
else {ADDWORD( pak, 0x0000 );}//Friendly
|
||||
ADDDWORD( pak, GServer->BuildBuffs( this ) );
|
||||
ADDWORD ( pak, montype );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
if(IsSummon( ))
|
||||
{
|
||||
ADDWORD( pak, owner );
|
||||
ADDWORD( pak, 0x0000 ); //
|
||||
}
|
||||
player->client->SendPacket( &pak );
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
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 "worldmonster.h"
|
||||
|
||||
// Monster Contructor
|
||||
CMonster::CMonster( fPoint MonsterPosition, UINT MonsterType, UINT MonsterMap, UINT Owner, UINT spawnid )
|
||||
{
|
||||
montype = MonsterType;
|
||||
SpawnTime = clock( );
|
||||
lastSighCheck = clock( );
|
||||
MonsterDrop = NULL;
|
||||
Position->source = MonsterPosition;
|
||||
Position->current = MonsterPosition;
|
||||
Position->destiny = MonsterPosition;
|
||||
Position->Map = MonsterMap;
|
||||
Position->respawn = spawnid;
|
||||
Position->lastMoveTime = clock();
|
||||
// Magic status (buffs)
|
||||
for(unsigned int i=0;i<30;i++)
|
||||
{
|
||||
MagicStatus[i].Status = 0;
|
||||
MagicStatus[i].Buff = 0;
|
||||
MagicStatus[i].Value = 0;
|
||||
MagicStatus[i].Duration = 0;
|
||||
MagicStatus[i].BuffTime = 0;
|
||||
}
|
||||
|
||||
// Monster drop
|
||||
MonsterDrop = new MDrop;
|
||||
assert(MonsterDrop);
|
||||
MonsterDrop->drop = false;
|
||||
MonsterDrop->firsthit = 0;
|
||||
MonsterDrop->firstlevel = 0;
|
||||
MonsterDrop->mobdrop = NULL;
|
||||
MonsterDrop->mapdrop = NULL;
|
||||
clientid = GServer->GetNewClientID();
|
||||
owner = Owner;
|
||||
thisparty = NULL;
|
||||
if(owner == 0) CharType = TMONSTER;
|
||||
else CharType = TSUMMON;
|
||||
|
||||
}
|
||||
|
||||
// Monster destructor
|
||||
CMonster::~CMonster( )
|
||||
{
|
||||
if(MonsterDrop!=NULL);
|
||||
delete MonsterDrop;
|
||||
for(UINT i=0;i<PlayersDamage.size();i++)
|
||||
delete PlayersDamage.at(i);
|
||||
PlayersDamage.clear();
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#ifndef __ROSE_MONSTER__
|
||||
#define __ROSE_MONSTER__
|
||||
#include "character.h"
|
||||
#include "player.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
class CMonster : public CCharacter
|
||||
{
|
||||
public:
|
||||
CMonster( fPoint Position, UINT MonsterType, UINT MonsterMap, UINT Owner=0, UINT spawnid=0 );
|
||||
~CMonster( );
|
||||
|
||||
// times
|
||||
clock_t SpawnTime;
|
||||
clock_t lastSighCheck;
|
||||
clock_t lastDegenTime;
|
||||
clock_t lastAiUpdate;
|
||||
clock_t DeathDelayTimer;
|
||||
|
||||
MDrop* MonsterDrop;
|
||||
UINT activecycle;
|
||||
|
||||
unsigned int montype;
|
||||
unsigned int owner;
|
||||
unsigned int monAI;
|
||||
unsigned int AItimer;
|
||||
unsigned char hitcount; //for guardiantree scarab spawning
|
||||
unsigned int IsCasting;
|
||||
|
||||
|
||||
unsigned int NextWayPoint;
|
||||
|
||||
CParty* thisparty;
|
||||
CNPCData* thisnpc;
|
||||
|
||||
vector<MonsterDamage*> PlayersDamage;
|
||||
|
||||
// Monster Functions
|
||||
void DoAi(int ainumber,char type);
|
||||
//bool Guardiantree(CMonster* monster, CMap* map);
|
||||
bool Scarab(CMonster* monster, CMap* map);
|
||||
bool SummonUpdate(CMonster* monster, CMap* map, UINT j);
|
||||
bool SetStats( );
|
||||
bool UnspawnMonster( );
|
||||
bool IsGhost( );
|
||||
bool IsGhostSeed( );
|
||||
bool CanMove( );
|
||||
void OnEnemyDie( void* enemy );
|
||||
bool ReadyToMove( );
|
||||
void MoveTo( fPoint nPos, bool randcircle=true );
|
||||
void Move( );
|
||||
CPlayer* GetOwner( );
|
||||
void SpawnMonster( CPlayer* player, CMonster* thismon );
|
||||
bool PlayerInRange( );
|
||||
CPlayer* GetNearPlayer( UINT mdist = 20 );
|
||||
void AddDamage( CCharacter* enemy, long int hitpower);
|
||||
CDrop* GetDrop( );
|
||||
|
||||
//
|
||||
bool IsMonster( );
|
||||
bool UpdateValues( );
|
||||
|
||||
// Stats
|
||||
unsigned int GetAttackPower( );
|
||||
unsigned int GetDefense( );
|
||||
unsigned int GetXPRate( );
|
||||
unsigned int GetItemDropRate( );
|
||||
unsigned int GetItemDropCountRate( );
|
||||
unsigned int GetZulyDropRate( );
|
||||
unsigned int GetGreyDrops( );
|
||||
unsigned int GetDodge( );
|
||||
unsigned int GetAccury( );
|
||||
unsigned int GetMagicDefense( );
|
||||
unsigned int GetMagicAttack( );
|
||||
unsigned int GetCritical( );
|
||||
unsigned int GetAttackSpeed( );
|
||||
unsigned int GetMoveSpeed( );
|
||||
unsigned int GetMaxHP( );
|
||||
float GetAttackDistance( );
|
||||
|
||||
// Events
|
||||
bool OnBeAttacked( CCharacter* Enemy );
|
||||
bool OnEnemyOnSight( CCharacter* Enemy );
|
||||
bool OnDie( );
|
||||
bool OnEnemyDie( CCharacter* Enemy );
|
||||
bool OnSpawn( bool );
|
||||
bool OnAlmostDie( );
|
||||
bool OnFar( );
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,458 @@
|
||||
/*
|
||||
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
|
||||
+ parts copied from/based on work from ExJem
|
||||
*/
|
||||
//#define debugging
|
||||
#ifndef __ROSE_SERVERS__
|
||||
#define __ROSE_SERVERS__
|
||||
#include "datatypes.h"
|
||||
#include "sockets.h"
|
||||
#include "worldmonster.h"
|
||||
#include "quest/CRoseArray.hpp"
|
||||
#include "quest/CRoseFile.hpp"
|
||||
#include "quest/CStrStb.hpp"
|
||||
#include "quest/CIntStb.hpp"
|
||||
#include "quest/QuestTrigger.h"
|
||||
#include "aip/Aip.h"
|
||||
#include "worldmap.h"
|
||||
#include "player.h"
|
||||
#include "character.h"
|
||||
#include "party.h"
|
||||
#include "stbhandler.h"
|
||||
#include "stlhandler.h"
|
||||
#include "strhashing.h"
|
||||
|
||||
#define MAXVISUALRANGE 90
|
||||
#define MINVISUALRANGE 80
|
||||
#define ClearItem(i) { i.appraised=false; i.count=0; i.durability=0; i.itemnum=0; i.itemtype=0; i.lifespan=0; i.refine=0; i.socketed=false; i.stats=0; i.gem=0; }
|
||||
|
||||
#define WORLD_THREAD 0
|
||||
#define VISUALITY_THREAD 1
|
||||
#define SHUTDOWN_THREAD 2
|
||||
|
||||
#define MAX_MAPS 115
|
||||
|
||||
// Main loginserver server class
|
||||
class CWorldServer : public CServerSocket
|
||||
{
|
||||
public:
|
||||
//------------------ MAIN (worldserver.cpp)
|
||||
CWorldServer ( string );
|
||||
CClientSocket* CreateClientSocket( );
|
||||
~CWorldServer( );
|
||||
bool OnReceivePacket( CClientSocket* thisclient, CPacket* P );
|
||||
void ReceivedISCPacket( CPacket* pak ); // Raven0123
|
||||
void SendISCServerInfo( );
|
||||
void OnClientDisconnect( CClientSocket* thisclient );
|
||||
void DeleteClientSocket( CClientSocket* thisclient );
|
||||
bool OnServerReady( );
|
||||
void SpawnMonsters( );
|
||||
bool Ping( );
|
||||
void LoadConfigurations( const char* );
|
||||
void LoadCommandLevels( void );
|
||||
void ServerLoop( );
|
||||
void LoadEncryption( );
|
||||
bool pakGiveItem( CPlayer* thisclient, unsigned char itemtype, unsigned short itemid, unsigned short count );
|
||||
bool pakGiveExp( CPlayer* thisclient, unsigned exp );
|
||||
bool pakGiveMoney( CPlayer* thisclient, unsigned amount );
|
||||
bool pakTeleport( CPlayer* thisclient, unsigned short map, float x, float y );
|
||||
bool pakLearnSkill( CPlayer* thisclient, unsigned short skillid );
|
||||
void DePopulateZone( CPlayer* thisclient, int zonex, int zoney );
|
||||
bool pakQuestTrigger( CPlayer* thisclient, CPacket* pak );
|
||||
|
||||
//------------------ WORLD PROCESS (worldprocess.cpp)
|
||||
bool GiveExp( CMonster* thismon );
|
||||
|
||||
//------------------ BUFFS (buff.cpp)
|
||||
CBValue GetBuffValue( CSkills* thisskill, CCharacter* character, UINT Evalue, UINT i, UINT up, UINT down, UINT CurrentValue, bool Buff=true, bool Status=false );
|
||||
//CBValue GetBuffValue( CSkills* thisskill, CCharacter* character, UINT Evalue, UINT i, UINT up, UINT down, UINT CurrentValue, bool Buff=true );
|
||||
bool CheckABuffs( CSkills* thisskill, CCharacter* character, int Evalue ,int i);
|
||||
//bool CheckDBuffs( CSkills* thisskill, CCharacter* character, int Evalue ,int i);
|
||||
bool AddBuffs( CSkills* thisskill, CCharacter* character, int Evalue , bool flag);
|
||||
//bool AddDeBuffs( CSkills* thisskill, CCharacter* character, int Evalue );
|
||||
bool AddBuffs( CSkills* thisskill, CCharacter* character, int Evalue );
|
||||
bool InstantBuff( CSkills* thisskill, CCharacter* character, int Evalue, int i );
|
||||
|
||||
|
||||
//------------------ SERVER EXTRAS (extrafunctions.cpp)
|
||||
bool IsValidItem(UINT type, UINT id );
|
||||
unsigned BuildItemShow(CItem thisitem);
|
||||
UINT GetUIntValue( const char* s , void* var=NULL );
|
||||
int GetIntValue( const char* s , void* var=NULL );
|
||||
char* GetStrValue( const char* s , void* var=NULL );
|
||||
bool SendSysMsg( CPlayer* thisclient, string message );
|
||||
UINT RandNumber( UINT init, UINT range, UINT seed=0 );
|
||||
UINT GetColorExp( UINT playerlevel,UINT moblevel, UINT exp );
|
||||
bool CheckInventorySlot( CPlayer* thisclient, int slot );
|
||||
bool pakGMClass( CPlayer* thisclient, char* classid );
|
||||
bool pakGMKillInRange( CPlayer* thisclient, int range );
|
||||
bool pakGMHide( CPlayer* thisclient, int mode );
|
||||
void SendToVisible( CPacket* pak, CPlayer* thisclient, bool thisclients=true );
|
||||
void SendToVisible( CPacket* pak, CPlayer* thisclient, CPlayer* xotherclient );
|
||||
|
||||
void SendToVisible( CPacket* pak, class CCharacter* character, CDrop* thisdrop=NULL );
|
||||
|
||||
bool IsMonInCircle( CPlayer* thisclient, CMonster* thismon, float radius );
|
||||
bool IsPlayerInCircle( CPlayer* thisclient, CPlayer* otherclient, float radius );
|
||||
CUseInfo* GetUseItemInfo( CPlayer* thisclient, UINT slot );
|
||||
bool IsVisible( CPlayer* thisclient, CPlayer* otherclient );
|
||||
bool IsMonInCircle( fPoint center, fPoint position, float radius );
|
||||
bool IsVisible( CPlayer* thisclient, CMonster* thismon );
|
||||
bool IsVisible( CPlayer* thisclient, CDrop* thisdrop );
|
||||
CItem GetItemByHeadAndData( unsigned head, unsigned data );
|
||||
bool IsVisible( CPlayer* thisclient, CNPC* thisnpc );
|
||||
void SendToVisible( CPacket* pak, CMonster* thismon, CDrop* thisdrop=NULL );
|
||||
void SendToVisible( CPacket* pak, CDrop* thisdrop );
|
||||
CPlayer* GetClientByUserName( char *username );
|
||||
UINT BuildBuffs( CCharacter* player );
|
||||
UINT BuildUpBuffs( CCharacter* player );
|
||||
UINT BuildDeBuffs( CCharacter* player );
|
||||
void CheckForLevelUp ( CPlayer* thisclient );
|
||||
bool isSkillTargetFriendly( CSkills* thisskill );
|
||||
CRespawnPoint* GetRespawnByID( UINT id );
|
||||
CPlayer* GetClientByCID( UINT id, UINT map=0 );
|
||||
CPlayer* GetClientByCharName( char* name );
|
||||
CPlayer* GetClientByID( UINT id, UINT map=0 );
|
||||
CTeleGate* GetTeleGateByID( UINT id );
|
||||
UINT BuildItemHead( CItem thisitem );
|
||||
UINT BuildItemData( CItem thisitem );
|
||||
CMonster* GetMonsterByID( UINT id, UINT map );
|
||||
CSpawnArea* GetSpawnArea( UINT id, UINT map=0 );
|
||||
#ifdef USEIFO
|
||||
CMobGroup* GetMobGroup( UINT id, UINT map=0 );
|
||||
#endif
|
||||
bool DeleteSpawn( CSpawnArea* spawn );
|
||||
CNPCData* GetNPCDataByID( UINT id );
|
||||
void SendToMap( CPacket* pak, int mapid );
|
||||
unsigned BuildItemRefine(CItem thisitem );
|
||||
CMDrops* GetDropData( UINT id );
|
||||
CRespawnPoint* GetRespawnByMap( int id );
|
||||
CSkills* GetSkillByID( UINT id );
|
||||
CStatus* GetStatusByID( UINT id );
|
||||
void ClearClientID( UINT id );
|
||||
CDrop* GetDropByID( UINT id, UINT map=0 );
|
||||
CNPC* GetNPCByID( UINT id, UINT map=0 );
|
||||
CDrop* GetNewDrop( CMonster* thismon );
|
||||
CDrop* GetPYDrop( CMonster* thismon, UINT droptype );
|
||||
void SendToAll( CPacket* pak );
|
||||
UINT GetNewClientID( );
|
||||
void DisconnectAll();
|
||||
CPlayer* GetClientByUserID( UINT userid );
|
||||
UINT GetMatIDfromRecipe (UINT CraftLevel, UINT matType);
|
||||
UINT GetFairyRange( UINT part );
|
||||
void RefreshFairy( );
|
||||
inline dword GetStorageFee(dword iBasePrice, dword iPriceRate, dword uiDupCnt) {return ((((iBasePrice*5) / 0x3E8)+1)*(iPriceRate+1)) * uiDupCnt;};
|
||||
|
||||
|
||||
//------------------ Fairies ---------------------
|
||||
void DoFairyStuff( CPlayer* targetclient, int action );
|
||||
void DoFairyFree( int fairy );
|
||||
|
||||
//------------------ MATH (extrafunctions.cpp)
|
||||
fPoint RandInCircle(fPoint center, float radius);
|
||||
fPoint RandInPoly(fPoint p[], int pcount);
|
||||
fPoint RandInTriangle(fPoint p1, fPoint p2, fPoint p3);
|
||||
float AreaOfTriangle(fPoint p1, fPoint p2, fPoint p3);
|
||||
float distance( fPoint pos1, fPoint pos2 );
|
||||
|
||||
//------------------ PACKETS (worldpackets.cpp)
|
||||
bool pakItemMall( CPlayer* thisclient, CPacket* P );
|
||||
bool pakWeight( CPlayer* thisclient, CPacket* P );
|
||||
bool pakPrintscreen( CPlayer* thisclient, CPacket* P );
|
||||
bool pakModifiedItemDone( CPlayer* thisclient, CPacket* P );
|
||||
bool pakSpawnDrop( CPlayer* thisclient, CDrop* thisdrop );
|
||||
bool pakChangeStorage( CPlayer* thisclient, CPacket* P );
|
||||
bool pakModifiedItem( CPlayer* thisclient, CPacket* P );
|
||||
bool pakPartyManager( CPlayer* thisclient, CPacket* P );
|
||||
bool pakLevelUpSkill( CPlayer *thisclient, CPacket* P );
|
||||
bool pakChangeStance( CPlayer* thisclient, CPacket* P );
|
||||
bool pakCharDSClient( CPlayer* thisclient, CPacket* P );
|
||||
bool pakChangeEquip( CPlayer* thisclient, CPacket* P );
|
||||
bool pakStartAttack( CPlayer* thisclient, CPacket* P );
|
||||
bool pakSpawnNPC( CPlayer* thisclient, CNPC* thisnpc );
|
||||
bool pakRideRequest( CPlayer* thisclient, CPacket* P );
|
||||
bool pakPartyActions( CPlayer* thisclient, CPacket* P );
|
||||
bool pakPartyOption( CPlayer* thisclient, CPacket* P );
|
||||
bool pakTradeAction( CPlayer* thisclient, CPacket* P );
|
||||
bool pakChangeCart( CPlayer* thisclient, CPacket* P );
|
||||
bool pakSkillSelf ( CPlayer* thisclient, CPacket* P );
|
||||
bool pakDoIdentify( CPlayer *thisclient, CPacket *P );
|
||||
bool pakNormalChat( CPlayer* thisclient, CPacket* P );
|
||||
bool pakCharSelect( CPlayer* thisclient, CPacket* P );
|
||||
bool pakStartSkill( CPlayer* thisclient, CPacket* P );
|
||||
bool pakRepairHammer( CPlayer* thisclient, CPacket* P );
|
||||
bool pakRepairItem( CPlayer* thisclient, CPacket* P );
|
||||
bool pakMoveSkill( CPlayer* thisclient, CPacket* P );
|
||||
bool pakCloseShop( CPlayer* thisclient, CPacket* P );
|
||||
bool pakStoreZuly( CPlayer* thisclient, CPacket* P );
|
||||
bool pakPartyChat( CPlayer* thisclient, CPacket* P );
|
||||
bool pakGameGuard( CPlayer* thisclient, CPacket* P );
|
||||
bool pakOpenShop( CPlayer* thisclient, CPacket* P );
|
||||
bool pakShowShop( CPlayer* thisclient, CPacket* P );
|
||||
bool pakSellShop( CPlayer* thisclient, CPacket* P );
|
||||
bool pakMoveChar( CPlayer* thisclient, CPacket* P );
|
||||
bool pakStopChar( CPlayer* thisclient, CPacket* P );
|
||||
bool pakPickDrop( CPlayer* thisclient, CPacket* P );
|
||||
bool pakAddStats( CPlayer* thisclient, CPacket* P );
|
||||
bool pakShowHeal( CPlayer* thisclient, CPacket* P );
|
||||
bool pakSkillAOE( CPlayer* thisclient, CPacket* P );
|
||||
bool pakidentify( CPlayer* thisclient, CPacket* P );
|
||||
bool pakUserDied( CPlayer* thisclient, CPacket* P );
|
||||
bool pakSaveTown( CPlayer* thisclient, CPacket* P );
|
||||
bool pakTradeAdd( CPlayer* thisclient, CPacket* P );
|
||||
bool pakUseItem ( CPlayer* thisclient, CPacket* P );
|
||||
bool pakWhisper( CPlayer* thisclient, CPacket* P );
|
||||
bool pakEquipABC(CPlayer* thisclient, CPacket *P );
|
||||
bool pakStorage( CPlayer* thisclient, CPacket* P );
|
||||
bool pakBuyShop( CPlayer* thisclient, CPacket* P );
|
||||
bool pakDoEmote( CPlayer* thisclient, CPacket* P );
|
||||
bool pakNPCBuy( CPlayer* thisclient, CPacket* P );
|
||||
bool pakDoDrop( CPlayer* thisclient, CPacket* P );
|
||||
bool pakShout( CPlayer* thisclient, CPacket* P );
|
||||
bool pakCraft( CPlayer* thisclient, CPacket* P );
|
||||
bool pakRespawn( CPlayer* thisclient, CPacket* P );
|
||||
bool pakDoID( CPlayer* thisclient, CPacket* P );
|
||||
bool pakGate( CPlayer* thisclient, CPacket* P );
|
||||
bool pakExit( CPlayer* thisclient, CPacket* P );
|
||||
bool pakPing( CPlayer* thisclient, CPacket* P );
|
||||
bool SendLevelUPtoChar(CPlayer* thisclient );
|
||||
void pakPlayer( CPlayer *thisclient );
|
||||
void pakInventory( CPlayer *thisclient );
|
||||
void pakQuestData( CPlayer *thisclient );
|
||||
void pakClearUser( CPlayer* thisclient );
|
||||
bool LearnSkill( CPlayer* thisclient, UINT skill, bool cost = true );
|
||||
|
||||
//-------------------------- Clan functions
|
||||
bool pakClanManager ( CPlayer* thisclient, CPacket* P );
|
||||
bool pakCreateClan ( CPlayer* thisclient, CPacket* P );
|
||||
|
||||
//------------------ GM COMMANDS (gmcmds.cpp)
|
||||
bool pakGMMoveTo( CPlayer* thisclient, fPoint position );
|
||||
bool pakGMTeleAllHere( CPlayer* thisclient );
|
||||
bool GMShowTargetInfo( CPlayer* thisclient );
|
||||
bool pakGMServerInfo( CPlayer* thisclient );
|
||||
bool pakGMHeal( CPlayer* thisclient, int hpamount, int mpamount );
|
||||
bool pakGMStat( CPlayer* thisclient, const char* statname, int statvalue );
|
||||
bool pakGMItemtoplayer(CPlayer* thisclient, char* name , UINT itemid , UINT itemtype , UINT itemamount , UINT itemrefine , UINT itemls, UINT itemstats , UINT itemsocket) ;
|
||||
bool pakGMItem( CPlayer* thisclient, UINT itemid , UINT itemtype , UINT itemamount , UINT itemrefine , UINT itemls, UINT itemstats , UINT itemsocket );
|
||||
bool pakGMTeleOtherPlayer( CPlayer *thisclient, char* name, int map, float x, float y );
|
||||
bool pakGMZuly( CPlayer* thisclient, int mode, int amount, char* charname );
|
||||
bool pakGMTele( CPlayer* thisclient, int map, float x, float y );
|
||||
bool pakGMMon( CPlayer* thisclient, int montype, int moncount, int monAI );
|
||||
bool pakGMZulygive(CPlayer* thisclient, char* name, int zuly);
|
||||
bool pakGMFairyto(CPlayer* thisclient, char* name, int mode);
|
||||
bool pakGMManageFairy(CPlayer* thisclient, int mode);
|
||||
bool pakGMChangeFairyWait(CPlayer* thisclient, int newvalue);
|
||||
bool pakGMChangeFairyStay(CPlayer* thisclient, int newvalue);
|
||||
bool pakGMChangeFairyTestMode(CPlayer* thisclient, int mode);
|
||||
bool pakGMTelePlayerHere( CPlayer* thisclient, char* name );
|
||||
bool pakGMLevel( CPlayer* thisclient, int level , char* name);
|
||||
bool pakGMTeleToPlayer( CPlayer* thisclient, char* name );
|
||||
bool pakGMDoEmote( CPlayer* thisclient, int emotionid );
|
||||
bool pakGMCommand( CPlayer* thisclient, CPacket* P );
|
||||
bool ReloadMobSpawn( CPlayer* thisclient, int id );
|
||||
bool pakGMKick( CPlayer* thisclient, char* name );
|
||||
bool pakGMAnn( CPlayer* thisclient, CPacket *P );
|
||||
bool pakGMBan( CPlayer* thisclient, char* name );
|
||||
bool pakGMInfo(CPlayer* thisclient, char* name);
|
||||
bool pakGMNpc(CPlayer* thisclient, int npcid, int dialogid);
|
||||
bool pakGMGotomap( CPlayer* thisclient, int map );
|
||||
bool pakGMEffect( CPlayer* thisclient, int effect);
|
||||
bool pakGMMute( CPlayer* thisclient, char* name, int time);
|
||||
bool pakGMPartylvl( CPlayer* partyclient, int level );
|
||||
bool pakGMChangePlayerDmg(CPlayer* thisclient, int rate);
|
||||
bool pakGMChangeMonsterDmg(CPlayer* thisclient, int rate);
|
||||
bool pakGMChangeMSpeedModif(CPlayer* thisclient, int modif);
|
||||
bool pakGMChangeCfmode(CPlayer* thisclient, int mode);
|
||||
bool pakGMUnionPoints(CPlayer* thisclient, char* name, int nb_points);
|
||||
|
||||
//------------------ CHAR SERVER COMMANDS (worldpackets.cpp)
|
||||
bool TeleportTo ( CPlayer* thisclient, int map, fPoint position );
|
||||
bool pakCSCharSelect( CPlayer* thisclient, CPacket* P );
|
||||
bool pakCSReady( CPlayer* thisclient, CPacket* P );
|
||||
|
||||
//----------------- Server StartUp Functions (startup.cpp)
|
||||
bool LoadSTBData( );
|
||||
bool LoadZoneData( );
|
||||
bool LoadConsItem( );
|
||||
bool LoadSellData( );
|
||||
bool LoadProductItem( );
|
||||
bool LoadPatItem( );
|
||||
bool LoadNaturalItem( );
|
||||
bool LoadJemItem( );
|
||||
bool LoadEquip( );
|
||||
bool LoadItemStats( );
|
||||
bool LoadTeleGateData( );
|
||||
bool LoadMonsterSpawn( );
|
||||
bool LoadMobGroups( );
|
||||
bool LoadRespawnData( );
|
||||
bool LoadWayPoints( );
|
||||
bool LoadBreak( );
|
||||
bool LoadGrades(); //LMA: loading refines (grades) bonuses.
|
||||
|
||||
bool LoadNewDrops( );
|
||||
bool LoadPYDropsData( );
|
||||
bool LoadSkillBookDropsData( );
|
||||
bool LoadConfig( );
|
||||
bool LoadLTB( );
|
||||
|
||||
bool LoadStatusData( );
|
||||
bool LoadSkillData( );
|
||||
bool LoadMonsters( );
|
||||
bool LoadNPCData( );
|
||||
bool LoadUpgrade( );
|
||||
bool LoadNPCs( );
|
||||
bool LoadSTB( );
|
||||
bool LoadAIP( );
|
||||
bool CleanConnectedList( );
|
||||
|
||||
void LoadZones();
|
||||
|
||||
// QSD Fuctions
|
||||
void ReadQSD(strings path, dword index);
|
||||
void LoadQuestData();
|
||||
inline int round(double x) {return int(x > 0.0 ? x + 0.5 : x - 0.5);};
|
||||
// AIP Functions
|
||||
void ReadAIP(strings path, dword index);
|
||||
void LoadAipData();
|
||||
|
||||
// Server Functions
|
||||
bool SendPM( CPlayer* thisclient, const char* Format, ... );
|
||||
bool SendGlobalMSG( CPlayer* thisclient, const char* Format, ... );
|
||||
//bool CWorldServer::NPCShout( CMonster* thismon, char msg[200], char npc[50] );
|
||||
bool NPCShout( int thismap, const char msg[200], const char npc[50] );
|
||||
bool NPCMessage( CMonster* thismon, const char msg[200], const char npc[50] );
|
||||
bool NPCWhisper( CPlayer* thisclient, CMonster* thismon, const char msg[200], const char npc[50] );
|
||||
bool NPCAnnounce( const char msg[200], const char npc[50] );
|
||||
UINT GetMaxPartyExp( UINT partylevel );
|
||||
bool DoSkillScript( CCharacter* character, CSkills* thisskill );
|
||||
|
||||
bool AddParty( CParty* );
|
||||
bool RemoveParty( CParty* );
|
||||
|
||||
UINT ClientIDList[0x10000]; // Clients List
|
||||
UINT ClientIDIndex; // Index pointer for Client ID List
|
||||
SOCKET csock; // Socket for accessing the char server
|
||||
char* cct; // Encryption table for char server
|
||||
|
||||
vector<CTeleGate*> TeleGateList; // Telegates List
|
||||
vector<CStatus*> StatusList; // Status List
|
||||
vector<CSkills*> SkillList; // Skills List
|
||||
vector<CMDrops*> MDropList; // Drops List
|
||||
|
||||
map<int, char*> NpcNameList; //LMA: npc name map.
|
||||
map<UINT, char*> STLNameList; //LMA: Name for all things taken from STL.
|
||||
map<UINT, char*> STLCommentList; //LMA: Comment for all things taken from STL.
|
||||
//IfoObject WarpGate; //LMA: Warp Gate for Union.
|
||||
|
||||
CGrade GradeList[10];
|
||||
int maxGrades; //Nb grades (refines)
|
||||
|
||||
vector<CMDrops*> SkillbookList; // Skillbook drop list
|
||||
|
||||
vector<CNPCData*> NPCData; // NPC/Mobs List
|
||||
//vector<CAIPData*> AIPData; // AIP filename list
|
||||
vector<CParty*> PartyList; // Party List
|
||||
vector<CFairy*> FairyList; // Fairy List
|
||||
std::map< unsigned, CNPCTrigger* > NPCActionList;
|
||||
|
||||
struct UpgradeList
|
||||
{
|
||||
UINT BindRune_Level;
|
||||
UINT Talisman_Level;
|
||||
UINT BindRune_Success;
|
||||
UINT Talisman_Success;
|
||||
};
|
||||
|
||||
|
||||
CItemStas StatsList[500];
|
||||
UpgradeList upgrade[10];
|
||||
CEquipList EquipList[10];
|
||||
CJemList JemList;
|
||||
CNaturalList NaturalList;
|
||||
CPatList PatList;
|
||||
CProductList ProductList;
|
||||
CBreakList BreakList;
|
||||
CSellList SellList;
|
||||
CUseList UseList;
|
||||
CMapList MapList;
|
||||
|
||||
CSTBData STB_NPC; // NPC data
|
||||
CSTBData STB_SKILL; // Skill data
|
||||
CSTBData STB_STATUS; // Status Data
|
||||
CSTBData STB_QUEST; // Quest data
|
||||
CSTBData STB_ITEM[14]; // Item data
|
||||
CSTBData STB_DROP; // Drop list
|
||||
CSTBData STB_ZONE; // Zone data
|
||||
CSTBData STB_PRODUCT; // Crafting data
|
||||
CSTBData STB_BREAK; // Break data
|
||||
CSTBData STB_SELL; // npc sell index
|
||||
CSTBData ListGrade; // LMA: used to store the refine bonuses (% and +).
|
||||
CSTBData ListUpGrade; // Bindrune and Tlisman upgrade data
|
||||
|
||||
vector<CQuestTrigger*> TriggerList;
|
||||
fpQuestCond qstCondFunc[31];
|
||||
fpQuestRewd qstRewdFunc[51];
|
||||
vector<CAip*> AipList;
|
||||
//vector<CAIPScript*> AIPList;
|
||||
UINT maxSpawnId;
|
||||
fpAiCond aiCondFunc[35];
|
||||
fpAiAct aiActFunc[40];
|
||||
int ObjVar[2000][20]; //NPC variables used in AI
|
||||
UINT ThisSession; //session ID from database
|
||||
UINT ServerStartTime; //time the server was started
|
||||
UINT ServerRunTime; //Hours the server has been running
|
||||
CLTBstring Ltbstring[200];
|
||||
TowerDef WPList[100][100]; //Way point list for tower def. [mapid][wp id]
|
||||
//int AIindex[500][6][20][1];
|
||||
|
||||
clock_t lastServerStep; // Last Update
|
||||
clock_t LastUpdateTime; // Store the last update World Time
|
||||
bool ServerOnline;
|
||||
string filename;
|
||||
|
||||
|
||||
pthread_t WorldThread[3];
|
||||
pthread_t MapThread[200];
|
||||
pthread_attr_t at;
|
||||
pthread_mutex_t MapMutex,SQLMutex,PlayerMutex;
|
||||
|
||||
CDatabase* DB;
|
||||
|
||||
//Test thingies
|
||||
unsigned ATTK_SPEED_MOD;
|
||||
unsigned HIT_DELAY_MOD;
|
||||
unsigned MOVE_SPEED_MOD;
|
||||
|
||||
};
|
||||
extern class CWorldServer* GServer;
|
||||
|
||||
PVOID VisibilityProcess( PVOID TS );
|
||||
PVOID MapProcess( PVOID TS );
|
||||
PVOID WorldProcess( PVOID TS );
|
||||
void ShutdownServer( PVOID TS );
|
||||
|
||||
void StartSignal( );
|
||||
void StopSignal( );
|
||||
void HandleSignal( int num );
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
// Give Exp the KTRose way
|
||||
bool CWorldServer::GiveExp( CMonster* thismon )
|
||||
{
|
||||
//Log(MSG_DEBUG,"Awarding EXP");
|
||||
int tmpMult = 1;
|
||||
if( thismon->owner != 0) // Summon
|
||||
{
|
||||
MapList.Index[thismon->Position->Map]->DeleteMonster( thismon );
|
||||
return true;
|
||||
}
|
||||
// Give Experience Drops and Quest Items
|
||||
vector<CPartyExp*> PartyExp;
|
||||
vector<CParty*> PartyList;
|
||||
for(UINT i=0;i<thismon->PlayersDamage.size();i++) // this is the list of players who did damage to the monster who just died
|
||||
{
|
||||
MonsterDamage* thisplayer = thismon->PlayersDamage.at(i);
|
||||
CPlayer* thisclient = GetClientByCID( thisplayer->charid, thismon->Position->Map );
|
||||
if( thisplayer->damage > 0 && thisclient != NULL ) //player did some damage. just making sure
|
||||
{
|
||||
//Log(MSG_DEBUG,"Player did %i damage max = %i",thisplayer->damage,thismon->Stats->MaxHP);
|
||||
float MyPercent = (float)thisplayer->damage / thismon->Stats->MaxHP; //Figure out how much of the actual kill I am responsible for
|
||||
if (MyPercent > 1.5)
|
||||
MyPercent = 1.5; //set overkill ceiling
|
||||
if( thisclient->Battle->target == thismon->clientid )
|
||||
{
|
||||
ClearBattle( thisclient->Battle )
|
||||
thisclient->Position->destiny = thisclient->Position->current;
|
||||
//Log(MSG_DEBUG,"(GiveExp) Destiny set to current position X: %f Y: %f.",thisclient->Position->current.x,thisclient->Position->current.y);
|
||||
}
|
||||
if( thismon->MonsterDrop->firsthit == thisclient->CharInfo->charid )
|
||||
{
|
||||
for( int q=0;q<10;q++)
|
||||
{
|
||||
// Give Quest Item
|
||||
if( thisclient->quest.quests[q].QuestID != 0 )
|
||||
{
|
||||
Log(MSG_DEBUG,"Giving quest reward item for quest %i",thisclient->quest.quests[q].QuestID);
|
||||
BEGINPACKET( pak, 0x731 )
|
||||
ADDWORD ( pak, thismon->montype );
|
||||
thisclient->client->SendPacket( &pak );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//assign my own exp for monsters that I personally damaged
|
||||
unsigned int exp = (unsigned int)floor(thismon->thisnpc->exp * MyPercent);
|
||||
//unsigned int exp = (unsigned int)ceil((double)((thismon->thisnpc->exp * thisplayer->damage) / (thismon->thisnpc->hp*thismon->thisnpc->level)));
|
||||
exp = exp * Config.EXP_RATE * tmpMult; //calculate base exp for this client. No medals or stuff accounted for yet
|
||||
//Log(MSG_DEBUG,"MaxXP: %i config rate: %i percent: %f Total XP: %i", thismon->thisnpc->exp, Config.EXP_RATE, MyPercent, exp);
|
||||
thisclient->CharInfo->Pending_Exp += (exp * thisclient->Stats->xprate); //store exp into thisclient's pending_exp using personal xprate adjustments
|
||||
|
||||
if( thisclient->Party->party != NULL ) //player is in a party so scan the party members
|
||||
{
|
||||
//Log(MSG_DEBUG,"Player is in a party");
|
||||
CParty* party = thisclient->Party->party; //assign a party
|
||||
if(party == NULL)
|
||||
return true;
|
||||
//Log(MSG_DEBUG,"party found. Counted = %i", party->counted);
|
||||
for(UINT p=0;p<party->Members.size();p++) //loop through all the members in the party
|
||||
{
|
||||
//Log(MSG_DEBUG,"member %i being parsed", p);
|
||||
if(!party->counted)
|
||||
{
|
||||
//Log(MSG_DEBUG,"party added to list. level = %i", party->PartyLevel);
|
||||
party->counted = true; //tag the party so we don't add it to the list twice
|
||||
PartyList.push_back( party ); //we will need this list later to convert pending exp to real exp
|
||||
}
|
||||
CPlayer* thismember = party->Members.at(p); //find a party member
|
||||
if(thismember == NULL)
|
||||
return true;
|
||||
float RawTmpExp = (exp * (party->PartyLevel + 25) / 50);
|
||||
unsigned int tempxp =(unsigned int)(floor)(RawTmpExp);
|
||||
if (tempxp < 1)tempxp = 1;
|
||||
//Log(MSG_DEBUG,"member %i pending exp = %i tempxp: %i", p, thismember->CharInfo->Pending_Exp, tempxp);
|
||||
thismember->CharInfo->Pending_Exp += tempxp; // add a percentage of thisclient's non-adjusted exp to all party members including himself
|
||||
//Log(MSG_DEBUG,"member %i pending exp (after tempxp) = %i", p, thismember->CharInfo->Pending_Exp);
|
||||
}
|
||||
if(party->PartyLevel < 50) //only give party exp if party level is under 50
|
||||
{
|
||||
party->Pending_Exp += exp; //add thisclient's non-adjusted xp to the pending exp of the party
|
||||
}
|
||||
}
|
||||
else //not in a party so deal with all the exp now
|
||||
{
|
||||
//SendPM(thisclient, "You receive %i EXP",thisclient->CharInfo->Pending_Exp);
|
||||
thisclient->CharInfo->Exp += thisclient->CharInfo->Pending_Exp;
|
||||
thisclient->CharInfo->Pending_Exp = 0;
|
||||
if(!thisclient->CheckPlayerLevelUP())
|
||||
{
|
||||
BEGINPACKET( pak, 0x79b );
|
||||
ADDDWORD ( pak, thisclient->CharInfo->Exp );
|
||||
ADDWORD ( pak, thisclient->CharInfo->stamina );
|
||||
ADDWORD ( pak, 0 );
|
||||
thisclient->client->SendPacket( &pak );
|
||||
}
|
||||
}
|
||||
//Log(MSG_DEBUG,"Player rewarded %i experience after all mods",thisexp);
|
||||
}
|
||||
}
|
||||
|
||||
//new way PY
|
||||
//Log(MSG_DEBUG,"Parsing party list now");
|
||||
for(int p=0;p<PartyList.size();p++) //loop through our party list to assign final exp to all party members. We already did non-party members up there^
|
||||
{
|
||||
CParty* thisparty = PartyList.at( p );
|
||||
if(thisparty == NULL)
|
||||
{
|
||||
//Log(MSG_DEBUG,"Party not valid");
|
||||
continue;
|
||||
}
|
||||
//Log(MSG_DEBUG,"Party %i exp: %i pending exp: %i", p, thisparty->Exp, thisparty->Pending_Exp );
|
||||
thisparty->counted = false; //reset the boolean for next time
|
||||
for(UINT i=0;i<thisparty->Members.size();i++) //loop through all the members in the party
|
||||
{
|
||||
CPlayer* thismember = thisparty->Members.at(i); //find a party member
|
||||
if(thismember == NULL)
|
||||
return true;
|
||||
thismember->CharInfo->Exp += thismember->CharInfo->Pending_Exp;
|
||||
//Log(MSG_DEBUG,"Added pending exp %i to regular exp %i for member %i", thismember->CharInfo->Pending_Exp, thismember->CharInfo->Exp, i);
|
||||
thismember->CharInfo->Pending_Exp = 0;
|
||||
if(!thismember->CheckPlayerLevelUP( ))
|
||||
{
|
||||
BEGINPACKET( pak, 0x79b );
|
||||
ADDDWORD ( pak, thismember->CharInfo->Exp );
|
||||
ADDWORD ( pak, thismember->CharInfo->stamina );
|
||||
ADDWORD ( pak, 0 );
|
||||
thismember->client->SendPacket( &pak );
|
||||
}
|
||||
}
|
||||
thisparty->Exp += thisparty->Pending_Exp;
|
||||
thisparty->Pending_Exp = 0;
|
||||
if( thisparty->Exp > GetMaxPartyExp(thisparty->PartyLevel)) //level up the party
|
||||
{
|
||||
thisparty->PartyLevel++;
|
||||
thisparty->Exp -= GetMaxPartyExp(thisparty->PartyLevel-1);
|
||||
}
|
||||
BEGINPACKET( pak, 0x7d4 );
|
||||
ADDBYTE ( pak, thisparty->PartyLevel );
|
||||
ADDDWORD ( pak, thisparty->Exp );
|
||||
thisparty->SendToMembers( &pak );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,247 @@
|
||||
// Props to ExJam for this code :D
|
||||
#define AIACT(act) int F_AI_ACT_ ## act (class CWorldServer* server, class CCharacter* entity, byte* raw)
|
||||
typedef int (*fpAiAct)(class CWorldServer*, class CCharacter*, byte*);
|
||||
#define GETAIACTDATA(act) STR_AI_ACT_ ## act * data = (STR_AI_ACT_ ## act *)raw;
|
||||
|
||||
|
||||
AIACT(000);AIACT(001);AIACT(002);
|
||||
AIACT(003);AIACT(004);AIACT(005);
|
||||
AIACT(006);AIACT(007);AIACT(008);
|
||||
AIACT(009);AIACT(010);AIACT(011);
|
||||
AIACT(012);AIACT(013);AIACT(014);
|
||||
AIACT(015);AIACT(016);AIACT(017);
|
||||
AIACT(018);AIACT(019);AIACT(020);
|
||||
AIACT(021);AIACT(022);AIACT(023);
|
||||
AIACT(024);AIACT(025);AIACT(026);
|
||||
AIACT(027);AIACT(028);AIACT(029);
|
||||
AIACT(030);AIACT(031);AIACT(032);
|
||||
AIACT(033);AIACT(034);AIACT(035);
|
||||
AIACT(036);AIACT(037);AIACT(038);
|
||||
|
||||
#define AI_ACT_COUNT 38
|
||||
|
||||
struct STR_AI_ACT_000 {
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_001 {
|
||||
//Do Action (0x04 bytes)
|
||||
byte cAction; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_002 {
|
||||
//Say LTB String (0x04 bytes)
|
||||
dword iStrID; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_003 {
|
||||
//Move (1) (0x08 bytes)
|
||||
dword iDistance; //Pos: 0x00
|
||||
byte cSpeed; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_004 {
|
||||
//Move (2) (0x08 bytes)
|
||||
dword iDistance; //Pos: 0x00
|
||||
byte cSpeed; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_005 {
|
||||
//Move (3) (0x04 bytes)
|
||||
byte cSpeed; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_006 {
|
||||
//Move (?) (0x08 bytes)
|
||||
dword iDistance; //Pos: 0x00
|
||||
byte cAbType; //Pos: 0x04
|
||||
byte cMoreLess; //Pos: 0x05
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_007 {
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_008 {
|
||||
//Move (4) (0x08 bytes)
|
||||
dword iDistance; //Pos: 0x00
|
||||
byte cSpeed; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_009 {
|
||||
//Spawn Monster (1) (0x04 bytes)
|
||||
word wMonster; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_010 {
|
||||
//Spawn Monster (2) (0x04 bytes)
|
||||
word wMonster; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_011 {
|
||||
//Spawn Monster (3) (0x08 bytes)
|
||||
int iDistance; //Pos: 0x00
|
||||
int iNumOfMonster; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_012 {
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_013 {
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_014 {
|
||||
//Unknown (0x04 bytes)
|
||||
int iDistance; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_015 {
|
||||
//Retaliate ? (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_016 {
|
||||
//Unknown (0x04 bytes)
|
||||
int iDistance; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_017 {
|
||||
//Drop Item (0x10 bytes)
|
||||
word items[6];
|
||||
byte btToOwner; //Pos: 0x0c
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_018 {
|
||||
//Spawn Monster (4) (0x08 bytes)
|
||||
short cMonster; //Pos: 0x00
|
||||
short wHowMany; //Pos: 0x02
|
||||
int iDistance; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_019 {
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_020 {
|
||||
//Spawn Monster (5) (0x08 bytes)
|
||||
word cMonster; //Pos: 0x00
|
||||
byte btPos; //Pos: 0x02
|
||||
int iDistance; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_021 {
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_022 {
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_023 {
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_024 {
|
||||
//Do Skill (0x08 bytes)
|
||||
byte btTarget; //Pos: 0x00
|
||||
word nSkill; //Pos: 0x02
|
||||
word nMotion; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_025
|
||||
{
|
||||
//Set Variable (1) (0x0c bytes)
|
||||
union
|
||||
{
|
||||
byte btVarIDX; //Pos: 0x00
|
||||
char cVarIdx[4];
|
||||
};
|
||||
dword iValue; //Pos: 0x04
|
||||
byte btOp; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_026 {
|
||||
//Set Variable (2) (0x00 bytes)
|
||||
union{word nVarNo; //Pos: 0x00
|
||||
char cVarNo[4];};
|
||||
dword iValue; //Pos: 0x04
|
||||
byte btOp; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_027 {
|
||||
//Set Variable (3) (0x00 bytes)
|
||||
union{word nVarNo; //Pos: 0x00
|
||||
char cVarNo[4];};
|
||||
dword iValue; //Pos: 0x04
|
||||
byte btOp; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_028
|
||||
{
|
||||
//Shout/Ann LTB String (0x08 bytes)
|
||||
union
|
||||
{
|
||||
byte btMsgType;
|
||||
int iMsgType;
|
||||
};
|
||||
dword iStrID; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_029 {
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_030 {
|
||||
//Do Trigger (0x0d bytes)
|
||||
word lenszTrigger; //Pos: 0x00
|
||||
char* szTrigger; //Pos: 0x02
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_031 {
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_032 {
|
||||
//Zone (1) (0x04 bytes)
|
||||
word nZoneNo; //Pos: 0x00
|
||||
byte btOnOff; //Pos: 0x02
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_033 {
|
||||
//Zone (2) (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_034 {
|
||||
//Item (?) (0x04 bytes)
|
||||
word nItemNum; //Pos: 0x00
|
||||
word nCount; //Pos: 0x02
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_035 {
|
||||
//Set Variable (4) (0x0c bytes)
|
||||
union{word nVarIDX; //Pos: 0x00
|
||||
char cVarIdx[4];};
|
||||
dword iValue; //Pos: 0x04
|
||||
byte btOp; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_036 {
|
||||
//Monster (1) (0x04 bytes)
|
||||
word nMonster; //Pos: 0x00
|
||||
byte btMaster; //Pos: 0x02
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_037 {
|
||||
//Monster (2) (0x0c bytes)
|
||||
word nMonster; //Pos: 0x00
|
||||
byte nPos; //Pos: 0x02
|
||||
dword iDistance; //Pos: 0x04
|
||||
byte btMaster; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_ACT_038
|
||||
{
|
||||
//TDEF set destiny position
|
||||
byte cSpeed;
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,266 @@
|
||||
// Props to ExJam for this code :D
|
||||
#define AICOND(cond) int F_AI_COND_ ## cond (class CWorldServer* server, class CCharacter* entity, byte* raw)
|
||||
typedef int (*fpAiCond)(class CWorldServer*, class CCharacter*, byte*);
|
||||
#define GETAICONDDATA(cond) STR_AI_COND_ ## cond * data = (STR_AI_COND_ ## cond *)raw;
|
||||
|
||||
AICOND(000);AICOND(001);AICOND(002);
|
||||
AICOND(003);AICOND(004);AICOND(005);
|
||||
AICOND(006);AICOND(007);AICOND(008);
|
||||
AICOND(009);AICOND(010);AICOND(011);
|
||||
AICOND(012);AICOND(013);AICOND(014);
|
||||
AICOND(015);AICOND(016);AICOND(017);
|
||||
AICOND(018);AICOND(019);AICOND(020);
|
||||
AICOND(021);AICOND(022);AICOND(023);
|
||||
AICOND(024);AICOND(025);AICOND(026);
|
||||
AICOND(027);AICOND(028);AICOND(029);
|
||||
AICOND(030);AICOND(031);AICOND(032);
|
||||
|
||||
#define AI_COND_COUNT 33
|
||||
|
||||
struct STR_AI_COND_000
|
||||
{
|
||||
//Hold or Attack (0x04 bytes)
|
||||
byte cNotfightOrDelay; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_COND_001
|
||||
{
|
||||
//Damage (0x08 bytes)
|
||||
dword iDamage; //Pos: 0x00
|
||||
byte cRecvOrGive; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_COND_002
|
||||
{
|
||||
//Check Near (1) (0x0c bytes)
|
||||
int iDistance; //Pos: 0x00
|
||||
union
|
||||
{
|
||||
byte btIsAllied; //Pos: 0x04
|
||||
word nIsAllied;
|
||||
};
|
||||
short nLevelDiff; //Pos: 0x06
|
||||
short nLevelDiff2; //Pos: 0x08
|
||||
word wChrNum; //Pos: 0x0a
|
||||
};
|
||||
|
||||
struct STR_AI_COND_003
|
||||
{
|
||||
//Check Distance (1) (0x04 bytes)
|
||||
int iDistance; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_COND_004
|
||||
{
|
||||
//Check Distance (2) (0x08 bytes)
|
||||
int iDistance; //Pos: 0x00
|
||||
byte cMoreLess; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_COND_005
|
||||
{
|
||||
//Check AB (1) (0x0c bytes)
|
||||
byte cAbType; //Pos: 0x00
|
||||
int iDiff; //Pos: 0x04
|
||||
byte cMoreLess; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_COND_006
|
||||
{
|
||||
//Check HP (0x08 bytes)
|
||||
dword wHP; //Pos: 0x00
|
||||
byte cMoreLess; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_COND_007
|
||||
{
|
||||
//Random Chance (0x04 bytes)
|
||||
byte cPercent; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_COND_008
|
||||
{
|
||||
//Check Near (2) (0x0c bytes)
|
||||
dword iDistance; //Pos: 0x00
|
||||
short nLevelDiff; //Pos: 0x04
|
||||
short nLevelDiff2; //Pos: 0x06
|
||||
byte btIsAllied; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_COND_009
|
||||
{
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_COND_010
|
||||
{
|
||||
//Check AB (2) (0x04 bytes)
|
||||
byte cAbType; //Pos: 0x00
|
||||
byte cMoreLess; //Pos: 0x01
|
||||
};
|
||||
|
||||
struct STR_AI_COND_011 {
|
||||
//Check AB (3) (0x0c bytes)
|
||||
byte cAbType; //Pos: 0x00
|
||||
word tBuffer; //Pos: 0x02
|
||||
dword iValue; //Pos: 0x04
|
||||
byte cMoreLess; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_COND_012
|
||||
{
|
||||
//Check Time (1) (0x04 bytes)
|
||||
byte cWhen; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_COND_013
|
||||
{
|
||||
//Check Target (1) (0x04 bytes)
|
||||
byte btCheckTarget; //Pos: 0x00
|
||||
byte btStatusType; //Pos: 0x01
|
||||
byte btHave; //Pos: 0x02
|
||||
};
|
||||
|
||||
struct STR_AI_COND_014
|
||||
{
|
||||
//Check Variable (1) (0x0c bytes)
|
||||
byte btVarIDX; //Pos: 0x00
|
||||
word tBuffer; //Pos: 0x02
|
||||
dword iValue; //Pos: 0x04
|
||||
byte btOp; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_COND_015
|
||||
{
|
||||
//Check Variable (2) (0x0c bytes)
|
||||
word nVarIDX; //Pos: 0x00
|
||||
word tBuffer; //Pos: 0x02
|
||||
dword iValue; //Pos: 0x04
|
||||
byte btOp; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_COND_016
|
||||
{
|
||||
//Check Variable (3) (0x00 bytes)
|
||||
word nVarIDX; //Pos: 0x00
|
||||
word tBuffer; //Pos: 0x02
|
||||
dword iValue; //Pos: 0x04
|
||||
byte btOp; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_COND_017
|
||||
{
|
||||
//Select NPC (0x04 bytes)
|
||||
dword iNpcNo; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_COND_018
|
||||
{
|
||||
//Check Distance (3) (0x08 bytes)
|
||||
dword iDistance; //Pos: 0x00
|
||||
byte btOp; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_COND_019
|
||||
{
|
||||
//Check Time (2) (0x08 bytes)
|
||||
dword ulTime; //Pos: 0x00
|
||||
dword ulEndTime; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_COND_020
|
||||
{
|
||||
//Check AB (4) (0x0c bytes)
|
||||
union
|
||||
{
|
||||
byte btAbType; //Pos: 0x00
|
||||
dword iAbType;
|
||||
};
|
||||
dword iValue; //Pos: 0x04
|
||||
byte btOp; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_COND_021
|
||||
{
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_COND_022
|
||||
{
|
||||
//Unknown (0x00 bytes)
|
||||
};
|
||||
|
||||
struct STR_AI_COND_023
|
||||
{
|
||||
//Check Time (3) (0x08 bytes)
|
||||
dword ulTime; //Pos: 0x00
|
||||
dword ulEndTime; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_COND_024
|
||||
{
|
||||
//Check Date Time (4) (0x08 bytes)
|
||||
byte btDate; //Pos: 0x00
|
||||
byte btHour1; //Pos: 0x01
|
||||
byte btMin1; //Pos: 0x02
|
||||
byte btHour2; //Pos: 0x03
|
||||
byte btMin2; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_COND_025
|
||||
{
|
||||
//Check Weekday Time (5) (0x08 bytes)
|
||||
byte btWeekDay; //Pos: 0x00
|
||||
byte btHour1; //Pos: 0x01
|
||||
byte btMin1; //Pos: 0x02
|
||||
byte btHour2; //Pos: 0x03
|
||||
byte btMin2; //Pos: 0x04
|
||||
};
|
||||
|
||||
struct STR_AI_COND_026
|
||||
{
|
||||
//Check Position (0x04 bytes)
|
||||
word nMin; //Pos: 0x00
|
||||
word nMax; //Pos: 0x02
|
||||
};
|
||||
|
||||
struct STR_AI_COND_027
|
||||
{
|
||||
//Check Near Character (0x10 bytes)
|
||||
int iDistance; //Pos: 0x00
|
||||
byte btIsAllied; //Pos: 0x04
|
||||
short nLevelDiff; //Pos: 0x06
|
||||
short nLevelDiff2; //Pos: 0x08
|
||||
short wChrNum; //Pos: 0x0a
|
||||
byte btOp; //Pos: 0x0c
|
||||
};
|
||||
|
||||
struct STR_AI_COND_028
|
||||
{
|
||||
//Check Variable (4) (0x0c bytes)
|
||||
word nVarIDX; //Pos: 0x00
|
||||
dword iValue; //Pos: 0x04
|
||||
byte btOp; //Pos: 0x08
|
||||
};
|
||||
|
||||
struct STR_AI_COND_029
|
||||
{
|
||||
//Check Target (2) (0x04 bytes)
|
||||
byte btTargetType; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_COND_030
|
||||
{
|
||||
//Unknown (0x04 bytes)
|
||||
dword Timer; //Pos: 0x00
|
||||
};
|
||||
|
||||
struct STR_AI_COND_031
|
||||
{
|
||||
//no data needed here
|
||||
};
|
||||
|
||||
struct STR_AI_COND_032
|
||||
{
|
||||
//no data needed here
|
||||
};
|
||||
@@ -0,0 +1,167 @@
|
||||
// Props to ExJam for this code :D
|
||||
#include "../WorldServer.h"
|
||||
|
||||
void CWorldServer::ReadAIP(strings path, dword index)
|
||||
{
|
||||
CRoseFile* fh = new CRoseFile(path, FM_READ | FM_BINARY);
|
||||
|
||||
if(fh->IsOpen())
|
||||
{ // goto done;
|
||||
//Log(MSG_LOAD, "Loading %s ", path);
|
||||
dword BlockCount = fh->Get<dword>();
|
||||
dword mintime = fh->Get<dword>();
|
||||
dword mindamage = fh->Get<dword>();
|
||||
dword titlestringlength = fh->Get<dword>();
|
||||
strings title = new char[titlestringlength+1];
|
||||
strings title2 = new char[32+1];
|
||||
fh->Read(title, titlestringlength, 1);
|
||||
for(dword i = 0; i < BlockCount; i++)
|
||||
{
|
||||
titlestringlength = 32;
|
||||
fh->Read(title2, titlestringlength, 1);//on spawn-idle-attack-attacked-killed target-dead
|
||||
dword RecordCount = fh->Get<dword>();
|
||||
|
||||
for(dword j = 0; j < RecordCount; j++)
|
||||
{
|
||||
CAip* script = new CAip();
|
||||
fh->Read(title2, titlestringlength, 1);// 00 ==============================
|
||||
script->ConditionCount = fh->Get<dword>();
|
||||
if(script->ConditionCount > 0)
|
||||
{
|
||||
script->Conditions = new CAip::SAipDatum*[script->ConditionCount];
|
||||
for(dword k = 0; k < script->ConditionCount; k++)
|
||||
{
|
||||
CAip::SAipDatum* data = new CAip::SAipDatum();
|
||||
data->size = fh->Get<dword>();
|
||||
data->opcode = fh->Get<dword>() - 0x04000001;
|
||||
data->data = new byte[data->size - 8];
|
||||
fh->Read(data->data, data->size - 8, 1);
|
||||
script->Conditions[k] = data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
script->Conditions = NULL;
|
||||
}
|
||||
script->ActionCount = fh->Get<dword>();
|
||||
|
||||
if(script->ActionCount > 0)
|
||||
{
|
||||
script->Actions = new CAip::SAipDatum*[script->ActionCount];
|
||||
for(dword k = 0; k < script->ActionCount; k++)
|
||||
{
|
||||
CAip::SAipDatum* data = new CAip::SAipDatum();
|
||||
data->size = fh->Get<dword>();
|
||||
data->opcode = fh->Get<dword>() - 0x0B000001;
|
||||
data->data = new byte[data->size - 8];
|
||||
fh->Read(data->data, data->size - 8, 1);
|
||||
script->Actions[k] = data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
script->Actions = NULL;
|
||||
}
|
||||
script->AInumber = index;
|
||||
script->AipID=((index*0x10000)+(i*0x100)+j);
|
||||
script->minDamage = mindamage;
|
||||
script->minTime = mintime;
|
||||
script->recordcount[i] = RecordCount;
|
||||
AipList.push_back( script );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
Log( MSG_ERROR, "AIP File: '%s' failed to load properly", path );
|
||||
fh->Close();
|
||||
delete fh;
|
||||
}
|
||||
|
||||
void CWorldServer::LoadAipData()
|
||||
{
|
||||
CStrStb* stbAip = new CStrStb("3DDATA\\STB\\FILE_AI.STB");
|
||||
Log(MSG_INFO, "Loading AIP files");
|
||||
for(dword i = 1; i < stbAip->Rows(); i++)
|
||||
{
|
||||
if(stbAip->Data(i, 0))
|
||||
{
|
||||
GServer->ReadAIP(stbAip->Data(i, 0),i);
|
||||
}
|
||||
}
|
||||
//Log(MSG_INFO, "%i aip entrys",AipList.size());
|
||||
Log(MSG_INFO, "Finished loading aip data ");
|
||||
|
||||
aiCondFunc[0] = &F_AI_COND_000;
|
||||
aiCondFunc[1] = &F_AI_COND_001;
|
||||
aiCondFunc[2] = &F_AI_COND_002;
|
||||
aiCondFunc[3] = &F_AI_COND_003;
|
||||
aiCondFunc[4] = &F_AI_COND_004;
|
||||
aiCondFunc[5] = &F_AI_COND_005;
|
||||
aiCondFunc[6] = &F_AI_COND_006;
|
||||
aiCondFunc[7] = &F_AI_COND_007;
|
||||
aiCondFunc[8] = &F_AI_COND_008;
|
||||
aiCondFunc[9] = &F_AI_COND_009;
|
||||
aiCondFunc[10] = &F_AI_COND_010;
|
||||
aiCondFunc[11] = &F_AI_COND_011;
|
||||
aiCondFunc[12] = &F_AI_COND_012;
|
||||
aiCondFunc[13] = &F_AI_COND_013;
|
||||
aiCondFunc[14] = &F_AI_COND_014;
|
||||
aiCondFunc[15] = &F_AI_COND_015;
|
||||
aiCondFunc[16] = &F_AI_COND_016;
|
||||
aiCondFunc[17] = &F_AI_COND_017;
|
||||
aiCondFunc[18] = &F_AI_COND_018;
|
||||
aiCondFunc[19] = &F_AI_COND_019;
|
||||
aiCondFunc[20] = &F_AI_COND_020;
|
||||
aiCondFunc[21] = &F_AI_COND_021;
|
||||
aiCondFunc[22] = &F_AI_COND_022;
|
||||
aiCondFunc[23] = &F_AI_COND_023;
|
||||
aiCondFunc[24] = &F_AI_COND_024;
|
||||
aiCondFunc[25] = &F_AI_COND_025;
|
||||
aiCondFunc[26] = &F_AI_COND_026;
|
||||
aiCondFunc[27] = &F_AI_COND_027;
|
||||
aiCondFunc[28] = &F_AI_COND_028;
|
||||
aiCondFunc[29] = &F_AI_COND_029;
|
||||
aiCondFunc[30] = &F_AI_COND_030;
|
||||
|
||||
aiActFunc[0] = &F_AI_ACT_000;
|
||||
aiActFunc[1] = &F_AI_ACT_001;
|
||||
aiActFunc[2] = &F_AI_ACT_002;
|
||||
aiActFunc[3] = &F_AI_ACT_003;
|
||||
aiActFunc[4] = &F_AI_ACT_004;
|
||||
aiActFunc[5] = &F_AI_ACT_005;
|
||||
aiActFunc[6] = &F_AI_ACT_006;
|
||||
aiActFunc[7] = &F_AI_ACT_007;
|
||||
aiActFunc[8] = &F_AI_ACT_008;
|
||||
aiActFunc[9] = &F_AI_ACT_009;
|
||||
aiActFunc[10] = &F_AI_ACT_010;
|
||||
aiActFunc[11] = &F_AI_ACT_011;
|
||||
aiActFunc[12] = &F_AI_ACT_012;
|
||||
aiActFunc[13] = &F_AI_ACT_013;
|
||||
aiActFunc[14] = &F_AI_ACT_014;
|
||||
aiActFunc[15] = &F_AI_ACT_015;
|
||||
aiActFunc[16] = &F_AI_ACT_016;
|
||||
aiActFunc[17] = &F_AI_ACT_017;
|
||||
aiActFunc[18] = &F_AI_ACT_018;
|
||||
aiActFunc[19] = &F_AI_ACT_019;
|
||||
aiActFunc[20] = &F_AI_ACT_020;
|
||||
aiActFunc[21] = &F_AI_ACT_021;
|
||||
aiActFunc[22] = &F_AI_ACT_022;
|
||||
aiActFunc[23] = &F_AI_ACT_023;
|
||||
aiActFunc[24] = &F_AI_ACT_024;
|
||||
aiActFunc[25] = &F_AI_ACT_025;
|
||||
aiActFunc[26] = &F_AI_ACT_026;
|
||||
aiActFunc[27] = &F_AI_ACT_027;
|
||||
aiActFunc[28] = &F_AI_ACT_028;
|
||||
aiActFunc[29] = &F_AI_ACT_029;
|
||||
aiActFunc[30] = &F_AI_ACT_030;
|
||||
aiActFunc[31] = &F_AI_ACT_031;
|
||||
aiActFunc[32] = &F_AI_ACT_032;
|
||||
aiActFunc[33] = &F_AI_ACT_033;
|
||||
aiActFunc[34] = &F_AI_ACT_034;
|
||||
aiActFunc[35] = &F_AI_ACT_035;
|
||||
aiActFunc[36] = &F_AI_ACT_036;
|
||||
aiActFunc[37] = &F_AI_ACT_037;
|
||||
|
||||
delete stbAip;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Props to ExJam for this code :]
|
||||
class CAip
|
||||
{
|
||||
public:
|
||||
struct SAipDatum
|
||||
{
|
||||
int size;
|
||||
int opcode;
|
||||
byte* data;
|
||||
};
|
||||
|
||||
dword AInumber;
|
||||
dword AipID;
|
||||
dword minDamage;
|
||||
dword minTime;
|
||||
byte recordcount[6];
|
||||
|
||||
dword ConditionCount;
|
||||
SAipDatum** Conditions;
|
||||
|
||||
dword ActionCount;
|
||||
SAipDatum** Actions;
|
||||
int AipVar;
|
||||
private:
|
||||
};
|
||||
#define AI_FAILURE 8
|
||||
#define AI_SUCCESS 7
|
||||
#include "AiConditions.h"
|
||||
#include "AiActions.h"
|
||||
@@ -0,0 +1,975 @@
|
||||
/*
|
||||
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 "character.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
void CCharacter::DoAttack( )
|
||||
{
|
||||
//if(Status->Sleep <= 0xff)
|
||||
//{
|
||||
// ClearBattle( Battle );
|
||||
// return; // can't take any action while we are asleep can we?
|
||||
//}
|
||||
//if(IsPlayer())
|
||||
//{
|
||||
// Log(MSG_INFO,"DoAttack Attack type %i selected. Chartype = %i casting = %i",Battle->atktype, CharType, Battle->iscasting);
|
||||
//}
|
||||
//if(CharType = 4) //it's an npc
|
||||
//{
|
||||
// Log(MSG_DEBUG,"NPC battle sequence");
|
||||
//}
|
||||
if(Battle->iscasting == 1)
|
||||
{
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
//Log(MSG_DEBUG,"Iscasting detected as true.");
|
||||
if(CanAttack())
|
||||
{
|
||||
if(IsPlayer())
|
||||
{
|
||||
//Log(MSG_DEBUG,"Should reach here once on normal attack. iscasting state = %i attack type = %i",Battle->iscasting, Battle->atktype);
|
||||
|
||||
if(Battle->atktype == 3) //buff
|
||||
{
|
||||
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
else // if not buffing then resume normal attack.
|
||||
{
|
||||
//ClearBattle( Battle );
|
||||
StartAction(Enemy, NORMAL_ATTACK, 0);
|
||||
}
|
||||
}
|
||||
else //monsters should always resume normal attack
|
||||
{
|
||||
StartAction(Enemy, NORMAL_ATTACK, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(!CanAttack())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(IsSummon())
|
||||
{
|
||||
//Log(MSG_DEBUG,"Summon attacking in function DoAttack");
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL) // || (Battle->atktype != SKILL_AOE && Battle->atktype != BUFF_AOE))
|
||||
{
|
||||
Log(MSG_DEBUG,"No Enemy found");
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(this == Enemy) //summoned monster is attacking itself. It has been observed to happen
|
||||
{
|
||||
//Log(MSG_INFO,"Clearing Battle for this summon");
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(Enemy->IsSummon())
|
||||
{
|
||||
CMonster* thisMonster = reinterpret_cast<CMonster*>(this);
|
||||
if(thisMonster == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
CMonster* otherMonster = reinterpret_cast<CMonster*>(Enemy);
|
||||
if(otherMonster == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(thisMonster->owner == otherMonster->owner)
|
||||
{
|
||||
//Log(MSG_INFO,"Clearing Battle for this summon");
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
//if(IsPlayer())
|
||||
//{
|
||||
// Log(MSG_INFO,"DoAttack Battle Attack type = %i",Battle->atktype);
|
||||
//}
|
||||
switch(Battle->atktype)
|
||||
{
|
||||
case NORMAL_ATTACK://normal attack
|
||||
{
|
||||
//if(!Status->CanAttack)
|
||||
// return; //can't attack right now.
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(Enemy == this)
|
||||
{
|
||||
//Log(MSG_INFO,"WTF?? I AM trying to attack myself");
|
||||
ClearBattle( Battle );
|
||||
}
|
||||
if(IsTargetReached( Enemy ))
|
||||
{
|
||||
//if(IsMonster())
|
||||
// Log(MSG_INFO,"Monster is trying to hit the player");
|
||||
NormalAttack( Enemy );
|
||||
if (Enemy->IsMonster()) // do monster AI script when monster is attacked.
|
||||
{
|
||||
CMonster* monster = GServer->GetMonsterByID(Enemy->clientid, Enemy->Position->Map);
|
||||
if(monster == NULL)return;
|
||||
//monster->DoAi(monster->thisnpc->AI, 3);
|
||||
monster->DoAi(monster->monAI, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SKILL_ATTACK://skill attack
|
||||
{
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
//ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(IsTargetReached( Enemy, skill ))
|
||||
{
|
||||
//Log(MSG_DEBUG,"Skill Attack reached target and started");
|
||||
SkillAttack( Enemy, skill );
|
||||
if (Enemy->IsMonster())
|
||||
{
|
||||
CMonster* monster = GServer->GetMonsterByID(Enemy->clientid, Enemy->Position->Map);
|
||||
if(monster == NULL)return;
|
||||
monster->DoAi(monster->monAI, 3);
|
||||
Battle->atktype = NORMAL_ATTACK;
|
||||
}
|
||||
}
|
||||
//else
|
||||
//{
|
||||
//Log(MSG_DEBUG,"Skill Attack failed to reach target");
|
||||
//}
|
||||
}
|
||||
break;
|
||||
case SKILL_BUFF://buffs
|
||||
{
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL)
|
||||
{
|
||||
//Log(MSG_DEBUG,"this target is NULL");
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
//Log(MSG_DEBUG,"this skill is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
if(IsTargetReached( Enemy, skill ))
|
||||
{
|
||||
//Log(MSG_DEBUG,"Skill successfully cast");
|
||||
BuffSkill( Enemy, skill );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SKILL_AOE:
|
||||
//case SKILL_SELF: //this is impossible. it can never be set
|
||||
case AOE_TARGET:
|
||||
{
|
||||
|
||||
|
||||
//Log(MSG_DEBUG,"Called skill type %i",Battle->atktype);
|
||||
CCharacter* Enemy = NULL;
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(Battle->atktype == AOE_TARGET)
|
||||
{
|
||||
float distance = GServer->distance( Position->current, Position->destiny );
|
||||
if(distance <= skill->range)
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
AoeSkill( skill, Enemy );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Position->skilltargetpos = Position->current;
|
||||
AoeSkill( skill, NULL );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BUFF_SELF:
|
||||
{
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
//ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
BuffSkill( this, skill );
|
||||
}
|
||||
break;
|
||||
case BUFF_AOE:
|
||||
{
|
||||
//Log(MSG_INFO,"BUFF AOE selected");
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
AoeBuff( skill );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// do normal attack
|
||||
void CCharacter::NormalAttack( CCharacter* Enemy )
|
||||
{
|
||||
Enemy->OnBeAttacked( this );
|
||||
Position->destiny = Position->current;
|
||||
//Log(MSG_DEBUG,"(NormalAttack) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
|
||||
|
||||
// Even newer formula based on Jayce's idea
|
||||
//dodge. gives a percentage chance
|
||||
unsigned int hitpower = 0;
|
||||
bool critical = false;
|
||||
unsigned int totalchance = Stats->Accury + Enemy->Stats->Dodge;
|
||||
double acdm = Stats->Accury * 100 / totalchance;
|
||||
unsigned int hitchance = (unsigned int)floor(acdm);
|
||||
if(GServer->RandNumber( 0, 100 ) > hitchance)
|
||||
{
|
||||
hitpower = 0; // dodged
|
||||
//Log(MSG_DEBUG,"accuracy %i dodge %i",Stats->Accury,Enemy->Stats->Dodge);
|
||||
//Log(MSG_DEBUG,"Dodged total %i mod %f hit chance %i",totalchance,acdm,hitchance);
|
||||
}
|
||||
else
|
||||
{
|
||||
//level adjustment. Not yet implemented
|
||||
unsigned int leveltotal = Stats->Level + Enemy->Stats->Level;
|
||||
double levelmod = Stats->Level * 200 / leveltotal;
|
||||
|
||||
//calculate damage
|
||||
if(Stats->magicattack == 1) //magic attacks such as wands and some monsters
|
||||
{
|
||||
unsigned int totalpower = Stats->Attack_Power + Enemy->Stats->Magic_Defense;
|
||||
double hitmod = Stats->Attack_Power * 100 / totalpower + Stats->Level; //percentage of hitpower to use * 100
|
||||
hitpower = (unsigned int)floor((double)(hitmod * Stats->Attack_Power / 100));
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int totalpower = Stats->Attack_Power + Enemy->Stats->Defense;
|
||||
double hitmod = Stats->Attack_Power * 100 / totalpower + Stats->Level; //percentage of hitpower to use * 100
|
||||
hitpower = (unsigned int)floor((double)(Stats->Attack_Power * hitmod / 100));
|
||||
//Log(MSG_DEBUG,"Normal Attack: AP %i DEF %i",Stats->Attack_Power,Enemy->Stats->Defense);
|
||||
//Log(MSG_DEBUG,"Normal Attack: hit total %i hitmod %f hit power %i",totalpower,hitmod,hitpower);
|
||||
}
|
||||
//add some randomness. + or - 5% of hitpower
|
||||
int min = (int)floor((double)(hitpower * 0.85));
|
||||
int max = (int)floor((double)(hitpower * 1.15));
|
||||
int dif = max - min;
|
||||
int mod = GServer->RandNumber( 0, dif );
|
||||
hitpower = min + mod;
|
||||
//Log(MSG_DEBUG,"min %i max %i dif %i mod %i modified hit power %i", min, max, dif, mod, hitpower);
|
||||
if(hitpower <= 5)
|
||||
{
|
||||
hitpower = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(GServer->RandNumber(0,300)<Stats->Critical)
|
||||
{
|
||||
hitpower = (long int)floor(hitpower * 1.5);
|
||||
critical = true;
|
||||
//Log(MSG_DEBUG,"CRITICAL hit power %i", hitpower);
|
||||
}
|
||||
}
|
||||
if (hitpower > 0x7ff)//2047 packet size limit.
|
||||
hitpower = 0x7ff;
|
||||
//Log(MSG_DEBUG,"Successful hit hit chance %i damage %i",hitchance, hitpower);
|
||||
}
|
||||
|
||||
|
||||
//end jayce formula
|
||||
Battle->atktarget = Battle->target;
|
||||
//if(IsMonster())
|
||||
//{
|
||||
//Log(MSG_INFO,"Monster hits player for %i damage. Attack target = %i",hitpower,Battle->atktarget);
|
||||
//}
|
||||
//Log( MSG_INFO, "hitpower %i. Attack %f ", hitpower,attack );
|
||||
if(!Enemy->IsSummon( ) && Enemy->IsMonster( ))
|
||||
{
|
||||
Enemy->AddDamage( this, hitpower );
|
||||
Enemy->damagecounter += hitpower;// is for AI
|
||||
}
|
||||
Enemy->Stats->HP -= hitpower;
|
||||
//if(IsPlayer())
|
||||
// Log(MSG_DEBUG,"Player attack did damage %i",hitpower);
|
||||
//if(IsMonster())
|
||||
// Log(MSG_DEBUG,"Monster attack did damage %i",hitpower);
|
||||
|
||||
|
||||
|
||||
BEGINPACKET( pak, 0x799 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Battle->atktarget );
|
||||
|
||||
if(Enemy->IsDead())
|
||||
{
|
||||
CDrop* thisdrop = NULL;
|
||||
ADDWORD ( pak, (hitpower | ( critical?0xb000:0x8000 ) ));
|
||||
if(!Enemy->IsSummon( ) && !Enemy->IsPlayer( ))
|
||||
{
|
||||
int thisdroprate = Stats->itemdroprate + GServer->Config.DROP_RATE;
|
||||
//Log(MSG_DEBUG,"Monster killed. calling drops function %i times",thisdroprate);
|
||||
for(int j=0;j<thisdroprate;j++)
|
||||
{
|
||||
thisdrop = Enemy->GetDrop( );
|
||||
if(thisdrop != NULL)
|
||||
{
|
||||
CMap* map = GServer->MapList.Index[thisdrop->posMap];
|
||||
map->AddDrop( thisdrop );
|
||||
}
|
||||
}
|
||||
}
|
||||
GServer->SendToVisible( &pak, Enemy );
|
||||
ClearBattle(Battle);
|
||||
//ClearBattle(Enemy->Battle);
|
||||
if(Enemy->IsMonster())
|
||||
{
|
||||
CMonster* monster = reinterpret_cast<CMonster*>(Enemy);
|
||||
if(monster == NULL)
|
||||
{
|
||||
//Log(MSG_DEBUG,"Monster killed. Failed to create monster object");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Log(MSG_DEBUG,"Monster killed. Setting death delay timer");
|
||||
monster->DeathDelayTimer = clock();
|
||||
}
|
||||
}
|
||||
OnEnemyDie( Enemy );
|
||||
ClearBattle(Battle);
|
||||
GServer->SendToVisible( &pak, Enemy );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, (hitpower|(hitpower>0?(critical?0x4000:0):0)));
|
||||
//ADDDWORD ( pak, (hitpower>0?(critical?12:0):0) );
|
||||
GServer->SendToVisible( &pak, Enemy );
|
||||
}
|
||||
ReduceABC( );
|
||||
Battle->lastAtkTime = clock( );
|
||||
//Battle->lastAtkTime = Battle->NextAtkTime;
|
||||
}
|
||||
|
||||
// do skill attack
|
||||
bool CCharacter::SkillAttack( CCharacter* Enemy, CSkills* skill )
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
//Log(MSG_DEBUG,"(SkillAttack) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
//if(Battle->castTime == 0)
|
||||
//{
|
||||
BEGINPACKET( pak, 0x7bb );
|
||||
ADDWORD ( pak, clientid );
|
||||
GServer->SendToVisible( &pak, this );
|
||||
// Battle->castTime = clock();
|
||||
// return true;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// clock_t etime = clock() - Battle->castTime;
|
||||
// if(etime < SKILL_DELAY)
|
||||
// return true;
|
||||
//}
|
||||
//Battle->castTime = 0;
|
||||
UseAtkSkill( Enemy, skill );
|
||||
Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100));
|
||||
if(Stats->MP < 0)
|
||||
Stats->MP = 0;
|
||||
//Battle->atktype = NORMAL_ATTACK;
|
||||
//Battle->skilltarget = 0;
|
||||
//Battle->atktarget = Battle->target;
|
||||
//Battle->skillid = 0;
|
||||
GServer->DoSkillScript( this, skill );
|
||||
Battle->lastAtkTime = clock( );
|
||||
//Battle->lastAtkTime = Battle->NextAtkTime;
|
||||
if(Enemy->IsDead())
|
||||
ClearBattle(Battle);
|
||||
|
||||
|
||||
Battle->iscasting = 1;
|
||||
//Log(MSG_DEBUG,"Cast a skill. Iscasting set to true");
|
||||
return true;
|
||||
}
|
||||
|
||||
// do buff skill
|
||||
bool CCharacter::BuffSkill( CCharacter* Target, CSkills* skill )
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
//Log(MSG_DEBUG,"(BuffSkill) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
//if(Battle->castTime == 0)
|
||||
//{
|
||||
BEGINPACKET( pak, 0x7bb );
|
||||
ADDWORD ( pak, clientid );
|
||||
GServer->SendToVisible( &pak, (CCharacter*)this );
|
||||
// Battle->castTime = clock();
|
||||
// return true;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// clock_t etime = clock() - Battle->castTime;
|
||||
// if(etime < SKILL_DELAY)
|
||||
// return true;
|
||||
//}
|
||||
//Battle->castTime = 0;
|
||||
//Log(MSG_DEBUG,"applied buff skill %i",skill->id);
|
||||
UseBuffSkill( Target, skill );
|
||||
Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100));
|
||||
if(Stats->MP < 0) Stats->MP = 0;
|
||||
GServer->DoSkillScript( this, skill ); //summons a monster. Why do we do this for all skills?
|
||||
if(!IsMonster())
|
||||
{
|
||||
//ClearBattle( Battle );
|
||||
//Battle->bufftarget = 0;
|
||||
//Battle->skilltarget = 0;
|
||||
//Battle->skillid = 0;
|
||||
//Battle->atktype = NORMAL_ATTACK;
|
||||
}
|
||||
else //Monsters need to be reset to normal attack and clear skill attacks.
|
||||
{
|
||||
//Battle->atktarget = Battle->target;
|
||||
//Battle->bufftarget = 0;
|
||||
//Battle->skilltarget = 0;
|
||||
//Battle->skillid = 0;
|
||||
//Battle->atktype = NORMAL_ATTACK;
|
||||
//StartAction(Target, NORMAL_ATTACK);
|
||||
}
|
||||
//Battle->lastAtkTime = clock( );
|
||||
Battle->lastAtkTime = Battle->NextAtkTime;
|
||||
Battle->iscasting = 1;
|
||||
return true;
|
||||
}
|
||||
//arnold
|
||||
// do AoE skill
|
||||
bool CCharacter::AoeSkill( CSkills* skill, CCharacter* Enemy )
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
//Log(MSG_DEBUG,"(AoeSkill) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
//Log(MSG_INFO,"Doing AOE Skill");
|
||||
//if(Battle->castTime == 0)
|
||||
//{
|
||||
BEGINPACKET( pak, 0x7bb );
|
||||
ADDWORD ( pak, clientid );
|
||||
GServer->SendToVisible( &pak, (CCharacter*)this );
|
||||
// Battle->castTime = clock();
|
||||
// return true;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// clock_t etime = clock() - Battle->castTime;
|
||||
// if(etime < SKILL_DELAY)
|
||||
// return true;
|
||||
//}
|
||||
//Battle->castTime = 0;
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
|
||||
//PY replacement code
|
||||
if(IsPlayer() || IsSummon())
|
||||
{
|
||||
for(UINT i=0;i<map->MonsterList.size();i++)
|
||||
{
|
||||
CMonster* monster = map->MonsterList.at(i);
|
||||
if(monster == NULL) continue;
|
||||
if(monster->clientid == clientid) continue;
|
||||
if(monster->IsSummon( ) && (map->allowpvp == 0 || monster->owner == clientid)) continue;
|
||||
if(GServer->IsMonInCircle( Position->skilltargetpos, monster->Position->current,(float)skill->aoeradius + 1))
|
||||
UseAtkSkill( (CCharacter*) monster, skill,1 );
|
||||
}
|
||||
}
|
||||
if(IsMonster() && !IsSummon())
|
||||
{
|
||||
for(UINT i=0;i<map->PlayerList.size();i++)
|
||||
{
|
||||
CPlayer* player = map->PlayerList.at(i);
|
||||
if(player == NULL)continue;
|
||||
if(player->clientid == clientid) continue;
|
||||
if(GServer->IsMonInCircle( Position->skilltargetpos, player->Position->current, (float)skill->aoeradius+1))
|
||||
UseAtkSkill( (CCharacter*) player, skill,1 );
|
||||
}
|
||||
}
|
||||
//PY end
|
||||
|
||||
// this code appears to me to be completely wrong replaced with code above
|
||||
/*
|
||||
for(UINT i=0;i<map->MonsterList.size();i++)
|
||||
{
|
||||
CMonster* monster = map->MonsterList.at(i);
|
||||
if(monster->clientid == clientid) continue;
|
||||
if(IsSummon( ) || IsPlayer( ))
|
||||
{
|
||||
if(monster->IsSummon( ) && (map->allowpvp==0 || monster->owner == clientid)) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!monster->IsSummon( )) continue;
|
||||
}
|
||||
if(GServer->IsMonInCircle( Position->skilltargetpos, monster->Position->current,(float)skill->aoeradius + 1))
|
||||
UseAtkSkill( (CCharacter*) monster, skill );
|
||||
}
|
||||
if(map->allowpvp!=0 || (IsMonster( ) && !IsSummon( )))
|
||||
{
|
||||
for(UINT i=0;i<map->PlayerList.size();i++)
|
||||
{
|
||||
CPlayer* player = map->PlayerList.at(i);
|
||||
if(player->clientid==clientid) continue;
|
||||
if(GServer->IsMonInCircle( Position->skilltargetpos,player->Position->current,(float)skill->aoeradius+1))
|
||||
UseAtkSkill( (CCharacter*) player, skill );
|
||||
}
|
||||
}*/
|
||||
Battle->iscasting = 1;
|
||||
//Log(MSG_DEBUG,"Cast a skill. Iscasting set to true");
|
||||
if(Enemy != NULL)
|
||||
{
|
||||
if(Enemy->IsDead( ))
|
||||
ClearBattle( Battle );
|
||||
}
|
||||
else
|
||||
ClearBattle( Battle );
|
||||
Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100));
|
||||
if(Stats->MP < 0) Stats->MP = 0;
|
||||
Battle->lastAtkTime = clock( );
|
||||
//Battle->lastAtkTime = Battle->NextAtkTime;
|
||||
//Send skill animation to the world
|
||||
RESETPACKET( pak, 0x7b9);
|
||||
ADDWORD ( pak, clientid);
|
||||
ADDWORD ( pak, Battle->skillid);
|
||||
ADDWORD ( pak, 1);
|
||||
GServer->SendToVisible( &pak, this );
|
||||
Battle->bufftarget = 0;
|
||||
Battle->skilltarget = 0;
|
||||
Battle->skillid = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCharacter::AoeBuff( CSkills* skill )
|
||||
{
|
||||
//Log(MSG_INFO,"BUFF AOE called with skill target ",skill->target);
|
||||
Position->destiny = Position->current;
|
||||
//Log(MSG_DEBUG,"(AoeBuff) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
BEGINPACKET( pak, 0x7bb );
|
||||
ADDWORD ( pak, clientid );
|
||||
GServer->SendToVisible( &pak, (CCharacter*)this );
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
|
||||
if(CharType != 2) //player || Summon || NPC. Anything that is not a monster
|
||||
{
|
||||
//Log(MSG_INFO,"It's a Player, Summon or NPC");
|
||||
for(UINT i=0;i<map->PlayerList.size();i++)
|
||||
{
|
||||
CPlayer* player = map->PlayerList.at(i);
|
||||
if(player == NULL)continue;
|
||||
switch(skill->target)
|
||||
{
|
||||
case 1: //tPartyMember
|
||||
if(player->Party->party == GetParty())
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough party member");
|
||||
if (player == this) //it is me so I had better cast it anyway ^-^
|
||||
{
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
else if(player->Party->party != NULL) // Don't cast the skill on players who are not in any party at all.
|
||||
{
|
||||
if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1))
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2: //tClanMember
|
||||
if (player->Clan == GetClan( ) && (player->Clan != 0 || player == this))
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough clan member");
|
||||
if(GServer->IsMonInCircle( Position->current, player->Position->current, (float)skill->aoeradius + 1 ) )
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
break;
|
||||
case 3: //tAlly
|
||||
case 7: //tAllCharacters
|
||||
case 8: //tAllMembers. all characters
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough allied Player");
|
||||
if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1))
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
break;
|
||||
case 5: //tHostileCharacter
|
||||
{
|
||||
if (CharType == 2)
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough hostile Player");
|
||||
if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1))
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//Log(MSG_INFO,"No valid skill target found");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (CharType == 2) //monster
|
||||
{
|
||||
//Log(MSG_INFO,"It's a monster");
|
||||
for(UINT i=0;i<map->MonsterList.size();i++)
|
||||
{
|
||||
CMonster* monster = map->MonsterList.at(i);
|
||||
if(monster == NULL)continue;
|
||||
switch(skill->target)
|
||||
{
|
||||
case tPartyMember: // party
|
||||
break;
|
||||
case 2: //tClanMember
|
||||
break;
|
||||
case 3: //tAlly
|
||||
case 7: //tAllCharacters
|
||||
case 8: //tAllMembers. all characters
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough monster");
|
||||
if(GServer->IsMonInCircle( Position->current,monster->Position->current,(float)skill->aoeradius + 1))
|
||||
UseBuffSkill( (CCharacter*)monster, skill );
|
||||
}
|
||||
break;
|
||||
case tHostileCharacter:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100));
|
||||
if(Stats->MP < 0) Stats->MP = 0;
|
||||
//Battle->atktarget = Battle->target;
|
||||
//Battle->bufftarget = 0;
|
||||
//Battle->skilltarget = 0;
|
||||
//Battle->skillid = 0;
|
||||
//Battle->atktype = NORMAL_ATTACK;
|
||||
ClearBattle( Battle );
|
||||
Battle->lastAtkTime = clock( );
|
||||
//Battle->lastAtkTime = Battle->NextAtkTime;
|
||||
Battle->iscasting = 1;
|
||||
//Send skill animation to the world
|
||||
BEGINPACKET( pak1, 0x7b9);
|
||||
ADDWORD ( pak1, clientid);
|
||||
ADDWORD ( pak1, Battle->skillid);
|
||||
ADDWORD ( pak1, 1);
|
||||
GServer->SendToVisible( &pak1, this );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// use skill attack
|
||||
void CCharacter::UseAtkSkill( CCharacter* Enemy, CSkills* skill, bool deBuff)
|
||||
{
|
||||
|
||||
// Even newer formula based on Jayce's idea
|
||||
//dodge. gives a percentage chance
|
||||
Battle->iscasting = 1;
|
||||
unsigned int hitpower = 0;
|
||||
unsigned int Attack = 0;
|
||||
// skill attacks now based on a percentage of base attack power so that they scale with level
|
||||
Attack = (unsigned int)floor((double)skill->atkpower * Stats->Attack_Power / 100);
|
||||
int success = GServer->RandNumber(0,100);
|
||||
if(success <= skill->success)
|
||||
{
|
||||
//calculate damage
|
||||
if(skill->skilltype == 6 || skill->skilltype == 7) //magical damage
|
||||
{
|
||||
//unsigned int totalpower = skill->atkpower + Enemy->Stats->Magic_Defense;
|
||||
unsigned int totalpower = Attack + Enemy->Stats->Magic_Defense;
|
||||
double hitmod = Attack * 100 / totalpower; //percentage of hitpower to use * 100
|
||||
hitpower = (unsigned int)floor((double)(hitmod * Attack / 100));
|
||||
//Log(MSG_DEBUG,"Skill magic: AP %i DEF %i",Stats->Attack_Power,Enemy->Stats->Defense);
|
||||
//Log(MSG_DEBUG,"Skill magic: Skill power %i. Attack %i",skill->atkpower,Attack);
|
||||
//Log(MSG_DEBUG,"Skill magic: hit total %i mod %f hit power %i",totalpower,hitmod,hitpower);
|
||||
}
|
||||
else
|
||||
{
|
||||
//unsigned int totalpower = skill->atkpower + Enemy->Stats->Defense;
|
||||
unsigned int totalpower = Attack + Enemy->Stats->Defense;
|
||||
double hitmod = Attack * 100 / totalpower; //percentage of hitpower to use * 100
|
||||
hitpower = (unsigned int)floor((double)(hitmod * Attack / 100));
|
||||
//Log(MSG_DEBUG,"Skill physical: AP %i DEF %i",Stats->Attack_Power,Enemy->Stats->Defense);
|
||||
//Log(MSG_DEBUG,"Skill physical: Skill power %i. Attack %i",skill->atkpower,Attack);
|
||||
//Log(MSG_DEBUG,"skill physical: hit total %i mod %f hit power %i",totalpower,hitmod,hitpower);
|
||||
}
|
||||
//add some randomness. + or - 5% of hitpower
|
||||
int min = (int)floor((double)(hitpower * 0.85));
|
||||
int max = (int)floor((double)(hitpower * 1.15));
|
||||
int dif = max - min;
|
||||
int mod = GServer->RandNumber( 0, dif );
|
||||
hitpower = min + mod;
|
||||
}
|
||||
|
||||
bool bflag = false;
|
||||
Enemy->OnBeAttacked( this );
|
||||
if(hitpower <= 0) hitpower = 0;
|
||||
if(IsPlayer())
|
||||
{
|
||||
hitpower += (int)(hitpower * (Stats->ExtraDamage))/100;
|
||||
}
|
||||
if (skill->atkpower == 0)
|
||||
{
|
||||
hitpower = 0; // If the skill has a 0 atk power, it isn't meant to damage
|
||||
Log( MSG_INFO, "This skill does no damage" );
|
||||
//ClearBattle( Battle );
|
||||
return; // probably need to add some alternative code here for legitimate skills that have no attack power.
|
||||
}
|
||||
if(hitpower > 2046)hitpower = 2046;
|
||||
//Log(MSG_DEBUG,"Cast a skill. damage is %i",skillpower);
|
||||
if(!Enemy->IsSummon( ) && Enemy->IsMonster( ))
|
||||
{
|
||||
Enemy->AddDamage( this, hitpower );
|
||||
Enemy->damagecounter += hitpower;// is for AI
|
||||
}
|
||||
//Log( MSG_INFO, "modified skillpower = %i",hitpower );
|
||||
Enemy->Stats->HP -= hitpower;
|
||||
//if(IsPlayer())
|
||||
// Log(MSG_DEBUG,"Player Spell did damage %i",hitpower);
|
||||
//if(IsMonster())
|
||||
// Log(MSG_DEBUG,"Monster spell did damage %i",hitpower);
|
||||
BEGINPACKET( pak, 0x7b6 );
|
||||
ADDWORD ( pak, Enemy->clientid );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDDWORD ( pak, 0x000007f8 );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
if (Enemy->Stats->HP < 1)
|
||||
{
|
||||
ADDWORD ( pak, hitpower + 0x8000 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, hitpower + 0x2000 );
|
||||
}
|
||||
if(Enemy->IsDead())
|
||||
{
|
||||
//Log(MSG_DEBUG,"Enemy died");
|
||||
CDrop* thisdrop = NULL;
|
||||
ADDDWORD ( pak, 16 );
|
||||
if(!Enemy->IsSummon( ) && !Enemy->IsPlayer( ))
|
||||
{
|
||||
//Log(MSG_DEBUG,"Monster killed. calling drops function %i times",Stats->itemdroprate);
|
||||
int thisdroprate = Stats->itemdroprate + GServer->Config.DROP_RATE;
|
||||
for(int j=0;j<thisdroprate;j++)
|
||||
{
|
||||
thisdrop = Enemy->GetDrop( );
|
||||
if(thisdrop != NULL)
|
||||
{
|
||||
CMap* map = GServer->MapList.Index[thisdrop->posMap];
|
||||
map->AddDrop( thisdrop );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CMonster* monster = reinterpret_cast<CMonster*>(Enemy);
|
||||
if(Enemy->IsMonster())
|
||||
{
|
||||
if(monster == NULL)
|
||||
{
|
||||
//Log(MSG_DEBUG,"Monster killed. Failed to create monster object");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Log(MSG_DEBUG,"Monster killed. Setting death delay timer");
|
||||
monster->DeathDelayTimer = clock();
|
||||
}
|
||||
}
|
||||
OnEnemyDie( Enemy );
|
||||
ClearBattle(Battle);
|
||||
GServer->SendToVisible( &pak, Enemy );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDDWORD ( pak, 4 );
|
||||
GServer->SendToVisible( &pak, Enemy );
|
||||
bflag = GServer->AddBuffs( skill, Enemy, GetInt( ) );
|
||||
|
||||
//Send (de)buff information to the whole world
|
||||
if(skill->nbuffs > 0 && bflag)
|
||||
{
|
||||
RESETPACKET( pak, 0x7b5 );
|
||||
ADDWORD ( pak, Battle->skilltarget );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Battle->skillid );
|
||||
ADDWORD ( pak, GetInt( ) );
|
||||
ADDBYTE ( pak, skill->nbuffs );
|
||||
GServer->SendToVisible( &pak, Enemy );
|
||||
}
|
||||
}
|
||||
if (deBuff) return;
|
||||
//Send skill animation to the world
|
||||
RESETPACKET( pak, 0x7b9);
|
||||
ADDWORD ( pak, clientid);
|
||||
ADDWORD ( pak, Battle->skillid);
|
||||
ADDWORD ( pak, 1);
|
||||
GServer->SendToVisible( &pak, this );
|
||||
Battle->bufftarget = 0;
|
||||
Battle->skilltarget = 0;
|
||||
Battle->skillid = 0;
|
||||
//Battle->lastAtkTime = clock( );
|
||||
Battle->lastAtkTime = Battle->NextAtkTime; //trying this to time skills better
|
||||
//Battle->atktype = NORMAL_ATTACK;
|
||||
}
|
||||
|
||||
// use buff skill
|
||||
void CCharacter::UseBuffSkill( CCharacter* Target, CSkills* skill, bool deBuff )
|
||||
{
|
||||
bool bflag = false;
|
||||
bflag = GServer->AddBuffs( skill, Target, GetInt( ) );
|
||||
//Log(MSG_INFO,"In UseBuffSkills, skill %i, nbuffs %i, bflag %i to %u",skill->id,skill->nbuffs,bflag,Target->clientid);
|
||||
if(skill->nbuffs > 0 && bflag == true)
|
||||
{
|
||||
BEGINPACKET( pak, 0x7b5 );
|
||||
ADDWORD ( pak, Target->clientid );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Battle->skillid );
|
||||
ADDWORD ( pak, GetInt( )/4 );
|
||||
ADDBYTE ( pak, skill->nbuffs );
|
||||
GServer->SendToVisible( &pak, Target );
|
||||
//Log(MSG_DEBUG,"Buff Skill. buffs applied 1");
|
||||
}
|
||||
if (deBuff) return;
|
||||
BEGINPACKET( pak, 0x7b9);
|
||||
ADDWORD ( pak, clientid);
|
||||
ADDWORD ( pak, Battle->skillid);
|
||||
ADDWORD ( pak, 1);
|
||||
GServer->SendToVisible( &pak, this );
|
||||
//Log(MSG_DEBUG,"Buff Skill. buffs applied 2 nbuffs = %i",skill->nbuffs);
|
||||
//Log(MSG_INFO,"UseBuffSkill: Target HP = %i",Target->Stats->HP);
|
||||
}
|
||||
|
||||
// use buff skill from QSD
|
||||
void CCharacter::UseBuffSkillQSD( CCharacter* Target, CSkills* skill, bool deBuff )
|
||||
{
|
||||
bool bflag = false;
|
||||
bflag = GServer->AddBuffs( skill, Target, 1 );
|
||||
//Log(MSG_INFO,"In UseBuffSkillsQSD, skill %i, nbuffs %i, bflag %i to %u",skill->id,skill->nbuffs,bflag,Target->clientid);
|
||||
if(skill->nbuffs > 0 && bflag == true)
|
||||
{
|
||||
BEGINPACKET( pak, 0x7b5 );
|
||||
ADDWORD ( pak, Target->clientid );
|
||||
ADDWORD ( pak, Target->clientid );
|
||||
ADDWORD ( pak, skill->id );
|
||||
ADDWORD ( pak, 1 );
|
||||
ADDBYTE ( pak, skill->nbuffs );
|
||||
GServer->SendToVisible( &pak, Target );
|
||||
//Log(MSG_DEBUG,"Buff Skill. buffs applied 1");
|
||||
}
|
||||
if (deBuff) return;
|
||||
BEGINPACKET( pak, 0x7b9);
|
||||
ADDWORD ( pak, Target->clientid);
|
||||
ADDWORD ( pak, skill->id);
|
||||
ADDWORD ( pak, 1);
|
||||
GServer->SendToVisible( &pak, this );
|
||||
//Log(MSG_DEBUG,"Buff Skill. buffs applied 2 nbuffs = %i",skill->nbuffs);
|
||||
//Log(MSG_INFO,"UseBuffSkillQSD: Target HP = %i",Target->Stats->HP);
|
||||
}
|
||||
|
||||
// Use a skill
|
||||
bool CCharacter::UseSkill( CSkills* skill, CCharacter *Target )
|
||||
{
|
||||
if (skill->atkpower > 0)
|
||||
{
|
||||
Log(MSG_INFO, "Need to do %i%s %s in range %i to target %i",
|
||||
( skill->atkpower ),
|
||||
( (skill->range > 0) ? " AOE" : "" ),
|
||||
( (GServer->isSkillTargetFriendly(skill)) ? "healing" : "damage" ),
|
||||
( skill->range ),
|
||||
( skill->target ) );
|
||||
}
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Log(MSG_INFO, "Status ID: %i", skill->status[i]);
|
||||
if (skill->status[i] == 0) continue;
|
||||
CStatus* status = GServer->GetStatusByID(skill->status[i]);
|
||||
if (status == NULL) continue;
|
||||
/************************
|
||||
We'll probably need to use status->decrease (Figure out whether buf is up/down
|
||||
status->repeat will tell us whether it's a one-time (Stat Boost [2]), repeat
|
||||
(Recovery, continueing damage [1]) or Special (Status Effect [3]).
|
||||
************************/
|
||||
if (status->repeat == 1) // Continuous
|
||||
{
|
||||
Log(MSG_INFO, "Need to take stat %i and %s it by %i%s over %i seconds", skill->buff[i],
|
||||
((status->decrease) ? "decrease" : "increase"),
|
||||
((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]),
|
||||
((skill->value1[i] != 0) ? "" : "%"), skill->duration);
|
||||
}
|
||||
else if (status->repeat == 2) // Once (Stat Boost)
|
||||
{
|
||||
Log(MSG_INFO, "Need to take stat %i and %s it by %i%s for %i seconds", skill->buff[i],
|
||||
((status->decrease) ? "decrease" : "increase"),
|
||||
((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]),
|
||||
((skill->value1[i] != 0) ? "" : "%"), skill->duration);
|
||||
|
||||
} else if (status->repeat == 3) // Status Effect (Poison,etc)
|
||||
{
|
||||
Log(MSG_INFO, "Need to give user status effect %i for %i seconds", skill->buff[i],
|
||||
((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]),
|
||||
( skill->duration ) );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,802 @@
|
||||
/*
|
||||
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 "character.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
void CCharacter::DoAttack( )
|
||||
{
|
||||
//if(Status->Sleep <= 0xff)
|
||||
//{
|
||||
// ClearBattle( Battle );
|
||||
// return; // can't take any action while we are asleep can we?
|
||||
//}
|
||||
if(IsSummon())
|
||||
{
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL && Battle->atktype != SKILL_AOE && Battle->atktype != BUFF_AOE)
|
||||
{
|
||||
//Log(MSG_DEBUG,"No Enemy found");
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
//Log(MSG_DEBUG,"Client id: %i Enemy client id: %i",clientid, Enemy->clientid);
|
||||
if(this == Enemy) //summoned monster is attacking itself. It has been observed to happen
|
||||
{
|
||||
//Log(MSG_INFO,"Clearing Battle for this summon");
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
}
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
//Log(MSG_INFO,"DoAttack Battle Attack type = %i",Battle->atktype);
|
||||
switch(Battle->atktype)
|
||||
{
|
||||
case NORMAL_ATTACK://normal attack
|
||||
{
|
||||
//if(!Status->CanAttack)
|
||||
// return; //can't attack right now.
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(Enemy == this)
|
||||
{
|
||||
//Log(MSG_INFO,"WTF?? I AM trying to attack myself");
|
||||
ClearBattle( Battle );
|
||||
}
|
||||
if(IsTargetReached( Enemy ) && CanAttack( ))
|
||||
{
|
||||
NormalAttack( Enemy );
|
||||
if (Enemy->IsMonster()) // do monster AI script when monster is attacked.
|
||||
{
|
||||
CMonster* monster = GServer->GetMonsterByID(Enemy->clientid, Enemy->Position->Map);
|
||||
if(monster == NULL)return;
|
||||
monster->DoAi(monster->thisnpc->AI, 3);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SKILL_ATTACK://skill attack
|
||||
{
|
||||
//if(!Status->CanCastSkill)
|
||||
// return; // can't cast skills right now
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
//ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(IsTargetReached( Enemy, skill ) && CanAttack( ))
|
||||
{
|
||||
SkillAttack( Enemy, skill );
|
||||
if (Enemy->IsMonster())
|
||||
{
|
||||
CMonster* monster = GServer->GetMonsterByID(Enemy->clientid, Enemy->Position->Map);
|
||||
if(monster == NULL)return;
|
||||
monster->DoAi(monster->thisnpc->AI, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SKILL_BUFF://buffs
|
||||
{
|
||||
//if(!Status->CanCastSkill)
|
||||
// return; // can't cast skills right now
|
||||
CCharacter* Enemy = GetCharTarget( );
|
||||
if(Enemy == NULL)
|
||||
{
|
||||
//Log(MSG_DEBUG,"this target is NULL");
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
//Log(MSG_DEBUG,"this skill is NULL");
|
||||
//ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
//Log(MSG_DEBUG,"Called skill buff code for skill %i",skill->id);
|
||||
if(IsTargetReached( Enemy, skill ) && CanAttack( ))
|
||||
{
|
||||
//Log(MSG_DEBUG,"Skill successfully cast");
|
||||
BuffSkill( Enemy, skill );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SKILL_AOE:
|
||||
//case SKILL_SELF: //this is impossible. it can never be set
|
||||
case AOE_TARGET:
|
||||
{
|
||||
//if(!Status->CanCastSkill)
|
||||
// return; // can't cast skills right now
|
||||
//Log(MSG_DEBUG,"Called skill type %i",Battle->atktype);
|
||||
CCharacter* Enemy = NULL;
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
if(Battle->atktype == AOE_TARGET)
|
||||
{
|
||||
float distance = GServer->distance( Position->current, Position->destiny );
|
||||
if(distance <= skill->range && CanAttack( ))
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
AoeSkill( skill, Enemy );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Position->skilltargetpos = Position->current;
|
||||
AoeSkill( skill, NULL );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BUFF_SELF:
|
||||
{
|
||||
//if(!Status->CanCastSkill)
|
||||
// return; // can't cast skills right now
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
//ClearBattle( Battle );
|
||||
return;
|
||||
}
|
||||
BuffSkill( this, skill );
|
||||
}
|
||||
break;
|
||||
case BUFF_AOE:
|
||||
{
|
||||
//if(!Status->CanCastSkill)
|
||||
// return; // can't cast skills right now
|
||||
Log(MSG_INFO,"BUFF AOE selected");
|
||||
CSkills* skill = GServer->GetSkillByID( Battle->skillid );
|
||||
if(skill == NULL)
|
||||
{
|
||||
//ClearBattle( Battle );
|
||||
|
||||
return;
|
||||
}
|
||||
AoeBuff( skill );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// do normal attack
|
||||
void CCharacter::NormalAttack( CCharacter* Enemy )
|
||||
{
|
||||
Enemy->OnBeAttacked( this );
|
||||
Position->destiny = Position->current;
|
||||
// PY code
|
||||
float mod = 0;
|
||||
if(IsPlayer())
|
||||
{
|
||||
mod = 23 + (Stats->Level / 1.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
mod = 23 + (Stats->Level / 4);
|
||||
|
||||
}
|
||||
float attack = 0;
|
||||
if(Stats->magicattack == 1)
|
||||
{
|
||||
attack = (float) Stats->Attack_Power / Enemy->Stats->Magic_Defense;
|
||||
attack *= mod;
|
||||
}
|
||||
else
|
||||
{
|
||||
attack = (float) Stats->Attack_Power / Enemy->Stats->Defense;
|
||||
attack *= mod;
|
||||
}
|
||||
//PY end
|
||||
if(attack < 5) attack = 5;
|
||||
float d_attack = attack / 100;
|
||||
mod = GServer->RandNumber( 0, 10 ) * d_attack;
|
||||
attack += mod;
|
||||
long int hitpower = (long int)floor(attack + GServer->RandNumber(0, 10));
|
||||
if(IsPlayer( )) //temp fix to find balance btw monster and player
|
||||
{
|
||||
hitpower = (long int)floor(attack * (GServer->Config.PlayerDmg/100.00));
|
||||
hitpower+=((hitpower*(Stats->ExtraDamage))/100);
|
||||
}
|
||||
if(IsMonster( )) //temp fix to find balance btw monster and player
|
||||
hitpower = (long int)floor(attack * (GServer->Config.MonsterDmg/100.00));
|
||||
bool critical = false;
|
||||
if(hitpower <= 5)
|
||||
{
|
||||
hitpower = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(GServer->RandNumber(0,300)<Stats->Critical)
|
||||
{
|
||||
hitpower = (long int)floor(hitpower * 1.5);
|
||||
critical = true;
|
||||
}
|
||||
}
|
||||
// dodge
|
||||
unsigned int hitvalue = (unsigned int)floor((double)(Stats->Accury * 100 / Enemy->Stats->Dodge));
|
||||
if(hitvalue>100) hitvalue = 100;
|
||||
if(GServer->RandNumber( 0, 100 )>hitvalue)
|
||||
hitpower = 0; // dodged
|
||||
if (hitpower > 0x7ff)//2047 packet size limit.
|
||||
{
|
||||
hitpower=0x7ff;
|
||||
}
|
||||
|
||||
//maybe this little line here will fix my ghosty problem
|
||||
//they are definitely attacking. But no animation so maybe atktarget is empty
|
||||
Battle->atktarget = Battle->target;
|
||||
//Well it does make the damage show up at teh client but the animation is still dodgey
|
||||
|
||||
if(IsMonster())
|
||||
{
|
||||
Log(MSG_INFO,"Monster hits player for %i damage. Attack target = %i",hitpower,Battle->atktarget);
|
||||
}
|
||||
//Log( MSG_INFO, "hitpower %i. Attack %f ", hitpower,attack );
|
||||
if(!Enemy->IsSummon( ) && Enemy->IsMonster( ))
|
||||
{
|
||||
Enemy->AddDamage( this, hitpower );
|
||||
Enemy->damagecounter += hitpower;// is for AI
|
||||
}
|
||||
Enemy->Stats->HP -= hitpower;
|
||||
|
||||
|
||||
|
||||
BEGINPACKET( pak, 0x799 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Battle->atktarget );
|
||||
#ifdef USE124
|
||||
ADDDWORD ( pak, hitpower );
|
||||
#endif
|
||||
|
||||
if(Enemy->IsDead())
|
||||
{
|
||||
CDrop* thisdrop = NULL;
|
||||
#ifndef USE124
|
||||
ADDWORD ( pak, (hitpower | ( critical?0xb000:0x8000 ) ));
|
||||
#else
|
||||
ADDDWORD ( pak, critical?28:16 );
|
||||
#endif
|
||||
if(!Enemy->IsSummon( ) && !Enemy->IsPlayer( ))
|
||||
{
|
||||
thisdrop = Enemy->GetDrop( );
|
||||
if(thisdrop!=NULL)
|
||||
{
|
||||
ADDFLOAT ( pak, thisdrop->pos.x*100 );
|
||||
ADDFLOAT ( pak, thisdrop->pos.y*100 );
|
||||
if(thisdrop->type == 1)
|
||||
{
|
||||
ADDWORD( pak, 0xccdf );
|
||||
ADDDWORD( pak, thisdrop->amount );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, GServer->BuildItemHead( thisdrop->item ) );
|
||||
ADDDWORD ( pak, GServer->BuildItemData( thisdrop->item ) );
|
||||
}
|
||||
ADDWORD ( pak, thisdrop->clientid );
|
||||
ADDWORD ( pak, thisdrop->owner );
|
||||
CMap* map = GServer->MapList.Index[thisdrop->posMap];
|
||||
map->AddDrop( thisdrop );
|
||||
}
|
||||
}
|
||||
GServer->SendToVisible( &pak, Enemy, thisdrop );
|
||||
OnEnemyDie( Enemy );
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef USE124
|
||||
ADDWORD ( pak, (hitpower|(hitpower>0?(critical?0x4000:0):0)));
|
||||
#else
|
||||
ADDDWORD ( pak, (hitpower>0?(critical?12:0):0) );
|
||||
#endif
|
||||
GServer->SendToVisible( &pak, Enemy );
|
||||
}
|
||||
ReduceABC( );
|
||||
Battle->lastAtkTime = clock( );
|
||||
}
|
||||
|
||||
// do skill attack
|
||||
bool CCharacter::SkillAttack( CCharacter* Enemy, CSkills* skill )
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
//if(Battle->castTime==0)
|
||||
//{
|
||||
BEGINPACKET( pak, 0x7bb );
|
||||
ADDWORD ( pak, clientid );
|
||||
GServer->SendToVisible( &pak, this );
|
||||
// Battle->castTime = clock();
|
||||
// return true;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// clock_t etime = clock() - Battle->castTime;
|
||||
// if(etime<SKILL_DELAY)
|
||||
// return true;
|
||||
//}
|
||||
//Battle->castTime = 0;
|
||||
UseAtkSkill( Enemy, skill );
|
||||
Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100));
|
||||
if(Stats->MP<0) Stats->MP=0;
|
||||
if(Battle->contatk)
|
||||
{
|
||||
Battle->atktype = NORMAL_ATTACK;
|
||||
Battle->skilltarget = 0;
|
||||
Battle->atktarget = Battle->target;
|
||||
Battle->skillid = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//ClearBattle( Battle );
|
||||
}
|
||||
GServer->DoSkillScript( this, skill );
|
||||
Battle->lastAtkTime = clock( );
|
||||
return true;
|
||||
}
|
||||
|
||||
// do buff skill
|
||||
bool CCharacter::BuffSkill( CCharacter* Target, CSkills* skill )
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
//if(Battle->castTime == 0)
|
||||
//{
|
||||
BEGINPACKET( pak, 0x7bb );
|
||||
ADDWORD ( pak, clientid );
|
||||
GServer->SendToVisible( &pak, (CCharacter*)this );
|
||||
// Battle->castTime = clock();
|
||||
// return true;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// clock_t etime = clock() - Battle->castTime;
|
||||
// if(etime < SKILL_DELAY)
|
||||
// return true;
|
||||
//}
|
||||
//Battle->castTime = 0;
|
||||
//Log(MSG_DEBUG,"applied buff skill %i",skill->id);
|
||||
UseBuffSkill( Target, skill );
|
||||
Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100));
|
||||
if(Stats->MP < 0) Stats->MP = 0;
|
||||
GServer->DoSkillScript( this, skill ); //summons a monster. Why do we do this for all skills?
|
||||
if(!IsMonster())
|
||||
{
|
||||
ClearBattle( Battle ); // clear battle for players when they use buff skills
|
||||
//Battle->bufftarget = 0;
|
||||
//Battle->skilltarget = 0;
|
||||
//Battle->skillid = 0;
|
||||
//Battle->atktype = NORMAL_ATTACK;
|
||||
}
|
||||
else //Monsters need to be reset to normal attack and clear skill attacks.
|
||||
{
|
||||
Battle->bufftarget = 0;
|
||||
Battle->skilltarget = 0;
|
||||
Battle->skillid = 0;
|
||||
Battle->atktype = NORMAL_ATTACK;
|
||||
}
|
||||
Battle->lastAtkTime = clock( );
|
||||
return true;
|
||||
}
|
||||
//arnold
|
||||
// do AoE skill
|
||||
bool CCharacter::AoeSkill( CSkills* skill, CCharacter* Enemy )
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
//Log(MSG_INFO,"Doing AOE Skill");
|
||||
//if(Battle->castTime == 0)
|
||||
//{
|
||||
BEGINPACKET( pak, 0x7bb );
|
||||
ADDWORD ( pak, clientid );
|
||||
GServer->SendToVisible( &pak, (CCharacter*)this );
|
||||
// Battle->castTime = clock();
|
||||
// return true;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// clock_t etime = clock() - Battle->castTime;
|
||||
// if(etime < SKILL_DELAY)
|
||||
// return true;
|
||||
//}
|
||||
//Battle->castTime = 0;
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
|
||||
//PY replacement code
|
||||
if(IsPlayer() || IsSummon())
|
||||
{
|
||||
for(UINT i=0;i<map->MonsterList.size();i++)
|
||||
{
|
||||
CMonster* monster = map->MonsterList.at(i);
|
||||
if(monster->clientid == clientid) continue;
|
||||
if(monster->IsSummon( ) && (map->allowpvp == 0 || monster->owner == clientid)) continue;
|
||||
if(GServer->IsMonInCircle( Position->skilltargetpos, monster->Position->current,(float)skill->aoeradius + 1))
|
||||
UseAtkSkill( (CCharacter*) monster, skill );
|
||||
}
|
||||
}
|
||||
if(IsMonster() && !IsSummon())
|
||||
{
|
||||
for(UINT i=0;i<map->PlayerList.size();i++)
|
||||
{
|
||||
CPlayer* player = map->PlayerList.at(i);
|
||||
if(player->clientid == clientid) continue;
|
||||
if(GServer->IsMonInCircle( Position->skilltargetpos,player->Position->current,(float)skill->aoeradius+1))
|
||||
UseAtkSkill( (CCharacter*) player, skill );
|
||||
}
|
||||
}
|
||||
//PY end
|
||||
|
||||
// this code appears to me to be completely wrong replaced with code above
|
||||
/*
|
||||
for(UINT i=0;i<map->MonsterList.size();i++)
|
||||
{
|
||||
CMonster* monster = map->MonsterList.at(i);
|
||||
if(monster->clientid == clientid) continue;
|
||||
if(IsSummon( ) || IsPlayer( ))
|
||||
{
|
||||
if(monster->IsSummon( ) && (map->allowpvp==0 || monster->owner == clientid)) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!monster->IsSummon( )) continue;
|
||||
}
|
||||
if(GServer->IsMonInCircle( Position->skilltargetpos, monster->Position->current,(float)skill->aoeradius + 1))
|
||||
UseAtkSkill( (CCharacter*) monster, skill );
|
||||
}
|
||||
if(map->allowpvp!=0 || (IsMonster( ) && !IsSummon( )))
|
||||
{
|
||||
for(UINT i=0;i<map->PlayerList.size();i++)
|
||||
{
|
||||
CPlayer* player = map->PlayerList.at(i);
|
||||
if(player->clientid==clientid) continue;
|
||||
if(GServer->IsMonInCircle( Position->skilltargetpos,player->Position->current,(float)skill->aoeradius+1))
|
||||
UseAtkSkill( (CCharacter*) player, skill );
|
||||
}
|
||||
}*/
|
||||
|
||||
if(Enemy != NULL)
|
||||
{
|
||||
if(!Enemy->IsDead( ))
|
||||
{
|
||||
Battle->atktarget = Battle->target;
|
||||
Battle->atktype = NORMAL_ATTACK;
|
||||
Battle->skilltarget = 0;
|
||||
Battle->skillid = 0;
|
||||
}
|
||||
else ClearBattle( Battle );
|
||||
}
|
||||
else ClearBattle( Battle );
|
||||
Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100));
|
||||
if(Stats->MP < 0) Stats->MP = 0;
|
||||
Battle->lastAtkTime = clock( );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCharacter::AoeBuff( CSkills* skill )
|
||||
{
|
||||
//Log(MSG_INFO,"BUFF AOE called");
|
||||
Position->destiny = Position->current;
|
||||
BEGINPACKET( pak, 0x7bb );
|
||||
ADDWORD ( pak, clientid );
|
||||
GServer->SendToVisible( &pak, (CCharacter*)this );
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
|
||||
if(CharType == 1 || CharType == 3) //player || Summon
|
||||
{
|
||||
for(UINT i=0;i<map->PlayerList.size();i++)
|
||||
{
|
||||
CPlayer* player = map->PlayerList.at(i);
|
||||
switch(skill->target)
|
||||
{
|
||||
case 1: //tPartyMember
|
||||
if(player->Party->party == GetParty( ))
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough party member");
|
||||
if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1))
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
break;
|
||||
case 2: //tClanMember
|
||||
if (player->Clan == GetClan( ))
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough clan member");
|
||||
if(GServer->IsMonInCircle( Position->current, player->Position->current, (float)skill->aoeradius + 1 ) )
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
break;
|
||||
case 3: //tAlly
|
||||
case 7: //tAllCharacters
|
||||
case 8: //tAllMembers. all characters
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough allied Player");
|
||||
if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1))
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
break;
|
||||
case 5: //tHostileCharacter
|
||||
{
|
||||
if (CharType == 2)
|
||||
{
|
||||
//Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough hostile Player");
|
||||
if(GServer->IsMonInCircle( Position->current,player->Position->current,(float)skill->aoeradius + 1))
|
||||
UseBuffSkill( (CCharacter*)player, skill );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (CharType == 2) //monster
|
||||
{
|
||||
for(UINT i=0;i<map->MonsterList.size();i++)
|
||||
{
|
||||
CMonster* monster = map->MonsterList.at(i);
|
||||
switch(skill->target)
|
||||
{
|
||||
case tPartyMember: // party
|
||||
break;
|
||||
case 2: //tClanMember
|
||||
break;
|
||||
case 3: //tAlly
|
||||
case 7: //tAllCharacters
|
||||
case 8: //tAllMembers. all characters
|
||||
{
|
||||
Log(MSG_INFO,"Applying AOE buff as long as I can find a close enough monster");
|
||||
if(GServer->IsMonInCircle( Position->current,monster->Position->current,(float)skill->aoeradius + 1))
|
||||
UseBuffSkill( (CCharacter*)monster, skill );
|
||||
}
|
||||
break;
|
||||
case tHostileCharacter:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Stats->MP -= (skill->mp - (skill->mp * Stats->MPReduction / 100));
|
||||
if(Stats->MP < 0) Stats->MP = 0;
|
||||
Battle->bufftarget = 0;
|
||||
Battle->skilltarget = 0;
|
||||
Battle->skillid = 0;
|
||||
Battle->atktype = NORMAL_ATTACK;
|
||||
//ClearBattle( Battle );
|
||||
Battle->lastAtkTime = clock( );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// use skill attack
|
||||
void CCharacter::UseAtkSkill( CCharacter* Enemy, CSkills* skill, bool deBuff)
|
||||
{
|
||||
long int skillpower = 0;
|
||||
if(skill->skilltype == 6 || skill->skilltype == 7) //magical damage
|
||||
{
|
||||
skillpower = skill->atkpower * 2;
|
||||
}
|
||||
else // physical damage
|
||||
{
|
||||
skillpower = skill->atkpower * 2;
|
||||
}
|
||||
UINT weapontype = 0;
|
||||
|
||||
float mod = 0;
|
||||
float attack = 0;
|
||||
if(IsPlayer())
|
||||
{
|
||||
mod = 35 + (Stats->Level / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
mod = 35 + (Stats->Level / 2);
|
||||
}
|
||||
if(skill->skilltype == 6 || skill->skilltype == 7) //magical damage
|
||||
{
|
||||
attack = skillpower / Enemy->Stats->Magic_Defense;
|
||||
}
|
||||
else
|
||||
{
|
||||
attack = skillpower / Enemy->Stats->Defense;
|
||||
}
|
||||
attack *= mod;
|
||||
attack += (Stats->Attack_Power / 2); // let's add our AP to the skillpower to make it worthwhile to cast skills.
|
||||
|
||||
skillpower = (int)attack;
|
||||
bool bflag = false;
|
||||
Enemy->OnBeAttacked( this );
|
||||
if(skillpower <= 0) skillpower = 0;
|
||||
if(IsPlayer())
|
||||
{
|
||||
skillpower += (int)(skillpower * (Stats->ExtraDamage))/100;
|
||||
}
|
||||
if (skill->atkpower == 0)
|
||||
{
|
||||
skillpower = 0; // If the skill has a 0 atk power, it isn't meant to damage
|
||||
Log( MSG_INFO, "This skill does no damage" );
|
||||
//ClearBattle( Battle );
|
||||
return; // probably need to add some alternative code here for legitimate skills that have no attack power.
|
||||
}
|
||||
if(skillpower >2046)skillpower = 2046;
|
||||
if(!Enemy->IsSummon( ) && Enemy->IsMonster( ))
|
||||
{
|
||||
Enemy->AddDamage( this, skillpower );
|
||||
Enemy->damagecounter += skillpower;// is for AI
|
||||
}
|
||||
Enemy->Stats->HP -= skillpower;
|
||||
BEGINPACKET( pak, 0x7b6 );
|
||||
ADDWORD ( pak, Enemy->clientid );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDDWORD ( pak, 0x000007f8 );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
if (Enemy->Stats->HP<1)
|
||||
{
|
||||
ADDWORD ( pak, skillpower + 0x8000 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, skillpower + 0x2000 );
|
||||
}
|
||||
if(Enemy->IsDead())
|
||||
{
|
||||
CDrop* thisdrop = NULL;
|
||||
ADDDWORD ( pak, 16 );
|
||||
if(!Enemy->IsSummon( ) && !Enemy->IsPlayer( ))
|
||||
{
|
||||
thisdrop = Enemy->GetDrop( );
|
||||
if(thisdrop!=NULL)
|
||||
{
|
||||
ADDFLOAT ( pak, thisdrop->pos.x*100 );
|
||||
ADDFLOAT ( pak, thisdrop->pos.y*100 );
|
||||
if(thisdrop->type==1)
|
||||
{
|
||||
ADDWORD( pak, 0xccdf );
|
||||
ADDDWORD( pak, thisdrop->amount );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, GServer->BuildItemHead( thisdrop->item ) );
|
||||
ADDDWORD ( pak, GServer->BuildItemData( thisdrop->item ) );
|
||||
}
|
||||
ADDWORD ( pak, thisdrop->clientid );
|
||||
ADDWORD ( pak, thisdrop->owner );
|
||||
CMap* map = GServer->MapList.Index[thisdrop->posMap];
|
||||
map->AddDrop( thisdrop );
|
||||
}
|
||||
}
|
||||
GServer->SendToVisible( &pak, Enemy, thisdrop );
|
||||
OnEnemyDie( Enemy );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDDWORD ( pak, 4 );
|
||||
GServer->SendToVisible( &pak, Enemy );
|
||||
bflag = GServer->AddBuffs( skill, Enemy, GetInt( ) );
|
||||
|
||||
//Send (de)buff information to the whole world
|
||||
if(skill->nbuffs>0 && bflag)
|
||||
{
|
||||
BEGINPACKET( pak, 0x7b5 );
|
||||
ADDWORD ( pak, Battle->skilltarget );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Battle->skillid );
|
||||
ADDWORD ( pak, GetInt( ) );
|
||||
ADDBYTE ( pak, skill->nbuffs );
|
||||
GServer->SendToVisible( &pak, Enemy );
|
||||
}
|
||||
}
|
||||
if (deBuff) return;
|
||||
//Send skill animation to the world
|
||||
RESETPACKET( pak, 0x7b9);
|
||||
ADDWORD ( pak, clientid);
|
||||
ADDWORD ( pak, Battle->skillid);
|
||||
ADDWORD ( pak, 1);
|
||||
GServer->SendToVisible( &pak, this );
|
||||
Battle->bufftarget = 0;
|
||||
Battle->skilltarget = 0;
|
||||
Battle->skillid = 0;
|
||||
Battle->atktype = NORMAL_ATTACK;
|
||||
}
|
||||
|
||||
// use buff skill
|
||||
void CCharacter::UseBuffSkill( CCharacter* Target, CSkills* skill )
|
||||
{
|
||||
bool bflag = false;
|
||||
bflag = GServer->AddBuffs( skill, Target, GetInt( ) );
|
||||
if(skill->nbuffs > 0 && bflag == true)
|
||||
{
|
||||
BEGINPACKET( pak, 0x7b5 );
|
||||
ADDWORD ( pak, Target->clientid );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Battle->skillid );
|
||||
ADDWORD ( pak, GetInt( )/4 );
|
||||
ADDBYTE ( pak, skill->nbuffs );
|
||||
GServer->SendToVisible( &pak, Target );
|
||||
//Log(MSG_DEBUG,"Buff Skill. buffs applied 1");
|
||||
}
|
||||
|
||||
BEGINPACKET( pak, 0x7b9);
|
||||
ADDWORD ( pak, clientid);
|
||||
ADDWORD ( pak, Battle->skillid);
|
||||
ADDWORD ( pak, 1);
|
||||
GServer->SendToVisible( &pak, this );
|
||||
//Log(MSG_DEBUG,"Buff Skill. buffs applied 2 nbuffs = %i",skill->nbuffs);
|
||||
//Log(MSG_INFO,"Target HP = %i",Target->Stats->HP);
|
||||
}
|
||||
|
||||
// Use a skill
|
||||
bool CCharacter::UseSkill( CSkills* skill, CCharacter *Target )
|
||||
{
|
||||
if (skill->atkpower > 0)
|
||||
{
|
||||
Log(MSG_INFO, "Need to do %i%s %s in range %i to target %i",
|
||||
( skill->atkpower ),
|
||||
( (skill->range > 0) ? " AOE" : "" ),
|
||||
( (GServer->isSkillTargetFriendly(skill)) ? "healing" : "damage" ),
|
||||
( skill->range ),
|
||||
( skill->target ) );
|
||||
}
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Log(MSG_INFO, "Status ID: %i", skill->status[i]);
|
||||
if (skill->status[i] == 0) continue;
|
||||
CStatus* status = GServer->GetStatusByID(skill->status[i]);
|
||||
if (status == NULL) continue;
|
||||
/************************
|
||||
We'll probably need to use status->decrease (Figure out whether buf is up/down
|
||||
status->repeat will tell us whether it's a one-time (Stat Boost [2]), repeat
|
||||
(Recovery, continueing damage [1]) or Special (Status Effect [3]).
|
||||
************************/
|
||||
if (status->repeat == 1) // Continuous
|
||||
{
|
||||
Log(MSG_INFO, "Need to take stat %i and %s it by %i%s over %i seconds", skill->buff[i],
|
||||
((status->decrease) ? "decrease" : "increase"),
|
||||
((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]),
|
||||
((skill->value1[i] != 0) ? "" : "%"), skill->duration);
|
||||
}
|
||||
else if (status->repeat == 2) // Once (Stat Boost)
|
||||
{
|
||||
Log(MSG_INFO, "Need to take stat %i and %s it by %i%s for %i seconds", skill->buff[i],
|
||||
((status->decrease) ? "decrease" : "increase"),
|
||||
((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]),
|
||||
((skill->value1[i] != 0) ? "" : "%"), skill->duration);
|
||||
|
||||
} else if (status->repeat == 3) // Status Effect (Poison,etc)
|
||||
{
|
||||
Log(MSG_INFO, "Need to give user status effect %i for %i seconds", skill->buff[i],
|
||||
((skill->value1[i] != 0) ? skill->value1[i] : skill->value2[i]),
|
||||
( skill->duration ) );
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
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 "character.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
//constructor
|
||||
CCharacter::CCharacter( )
|
||||
{
|
||||
// STATS
|
||||
Stats = new STATS;
|
||||
//refnpc=NULL;
|
||||
assert(Stats);
|
||||
Stats->Level = 0;
|
||||
Stats->HP = 0;
|
||||
Stats->MP = 0;
|
||||
Stats->MaxHP = 0;
|
||||
Stats->MaxMP = 0;
|
||||
Stats->Attack_Power = 0;
|
||||
Stats->Defense = 0;
|
||||
Stats->Critical = 0;
|
||||
Stats->Dodge = 0;
|
||||
Stats->Accury = 0;
|
||||
Stats->Magic_Defense = 0;
|
||||
Stats->Move_Speed = 0;
|
||||
Stats->Attack_Speed = 0;
|
||||
Stats->Attack_Distance = 0;
|
||||
Stats->MaxWeight = 0;
|
||||
Stats->MaxSummonGauge = 0;
|
||||
Stats->MPReduction = 0;
|
||||
Stats->ExtraDamage = 0;
|
||||
// POSITION
|
||||
Position = new POSITION;
|
||||
assert(Position);
|
||||
Position->Map = 0;
|
||||
Position->lastMoveTime = 0;
|
||||
Position->saved = 0;
|
||||
Position->respawn = 0;
|
||||
//BATLE
|
||||
Battle = new BATTLE;
|
||||
assert(Battle);
|
||||
ClearBattle( Battle );
|
||||
Battle->castTime = 0;
|
||||
//STATUS
|
||||
Status = new STATUS;
|
||||
assert(Status);
|
||||
//Stats up
|
||||
Status->Attack_up = 0xff;
|
||||
Status->Defense_up = 0xff;
|
||||
Status->Magic_Defense_up = 0xff;
|
||||
Status->Accury_up = 0xff;
|
||||
Status->Critical_up = 0xff;
|
||||
Status->Dodge_up = 0xff;
|
||||
Status->Haste_up = 0xff;
|
||||
Status->Dash_up = 0xff;
|
||||
Status->HP_up = 0xff;
|
||||
Status->MP_up = 0xff;
|
||||
//Stats down
|
||||
Status->Attack_down = 0xff;
|
||||
Status->Defense_down = 0xff;
|
||||
Status->Magic_Defense_down = 0xff;
|
||||
Status->Accury_down = 0xff;
|
||||
Status->Critical_down = 0xff;
|
||||
Status->Dodge_down = 0xff;
|
||||
Status->Haste_down = 0xff;
|
||||
Status->Dash_down = 0xff;
|
||||
Status->HP_down = 0xff;
|
||||
Status->MP_down = 0xff;
|
||||
//Status
|
||||
Status->Stuned = 0xff;
|
||||
Status->Poisoned = 0xff;
|
||||
Status->Muted = 0xff;
|
||||
Status->ExtraDamage_up = 0xff;
|
||||
Status->ExtraDamage_down = 0xff;
|
||||
Status->Sleep = 0xff;
|
||||
Status->CanAttack = true;
|
||||
Status->CanRun = true;
|
||||
Status->CanMove = true;
|
||||
Status->CanCastSkill = true;
|
||||
// Stance
|
||||
Status->Stance = RUNNING;
|
||||
for(int i=0;i<30;i++)
|
||||
{
|
||||
MagicStatus[i].Status = 0;
|
||||
MagicStatus[i].Buff = 0;
|
||||
MagicStatus[i].Value =0;
|
||||
MagicStatus[i].Duration = 0;
|
||||
MagicStatus[i].BuffTime = 0;
|
||||
}
|
||||
CharType = 0;
|
||||
clientid = 0xffff;
|
||||
nearChar = NULL;
|
||||
findChar = NULL;
|
||||
}
|
||||
|
||||
//deconstructor
|
||||
CCharacter::~CCharacter( )
|
||||
{
|
||||
delete Stats;
|
||||
delete Position;
|
||||
delete Battle;
|
||||
delete Status;
|
||||
}
|
||||
|
||||
// Virtual
|
||||
bool CCharacter::UpdateValues( )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// update position
|
||||
void CCharacter::UpdatePosition( )
|
||||
{
|
||||
float ntime;
|
||||
clock_t etime = clock() - Position->lastMoveTime;
|
||||
if(IsOnBattle( ) && Battle->target != 0)
|
||||
{
|
||||
CCharacter* Target = GetCharTarget( );
|
||||
if(Target == NULL)
|
||||
ClearBattle( Battle );
|
||||
}
|
||||
if(!IsMoving())
|
||||
{
|
||||
if(IsPlayer())Position->lastMoveTime = clock();
|
||||
return;
|
||||
}
|
||||
float dx = Position->destiny.x - Position->current.x;
|
||||
float dy = Position->destiny.y - Position->current.y;
|
||||
float distance = sqrt( (dx*dx) + (dy*dy) );
|
||||
if(IsPlayer())
|
||||
{
|
||||
|
||||
ntime = ( distance / (float(Stats->Move_Speed)/GServer->MOVE_SPEED_MOD)) * CLOCKS_PER_SEC; // calculates clock ticks to target position
|
||||
//Log( MSG_DEBUG, "Dist: %f Speed: %i Mod: %i ntime: %f", distance, Stats->Move_Speed, GServer->MOVE_SPEED_MOD, ntime);
|
||||
}
|
||||
if(IsMonster())
|
||||
{
|
||||
ntime = ( distance / (float(Stats->Move_Speed)/GServer->MOVE_SPEED_MOD)) * CLOCKS_PER_SEC; // calculates clock ticks to target position
|
||||
Position->current.x = dx*(etime/ntime) + Position->current.x;
|
||||
Position->current.y = dy*(etime/ntime) + Position->current.y;
|
||||
|
||||
}
|
||||
//float ntime = ( distance / Stats->Move_Speed * GServer->MOVE_SPEED_MOD ); // lmame method
|
||||
/*if(IsPlayer())
|
||||
{
|
||||
Log(MSG_DEBUG,"ntime = %f distance = %f ",ntime, distance );
|
||||
}*/
|
||||
|
||||
if(IsOnBattle() && distance <= Stats->Attack_Distance) //this will prevent the character from moving if in battle and in range
|
||||
{
|
||||
Position->destiny.x = Position->current.x;
|
||||
Position->destiny.y = Position->current.y;
|
||||
dx = 0;
|
||||
dy = 0;
|
||||
}
|
||||
if (ntime <= etime || distance == 0)
|
||||
{
|
||||
/*if(IsPlayer())
|
||||
{
|
||||
Log(MSG_DEBUG,"Movement timed out or target reached. Set current to destiny.");
|
||||
}*/
|
||||
Position->current.x = Position->destiny.x;
|
||||
Position->current.y = Position->destiny.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(IsPlayer())
|
||||
{
|
||||
/*{
|
||||
Log(MSG_DEBUG,"Still moving. Distance to target %f time to target %f", distance, ntime);
|
||||
}*/
|
||||
Position->current.x = dx*(etime/ntime) + Position->current.x;
|
||||
Position->current.y = dy*(etime/ntime) + Position->current.y;
|
||||
}
|
||||
}
|
||||
/*if(IsPlayer())
|
||||
{
|
||||
if(Position->current.x == Position->destiny.x)
|
||||
{
|
||||
Log(MSG_DEBUG,"Character is at destiny position in the X direction.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(MSG_DEBUG,"Character is still moving. current x %f Destination x %f",Position->current.x,Position->destiny.x);
|
||||
}
|
||||
}*/
|
||||
Position->lastMoveTime = clock();
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#ifndef _ROSE_CHARACTER_
|
||||
#define _ROSE_CHARACTER_
|
||||
#include "chartype.h"
|
||||
//#include "party.h"
|
||||
|
||||
class CCharacter
|
||||
{
|
||||
public:
|
||||
CCharacter( );
|
||||
~CCharacter( );
|
||||
|
||||
unsigned int clientid;
|
||||
BYTE CharType; // 0 = undefined | 1 = player | 2 = monster | 3 = summon | 4 = NPC
|
||||
CCharacter* nearChar;
|
||||
CCharacter* findChar;
|
||||
int damagecounter;
|
||||
|
||||
STATS *Stats;
|
||||
POSITION *Position;
|
||||
BATTLE* Battle;
|
||||
STATUS* Status;
|
||||
MAGICSTATUS MagicStatus[30];
|
||||
int AIVar[20];
|
||||
|
||||
// Batle Functions
|
||||
void DoAttack( );
|
||||
void NormalAttack( CCharacter* Enemy );
|
||||
bool SkillAttack( CCharacter* Enemy, CSkills* skill );
|
||||
bool BuffSkill( CCharacter* Target, CSkills* skill );
|
||||
bool AoeSkill( CSkills* skill, CCharacter* Enemy=NULL );
|
||||
bool AoeBuff( CSkills* skill );
|
||||
void UseBuffSkill( CCharacter* Target, CSkills* skill, bool deBuff = false );
|
||||
void UseBuffSkillQSD( CCharacter* Target, CSkills* skill, bool deBuff = false );
|
||||
|
||||
//void UseAtkSkill( CCharacter* Enemy, CSkills* skill );
|
||||
void UseAtkSkill( CCharacter* Enemy, CSkills* skill, bool deBuff= false );
|
||||
int GetHitRate(CCharacter* pDEF);
|
||||
int GetCritRate();
|
||||
int GetAtkDmg(CCharacter* pDEF, word wHitCNT, int iSuc);
|
||||
int GetMgcDmg(CCharacter* pDEF, word wHitCNT, int iSuc);
|
||||
|
||||
bool UseSkill( CSkills* skill, CCharacter *Target = NULL );
|
||||
//functions
|
||||
bool IsMonster( );
|
||||
bool IsPlayer( );
|
||||
bool IsDead( );
|
||||
bool IsSummon( );
|
||||
bool IsOnBattle( );
|
||||
bool IsNPC( );
|
||||
void StartAction( CCharacter* Target, BYTE action, UINT skillid=0 );
|
||||
//void StartAction2( CCharacter* Target, BYTE action, UINT skillid=0, bool restart=false, CCharacter* receiver=NULL);
|
||||
bool IsTargetReached( CCharacter* , CSkills* skill=NULL );
|
||||
bool CanAttack( );
|
||||
bool IsMoving( );
|
||||
CCharacter* GetCharTarget( );
|
||||
bool IsAttacking( );
|
||||
|
||||
// virtual functions
|
||||
// Events
|
||||
virtual bool OnBeAttacked( CCharacter* );
|
||||
virtual bool OnDie( );
|
||||
virtual bool OnEnemyDie( CCharacter* );
|
||||
virtual bool OnEnemyOnSight( CCharacter* );
|
||||
virtual bool OnSpawn( bool );
|
||||
virtual bool OnAlmostDie( );
|
||||
virtual bool OnFar( );
|
||||
|
||||
// Functions
|
||||
virtual CDrop* GetDrop( );
|
||||
virtual void AddDamage( CCharacter* enemy, long int hitpower);
|
||||
virtual void UpdatePosition( );
|
||||
virtual bool UpdateValues( );
|
||||
virtual void ReduceABC( );
|
||||
virtual int GetEVBuff( );
|
||||
void RefreshBuff( );
|
||||
virtual CParty* GetParty( );
|
||||
virtual CLAN* GetClan( );
|
||||
|
||||
// stats
|
||||
virtual unsigned int GetAttackPower( );
|
||||
virtual unsigned int GetDefense( );
|
||||
virtual unsigned int GetXPRate( );
|
||||
virtual unsigned int GetItemDropRate( );
|
||||
virtual unsigned int GetItemDropCountRate( );
|
||||
virtual unsigned int GetDodge( );
|
||||
virtual unsigned int GetAccury( );
|
||||
virtual unsigned int GetMagicDefense( );
|
||||
virtual unsigned int GetMagicAttack( );
|
||||
virtual unsigned int GetCritical( );
|
||||
virtual unsigned int GetAttackSpeed( );
|
||||
virtual unsigned int GetMoveSpeed( );
|
||||
virtual unsigned int GetMaxHP( );
|
||||
virtual unsigned int GetMaxMP( );
|
||||
virtual float GetAttackDistance( );
|
||||
virtual unsigned int GetInt( );
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
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 "character.h"
|
||||
|
||||
//virtual [called when a enemy hit this character]
|
||||
bool CCharacter::OnBeAttacked( CCharacter* Enemy )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// virtual [called when this character die]
|
||||
bool CCharacter::OnDie( )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// virtual [called when enemy die]
|
||||
bool CCharacter::OnEnemyDie( CCharacter* Enemy )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// virtual [called when a enmy is on sigh]
|
||||
bool CCharacter::OnEnemyOnSight( CCharacter* Enemy )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// virtual [called when this character spawn]
|
||||
bool CCharacter::OnSpawn( bool )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// virtual [called when this character is almost dead]
|
||||
bool CCharacter::OnAlmostDie( )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// virtual [called when character is far]
|
||||
bool CCharacter::OnFar( )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,606 @@
|
||||
/*
|
||||
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 "character.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
// return true if this character is monster
|
||||
bool CCharacter::IsMonster( )
|
||||
{
|
||||
if(CharType == 2 || CharType == 3)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if this character is player
|
||||
bool CCharacter::IsPlayer( )
|
||||
{
|
||||
if(CharType == 1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//return true if this character is an NPC
|
||||
bool CCharacter::IsNPC( )
|
||||
{
|
||||
if(CharType == 4)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if HP is lower to 1
|
||||
bool CCharacter::IsDead( )
|
||||
{
|
||||
if(Stats->HP <= 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if this character is summon
|
||||
bool CCharacter::IsSummon( )
|
||||
{
|
||||
if(this == NULL) return false;
|
||||
if(CharType == 3)// this line is erroring. No idea why
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Virtual function [return item drop]
|
||||
CDrop* CCharacter::GetDrop( )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// start action [attack]
|
||||
void CCharacter::StartAction( CCharacter* Target, BYTE action, UINT skillid)
|
||||
{
|
||||
|
||||
BEGINPACKET( pak, 0 );
|
||||
switch(action)
|
||||
{
|
||||
case NORMAL_ATTACK:
|
||||
{
|
||||
//Log( MSG_INFO, "case NORMAL_ATTACK");
|
||||
if(Target == NULL)return;
|
||||
RESETPACKET( pak, 0x798 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Target->clientid );
|
||||
ADDWORD ( pak, Stats->Move_Speed );
|
||||
ADDFLOAT ( pak, Target->Position->current.x * 100 );
|
||||
ADDFLOAT ( pak, Target->Position->current.y * 100 );
|
||||
Battle->target = Target->clientid;
|
||||
Battle->atktarget = Target->clientid;
|
||||
Battle->atktype = action;
|
||||
Battle->skilltarget = 0;
|
||||
Battle->bufftarget = 0;
|
||||
Battle->skillid = 0;
|
||||
Position->destiny = Target->Position->current; //move to intercept target
|
||||
Position->lastMoveTime = clock();
|
||||
//Log(MSG_DEBUG,"Normal Attack set destiny X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
}
|
||||
break;
|
||||
case SKILL_ATTACK: //Log( MSG_INFO, "case SKILL_ATTACK");
|
||||
case SKILL_BUFF:
|
||||
{
|
||||
if(Target == NULL)return;
|
||||
RESETPACKET( pak, 0x7b3 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, Target->clientid );
|
||||
ADDWORD ( pak, skillid );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
ADDFLOAT ( pak, Target->Position->current.x * 100 );
|
||||
ADDFLOAT ( pak, Target->Position->current.y * 100 );
|
||||
Battle->target = Target->clientid;
|
||||
|
||||
if(action == SKILL_ATTACK)
|
||||
{
|
||||
//Log( MSG_INFO, "case SKILL_ATTACK");
|
||||
Battle->skilltarget = Target->clientid;
|
||||
Battle->bufftarget = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Log( MSG_INFO, "case SKILL_BUFF");
|
||||
Battle->bufftarget = Target->clientid;
|
||||
Battle->skilltarget = 0;
|
||||
}
|
||||
|
||||
Battle->atktype = action;
|
||||
Position->destiny = Target->Position->current; //move toward target
|
||||
Battle->skillid = skillid;
|
||||
Position->lastMoveTime = clock();
|
||||
//Log(MSG_DEBUG,"Skill Attack set destiny X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
}
|
||||
break;
|
||||
case SKILL_AOE://Log( MSG_INFO, "case SKILL_AOE");
|
||||
case BUFF_SELF://Log( MSG_INFO, "case BUFF_SELF");
|
||||
case BUFF_AOE:
|
||||
{
|
||||
|
||||
//Log( MSG_INFO, "Battle set for skill %i Action = %i", skillid, action);
|
||||
//Log( MSG_INFO, "case BUFF_AOE");
|
||||
RESETPACKET( pak, 0x7b2);
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, skillid );
|
||||
Battle->atktype = action;
|
||||
Battle->skillid = skillid;
|
||||
Position->skilltargetpos.x = Position->current.x;
|
||||
Position->skilltargetpos.y = Position->current.y;
|
||||
//Log( MSG_INFO, "BUFF_AOE packet sent");
|
||||
}
|
||||
break;
|
||||
case AOE_TARGET:
|
||||
{
|
||||
//Log( MSG_INFO, "case AOE_TARGET");
|
||||
//Target is NULL for this function when cast by a player
|
||||
//Target is NOT NULL when it comes from a monster
|
||||
if(Target != NULL) //monster attack
|
||||
{
|
||||
Position->skilltargetpos.x = Target->Position->current.x;
|
||||
Position->skilltargetpos.y = Target->Position->current.y;
|
||||
}
|
||||
RESETPACKET( pak, 0x7b4 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, skillid );
|
||||
ADDFLOAT ( pak, Position->skilltargetpos.x * 100 );
|
||||
ADDFLOAT ( pak, Position->skilltargetpos.y * 100 );
|
||||
Battle->atktype = action;
|
||||
Battle->skillid = skillid;
|
||||
Battle->skilltarget = 0;
|
||||
Battle->bufftarget = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
GServer->SendToVisible( &pak, this );
|
||||
Battle->contatk = true;
|
||||
|
||||
//Log(MSG_INFO,"Battle Attacktype = %i",Battle->atktype);
|
||||
return;
|
||||
}
|
||||
|
||||
bool CCharacter:: IsOnBattle( )
|
||||
{
|
||||
if(Battle->atktype == 0)
|
||||
{
|
||||
if(Battle->iscasting == 1)
|
||||
{
|
||||
clock_t etime = clock() - Battle->lastAtkTime;
|
||||
if( etime < CLOCKS_PER_SEC * 100 / Stats->Attack_Speed )
|
||||
{
|
||||
//Log(MSG_DEBUG,"Iscasting is true while NOT in battle. Reset to false.");
|
||||
Battle->iscasting = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch(Battle->atktype)
|
||||
{
|
||||
case 1: //NORMAL_ATTACK:
|
||||
{
|
||||
if(Battle->atktarget != 0) return true;
|
||||
}
|
||||
break;
|
||||
case 2: //SKILL_ATTACK:
|
||||
case 8: //AOE_TARGET:
|
||||
{
|
||||
if(Battle->skilltarget != 0 && Battle->skillid != 0) return true;
|
||||
}
|
||||
break;
|
||||
case 3: //SKILL_BUFF:
|
||||
{
|
||||
if(Battle->bufftarget != 0 && Battle->skillid != 0) return true;
|
||||
}
|
||||
break;
|
||||
case 4: //SKILL_AOE:
|
||||
//case SKILL_SELF: //no such skill type
|
||||
case 6: //BUFF_SELF:
|
||||
case 7: //BUFF_AOE:
|
||||
{
|
||||
if(Battle->skillid != 0) return true;
|
||||
}
|
||||
break;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CCharacter::CanAttack( )
|
||||
{
|
||||
|
||||
clock_t etime = clock() - Battle->lastAtkTime;
|
||||
if(CharType == 4) //it's an npc
|
||||
{
|
||||
|
||||
//Log(MSG_DEBUG, "It's an NPC so set speed to 1");
|
||||
//always return true for an NPC
|
||||
Stats->Attack_Speed = 1;
|
||||
}
|
||||
if (Stats->Attack_Speed < 1)
|
||||
{
|
||||
|
||||
Log( MSG_INFO, "weapon with no attackspeed.");
|
||||
return false;
|
||||
}
|
||||
if(Battle->iscasting == 1)
|
||||
{
|
||||
if( etime < CLOCKS_PER_SEC * GServer->Config.SkillMult / Stats->Attack_Speed ) //should make the delay 4 times longer
|
||||
{
|
||||
//Log(MSG_INFO,"Cannot attack yet. Last attack time = %i",Battle->lastAtkTime);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(MSG_INFO,"iscasting reset to 0 after time delay");
|
||||
Battle->iscasting = 0;
|
||||
Battle->NextAtkTime = Battle->lastAtkTime + (CLOCKS_PER_SEC * GServer->Config.SkillMult / Stats->Attack_Speed);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else //normal attack
|
||||
{
|
||||
if( etime < CLOCKS_PER_SEC * GServer->Config.AttkMult / Stats->Attack_Speed )
|
||||
{
|
||||
//Log(MSG_INFO,"Cannot attack yet. Last attack time = %i",Battle->lastAtkTime);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Battle->NextAtkTime = Battle->lastAtkTime + (CLOCKS_PER_SEC * GServer->Config.SkillMult / Stats->Attack_Speed);
|
||||
return true;
|
||||
}
|
||||
|
||||
// return true if target is reached
|
||||
bool CCharacter::IsTargetReached( CCharacter* Enemy, CSkills* skill )
|
||||
{
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
if(map == NULL)
|
||||
return true;
|
||||
float distance = GServer->distance( Position->current, Enemy->Position->current );
|
||||
|
||||
if(skill == NULL) //it's a normal attack
|
||||
{
|
||||
if(distance <= Stats->Attack_Distance)
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
//Log(MSG_DEBUG,"(istargetreached) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Position->destiny = Enemy->Position->current;
|
||||
//Log(MSG_DEBUG,"(istargetreached) Destiny set to Enemy position X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
}
|
||||
}
|
||||
else // skill attack
|
||||
{
|
||||
float SkillRange = (float)skill->range;
|
||||
if(SkillRange == 0)
|
||||
{
|
||||
SkillRange = Stats->Attack_Distance;
|
||||
}
|
||||
if(distance <= SkillRange)
|
||||
{
|
||||
Position->destiny = Position->current;
|
||||
//Log(MSG_DEBUG,"(istargetreached skill) Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Position->destiny = Enemy->Position->current;
|
||||
//Log(MSG_DEBUG,"(istargetreached skill) Destiny set to Enemy position X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
//Log(MSG_DEBUG,"(istargetreached skill) Distance: %f range: %i.", distance, SkillRange);
|
||||
}
|
||||
}
|
||||
//Log(MSG_INFO,"(istargetreached) Cannot attack. Too far away");
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if the character is moving
|
||||
bool CCharacter::IsMoving( )
|
||||
{
|
||||
if(Position->current.x == Position->destiny.x && Position->current.y == Position->destiny.y )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// VIRTUAL [add player damage]
|
||||
void CCharacter::AddDamage( CCharacter* enemy, long int hitpower)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// return character target
|
||||
CCharacter* CCharacter::GetCharTarget( )
|
||||
{
|
||||
CCharacter* thistarget = GServer->MapList.Index[Position->Map]->GetCharInMap( Battle->target );
|
||||
if (thistarget != NULL)
|
||||
{
|
||||
//Log(MSG_DEBUG, "getchartarget found target OK");
|
||||
return thistarget;
|
||||
}
|
||||
//Log(MSG_DEBUG, "getchartarget failed to find target");
|
||||
//if I don't currently have a target then return the character who just hit me
|
||||
return GServer->MapList.Index[Position->Map]->GetCharInMap( Battle->hitby );
|
||||
}
|
||||
|
||||
// VIRTUAL [reduce ammon/bullets/cannons]
|
||||
void CCharacter::ReduceABC( )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// VIRTUAL [get extra buff value]
|
||||
int CCharacter::GetEVBuff( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// VIRTUAL [return intelligence]
|
||||
unsigned int CCharacter::GetInt( )
|
||||
{
|
||||
return 15;
|
||||
}
|
||||
|
||||
|
||||
// Check For Debuffs
|
||||
void CCharacter::RefreshBuff( )
|
||||
{
|
||||
bool bflag = false;
|
||||
for( UINT i=0;i<30;i++)
|
||||
{
|
||||
if(MagicStatus[i].Buff == 0) continue;
|
||||
clock_t etime = clock() - MagicStatus[i].BuffTime;
|
||||
if( etime >= MagicStatus[i].Duration * CLOCKS_PER_SEC )
|
||||
{
|
||||
switch(MagicStatus[i].Status)
|
||||
{
|
||||
case 18: // attack power up
|
||||
case 19: // attack power down
|
||||
case 48: // attack power up
|
||||
if(i<15)
|
||||
Status->Attack_up = 0xff;
|
||||
else
|
||||
Status->Attack_down = 0xff;
|
||||
Stats->Attack_Power = GetAttackPower( );
|
||||
break;
|
||||
case 20: // def up
|
||||
case 21: // def down
|
||||
case 49: // def up
|
||||
if(i<15)
|
||||
Status->Defense_up= 0xff;
|
||||
else
|
||||
Status->Defense_down = 0xff;
|
||||
Stats->Defense = GetDefense( );
|
||||
break;
|
||||
case 24: //accuracy up
|
||||
case 25: //accuracy down
|
||||
case 51: //attack accuracy up.
|
||||
if(i<15)
|
||||
Status->Accury_up= 0xff;
|
||||
else
|
||||
Status->Accury_down = 0xff;
|
||||
Stats->Accury = GetAccury( );
|
||||
break;
|
||||
case 22: // macic resistance up
|
||||
case 23: // magic resistance down
|
||||
case 50: // magic resistance up
|
||||
if(i<15)
|
||||
Status->Magic_Defense_up = 0xff;
|
||||
else
|
||||
Status->Magic_Defense_down = 0xff;
|
||||
Stats->Magic_Defense = GetMagicDefense( );
|
||||
break;
|
||||
case 28: //dodge up
|
||||
case 29: //dodge down
|
||||
case 53: //dodge rate up
|
||||
if(i<15)
|
||||
Status->Dodge_up = 0xff;
|
||||
else
|
||||
Status->Dodge_down = 0xff;
|
||||
Stats->Dodge = GetDodge( );
|
||||
break;
|
||||
case 14: //dash
|
||||
case 15: //slow
|
||||
case 46: //movement speed increased
|
||||
if(i<15)
|
||||
Status->Dash_up = 0xff;
|
||||
else
|
||||
Status->Dash_down = 0xff;
|
||||
Stats->Move_Speed = GetMoveSpeed( );
|
||||
break;
|
||||
case 16: // haste attack
|
||||
case 17: // slow attack
|
||||
case 47: // attack speed up
|
||||
if(i<15)
|
||||
Status->Haste_up = 0xff;
|
||||
else
|
||||
Status->Haste_down = 0xff;
|
||||
Stats->Attack_Speed = GetAttackSpeed( );
|
||||
break;
|
||||
case 26: // crit up
|
||||
case 27: // crit down
|
||||
case 52: // crit up
|
||||
if(i<15)
|
||||
Status->Critical_up = 0xff;
|
||||
else
|
||||
Status->Critical_down = 0xff;
|
||||
Stats->Critical = GetCritical( );
|
||||
break;
|
||||
case 12: // max HP up
|
||||
case 44: // max HP up
|
||||
if(i<15)
|
||||
Status->HP_up = 0xff;
|
||||
else
|
||||
Status->HP_down = 0xff;
|
||||
Stats->MaxHP = GetMaxHP( );
|
||||
break;
|
||||
case 13: // max MP up
|
||||
case 45: // max MP up
|
||||
if(i<15)
|
||||
Status->MP_up = 0xff;
|
||||
else
|
||||
Status->MP_down = 0xff;
|
||||
Stats->MaxMP = GetMaxMP( );
|
||||
break;
|
||||
case 32: // faint
|
||||
Status->Stuned = 0xff;
|
||||
Status->CanAttack = true;
|
||||
//printf("removing stun\n");
|
||||
break;
|
||||
case 7: case 8: case 9: case 10: case 11: //poisoned
|
||||
Status->Poisoned = 0xff;
|
||||
//printf("removing poison\n");
|
||||
break;
|
||||
case 30: // muted
|
||||
Status->Muted = 0xff;
|
||||
Status->CanCastSkill = true;
|
||||
break;
|
||||
case 31: // sleep May need to be fixed later to accomodate multiple status effects.
|
||||
Status->Sleep = 0xff;
|
||||
Status->CanMove = true;
|
||||
Status->CanCastSkill = true;
|
||||
Status->CanAttack = true;
|
||||
break;
|
||||
case 54: //A_GMExtra_Damage:
|
||||
case 36: //A_Extra_Damage:
|
||||
if(i<15)
|
||||
{
|
||||
Status->ExtraDamage_up = 0xff;
|
||||
Stats->ExtraDamage = 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
Status->ExtraDamage_down = 0xff;
|
||||
Stats->ExtraDamage = 0xff;
|
||||
}
|
||||
|
||||
break;
|
||||
case 56: // Taunt
|
||||
Status->Taunt = 0xff;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
MagicStatus[i].Status = 0;
|
||||
MagicStatus[i].Buff = 0;
|
||||
MagicStatus[i].BuffTime = 0;
|
||||
MagicStatus[i].Duration = 0;
|
||||
MagicStatus[i].Value = 0;
|
||||
bflag = true;
|
||||
}
|
||||
else if (MagicStatus[i].Status >= 7 && MagicStatus[i].Status <= 11 && etime > 1*CLOCKS_PER_SEC) //Do poison dmg every 1.5 seconds
|
||||
{
|
||||
Stats->HP -= MagicStatus[i].Status; //Actually take 7, 8, 9, 10 or 11 from the health. Based on the Status itself
|
||||
MagicStatus[i].BuffTime += 1*CLOCKS_PER_SEC;
|
||||
MagicStatus[i].Duration -= 1;
|
||||
//printf("did %i poison dmg to the player, still %i seconds and %i HP remain \n", MagicStatus[i].Status, MagicStatus[i].Duration, Stats->HP);
|
||||
|
||||
//A bunch of messy code to send dmg packet
|
||||
BEGINPACKET( pak, 0x7b6 );
|
||||
ADDWORD ( pak, clientid ); // client taking damage
|
||||
ADDWORD ( pak, 0 ); // client doing damage
|
||||
ADDDWORD ( pak, 0x000007f8 );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
if (Stats->HP < 1)
|
||||
{
|
||||
ADDWORD ( pak, MagicStatus[i].Status + 0x8000 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, MagicStatus[i].Status + 0x2000 );
|
||||
}
|
||||
//ADDDWORD ( pak, MagicStatus[i].Status ); //damage amount
|
||||
|
||||
//If poisoned entity (player or monster) is killed
|
||||
if( IsDead())
|
||||
{
|
||||
//printf("char died\n");
|
||||
Log( MSG_INFO, "found dead monster");
|
||||
CDrop* thisdrop = NULL;
|
||||
CMap* map = GServer->MapList.Index[thisdrop->posMap];
|
||||
ADDDWORD ( pak, 16 );
|
||||
if( !IsSummon( ) && !IsPlayer( ))
|
||||
{
|
||||
thisdrop = GetDrop( );
|
||||
if( thisdrop != NULL)
|
||||
{
|
||||
map->AddDrop( thisdrop );
|
||||
}
|
||||
}
|
||||
GServer->SendToVisible( &pak, this ); // removed thisdrop from the packet
|
||||
//kill the monster if its HP is less than 0
|
||||
BEGINPACKET( pak1, 0x799 );
|
||||
ADDWORD ( pak1, clientid );
|
||||
ADDWORD ( pak1, clientid );
|
||||
ADDDWORD ( pak1, 0x8005 );
|
||||
GServer->SendToVisible( &pak1, this );
|
||||
}
|
||||
|
||||
//If enemy is still alive
|
||||
else
|
||||
{
|
||||
ADDDWORD ( pak, 4 );
|
||||
GServer->SendToVisible( &pak, this );
|
||||
}
|
||||
}
|
||||
}
|
||||
if(bflag)
|
||||
{
|
||||
BEGINPACKET( pak,0x7b7 );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDDWORD ( pak, GServer->BuildBuffs( this ) );
|
||||
GServer->SendToVisible( &pak, this );
|
||||
}
|
||||
}
|
||||
|
||||
// VIRTUAL [return party pointer]
|
||||
CParty* CCharacter::GetParty( )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// VIRTUAL [return clan pointer]
|
||||
CLAN* CCharacter::GetClan( )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CCharacter::IsAttacking( )
|
||||
{
|
||||
if(IsOnBattle( ))
|
||||
{
|
||||
switch(Battle->atktype)
|
||||
{
|
||||
case SKILL_BUFF:
|
||||
case BUFF_SELF:
|
||||
case BUFF_AOE:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
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 "character.h"
|
||||
|
||||
// virtual [return attackpower]
|
||||
unsigned int CCharacter::GetAttackPower( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return defense]
|
||||
unsigned int CCharacter::GetDefense( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return dodge]
|
||||
unsigned int CCharacter::GetDodge( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return xprate]
|
||||
unsigned int CCharacter::GetXPRate( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return item drop rate]
|
||||
unsigned int CCharacter::GetItemDropRate( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return item drop rate]
|
||||
unsigned int CCharacter::GetItemDropCountRate( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return accury]
|
||||
unsigned int CCharacter::GetAccury( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return magic defense]
|
||||
unsigned int CCharacter::GetMagicDefense( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return magic attack]
|
||||
unsigned int CCharacter::GetMagicAttack( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return critical]
|
||||
unsigned int CCharacter::GetCritical( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return attackspeed]
|
||||
unsigned int CCharacter::GetAttackSpeed( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return movespeed]
|
||||
unsigned int CCharacter::GetMoveSpeed( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return maxhp]
|
||||
unsigned int CCharacter::GetMaxHP( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return maxmp]
|
||||
unsigned int CCharacter::GetMaxMP( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual [return attackdistance]
|
||||
float CCharacter::GetAttackDistance( )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#ifndef _CHARACTER_TYPE_
|
||||
#define _CHARACTER_TYPE_
|
||||
#define MAX_STORAGE 160
|
||||
#define MAX_INVENTORY 140
|
||||
#define MAX_BASICSKILL 42
|
||||
#define MAX_QUICKBAR 32
|
||||
#define ClearBattle(i) { i->target=0;i->atktarget=0; i->bufftarget=0; i->skilltarget=0; i->skillid=0; i->atktype=0; i->hitby=0; i->iscasting=0; }
|
||||
typedef unsigned char BYTE;
|
||||
#include "datatypes.h"
|
||||
|
||||
#define TPLAYER 1
|
||||
#define TMONSTER 2
|
||||
#define TSUMMON 3
|
||||
#define TNPC 4
|
||||
|
||||
//**************** SHARED ****************//
|
||||
struct POSITION
|
||||
{
|
||||
fPoint source; // source position
|
||||
fPoint current; // current position
|
||||
fPoint destiny; // destiny position
|
||||
fPoint skilltargetpos;
|
||||
unsigned int Map; // current map
|
||||
unsigned int respawn; // respawn id [player = respawnid | monster = spawnid]
|
||||
unsigned int saved; // saved id [player town | monster = 0]
|
||||
clock_t lastMoveTime;
|
||||
};
|
||||
|
||||
struct STATS
|
||||
{
|
||||
int Level;
|
||||
long int HP;
|
||||
long int MP;
|
||||
unsigned int MaxHP;
|
||||
unsigned int MaxMP;
|
||||
unsigned int stance;
|
||||
|
||||
unsigned int Attack_Power;
|
||||
unsigned int Defense;
|
||||
unsigned int Critical;
|
||||
unsigned int Dodge;
|
||||
unsigned int Accury;
|
||||
unsigned int Magic_Defense;
|
||||
unsigned int Move_Speed;
|
||||
unsigned int Mspd_base;
|
||||
unsigned int Attack_Speed;
|
||||
float Attack_Distance;
|
||||
unsigned int MaxWeight;
|
||||
unsigned int MaxSummonGauge;
|
||||
unsigned int MPReduction;
|
||||
unsigned int ExtraDamage;
|
||||
unsigned int xprate;
|
||||
unsigned int itemdroprate;
|
||||
unsigned int itemdropcountrate;
|
||||
unsigned int zulydroprate;
|
||||
unsigned int greydrop;
|
||||
unsigned int magicattack;
|
||||
int ItemXPRate;
|
||||
int ItemDropRate;
|
||||
clock_t ItemXPTimer;
|
||||
unsigned int ItemXPTime;
|
||||
clock_t ItemDropRateTimer;
|
||||
|
||||
};
|
||||
|
||||
struct BATTLE
|
||||
{
|
||||
unsigned int target;
|
||||
unsigned int atktarget;
|
||||
unsigned int bufftarget;
|
||||
unsigned int skilltarget;
|
||||
unsigned int skillid;
|
||||
unsigned int atktype;
|
||||
unsigned int hitby;
|
||||
unsigned int iscasting;
|
||||
bool contatk;
|
||||
clock_t lastAtkTime;
|
||||
clock_t castTime;
|
||||
clock_t NextAtkTime;
|
||||
};
|
||||
|
||||
struct STATUS
|
||||
{
|
||||
//Stats up
|
||||
BYTE Dash_up;
|
||||
BYTE Haste_up;
|
||||
BYTE Attack_up;
|
||||
BYTE Defense_up;
|
||||
BYTE Magic_Defense_up;
|
||||
BYTE Accury_up;
|
||||
BYTE Critical_up;
|
||||
BYTE Dodge_up;
|
||||
|
||||
BYTE HP_up;
|
||||
BYTE MP_up;
|
||||
BYTE MaxHP_up;
|
||||
BYTE MaxMP_up;
|
||||
BYTE HPMP_up;
|
||||
//Stats down
|
||||
BYTE Dash_down;
|
||||
BYTE Haste_down;
|
||||
BYTE Attack_down;
|
||||
BYTE Defense_down;
|
||||
BYTE Magic_Defense_down;
|
||||
BYTE Accury_down;
|
||||
BYTE Critical_down;
|
||||
BYTE Dodge_down;
|
||||
BYTE HP_down;
|
||||
BYTE MP_down;
|
||||
BYTE MaxHP_down;
|
||||
BYTE MaxMP_down;
|
||||
BYTE HPMP_down;
|
||||
|
||||
//Status Effects
|
||||
BYTE Poisoned;
|
||||
BYTE Muted;
|
||||
BYTE Sleep;
|
||||
BYTE Faint;
|
||||
BYTE Stuned;
|
||||
BYTE Camo;
|
||||
BYTE Invis;
|
||||
BYTE Shield;
|
||||
BYTE Curse;
|
||||
BYTE Recover;
|
||||
BYTE Dispell;
|
||||
BYTE Detect;
|
||||
BYTE Invinc;
|
||||
BYTE Flame;
|
||||
BYTE ExtraDamage_up;
|
||||
BYTE ExtraDamage_down;
|
||||
BYTE Taunt;
|
||||
bool CanAttack;
|
||||
bool CanRun;
|
||||
bool CanMove;
|
||||
bool CanCastSkill;
|
||||
bool IsTowerDef;
|
||||
bool nightonly;
|
||||
bool dayonly;
|
||||
UINT spawnid;
|
||||
|
||||
// Stance
|
||||
BYTE Stance;
|
||||
};
|
||||
|
||||
struct MAGICSTATUS
|
||||
{
|
||||
unsigned int Status; // Store the skill Status
|
||||
unsigned int Buff; // Store the buff type
|
||||
unsigned int Value; // Buff Value
|
||||
unsigned int Duration; // Skill Duration
|
||||
clock_t BuffTime; // Buff Time
|
||||
};
|
||||
|
||||
//**************************************************************/
|
||||
//***************Player only******************************************/
|
||||
struct ATTRIBUTES
|
||||
{
|
||||
unsigned int Str;
|
||||
unsigned int Dex;
|
||||
unsigned int Int;
|
||||
unsigned int Con;
|
||||
unsigned int Cha;
|
||||
unsigned int Sen;
|
||||
|
||||
unsigned int Estr;
|
||||
unsigned int Edex;
|
||||
unsigned int Eint;
|
||||
unsigned int Econ;
|
||||
unsigned int Echa;
|
||||
unsigned int Esen;
|
||||
|
||||
};
|
||||
|
||||
struct USEDITEM
|
||||
{
|
||||
clock_t lastRegTime; // Last Regeneration
|
||||
UINT usevalue; // Max Heal Value
|
||||
UINT usetype; // Consumible Type (MP/HP/STAMINA/...)
|
||||
UINT userate; // consumible rate
|
||||
UINT used; // Consumible Used
|
||||
};
|
||||
|
||||
// Player information
|
||||
struct INFO
|
||||
{
|
||||
char charname[17];
|
||||
unsigned int charid;
|
||||
BYTE Sex;
|
||||
UINT Face;
|
||||
UINT Hair;
|
||||
UINT Exp;
|
||||
UINT Pending_Exp; //temporary storage space for exp during calculations
|
||||
UINT Job;
|
||||
long int Zulies;
|
||||
long int Storage_Zulies;
|
||||
long int LastGlobal;
|
||||
int StatPoints;
|
||||
int SkillPoints;
|
||||
unsigned int stamina;
|
||||
UINT IsGM;
|
||||
UINT IsDev;
|
||||
//int xprate
|
||||
};
|
||||
|
||||
struct SESSION
|
||||
{
|
||||
unsigned int userid;
|
||||
char username[17];
|
||||
char password[33];
|
||||
int accesslevel;
|
||||
bool isLoggedIn;
|
||||
bool inGame;
|
||||
UINT Respawned;
|
||||
BYTE RespawnChoice;
|
||||
bool codedebug;
|
||||
UINT KTPoints;
|
||||
UINT NewPoints;
|
||||
UINT logtime; // tracks total log points. 1 point = 1 hour. 5 points = 1KTPoint
|
||||
clock_t LogTime; // time for the current session
|
||||
UINT TotalLogTime;
|
||||
UINT award;
|
||||
};
|
||||
|
||||
struct RIDE
|
||||
{
|
||||
bool Drive;
|
||||
bool Ride;
|
||||
UINT charid;
|
||||
};
|
||||
|
||||
struct TRADE
|
||||
{
|
||||
unsigned int short trade_itemid[0xa];
|
||||
unsigned int trade_count[0xb];
|
||||
unsigned int trade_status;
|
||||
unsigned int trade_target;
|
||||
};
|
||||
|
||||
struct PARTY
|
||||
{
|
||||
class CParty* party;
|
||||
bool IsMaster;
|
||||
};
|
||||
|
||||
struct SHOP
|
||||
{
|
||||
bool open;
|
||||
char name[64];
|
||||
CBuying BuyingList[30];
|
||||
CSelling SellingList[30];
|
||||
UINT Buying;
|
||||
UINT Selling;
|
||||
UINT ShopType;
|
||||
};
|
||||
|
||||
struct SKILLS
|
||||
{
|
||||
unsigned id;
|
||||
unsigned level;
|
||||
CSkills* thisskill;
|
||||
};
|
||||
|
||||
struct CLAN
|
||||
{
|
||||
unsigned int clanid;
|
||||
unsigned int clanrank;
|
||||
unsigned int grade;
|
||||
unsigned int CP;
|
||||
unsigned int logo;
|
||||
unsigned int back;
|
||||
char clanname[17];
|
||||
};
|
||||
|
||||
struct UNION_s
|
||||
{
|
||||
UINT unionvar[11];
|
||||
};
|
||||
|
||||
|
||||
//***************Monster only******************************************/
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
// Clan Manager
|
||||
bool CWorldServer::pakClanManager ( CPlayer* thisclient, CPacket* P )
|
||||
{
|
||||
int action = GETBYTE((*P),0);
|
||||
//Log(MSG_DEBUG, "Clan action %i", action );
|
||||
switch(action)
|
||||
{
|
||||
case 0xfa://new member added
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
int charid = GETWORD((*P),1);
|
||||
int clanid = GETWORD((*P),3);
|
||||
Log(MSG_INFO, "Adding member %d to clan %d", charid, clanid);
|
||||
CPlayer* otherclient = GetClientByCID ( charid );
|
||||
if(otherclient==NULL) {
|
||||
Log(MSG_ERROR, "Char with id %d doesn't exist", charid);
|
||||
return true;
|
||||
}
|
||||
MYSQL_RES *result = DB->QStore("SELECT logo,back,name,grade FROM list_clan where id = %i", clanid);
|
||||
if(result==NULL)
|
||||
{
|
||||
Log(MSG_INFO, "NULL result from query to list_clan");
|
||||
return true;
|
||||
}
|
||||
if(mysql_num_rows(result)!=1)
|
||||
{
|
||||
Log(MSG_WARNING, "Invalid clan %i", clanid );
|
||||
DB->QFree( );
|
||||
return true;
|
||||
}
|
||||
row = mysql_fetch_row(result);
|
||||
otherclient->Clan->logo = atoi(row[0]);
|
||||
otherclient->Clan->back = atoi(row[1]);
|
||||
strcpy(otherclient->Clan->clanname,row[2]);
|
||||
otherclient->Clan->grade = atoi(row[3]);
|
||||
DB->QFree( );
|
||||
otherclient->Clan->clanid=clanid;
|
||||
otherclient->Clan->clanrank=1;
|
||||
BEGINPACKET( pak, 0x7e0 );
|
||||
ADDBYTE ( pak, 0x35 );//funcion
|
||||
ADDWORD ( pak, otherclient->clientid );//client id
|
||||
ADDWORD ( pak, clanid );//?
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
ADDWORD ( pak, otherclient->Clan->back );
|
||||
ADDWORD ( pak, otherclient->Clan->logo );
|
||||
ADDBYTE ( pak, otherclient->Clan->grade );
|
||||
ADDBYTE ( pak, otherclient->Clan->clanrank );
|
||||
ADDSTRING ( pak, otherclient->Clan->clanname );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
SendToVisible( &pak, otherclient );
|
||||
}
|
||||
break;
|
||||
case 0xfb://Member Kicked
|
||||
{
|
||||
char nick[30];
|
||||
memcpy( nick, &P->Buffer[1], P->Size );
|
||||
CPlayer* otherclient = GetClientByCharName( nick );
|
||||
if(otherclient!=NULL)
|
||||
{
|
||||
otherclient->Clan->clanid=0;
|
||||
otherclient->Clan->clanrank=1;
|
||||
otherclient->Clan->back=0;
|
||||
otherclient->Clan->logo=0;
|
||||
otherclient->Clan->grade=0;
|
||||
strcpy(otherclient->Clan->clanname,"");
|
||||
BEGINPACKET( pak, 0x7e0 );
|
||||
ADDBYTE ( pak, 0x35 );
|
||||
ADDWORD ( pak, otherclient->clientid );
|
||||
ADDQWORD ( pak, 0 );
|
||||
ADDWORD ( pak, 0x0001 );
|
||||
SendToVisible( &pak, otherclient );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xfc://member change rank
|
||||
{
|
||||
char nick[30];
|
||||
int newrank = GETBYTE((*P),1);
|
||||
memcpy( nick, &P->Buffer[2], P->Size );
|
||||
CPlayer* otherclient = GetClientByCharName( nick );
|
||||
if(otherclient!=NULL)
|
||||
{
|
||||
otherclient->Clan->clanrank = newrank;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xfd://disorg
|
||||
{
|
||||
unsigned int clanid = GETWORD((*P),1);
|
||||
unsigned int charid = GETWORD((*P),3);
|
||||
CPlayer* tclient = GetClientByCID( charid );
|
||||
if(tclient == NULL)
|
||||
return true;
|
||||
tclient->Clan->clanid = 0;
|
||||
tclient->Clan->clanrank = 1;
|
||||
tclient->Clan->grade = 0;
|
||||
tclient->Clan->back = 0;
|
||||
tclient->Clan->logo = 0;
|
||||
memset( &tclient->Clan->clanname, '\0', 17 );
|
||||
BEGINPACKET( pak, 0x7e0 );
|
||||
ADDBYTE ( pak, 0x35 );
|
||||
ADDWORD ( pak, tclient->clientid );
|
||||
ADDQWORD ( pak, 0 );
|
||||
ADDWORD ( pak, 0x0001 );
|
||||
SendToVisible( &pak, tclient );
|
||||
}
|
||||
break;
|
||||
case 0xfe://Member Leave
|
||||
{
|
||||
char nick[17];
|
||||
memcpy( nick, &P->Buffer[1], P->Size );
|
||||
CPlayer* otherclient = GetClientByCharName(nick);
|
||||
if(otherclient!=NULL)
|
||||
{
|
||||
otherclient->Clan->clanid=0;
|
||||
otherclient->Clan->clanrank=0;
|
||||
otherclient->Clan->back=0;
|
||||
otherclient->Clan->logo=0;
|
||||
otherclient->Clan->grade=0;
|
||||
strcpy(otherclient->Clan->clanname,"");
|
||||
BEGINPACKET( pak, 0x7e0 );
|
||||
ADDBYTE ( pak, 0x35 );
|
||||
ADDWORD ( pak, otherclient->clientid );
|
||||
ADDQWORD ( pak, 0 );
|
||||
ADDWORD ( pak, 0x0001 );
|
||||
SendToVisible( &pak, otherclient );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xff: // update clan mark
|
||||
{
|
||||
unsigned int clanid = GETWORD((*P),1);
|
||||
unsigned int clanlogo = GETDWORD((*P), 3 );
|
||||
for(unsigned int i=0;i<ClientList.size();i++)
|
||||
{
|
||||
if(ClientList.at(i)->player==NULL) continue;
|
||||
CPlayer* player = (CPlayer*)ClientList.at(i)->player;
|
||||
if(player->Clan->clanid==clanid)
|
||||
{
|
||||
player->Clan->back = 0;
|
||||
player->Clan->logo = clanlogo;
|
||||
BEGINPACKET( pak, 0x7e0 );
|
||||
ADDBYTE ( pak, 0x35 );//funcion
|
||||
ADDWORD ( pak, player->clientid );//cleint id
|
||||
ADDWORD ( pak, clanid );
|
||||
ADDWORD ( pak, 0x0000 );//?
|
||||
ADDWORD ( pak, player->Clan->back );//?
|
||||
ADDWORD ( pak, player->Clan->logo );//?
|
||||
ADDBYTE ( pak, player->Clan->grade );
|
||||
ADDBYTE ( pak, player->Clan->clanrank );
|
||||
ADDSTRING ( pak, player->Clan->clanname );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
SendToVisible( &pak, player );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log( MSG_INFO, "Clan manager unknow action %i", action );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create a new clan
|
||||
bool CWorldServer::pakCreateClan ( CPlayer* thisclient, CPacket* P )
|
||||
{
|
||||
if(thisclient->CharInfo->Zulies<1000000)
|
||||
return true;
|
||||
thisclient->CharInfo->Zulies -= 1000000;
|
||||
MYSQL_ROW row;
|
||||
int background = GETWORD((*P),1);
|
||||
int icon = GETWORD((*P),3);
|
||||
const char *name = "";
|
||||
const char *slogan = "";
|
||||
name=(char*)&P->Buffer[5];
|
||||
slogan=(char*)&P->Buffer[strlen(name)+6];
|
||||
|
||||
//Check if name already exists
|
||||
MYSQL_RES *result = DB->QStore("SELECT name FROM list_clan WHERE name='%s'", name);
|
||||
if(result==NULL) return true;
|
||||
if ( mysql_num_rows( result ) > 0 )
|
||||
{
|
||||
BEGINPACKET( pak, 0x07e0 );
|
||||
ADDWORD ( pak, 0x42 );
|
||||
thisclient->client->SendPacket( &pak );
|
||||
DB->QFree( );
|
||||
return true;
|
||||
}
|
||||
DB->QFree( );
|
||||
|
||||
//Check if user can create a clan
|
||||
if( thisclient->Clan->clanid != 0 )
|
||||
{
|
||||
BEGINPACKET( pak, 0x07e0 );
|
||||
ADDWORD ( pak, 0x44 );
|
||||
thisclient->client->SendPacket( &pak );
|
||||
return true;
|
||||
}
|
||||
|
||||
//Add clan to sql table
|
||||
DB->QExecute( "INSERT into list_clan (logo,back,name,cp,grade,slogan,news) values (%i,%i,'%s',0,1,'%s','')",
|
||||
icon,background,name,slogan);
|
||||
thisclient->Clan->clanid = mysql_insert_id(DB->Mysql);
|
||||
thisclient->Clan->clanrank = 6;
|
||||
thisclient->Clan->logo = icon;
|
||||
thisclient->Clan->back = background;
|
||||
strncpy(thisclient->Clan->clanname,name,16);
|
||||
thisclient->Clan->grade = 1;
|
||||
|
||||
//Update user clan information
|
||||
DB->QExecute( "UPDATE characters set clanid=%i,clan_rank=6 where id=%i",
|
||||
thisclient->Clan->clanid,thisclient->CharInfo->charid);
|
||||
//load clan info in char server
|
||||
BEGINPACKET( pak, 0x7e0 );
|
||||
ADDBYTE ( pak, 0xfa ); //action to update clan informacion (charserver)
|
||||
ADDWORD ( pak, thisclient->Clan->clanid );
|
||||
ADDWORD ( pak, thisclient->CharInfo->charid );
|
||||
ADDWORD ( pak, thisclient->clientid );
|
||||
SendISCPacket( &pak );
|
||||
|
||||
//Send to other players
|
||||
RESETPACKET( pak, 0x7e0 );
|
||||
ADDBYTE ( pak, 0x35 );
|
||||
ADDWORD ( pak, thisclient->clientid );
|
||||
ADDWORD ( pak, thisclient->Clan->clanid);
|
||||
ADDWORD ( pak, 0x0000 );//???
|
||||
ADDWORD ( pak, background );
|
||||
ADDWORD ( pak, icon );
|
||||
ADDBYTE ( pak, 0x01 );//clan grade
|
||||
ADDBYTE ( pak, 0x06 );//clan rank
|
||||
ADDSTRING ( pak, name );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
SendToVisible( &pak, thisclient );
|
||||
return true;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,45 @@
|
||||
// Added By Raven0123
|
||||
|
||||
#include "datatypes.h"
|
||||
|
||||
unsigned CItem::GetVisInfo( )
|
||||
{
|
||||
if ( count == 0 ) Clear( );
|
||||
return ( ( refine >> 4 ) << 20 ) | ( socketed << 19 ) | ( stats << 10 ) | itemnum;
|
||||
}
|
||||
|
||||
unsigned short CItem::GetPakHeader( )
|
||||
{
|
||||
if ( count == 0 ) Clear( );
|
||||
return ( ( itemnum << 5 ) | ( itemtype ) );
|
||||
}
|
||||
|
||||
unsigned CItem::GetPakData( )
|
||||
{
|
||||
unsigned retval = 0;
|
||||
if ( count == 0 ) Clear( );
|
||||
if ( itemtype >= 10 && itemtype <= 13 || itemtype == 0x1f ) {
|
||||
retval = count;
|
||||
} else {
|
||||
retval |= (refine>>4) << 28;
|
||||
retval |= (appraised&1) << 27;
|
||||
retval |= (socketed&1) << 26;
|
||||
retval |= (lifespan*10) << 16;
|
||||
retval |= durability << 9;
|
||||
retval |= stats;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void CItem::Clear( )
|
||||
{
|
||||
itemnum = 0;
|
||||
itemtype = 0;
|
||||
count = 0;
|
||||
refine = 0;
|
||||
appraised = 0;
|
||||
socketed = 0;
|
||||
lifespan = 0;
|
||||
durability = 0;
|
||||
stats = 0;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Rose Online Server Emulator
|
||||
Copyright (C) 2006,2007,2008 OSRose Team http://www.dev-osrose.com
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
//lmame: LTB file reader.
|
||||
|
||||
#include "ltbhandler.h"
|
||||
|
||||
|
||||
void LTBStoreData(char* filename, LTBData* data)
|
||||
{
|
||||
long int first_data = 0;
|
||||
long int cur_offset =0;
|
||||
DWORD nb_box = 0;
|
||||
WORD nb_dial_box = 0;
|
||||
int nb_dialogs = 0;
|
||||
char templma[2];
|
||||
//char tempfield[1024];
|
||||
|
||||
|
||||
FILE* fh = fopen( filename, "rb" );
|
||||
if ( fh == NULL )
|
||||
{
|
||||
Log(MSG_WARNING,"Could not load LTB %s",filename);
|
||||
return;
|
||||
}
|
||||
|
||||
//How many dialogs?
|
||||
fread( &nb_box, 4, 1, fh);
|
||||
fread(&nb_dial_box,2,1,fh);
|
||||
nb_dialogs = nb_box*nb_dial_box;
|
||||
|
||||
data->nb_sentence = nb_box;
|
||||
|
||||
//reading location of first data.
|
||||
fseek(fh,8,SEEK_SET);
|
||||
fread(&first_data,4,1,fh);
|
||||
cur_offset=8;
|
||||
fseek(fh,cur_offset,SEEK_SET);
|
||||
|
||||
|
||||
//making the offset list.
|
||||
long int cpt = 0;
|
||||
map <int, Dialog> list_dialog;
|
||||
|
||||
while(cur_offset < first_data)
|
||||
{
|
||||
Dialog temp_dial;
|
||||
|
||||
//dialog.
|
||||
//offset is 4 bytes, lg is 2 bytes.
|
||||
temp_dial.dialognb = cpt;
|
||||
fread(&temp_dial.offsetd,4,1,fh);
|
||||
fread(&temp_dial.lengthd,2,1,fh);
|
||||
list_dialog[cpt] = temp_dial; //I know, it's faster with .insert...
|
||||
cur_offset += 6;
|
||||
cpt++;
|
||||
|
||||
if(temp_dial.offsetd == 0 && temp_dial.lengthd == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Getting dialogs...
|
||||
int lang=0;
|
||||
int no_block=0;
|
||||
NPCLTB templtb;
|
||||
string korean_save = "";
|
||||
|
||||
for (unsigned int k=0;k < list_dialog.size();k++)
|
||||
{
|
||||
lang = k%nb_box;
|
||||
no_block = int(k/nb_box);
|
||||
|
||||
if(lang == LTBINDEX)
|
||||
{
|
||||
templtb.name = "NoName";
|
||||
templtb.sentence = "NoSentence";
|
||||
korean_save = "";
|
||||
}
|
||||
|
||||
if(list_dialog[k].offsetd == 0)
|
||||
{
|
||||
if (lang == LTBENGLISH)
|
||||
{
|
||||
//did we have Korean sentence?
|
||||
if (korean_save != "")
|
||||
{
|
||||
templtb.sentence = korean_save;
|
||||
}
|
||||
|
||||
data->record.push_back(templtb);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//reading data
|
||||
//lg*2 because of unicode :)
|
||||
string temp;
|
||||
fseek(fh,list_dialog[k].offsetd,SEEK_SET);
|
||||
|
||||
if(lang!=LTBENGLISH&&lang!=LTBINDEX&&lang!=LTBKOREAN)
|
||||
{
|
||||
//LMA: jumping...
|
||||
//fread(&tempfield,list_dialog[k].lengthd*2,1,fh);
|
||||
fseek(fh,list_dialog[k].lengthd*2,SEEK_CUR);
|
||||
continue;
|
||||
}
|
||||
|
||||
//unicode is NOT our friend :(
|
||||
for(int j=0;j<(list_dialog[k].lengthd*2);j++)
|
||||
{
|
||||
fread(&templma,1,1,fh);
|
||||
if (templma[0]=='\0')
|
||||
continue;
|
||||
temp+=templma[0];
|
||||
}
|
||||
|
||||
//We save Korean as well...
|
||||
switch (lang)
|
||||
{
|
||||
case LTBKOREAN:
|
||||
{
|
||||
korean_save = temp;
|
||||
}
|
||||
break;
|
||||
case LTBENGLISH:
|
||||
{
|
||||
templtb.sentence = temp;
|
||||
data->record.push_back(templtb);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
templtb.name = temp;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//closing the file...
|
||||
fclose(fh);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Rose Online Server Emulator
|
||||
Copyright (C) 2006,2007 OSRose Team http://www.dev-osrose.com
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
//lmame
|
||||
#include "worldserver.h"
|
||||
#include <map>
|
||||
using namespace std;
|
||||
|
||||
|
||||
void LTBStoreData(char* filename, LTBData* data);
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#define REVISION 32
|
||||
#include "worldserver.h"
|
||||
//#pragma comment(lib,"ws2_32.lib")
|
||||
|
||||
CWorldServer* GServer;
|
||||
|
||||
unsigned char LOG_THISSERVER;
|
||||
|
||||
// Main server function
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
srand( (unsigned)time(NULL) );
|
||||
srand( rand()*time(NULL) );
|
||||
LOG_THISSERVER = LOG_WORLD_SERVER;
|
||||
InitWinSocket( );
|
||||
string fileconf = "worldserver.conf";
|
||||
if(argc>1)
|
||||
{
|
||||
fileconf = argv[1];
|
||||
}
|
||||
CWorldServer *server = new (nothrow) CWorldServer( fileconf );
|
||||
if(server==NULL)
|
||||
return -1;
|
||||
pthread_attr_init(&server->at);
|
||||
pthread_attr_setdetachstate(&server->at, PTHREAD_CREATE_JOINABLE);
|
||||
// Connect mysql
|
||||
MYSQL mysql;
|
||||
server->DB = new CDatabase( server->Config.SQLServer.pcServer,
|
||||
server->Config.SQLServer.pcUserName,
|
||||
server->Config.SQLServer.pcPassword,
|
||||
server->Config.SQLServer.pcDatabase,
|
||||
server->Config.SQLServer.pcPort, &mysql );
|
||||
server->port = server->Config.CharPort;
|
||||
if(server->Config.usethreads)
|
||||
Log( MSG_INFO, "Using Threads" );
|
||||
// Start server
|
||||
server->port = server->Config.WorldPort;
|
||||
|
||||
if(server->DB->Connect( )==0) Log( MSG_INFO, "Connected to MySQL server" );
|
||||
else
|
||||
{
|
||||
delete server;
|
||||
#ifdef _WIN32
|
||||
system("pause");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
server->LoadEncryption();
|
||||
|
||||
server->StartServer();
|
||||
// Close server
|
||||
pthread_attr_destroy(&server->at);
|
||||
server->DB->Disconnect( );
|
||||
CloseWinSocket( );
|
||||
Log(MSG_INFO, "Cleaning memory, please wait..." );
|
||||
delete server;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
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 "worldmap.h"
|
||||
|
||||
// convert a monster to other
|
||||
CMonster* CMap::ConverToMonster( CMonster* monster, UINT newmontype, bool heal )
|
||||
{
|
||||
CNPCData* thisnpc = GServer->GetNPCDataByID( newmontype );
|
||||
if(thisnpc == NULL)// invalid type
|
||||
return NULL;
|
||||
monster->montype = newmontype;
|
||||
monster->thisnpc = thisnpc;
|
||||
monster->monAI = monster->thisnpc->AI;
|
||||
monster->SetStats( );
|
||||
monster->Stats->MaxHP = monster->GetMaxHP( );
|
||||
monster->Stats->HP = monster->Stats->MaxHP;
|
||||
monster->Stats->MaxMP = monster->GetMaxMP( );
|
||||
monster->Stats->MP = monster->Stats->MaxMP;
|
||||
if(heal)
|
||||
monster->Stats->HP = monster->Stats->MaxHP;
|
||||
if(monster->owner == 0)
|
||||
{
|
||||
monster->MonsterDrop->mobdrop = GServer->GetDropData( monster->thisnpc->dropid );
|
||||
monster->MonsterDrop->mapdrop = GServer->GetDropData( id );
|
||||
}
|
||||
BEGINPACKET( pak, 0x774 );
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDWORD ( pak, newmontype );
|
||||
GServer->SendToVisible( &pak, monster );
|
||||
//monster->OnSpawn( false );
|
||||
return monster;
|
||||
}
|
||||
|
||||
// convert a npc to other [not working]
|
||||
CMonster* CMap::ConverToMonster( CNPC* npc, UINT newmontype )
|
||||
{
|
||||
CMonster* monster = AddMonster( newmontype, npc->pos, 0, 0, 1 );
|
||||
if(monster == NULL) // invalid montype
|
||||
return NULL;
|
||||
GServer->ClearClientID( monster->clientid );
|
||||
monster->clientid = npc->clientid;
|
||||
for(UINT i=0;i<NPCList.size();i++)
|
||||
{
|
||||
if(NPCList.at(i)==npc)
|
||||
NPCList.erase( NPCList.begin()+i );
|
||||
}
|
||||
delete npc;
|
||||
BEGINPACKET( pak, 0x774 );
|
||||
ADDWORD ( pak, npc->clientid );
|
||||
ADDWORD ( pak, newmontype );
|
||||
GServer->SendToVisible( &pak, monster );
|
||||
return monster;
|
||||
}
|
||||
|
||||
// Teleport player to this map and this coord.
|
||||
bool CMap::TeleportPlayer( CPlayer* player, fPoint coord, bool TelePassenger )
|
||||
{
|
||||
GServer->MapList.Index[player->Position->Map]->RemovePlayer( player, false );
|
||||
player->Position->Map = id;
|
||||
player->Position->current = coord;
|
||||
player->Position->destiny = coord;
|
||||
Log(MSG_DEBUG,"(TeleportPlayer) Destiny set to target position X: %f Y: %f.",player->Position->current.x,player->Position->current.y);
|
||||
player->Session->inGame = false;
|
||||
player->Position->lastMoveTime = clock();
|
||||
if(player->Stats->HP < 1)player->Stats->HP = player->Stats->MaxHP * 10 / 100;
|
||||
if(!allowpat || !TelePassenger)
|
||||
{
|
||||
if(!allowpat)
|
||||
player->Status->Stance = 0x03;
|
||||
player->Ride->Drive = false;
|
||||
player->Ride->charid= 0;
|
||||
player->Ride->Ride = false;
|
||||
}
|
||||
AddPlayer( player );
|
||||
BEGINPACKET( pak, 0x07a8 );
|
||||
ADDWORD ( pak, player->clientid );
|
||||
ADDWORD ( pak, player->Position->Map );
|
||||
ADDFLOAT ( pak, player->Position->current.x * 100 );
|
||||
ADDFLOAT ( pak, player->Position->current.y * 100 );
|
||||
ADDWORD ( pak, (player->Status->Stance == 0x04?0x0201:0x0001) );
|
||||
player->client->SendPacket( &pak );
|
||||
if( player->Ride->Drive && player->Ride->charid != 0 )
|
||||
{
|
||||
CPlayer* otherclient = GServer->GetClientByCID( player->Ride->charid );
|
||||
if( otherclient != NULL )
|
||||
{
|
||||
if(TelePassenger)
|
||||
{
|
||||
TeleportPlayer( otherclient, coord );
|
||||
}
|
||||
else
|
||||
{
|
||||
otherclient->Ride->Drive = false;
|
||||
otherclient->Ride->charid = 0;
|
||||
otherclient->Ride->Ride = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
GServer->pakClearUser( player );
|
||||
GServer->ClearClientID( player->clientid );
|
||||
player->RestartPlayerVal( );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMap::pakTeleport( CPlayer* player, unsigned int Map, fPoint coord, bool TelePassenger )
|
||||
{
|
||||
GServer->MapList.Index[player->Position->Map]->RemovePlayer( player, false );
|
||||
player->Position->Map = Map;
|
||||
player->Position->current = coord;
|
||||
player->Position->destiny = coord;
|
||||
Log(MSG_DEBUG,"(pakTeleport) Destiny set to target position X: %f Y: %f.",player->Position->current.x,player->Position->current.y);
|
||||
player->Session->inGame = false;
|
||||
player->Position->lastMoveTime = clock();
|
||||
if(!allowpat || !TelePassenger)
|
||||
{
|
||||
if(!allowpat)
|
||||
player->Status->Stance=0x03;
|
||||
player->Ride->Drive = false;
|
||||
player->Ride->charid= 0;
|
||||
player->Ride->Ride = false;
|
||||
}
|
||||
AddPlayer( player );
|
||||
BEGINPACKET( pak, 0x07a8 );
|
||||
ADDWORD ( pak, player->clientid );
|
||||
ADDWORD ( pak, player->Position->Map );
|
||||
ADDFLOAT ( pak, player->Position->current.x*100 );
|
||||
ADDFLOAT ( pak, player->Position->current.y*100 );
|
||||
ADDWORD ( pak, (player->Status->Stance == 0x04?0x0201:0x0001) );
|
||||
player->client->SendPacket( &pak );
|
||||
if( player->Ride->Drive && player->Ride->charid!=0 )
|
||||
{
|
||||
CPlayer* otherclient = GServer->GetClientByCID( player->Ride->charid );
|
||||
if( otherclient != NULL )
|
||||
{
|
||||
if(TelePassenger)
|
||||
{
|
||||
TeleportPlayer( otherclient, coord );
|
||||
}
|
||||
else
|
||||
{
|
||||
otherclient->Ride->Drive = false;
|
||||
otherclient->Ride->charid= 0;
|
||||
otherclient->Ride->Ride = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
GServer->pakClearUser( player );
|
||||
GServer->ClearClientID( player->clientid );
|
||||
player->RestartPlayerVal( );
|
||||
return true;
|
||||
}
|
||||
|
||||
// return near respawn [NULL if there are no respawn]
|
||||
CRespawnPoint* CMap::GetNearRespawn( CPlayer* player )
|
||||
{
|
||||
CRespawnPoint* respawn = NULL;
|
||||
float distance = 0xffff;
|
||||
for(UINT i=0;i<RespawnList.size();i++)
|
||||
{
|
||||
float resdist = GServer->distance( player->Position->current, RespawnList.at(i)->dest );
|
||||
if( resdist < distance )
|
||||
{
|
||||
respawn = RespawnList.at(i);
|
||||
distance = resdist;
|
||||
}
|
||||
}
|
||||
return respawn;
|
||||
}
|
||||
|
||||
// Return "start" respawn
|
||||
CRespawnPoint* CMap::GetStartRespawn( )
|
||||
{
|
||||
for( UINT i = 0; i < RespawnList.size(); i++ )
|
||||
{
|
||||
if( RespawnList.at(i)->masterdest )
|
||||
return RespawnList.at(i);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return first respawn
|
||||
CRespawnPoint* CMap::GetFirstRespawn( )
|
||||
{
|
||||
CRespawnPoint* respawn = NULL;
|
||||
for(UINT i=0;i<RespawnList.size();i++)
|
||||
{
|
||||
return RespawnList.at(i);
|
||||
}
|
||||
return respawn;
|
||||
}
|
||||
|
||||
// Search a client by clientid
|
||||
CPlayer* CMap::GetPlayerInMap( UINT id )
|
||||
{
|
||||
for(UINT i=0;i<PlayerList.size();i++)
|
||||
{
|
||||
if(PlayerList.at(i)->clientid == id )
|
||||
return PlayerList.at(i);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Search a client by charid
|
||||
CPlayer* CMap::GetCharIDInMap( UINT id )
|
||||
{
|
||||
for(UINT i=0;i<PlayerList.size();i++)
|
||||
{
|
||||
if(PlayerList.at(i)->CharInfo->charid == id )
|
||||
return PlayerList.at(i);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Search a monster by clientid
|
||||
CMonster* CMap::GetMonsterInMap( UINT id )
|
||||
{
|
||||
for(UINT i=0;i<MonsterList.size();i++)
|
||||
{
|
||||
if(MonsterList.at(i)->clientid == id )
|
||||
return MonsterList.at(i);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CDrop* CMap::GetDropInMap( UINT id )
|
||||
{
|
||||
for(UINT i=0;i<DropsList.size();i++)
|
||||
{
|
||||
if(DropsList.at(i)->clientid == id )
|
||||
return DropsList.at(i);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CNPC* CMap::GetNPCInMap( UINT id )
|
||||
{
|
||||
for(UINT i=0;i<NPCList.size();i++)
|
||||
{
|
||||
if(NPCList.at(i)->clientid == id )
|
||||
return NPCList.at(i);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CMap::UpdateTime( )
|
||||
{
|
||||
if(id == 0) return;
|
||||
UINT etime = (UINT)GServer->round((clock() - LastUpdate)/1000);
|
||||
if(etime < 10)return;
|
||||
MapTime += (UINT)floor((double)(etime/10));
|
||||
int Hourlength = int(0xffffffff / 24);
|
||||
int Minutelength = int(Hourlength / 60);
|
||||
if(MapTime == 0xffffffff)
|
||||
{
|
||||
MapTime = 0;
|
||||
Date ++;
|
||||
}
|
||||
ZoneTime = int(MapTime / 17895697);
|
||||
|
||||
if(dayperiod == 0)
|
||||
dayperiod = 1;
|
||||
UINT ctime = MapTime%dayperiod;
|
||||
if(ctime >= morningtime && ctime < daytime)
|
||||
{
|
||||
CurrentTime = MORNING;
|
||||
}
|
||||
else
|
||||
if(ctime >= daytime && ctime < eveningtime)
|
||||
{
|
||||
CurrentTime = DAY;
|
||||
}
|
||||
else
|
||||
if(ctime >= eveningtime && ctime < nighttime)
|
||||
{
|
||||
CurrentTime = EVENING;
|
||||
}
|
||||
else
|
||||
if(ctime >= nighttime)
|
||||
{
|
||||
CurrentTime = NIGHT;
|
||||
}
|
||||
hour = int(MapTime / Hourlength);
|
||||
minute = int((MapTime - (hour * Hourlength)) / Minutelength);
|
||||
LastUpdate = clock();
|
||||
}
|
||||
|
||||
// Clean Drops
|
||||
void CMap::CleanDrops( )
|
||||
{
|
||||
for(UINT j=0;j<DropsList.size();j++)
|
||||
{
|
||||
CDrop* thisdrop = DropsList.at(j);
|
||||
if( time(NULL) - thisdrop->droptime >= 50 )
|
||||
DeleteDrop( thisdrop );
|
||||
}
|
||||
}
|
||||
|
||||
// Respawn a Monster
|
||||
void CMap::RespawnMonster( )
|
||||
{
|
||||
if(GServer->Config.SpawnType == 1)
|
||||
{
|
||||
/*for(UINT k=0;k<MonsterSpawnList.size( );k++)
|
||||
{
|
||||
CSpawnArea* thisspawn = MonsterSpawnList.at(k);
|
||||
|
||||
if (thisspawn->respawntime<2)thisspawn->respawntime=2;
|
||||
clock_t rtime = clock() - thisspawn->lastRespawnTime;
|
||||
if((rtime > thisspawn->respawntime*CLOCKS_PER_SEC && thisspawn->amon<thisspawn->max)|| thisspawn->amon<thisspawn->min )
|
||||
{
|
||||
fPoint position = GServer->RandInPoly( thisspawn->points, thisspawn->pcount );
|
||||
AddMonster( thisspawn->montype, position, 0, thisspawn->mobdrop, thisspawn->mapdrop, thisspawn->id );
|
||||
thisspawn->lastRespawnTime = clock();
|
||||
}
|
||||
}*/
|
||||
//PY new spawn code
|
||||
for(UINT k=0;k<MonsterSpawnList.size( );k++)
|
||||
{
|
||||
CSpawnArea* thisspawn = MonsterSpawnList.at(k);
|
||||
clock_t rtime = clock() - thisspawn->lastRespawnTime;
|
||||
if(thisspawn->limit == 1 && IsNight()) //spawn limit is set to day only
|
||||
continue;
|
||||
if(thisspawn->RefVar != 0 && thisspawn->RefVal != 0) //only allow this spawn if the correct ObjVar is set to non zero
|
||||
{
|
||||
if(GServer->ObjVar[thisspawn->RefVar][0] == thisspawn->RefVal)
|
||||
continue;
|
||||
}
|
||||
if(thisspawn->limit == 2 && !IsNight()) //spawn limit is set to night only
|
||||
continue;
|
||||
switch (thisspawn->type)
|
||||
{
|
||||
case 0:
|
||||
// no spawn. this spawn area is inactive
|
||||
break;
|
||||
case 1: //normal spawn
|
||||
if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->amon < thisspawn->max)
|
||||
{
|
||||
fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius );
|
||||
UINT aggro = thisspawn->aggressive;
|
||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
||||
thisspawn->lastRespawnTime = clock();
|
||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
||||
thisspawn->triggercount = 0;
|
||||
}
|
||||
break;
|
||||
case 2: //normal but fill the spawn
|
||||
if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->amon < thisspawn->max)
|
||||
{
|
||||
for(int i=1;i<thisspawn->max;i++)
|
||||
{
|
||||
fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius );
|
||||
UINT aggro = thisspawn->aggressive;
|
||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3: //cascade spawn
|
||||
if(thisspawn->triggercount > thisspawn->triggeramount && thisspawn->amon < thisspawn->max)
|
||||
{
|
||||
fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius );
|
||||
UINT aggro = thisspawn->aggressive;
|
||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
||||
thisspawn->lastRespawnTime = clock();
|
||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
||||
thisspawn->triggercount = 0;
|
||||
}
|
||||
break;
|
||||
case 4: //cascade to max
|
||||
if(thisspawn->triggercount > thisspawn->triggeramount && thisspawn->amon < thisspawn->max)
|
||||
{
|
||||
for(int i=1;i<thisspawn->max;i++)
|
||||
{
|
||||
fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius );
|
||||
UINT aggro = thisspawn->aggressive;
|
||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
||||
}
|
||||
thisspawn->lastRespawnTime = clock();
|
||||
thisspawn->triggercount = 0;
|
||||
}
|
||||
break;
|
||||
case 5: //sub spawns. needs to trigger from a specific montype
|
||||
if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->amon < thisspawn->max)
|
||||
{
|
||||
//find if any of the trigger mobs exist
|
||||
for(UINT j=0;j<MonsterList.size();j++)
|
||||
{
|
||||
CMonster* monster = MonsterList.at(j);
|
||||
//if(monster->montype == thisspawn->triggertype && (thisspawn->spawnkey == 4 || thisspawn->triggercount >= thisspawn->triggeramount))
|
||||
if(monster->montype == thisspawn->triggertype)
|
||||
{
|
||||
//found the right monster. now spawn the servant mob in a circle around it
|
||||
fPoint position = GServer->RandInCircle(monster->Position->current,5);
|
||||
UINT aggro = thisspawn->aggressive;
|
||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
||||
thisspawn->triggercount = 0;
|
||||
thisspawn->lastRespawnTime = clock();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->amon < thisspawn->max)
|
||||
{
|
||||
//find if any of the trigger mobs exist
|
||||
for(UINT j=0;j<MonsterList.size();j++)
|
||||
{
|
||||
CMonster* monster = MonsterList.at(j);
|
||||
//if(monster->montype == thisspawn->triggertype && (thisspawn->spawnkey == 4 || thisspawn->triggercount >= thisspawn->triggeramount))
|
||||
if(monster->montype == thisspawn->triggertype)
|
||||
{
|
||||
//found the right monster. now spawn the servant mobs around it up to the spawn max
|
||||
for(int i=thisspawn->amon;i<thisspawn->max;i++)
|
||||
{
|
||||
fPoint position = GServer->RandInCircle(monster->Position->current,5);
|
||||
UINT aggro = thisspawn->aggressive;
|
||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
||||
}
|
||||
thisspawn->lastRespawnTime = clock();
|
||||
thisspawn->triggercount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 7: //TDEF spawn. Spawns thisspawn->max monsters one by one using triggercount to track them
|
||||
if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->triggercount < thisspawn->max)
|
||||
{
|
||||
fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius );
|
||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, 0, true, 0 );
|
||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 3); // update the counters
|
||||
thisspawn->lastRespawnTime = clock();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(GServer->Config.SpawnType == 0)
|
||||
{
|
||||
for (UINT j = 0; j < MobGroupList.size(); j++)
|
||||
{
|
||||
CMobGroup* thisgroup = MobGroupList.at(j);
|
||||
clock_t rtime = clock() - thisgroup->lastRespawnTime;
|
||||
if (rtime < thisgroup->respawntime*CLOCKS_PER_SEC || thisgroup->active >= thisgroup->limit)
|
||||
continue;
|
||||
for (UINT k = thisgroup->active; k < thisgroup->limit; k++)
|
||||
{
|
||||
CMob *thismob;
|
||||
if (thisgroup->tacMobs.size() > 0 && thisgroup->basicKills >= thisgroup->tacticalpoints)
|
||||
{
|
||||
thismob = thisgroup->tacMobs.at(thisgroup->curTac);
|
||||
thisgroup->basicKills = 0;
|
||||
thisgroup->curTac++;
|
||||
if (thisgroup->curTac >= thisgroup->tacMobs.size()) thisgroup->curTac = 0;
|
||||
} else {
|
||||
thismob = thisgroup->basicMobs.at(thisgroup->curBasic);
|
||||
thisgroup->curBasic++;
|
||||
if (thisgroup->curBasic >= thisgroup->basicMobs.size()) thisgroup->curBasic = 0;
|
||||
}
|
||||
for (UINT i = 0; i < thismob->amount; i++)
|
||||
{
|
||||
fPoint position = GServer->RandInCircle( thisgroup->point, thisgroup->range );
|
||||
AddMonster( thismob->mobId, position, 0, thisgroup->id, 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//update the trigger counts in cascade spawns
|
||||
void CMap::UpdateSpawnCounter( UINT montype, UINT spawnid, UINT key )
|
||||
{
|
||||
if(key != 0) //kick back if key is undefined
|
||||
{
|
||||
for(UINT k=0;k<MonsterSpawnList.size( );k++)
|
||||
{
|
||||
CSpawnArea* thisspawn = MonsterSpawnList.at(k);
|
||||
switch (thisspawn->typeoftrigger)
|
||||
{
|
||||
case 0: //no trigger
|
||||
continue;
|
||||
break;
|
||||
case 1: //at any location
|
||||
//specific monster spawns or dies or is attacked anywhere in the current map
|
||||
if(thisspawn->triggertype == montype && thisspawn->spawnkey == key)
|
||||
{
|
||||
thisspawn->triggercount++;
|
||||
}
|
||||
case 2: //at a specific spawn id
|
||||
//specific monster spawns or dies or is attacked at this specific spawn area
|
||||
if(thisspawn->triggertype == spawnid && thisspawn->spawnkey == key)
|
||||
{
|
||||
thisspawn->triggercount++;
|
||||
}
|
||||
break;
|
||||
case 3: //towerdef
|
||||
thisspawn->triggercount++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if is night
|
||||
bool CMap::IsNight( )
|
||||
{
|
||||
if(CurrentTime==NIGHT)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
CCharacter* CMap::GetCharInMap( unsigned int id )
|
||||
{
|
||||
CMonster* monster = GetMonsterInMap( id );
|
||||
//Log(MSG_DEBUG, "getcharinmap %i", id);
|
||||
if(monster != NULL)
|
||||
{
|
||||
//Log(MSG_DEBUG, "Monster found type = %i", monster->montype);
|
||||
return (CCharacter*) monster;
|
||||
}
|
||||
CPlayer* player = GetPlayerInMap( id );
|
||||
if(player != NULL)
|
||||
return (CCharacter*) player;
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
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 "worldmonster.h"
|
||||
|
||||
// check is a player is in range
|
||||
bool CMonster::PlayerInRange()
|
||||
{
|
||||
CMap* map = GServer->MapList.Index[Position->Map];
|
||||
for(int i=0; i<map->PlayerList.size(); i++)
|
||||
{
|
||||
CPlayer* thisclient = map->PlayerList.at(i);
|
||||
if(GServer->IsVisible(thisclient, this)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the near player
|
||||
CPlayer* CMonster::GetNearPlayer( UINT mdist )
|
||||
{
|
||||
CPlayer* thisplayer = NULL;
|
||||
float distance = 0xffff;
|
||||
for(UINT i=0;i<GServer->MapList.Index[Position->Map]->PlayerList.size();i++)
|
||||
{
|
||||
CPlayer* thisclient = GServer->MapList.Index[Position->Map]->PlayerList.at(i);
|
||||
float tempdist = GServer->distance( Position->current, thisclient->Position->current );
|
||||
if(tempdist<distance)
|
||||
{
|
||||
thisplayer = thisclient;
|
||||
distance = tempdist;
|
||||
}
|
||||
}
|
||||
if(distance>mdist)
|
||||
return NULL; // near player is too far
|
||||
return thisplayer;
|
||||
}
|
||||
|
||||
// return true if is a ghost
|
||||
bool CMonster::IsGhost( )
|
||||
{
|
||||
// candle host, elec ghost, yigore ghost
|
||||
if((montype>710 && montype<751) || (montype>679 && montype<693))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if is a ghost seed
|
||||
bool CMonster::IsGhostSeed( )
|
||||
{
|
||||
if((montype>700 && montype<711) && montype!=703)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if current monster can move
|
||||
bool CMonster::CanMove( )
|
||||
{
|
||||
if((montype<41 || montype>45) && (montype<325 || montype>329) && montype!=659 && (montype<771 || montype>810 ) && montype!=992 && (montype<1474 || montype>1489))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if this monster is ready to move
|
||||
bool CMonster::ReadyToMove( )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// return a player pointers
|
||||
CPlayer* CMonster::GetOwner( )
|
||||
{
|
||||
if(!IsSummon( )) return NULL;
|
||||
CPlayer* ownerclient = GServer->GetClientByID( owner, Position->Map );
|
||||
return ownerclient;
|
||||
}
|
||||
|
||||
bool CMonster::IsMonster( )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMonster::AddDamage( CCharacter* enemy, long int hitpower)
|
||||
{
|
||||
int MaxDamage = Stats->MaxHP * 2;
|
||||
//Log(MSG_DEBUG,"Max Damage = %i",MaxDamage);
|
||||
CPlayer* player = NULL;
|
||||
if(enemy->IsMonster( ))
|
||||
{
|
||||
CMonster* monster = (CMonster*) enemy;
|
||||
if(!enemy->IsSummon( ))
|
||||
return;
|
||||
player = monster->GetOwner( );
|
||||
if(player == NULL)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
player = (CPlayer*) enemy;
|
||||
}
|
||||
if(MonsterDrop->firsthit == 0 && hitpower > 0)
|
||||
{
|
||||
MonsterDrop->firsthit = player->CharInfo->charid;
|
||||
MonsterDrop->firstlevel = player->Stats->Level;
|
||||
if( player->Party->party != NULL )
|
||||
thisparty = player->Party->party;
|
||||
else
|
||||
thisparty = NULL;
|
||||
}
|
||||
for(UINT i=0;i<PlayersDamage.size();i++)
|
||||
{
|
||||
MonsterDamage* thisplayer = PlayersDamage.at(i);
|
||||
if(thisplayer->charid == player->CharInfo->charid)
|
||||
{
|
||||
if(hitpower > MaxDamage)
|
||||
hitpower = MaxDamage;
|
||||
thisplayer->damage += hitpower;
|
||||
if(thisplayer->damage > MaxDamage)
|
||||
thisplayer->damage = MaxDamage;
|
||||
return;
|
||||
}
|
||||
}
|
||||
MonsterDamage* newplayer = new MonsterDamage;
|
||||
newplayer->charid = player->CharInfo->charid;
|
||||
if( hitpower > MaxDamage )
|
||||
hitpower = MaxDamage;
|
||||
newplayer->damage = hitpower;
|
||||
PlayersDamage.push_back( newplayer );
|
||||
//Log(MSG_DEBUG,"Player Damage added= %i",hitpower);
|
||||
}
|
||||
|
||||
CDrop* CMonster::GetDrop( )
|
||||
{
|
||||
return GServer->GetNewDrop( this );
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#ifndef __ROSE_PARY__
|
||||
#define __ROSE_PARY__
|
||||
#include "worldserver.h"
|
||||
#include "datatypes.h"
|
||||
|
||||
class CParty
|
||||
{
|
||||
public:
|
||||
CParty( );
|
||||
~CParty( );
|
||||
|
||||
CPlayer* GetMember( UINT id );
|
||||
CPlayer* GetMemberByCharID( UINT charid );
|
||||
void SendToMembers( CPacket* pak, CPlayer* exclient=NULL );
|
||||
void SendToVisible( CPacket* pak, CPlayer* thisclient );
|
||||
bool AddPlayer( CPlayer* player );
|
||||
bool RemovePlayer( CPlayer* player );
|
||||
bool counted; //flag to help assign party exp
|
||||
|
||||
UINT Pending_Exp; //temporary value to hold exp during calculations
|
||||
UINT PartyLevel;
|
||||
UINT Exp;
|
||||
UINT Option;
|
||||
UINT LastItem;
|
||||
UINT LastETC;
|
||||
UINT LastZulies;
|
||||
UINT Capacity;
|
||||
vector<CPlayer*> Members;
|
||||
CPartyExp* PartyExp;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
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 "party.h"
|
||||
|
||||
CParty::CParty( )
|
||||
{
|
||||
PartyLevel = 1;
|
||||
Exp = 0;
|
||||
Option =0;
|
||||
LastItem = 0;
|
||||
LastETC = 0;
|
||||
LastZulies = 0;
|
||||
Capacity = 5;
|
||||
Members.clear();
|
||||
PartyExp = new CPartyExp;
|
||||
}
|
||||
|
||||
CParty::~CParty( )
|
||||
{
|
||||
delete PartyExp;
|
||||
}
|
||||
|
||||
CPlayer* CParty::GetMember( UINT id )
|
||||
{
|
||||
for(UINT i=0;i<Members.size( );i++)
|
||||
{
|
||||
CPlayer* member = Members.at(i);
|
||||
if(member == NULL)
|
||||
continue;
|
||||
if(member->clientid == id)
|
||||
return member;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CPlayer* CParty::GetMemberByCharID( UINT id )
|
||||
{
|
||||
for(UINT i=0;i<Members.size( );i++)
|
||||
{
|
||||
CPlayer* member = Members.at(i);
|
||||
if(member == NULL)
|
||||
continue;
|
||||
if(member->CharInfo->charid == id)
|
||||
return member;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CParty::SendToMembers( CPacket* pak, CPlayer* explayer )
|
||||
{
|
||||
for(UINT i=0;i<Members.size( );i++)
|
||||
{
|
||||
CPlayer* member = Members.at(i);
|
||||
if(member == NULL)
|
||||
continue;
|
||||
if(member == explayer)
|
||||
continue;
|
||||
member->client->SendPacketCpy( pak );
|
||||
}
|
||||
}
|
||||
|
||||
void CParty::SendToVisible( CPacket* pak, CPlayer* thisclient )
|
||||
{
|
||||
for(UINT i=0;i<Members.size( );i++)
|
||||
{
|
||||
CPlayer* member = Members.at(i);
|
||||
if(member == NULL)
|
||||
continue;
|
||||
if(member == thisclient)
|
||||
continue;
|
||||
if(GServer->IsVisible( thisclient, member ))
|
||||
member->client->SendPacketCpy( pak );
|
||||
}
|
||||
}
|
||||
|
||||
bool CParty::AddPlayer( CPlayer* player )
|
||||
{
|
||||
player->Party->party = this;
|
||||
Members.push_back( player );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CParty::RemovePlayer( CPlayer* player )
|
||||
{
|
||||
for(UINT i=0;i<Members.size( );i++)
|
||||
{
|
||||
if(Members.at(i)==player)
|
||||
{
|
||||
Members.erase( Members.begin( )+i );
|
||||
player->Party->IsMaster = true;
|
||||
player->Party->party = NULL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
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 "player.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
CPlayer::CPlayer( CClientSocket* CLIENT )
|
||||
{
|
||||
client = CLIENT;
|
||||
// USED ITEM
|
||||
UsedItem = new USEDITEM;
|
||||
assert(UsedItem);
|
||||
UsedItem->lastRegTime = 0;
|
||||
UsedItem->usevalue = 0;
|
||||
UsedItem->usetype = 0;
|
||||
UsedItem->userate = 0;
|
||||
UsedItem->used = 0;
|
||||
// CHARINFO
|
||||
CharInfo = new INFO;
|
||||
assert(CharInfo);
|
||||
memset( &CharInfo->charname, '\0', 17 );
|
||||
CharInfo->charid = 0;
|
||||
CharInfo->Sex = 0;
|
||||
CharInfo->Face = 0;
|
||||
CharInfo->Hair = 0;
|
||||
CharInfo->Exp = 0;
|
||||
CharInfo->Job = 0;
|
||||
CharInfo->Zulies = 0;
|
||||
CharInfo->Storage_Zulies = 0;
|
||||
CharInfo->LastGlobal = 0;
|
||||
CharInfo->StatPoints = 0;
|
||||
CharInfo->SkillPoints = 0;
|
||||
CharInfo->stamina = 0;
|
||||
|
||||
// RIDE
|
||||
Ride = new RIDE;
|
||||
assert(Ride);
|
||||
Ride->Drive = false;
|
||||
Ride->Ride = false;
|
||||
Ride->charid = 0;
|
||||
// TRADE
|
||||
Trade = new TRADE;
|
||||
assert(Trade);
|
||||
for(int i=0;i<10;i++)
|
||||
Trade->trade_itemid[i] = 0;
|
||||
for(int i=0;i<11;i++)
|
||||
Trade->trade_count[i] = 0;
|
||||
Trade->trade_status = 0;
|
||||
Trade->trade_target = 0;
|
||||
//PARTY
|
||||
Party = new PARTY;
|
||||
assert(Party);
|
||||
Party->party = NULL;
|
||||
Party->IsMaster = true;
|
||||
// SHOP
|
||||
Shop = new SHOP;
|
||||
assert(Shop);
|
||||
Shop->open = false;
|
||||
memset( &Shop->name, '\0',64 );
|
||||
for(int i=0;i<30;i++)
|
||||
{
|
||||
ClearItem(Shop->BuyingList[i].item);
|
||||
Shop->BuyingList[i].slot = 0;
|
||||
Shop->BuyingList[i].count = 0;
|
||||
Shop->BuyingList[i].price = 0;
|
||||
Shop->SellingList[i].slot = 0;
|
||||
Shop->SellingList[i].count = 0;
|
||||
Shop->SellingList[i].price = 0;
|
||||
}
|
||||
Shop->Buying = 0;
|
||||
Shop->Selling = 0;
|
||||
Shop->ShopType = 0;
|
||||
// SESSION
|
||||
Session = new SESSION;
|
||||
assert(Session);
|
||||
Session->userid = 0;
|
||||
memset( &Session->username, '\0', 17 );
|
||||
memset( &Session->password, '\0', 33 );
|
||||
Session->accesslevel = 0;
|
||||
Session->isLoggedIn = false;
|
||||
Session->inGame = false;
|
||||
// Inventory / storage
|
||||
for(unsigned int i=0;i<MAX_INVENTORY;i++)
|
||||
ClearItem( items[i] );
|
||||
for(unsigned int i=0;i<MAX_STORAGE;i++)
|
||||
ClearItem( storageitems[i] );
|
||||
// Clan
|
||||
Clan = new CLAN;
|
||||
assert(Clan);
|
||||
Clan->clanid = 0;
|
||||
Clan->clanrank = 0;
|
||||
Clan->grade = 0;
|
||||
Clan->CP = 0;
|
||||
Clan->logo = 0;
|
||||
Clan->back = 0;
|
||||
memset( &Clan->clanname, '\0', 17 );
|
||||
//union
|
||||
Union_s = new UNION_s;
|
||||
// ATTRIBUTES
|
||||
Attr = new ATTRIBUTES;
|
||||
assert(Attr);
|
||||
Attr->Str = 0;
|
||||
Attr->Dex = 0;
|
||||
Attr->Int = 0;
|
||||
Attr->Con = 0;
|
||||
Attr->Cha = 0;
|
||||
Attr->Sen = 0;
|
||||
Attr->Estr = 0;
|
||||
Attr->Edex = 0;
|
||||
Attr->Eint = 0;
|
||||
Attr->Econ = 0;
|
||||
Attr->Echa = 0;
|
||||
Attr->Esen = 0;
|
||||
|
||||
CharType = TPLAYER;
|
||||
questdebug = false;
|
||||
Session->codedebug = false;
|
||||
Saved = false;
|
||||
isInvisibleMode = false;
|
||||
Fairy = false;
|
||||
//FairyTime = 0;
|
||||
nstorageitems = 0;
|
||||
p_skills = 0;
|
||||
|
||||
for(int i=0;i<MAX_SKILL;i++)
|
||||
{
|
||||
cskills[i].id = 0;
|
||||
cskills[i].level = 0;
|
||||
}
|
||||
|
||||
for(int i=0;i<30;i++){
|
||||
bskills[i] = 0;
|
||||
askill[i] = 0;
|
||||
askilllvl[i] = 0;
|
||||
pskill[i] = 0;
|
||||
pskilllvl[i] = 0;
|
||||
}
|
||||
for(int i=0;i<MAX_QUICKBAR;i++)
|
||||
quickbar[i] = 0;
|
||||
ActiveQuest = 0;
|
||||
lastRegenTime = 0;
|
||||
lastSaveTime = clock( );
|
||||
lastGG = 0;
|
||||
VisiblePlayers.clear( );
|
||||
VisibleDrops.clear( );
|
||||
VisibleMonsters.clear( );
|
||||
VisibleNPCs.clear( );
|
||||
}
|
||||
|
||||
CPlayer::~CPlayer( )
|
||||
{
|
||||
if(client!=NULL) delete client;
|
||||
if(UsedItem!=NULL) delete UsedItem;
|
||||
if(CharInfo!=NULL) delete CharInfo;
|
||||
if(Ride!=NULL) delete Ride;
|
||||
if(Trade!=NULL) delete Trade;
|
||||
if(Party!=NULL) delete Party;
|
||||
if(Shop!=NULL) delete Shop;
|
||||
if(Session!=NULL) delete Session;
|
||||
if(Clan!=NULL) delete Clan;
|
||||
if(Attr!=NULL) delete Attr;
|
||||
//MyQuest.clear();
|
||||
VisiblePlayers.clear();
|
||||
VisibleDrops.clear();
|
||||
VisibleMonsters.clear();
|
||||
VisibleNPCs.clear();
|
||||
}
|
||||
|
||||
bool CPlayer::IsMonster( )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CPlayer::UpdateValues( )
|
||||
{
|
||||
if( Ride->Ride && !Ride->Drive ) //I'm a passenger in somebody else's cart
|
||||
{
|
||||
CPlayer* otherclient = GServer->GetClientByCID( Ride->charid, Position->Map ); //find the cart's driver
|
||||
if( otherclient != NULL ) // found the driver
|
||||
{
|
||||
if( otherclient->Status->Stance != 0x04 ) //not driving any more
|
||||
{
|
||||
otherclient->Ride->Drive = false;
|
||||
otherclient->Ride->Ride = false;
|
||||
otherclient->Ride->charid = 0;
|
||||
Ride->Drive = false;
|
||||
Ride->Ride = false;
|
||||
Ride->charid = 0;
|
||||
Position->destiny = Position->current;
|
||||
Log(MSG_DEBUG,"(UpdateValues) not in cart any more. Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
}
|
||||
else //still driving
|
||||
{
|
||||
Position->current = otherclient->Position->current;
|
||||
Position->destiny = otherclient->Position->destiny;
|
||||
Log(MSG_DEBUG,"(UpdateValues) Passenger in Cart Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
Position->lastMoveTime = otherclient->Position->lastMoveTime;
|
||||
return false; // will not update the player position
|
||||
}
|
||||
}
|
||||
else //no driver found so reset values to NOT be a passenger any more
|
||||
{
|
||||
Ride->Drive = false;
|
||||
Ride->Ride = false;
|
||||
Ride->charid = 0;
|
||||
Position->destiny = Position->current;
|
||||
Log(MSG_DEBUG,"(UpdateValues) No other client. Destiny set to current position X: %f Y: %f.",Position->current.x,Position->current.y);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPlayer::RespawnPlayer( CPlayer* thisclient )
|
||||
{
|
||||
CMap* map = GServer->MapList.Index[thisclient->Position->Map];
|
||||
CRespawnPoint* thisrespawn = NULL;
|
||||
thisclient->Stats->HP = thisclient->Stats->MaxHP * 10 / 100;
|
||||
byte respawn = thisclient->Session->RespawnChoice;
|
||||
Log( MSG_INFO, "RespawnZones choice: %i", respawn );
|
||||
//1 - Current / 2 - save point/dungeon entrance
|
||||
if(respawn == 1 || map->dungeon)
|
||||
{
|
||||
if (respawn != 1) // This is a dungeon map, spawn at start
|
||||
thisrespawn = map->GetStartRespawn( );
|
||||
else
|
||||
thisrespawn = map->GetNearRespawn( thisclient );
|
||||
if(thisrespawn!=NULL)
|
||||
{
|
||||
map->TeleportPlayer( thisclient, thisrespawn->dest, false );
|
||||
}
|
||||
}
|
||||
else // This is a field, spawn in save town
|
||||
{
|
||||
thisrespawn = GServer->GetRespawnByMap( thisclient->Position->saved );
|
||||
if(thisrespawn != NULL)
|
||||
{
|
||||
fPoint coord = thisrespawn->dest;
|
||||
GServer->MapList.Index[thisclient->Position->saved]->TeleportPlayer( thisclient, coord, false );
|
||||
}
|
||||
}
|
||||
|
||||
if(thisrespawn == NULL)
|
||||
{
|
||||
fPoint coord;
|
||||
coord.x = 5200;
|
||||
coord.y = 5200;
|
||||
GServer->MapList.Index[2]->TeleportPlayer( thisclient, coord, false );
|
||||
}
|
||||
Log( MSG_INFO, "Player teleported successfully" );
|
||||
for(unsigned int i=0;i<30;i++)
|
||||
{ // Clean Buffs
|
||||
thisclient->MagicStatus[i].Duration = 0;
|
||||
}
|
||||
Log( MSG_INFO, "Buffs Cleaned" );
|
||||
return true;
|
||||
}
|
||||
|
||||
// Spawn Another User on the Screen
|
||||
bool CPlayer::SpawnToPlayer( CPlayer* player, CPlayer* otherclient )
|
||||
{
|
||||
/* if( Party->party!=NULL && Party->party == player->Party->party)
|
||||
{
|
||||
BEGINPACKET( pak, 0x7d5 );
|
||||
ADDDWORD ( pak, CharInfo->charid );
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, GetMaxHP( ) );
|
||||
ADDWORD ( pak, Stats->HP );
|
||||
ADDDWORD ( pak, 0x01000000 );
|
||||
ADDDWORD ( pak, 0x0000000f );
|
||||
ADDWORD ( pak, 0x1388 );
|
||||
player->client->SendPacket( &pak );
|
||||
}
|
||||
*/
|
||||
/*
|
||||
if( Ride->Ride )
|
||||
{
|
||||
CPlayer* rideclient = GServer->GetClientByCID( Ride->charid, Position->Map );
|
||||
if(rideclient==NULL)
|
||||
{
|
||||
Ride->Ride = false;
|
||||
Ride->Drive= false;
|
||||
Ride->charid = 0;
|
||||
return true;
|
||||
}
|
||||
if( GServer->IsVisible( player, rideclient ) || player->CharInfo->charid == rideclient->CharInfo->charid )
|
||||
{
|
||||
BEGINPACKET( pak, 0x796 );
|
||||
if( Ride->Drive )
|
||||
{
|
||||
ADDWORD ( pak, rideclient->clientid );
|
||||
ADDFLOAT ( pak, rideclient->Position->current.x*100 );
|
||||
ADDFLOAT ( pak, rideclient->Position->current.y*100 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDFLOAT ( pak, Position->current.x*100 );
|
||||
ADDFLOAT ( pak, Position->current.y*100 );
|
||||
}
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
player->client->SendPacket( &pak );
|
||||
|
||||
RESETPACKET( pak, 0x7dd );
|
||||
ADDBYTE ( pak, 0x02 );
|
||||
if( rideclient->Ride->Drive )
|
||||
{
|
||||
ADDWORD ( pak, rideclient->clientid );
|
||||
ADDWORD ( pak, clientid );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD ( pak, clientid );
|
||||
ADDWORD ( pak, rideclient->clientid );
|
||||
}
|
||||
player->client->SendPacket( &pak );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
*/
|
||||
|
||||
BEGINPACKET( pak, 0x793 );
|
||||
ADDWORD( pak, clientid); // USER ID
|
||||
ADDFLOAT( pak, Position->current.x*100 ); // POS X
|
||||
ADDFLOAT( pak, Position->current.y*100 ); // POS Y
|
||||
ADDFLOAT( pak, Position->destiny.x*100 ); // GOING TO X
|
||||
ADDFLOAT( pak, Position->destiny.y*100 ); // GOINT TO Y
|
||||
if(Shop->open)
|
||||
{
|
||||
ADDWORD( pak, 0x000b );
|
||||
ADDWORD( pak, 0x0000 );
|
||||
}
|
||||
else
|
||||
if(Stats->HP <= 0)
|
||||
{
|
||||
ADDWORD( pak, 0x0003 );
|
||||
ADDWORD( pak, 0x0000 );
|
||||
}
|
||||
else
|
||||
// if(Status->Stance == SITTING)
|
||||
// {
|
||||
// ADDWORD( pak, 0x000a );
|
||||
// ADDWORD( pak, 0x0000 );
|
||||
// }
|
||||
// else
|
||||
if (Battle->atktarget!=0)
|
||||
{
|
||||
ADDWORD( pak, 0x0002 );
|
||||
ADDWORD( pak, Battle->atktarget );
|
||||
}
|
||||
else
|
||||
if(Position->destiny.x != Position->current.x || Position->destiny.y != Position->current.y)
|
||||
{
|
||||
ADDWORD( pak, 0x0001 );
|
||||
ADDWORD( pak, 0x0000 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD( pak, 0x0000 );
|
||||
ADDWORD( pak, 0x0000 );
|
||||
}
|
||||
ADDBYTE( pak, Ride->Drive == true?2:1);
|
||||
ADDWORD( pak, Stats->HP);
|
||||
ADDBYTE( pak, 0x00);
|
||||
//ADDWORD( pak, 0x6401 ); //??
|
||||
//ADDWORD( pak, 0x0000 );
|
||||
ADDWORD( pak, 0x0200 );
|
||||
ADDWORD( pak, 0x0000 );
|
||||
ADDBYTE( pak, 0x00 );
|
||||
ADDDWORD( pak, GServer->BuildBuffs( this ) ); // BUFFS
|
||||
ADDBYTE( pak, CharInfo->Sex ); // GENDER
|
||||
ADDWORD( pak, Stats->Mspd_base ); // WALK SPEED MAYBE?
|
||||
ADDBYTE( pak, 0x00 ); // ??
|
||||
ADDBYTE( pak, 0x00 ); // ??
|
||||
ADDBYTE( pak, 0x01 ); // ??
|
||||
ADDDWORD( pak, CharInfo->Face ); // FACE TYPE
|
||||
ADDDWORD( pak, CharInfo->Hair ); // HAIR TYPE
|
||||
ADDDWORD( pak, GServer->BuildItemShow( items[2] )); // CAP
|
||||
ADDDWORD( pak, GServer->BuildItemShow( items[3] )); // BODY
|
||||
ADDDWORD( pak, GServer->BuildItemShow( items[5] )); // GLOVES
|
||||
ADDDWORD( pak, GServer->BuildItemShow( items[6] )); // BOOTS
|
||||
ADDDWORD( pak, GServer->BuildItemShow( items[1] )); // FACE
|
||||
ADDDWORD( pak, GServer->BuildItemShow( items[4] )); // BACK
|
||||
ADDDWORD( pak, GServer->BuildItemShow( items[7] )); // WEAPON
|
||||
ADDDWORD( pak, GServer->BuildItemShow( items[8] )); // SUBWEAPON
|
||||
ADDWORD( pak, GServer->BuildItemHead(items[132] ));
|
||||
ADDWORD( pak, GServer->BuildItemHead(items[133] ));
|
||||
ADDWORD( pak, GServer->BuildItemHead(items[134] ));
|
||||
ADDWORD( pak, CharInfo->Job );
|
||||
ADDBYTE( pak, Stats->Level);
|
||||
if(items[135].itemnum>0){
|
||||
ADDWORD( pak, (items[135].itemnum +1024));}
|
||||
else{
|
||||
ADDWORD( pak, 0x0000 );}
|
||||
ADDWORD( pak, 0x0000 );
|
||||
if(items[136].itemnum>0){
|
||||
ADDWORD( pak, (items[136].itemnum +1024));}
|
||||
else{
|
||||
ADDWORD( pak, 0x0000 );}
|
||||
ADDWORD( pak, 0x0000 );
|
||||
if(items[137].itemnum>0){
|
||||
ADDWORD( pak, (items[137].itemnum +1024));}
|
||||
else{
|
||||
ADDWORD( pak, 0x0000 );}
|
||||
ADDWORD( pak, 0x0000 );
|
||||
if(items[138].itemnum>0){
|
||||
ADDWORD( pak, (items[138].itemnum +1024));}
|
||||
else{
|
||||
ADDWORD( pak, 0x0000 );}
|
||||
ADDWORD( pak, 0x0000 );
|
||||
|
||||
ADDWORD( pak, (Stats->HP<=0)?0x0:0xCDCD );
|
||||
if(Shop->open)
|
||||
{
|
||||
ADDWORD( pak, 0x0002 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDWORD( pak, 0x0000 );
|
||||
}
|
||||
ADDWORD( pak, 0x0000 );
|
||||
ADDDWORD( pak, 0x00000000 );
|
||||
ADDSTRING( pak, CharInfo->charname );
|
||||
ADDBYTE( pak, 0x00 );
|
||||
if(Status->Dash_up != 0xff)
|
||||
ADDWORD ( pak, (MagicStatus[Status->Dash_up].Value));
|
||||
if(Status->Haste_up!= 0xff)
|
||||
ADDWORD ( pak, (MagicStatus[Status->Haste_up].Value));
|
||||
if(Shop->open)
|
||||
{
|
||||
ADDBYTE( pak, Shop->ShopType);
|
||||
ADDBYTE( pak, 0x00);
|
||||
ADDSTRING(pak, Shop->name);
|
||||
ADDBYTE( pak, 0x00);
|
||||
}
|
||||
if(Clan->clanid!=0)
|
||||
{
|
||||
ADDWORD ( pak, Clan->clanid );
|
||||
ADDWORD ( pak, 0x0000 );
|
||||
ADDWORD ( pak, Clan->back );
|
||||
ADDWORD ( pak, Clan->logo );
|
||||
ADDBYTE ( pak, Clan->grade );
|
||||
ADDBYTE ( pak, Clan->clanrank );
|
||||
ADDSTRING ( pak, Clan->clanname );
|
||||
}
|
||||
ADDWORD( pak, 0x0000 );
|
||||
player->client->SendPacket(&pak);
|
||||
return true;
|
||||
}
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Returns new skill slot //bskill askill pskill
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
int CPlayer::GetNewSkillSlot(char skilltype)
|
||||
{
|
||||
switch (skilltype)
|
||||
{
|
||||
case 0:
|
||||
for(char s=0;s<30;s++)
|
||||
{
|
||||
if(bskills[s]==0)return s;
|
||||
}
|
||||
case 1:
|
||||
for(char t=0;t<30;t++)
|
||||
{
|
||||
if(askill[t]==0)return t;
|
||||
}
|
||||
case 2:
|
||||
for(char u=0;u<30;u++)
|
||||
{
|
||||
if(pskill[u]==0)return u;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0xffff;
|
||||
}
|
||||
|
||||
void CPlayer::onCheckForMonsterQuests_event (CMonster * Monster)
|
||||
{
|
||||
// We had First hit, and the monster was killed.
|
||||
for (int q = 0; q < 10; q++)
|
||||
{
|
||||
// Give Quest Item
|
||||
if (this->quest.quests[q].QuestID != 0)
|
||||
{
|
||||
//Log(MSG_DEBUG,"Giving quest reward item for quest %i Killed monster type %i",thisclient->quest.quests[q].QuestID,thismon->montype);
|
||||
|
||||
//PY: thismon->thisnpc->die_quest contains the hash needed to complete the trigger
|
||||
int success = this->ExecuteQuestTrigger (Monster->thisnpc->die_quest);
|
||||
if (success == QUEST_SUCCESS)
|
||||
{
|
||||
CPacket pakout (0x730);
|
||||
pakout.AddByte ((byte)success);
|
||||
pakout.AddByte (0);
|
||||
pakout.AddDWord (Monster->thisnpc->die_quest);
|
||||
this->client->SendPacket (&pakout);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#ifndef _ROSE_PLAYER_
|
||||
#define _ROSE_PLAYER_
|
||||
#include "character.h"
|
||||
|
||||
/// Forward Declarations
|
||||
class CMonster; // Forward declare
|
||||
class CPlayer: public CCharacter
|
||||
{
|
||||
|
||||
public:
|
||||
CPlayer( CClientSocket* );
|
||||
~CPlayer( );
|
||||
|
||||
//
|
||||
CClientSocket* client;
|
||||
bool questdebug;// Send PM's with quest information
|
||||
bool Saved;// already save this player?(used ondisconnect/returnchar)
|
||||
bool isInvisibleMode;
|
||||
|
||||
// Fairy
|
||||
bool Fairy;
|
||||
UINT FairyListIndex;
|
||||
|
||||
// variables
|
||||
USEDITEM* UsedItem;
|
||||
INFO* CharInfo;
|
||||
RIDE* Ride;
|
||||
TRADE* Trade;
|
||||
PARTY* Party;
|
||||
SHOP* Shop;
|
||||
SESSION* Session;
|
||||
CLAN* Clan;
|
||||
UNION_s* Union_s;
|
||||
ATTRIBUTES* Attr;
|
||||
CRespawnPoints GMRespawnPoints;
|
||||
|
||||
// Inventory/storage
|
||||
CItem storageitems[MAX_STORAGE];
|
||||
unsigned int nstorageitems;
|
||||
CItem items[MAX_INVENTORY];
|
||||
|
||||
// skills/quickbar
|
||||
SKILLS cskills[MAX_SKILL];
|
||||
UINT bskills[MAX_BASICSKILL];
|
||||
UINT quickbar[MAX_QUICKBAR];
|
||||
unsigned short askill[30];
|
||||
unsigned char askilllvl[30];
|
||||
unsigned short pskill[30];
|
||||
unsigned char pskilllvl[30];
|
||||
int p_skills;
|
||||
|
||||
// ExJam Quest Code - Info
|
||||
DWORD ActiveQuest; //active quest #
|
||||
int CheckQuest; // The currently checked quest.
|
||||
strings TriggerName[10];
|
||||
SQuestData quest; // Character's quest information
|
||||
int NPCvar; // holds reference value for ObjVar global structure
|
||||
int RefVar; // holds reference value for second part of ObjVar structure
|
||||
|
||||
// Time
|
||||
clock_t lastRegenTime;
|
||||
clock_t lastSaveTime;
|
||||
clock_t lastGG;
|
||||
|
||||
// Visible Lists
|
||||
vector<CPlayer*> VisiblePlayers; // Visible players
|
||||
vector<CDrop*> VisibleDrops; // Visible drops
|
||||
vector<unsigned int> VisibleMonsters; // Visible monsters
|
||||
vector<CNPC*> VisibleNPCs; // Visible npcs
|
||||
|
||||
// Battle Events
|
||||
void onCheckForMonsterQuests_event (CMonster* Monster); // Called if the character had first hit and monster was killed.
|
||||
|
||||
// Functions
|
||||
bool CheckPlayerLevelUP( );
|
||||
bool canUseSkill(CSkills* skill);
|
||||
void SetStats( );
|
||||
bool GetPlayerInfo( );
|
||||
bool IsMonster( );
|
||||
bool CleanPlayerVector( );
|
||||
bool loaddata( );
|
||||
void savedata( );
|
||||
void quicksave( );
|
||||
void saveinventory( );
|
||||
void loadinventory( );
|
||||
void savestorage( );
|
||||
void SaveBackupStorage( int logstate );
|
||||
void loadstorage( );
|
||||
UINT GetNewStorageItemSlot( CItem thisitem );
|
||||
UINT GetNewItemSlot( CItem thisitem );
|
||||
bool ClearObject( unsigned int otherclientid );
|
||||
void RestartPlayerVal( );
|
||||
bool Regeneration( );
|
||||
bool PlayerHeal( );
|
||||
bool VisiblityList( );
|
||||
bool ChangeDialog( unsigned int npc, unsigned int dialog, unsigned int eventid = 0 );
|
||||
bool SpawnToPlayer( CPlayer* player, CPlayer* otherclient );
|
||||
bool RespawnPlayer(CPlayer* thisclient);
|
||||
bool UpdateValues( );
|
||||
void ReduceABC( );
|
||||
CParty* GetParty( );
|
||||
CLAN* GetClan( );
|
||||
unsigned int AddItem( CItem item );
|
||||
unsigned int Getnstorageitems( CPlayer* thisclient );
|
||||
void UpdateInventory(unsigned int slot1, unsigned int slot2=0xffff );
|
||||
|
||||
// ExJam Quest Code
|
||||
void savequests( CPlayer* thisclient );
|
||||
SQuest* GetActiveQuest( );
|
||||
int GetQuestVar(short nVarType, short nVarNO);
|
||||
void SetQuestVar(short nVarType, short nVarNO, short nValue);
|
||||
int ExecuteQuestTrigger(dword hash);
|
||||
//PY Quest stuff
|
||||
void SetQuestFlag(dword flagid, bool value);
|
||||
void GetQuestFlag(dword falgid);
|
||||
|
||||
|
||||
// Player Stats
|
||||
unsigned int GetAttackPower( );
|
||||
unsigned int GetDefense( );
|
||||
unsigned int GetDodge( );
|
||||
unsigned int GetXPRate( );
|
||||
unsigned int GetItemDropRate( );
|
||||
unsigned int GetItemDropCountRate( );
|
||||
unsigned int GetZulyDropRate( );
|
||||
unsigned int GetGreyDrops( );
|
||||
unsigned int GetAccury( );
|
||||
unsigned int GetMagicDefense( );
|
||||
unsigned int GetMagicAttack( );
|
||||
unsigned int GetCritical( );
|
||||
unsigned int GetAttackSpeed( );
|
||||
unsigned int GetMoveSpeed( );
|
||||
unsigned int GetMaxHP( );
|
||||
unsigned int GetMaxMP( );
|
||||
float GetAttackDistance( );
|
||||
unsigned int GetHPRegenAmount( );
|
||||
unsigned int GetMPRegenAmount( );
|
||||
unsigned int GetMaxWeight( );
|
||||
unsigned int GetCurrentWeight( );
|
||||
unsigned int GetMPReduction( );
|
||||
unsigned int GetMaxSummonGauge( );
|
||||
void GetExtraStats( );
|
||||
unsigned int GetLevelEXP( );
|
||||
unsigned int GetInt( );
|
||||
// Skill
|
||||
int GetNewSkillSlot(char skilltype);
|
||||
void takeSkillCost( CSkills* skill );
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,2 @@
|
||||
// This file is no longer needed, will remove from later versions when I get around to it - Drakia
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
/*bool CWorldServer::DoQuestScript( CPlayer* thisclient, CQuest* thisquest )
|
||||
{
|
||||
BEGINPACKET( pak, 0 );
|
||||
switch( thisquest->script )
|
||||
{
|
||||
case 1: //Teleport
|
||||
{
|
||||
if(thisquest->value3>0)
|
||||
{
|
||||
thisclient->CharInfo->Zulies -= thisquest->value3;
|
||||
RESETPACKET( pak, 0x71e );
|
||||
ADDQWORD ( pak, thisclient->CharInfo->Zulies );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
thisclient->client->SendPacket( &pak );
|
||||
}
|
||||
fPoint thispoint;
|
||||
thispoint.x = floor( thisquest->value2/10000 );
|
||||
thispoint.y = floor( thisquest->value2%10000 );
|
||||
TeleportTo ( thisclient, thisquest->value1, thispoint );
|
||||
thisclient->EndQuest( thisquest->questid );
|
||||
}
|
||||
break;
|
||||
case 2: // Change Job
|
||||
{
|
||||
if( thisclient->CharInfo->Job == 0 )
|
||||
{
|
||||
thisclient->CharInfo->Job = thisquest->value1;
|
||||
RESETPACKET(pak, 0x721 );
|
||||
ADDWORD (pak, 0x0004 );
|
||||
ADDWORD (pak, thisclient->CharInfo->Job );
|
||||
ADDWORD (pak, 0x0000 );
|
||||
thisclient->client->SendPacket(&pak);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 20: // 2nd Change Job
|
||||
{ //Credits to tomiz
|
||||
if( thisclient->CharInfo->Job > 0 )
|
||||
{
|
||||
thisclient->CharInfo->Job = thisquest->value1;
|
||||
RESETPACKET(pak, 0x721 );
|
||||
ADDWORD (pak, 0x0004 );
|
||||
ADDWORD (pak, thisclient->CharInfo->Job );
|
||||
ADDWORD (pak, 0x0000 );
|
||||
thisclient->client->SendPacket(&pak);
|
||||
}
|
||||
if(thisquest->value3>0) // zuly need for second job
|
||||
{
|
||||
thisclient->CharInfo->Zulies-= thisquest->value3;
|
||||
RESETPACKET( pak, 0x71e );
|
||||
ADDQWORD ( pak, thisclient->CharInfo->Zulies );
|
||||
ADDBYTE ( pak, 0x00 );
|
||||
thisclient->client->SendPacket( &pak );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3: //Give Stats/Skill points
|
||||
{
|
||||
switch( thisquest->value1 )
|
||||
{
|
||||
case 1: // Give Stats points
|
||||
thisclient->CharInfo->StatPoints += thisquest->value2;
|
||||
RESETPACKET( pak, 0x720 );
|
||||
ADDWORD ( pak, 0x0020 );
|
||||
ADDDWORD ( pak, thisquest->value2 );
|
||||
thisclient->client->SendPacket( &pak );
|
||||
break;
|
||||
case 2: // Give Skill points
|
||||
thisclient->CharInfo->SkillPoints += thisquest->value2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4: //Restart Stats/Skills points
|
||||
switch( thisquest->value1 )
|
||||
{
|
||||
case 1: // Stats points [fixed by Geobot]
|
||||
thisclient->CharInfo->StatPoints = 0;
|
||||
for(UINT i=2;i<=thisclient->Stats->Level;i++)
|
||||
thisclient->CharInfo->StatPoints += 10 + (i/2);
|
||||
thisclient->Attr->Str = 15;
|
||||
thisclient->Attr->Dex = 15;
|
||||
thisclient->Attr->Int = 15;
|
||||
thisclient->Attr->Con = 15;
|
||||
thisclient->Attr->Cha = 10;
|
||||
thisclient->Attr->Sen = 10;
|
||||
break;
|
||||
case 2: // Skill points
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 10: //Delete a quest
|
||||
if( thisquest->value1==0 )
|
||||
return true;
|
||||
CQuest* athisquest = GetQuestByID( thisquest->value1 );
|
||||
QUESTS* myquest = thisclient->GetQuestByQuestID( athisquest->questid );
|
||||
if( myquest!=0 )
|
||||
myquest->active = false;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}*/
|
||||
@@ -0,0 +1,55 @@
|
||||
// Props to ExJam for this code :D
|
||||
|
||||
class CIntStb
|
||||
{
|
||||
public:
|
||||
CIntStb() : rows(0) {}
|
||||
CIntStb(char* filename) : rows(0){ LoadSTB(filename); }
|
||||
~CIntStb(){ CleanUp(); }
|
||||
|
||||
int LoadSTB( char* filename ){
|
||||
FILE* fh;
|
||||
char tmpfield[512];
|
||||
unsigned dataoffset = 0;
|
||||
unsigned short fieldlen = 0;
|
||||
|
||||
if ( (fh = fopen( filename, "rb" )) != 0 ) {
|
||||
Log( MSG_ERROR, "Could not load STB '%s'", filename );
|
||||
return 1;
|
||||
}else{
|
||||
fseek( fh, 4, SEEK_SET );
|
||||
fread( &dataoffset, 4, 1, fh );
|
||||
fread( &rowcount, 4, 1, fh );
|
||||
fread( &fieldcount, 4, 1, fh );
|
||||
|
||||
rowcount--;
|
||||
fieldcount--;
|
||||
|
||||
fseek( fh, dataoffset, SEEK_SET );
|
||||
int* tmp = new int[rowcount*fieldcount];
|
||||
rows = new int*[rowcount];
|
||||
for(dword i = 0; i < rowcount; i++ ) rows[i] = &tmp[i*fieldcount];
|
||||
for(dword j = 0; j < rowcount*fieldcount; j++) {
|
||||
fread( &fieldlen, 2, 1, fh );
|
||||
fread( tmpfield, 1, fieldlen, fh );
|
||||
tmpfield[fieldlen]=0;
|
||||
tmp[j] = atoi( tmpfield );
|
||||
}
|
||||
fclose( fh );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
dword rowcount;
|
||||
dword fieldcount;
|
||||
int** rows;
|
||||
|
||||
private:
|
||||
void CleanUp(){
|
||||
if(rows != NULL){
|
||||
delete [] rows[0];
|
||||
delete [] rows;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
//Made by Caali
|
||||
|
||||
#define __CARRAY_HPP__
|
||||
|
||||
/* Includes */
|
||||
#include <memory.h>
|
||||
|
||||
/* Variable type definitions */
|
||||
typedef size_t CARRAY_SIZE;
|
||||
|
||||
/* Defines */
|
||||
#ifndef NULL
|
||||
# ifdef __cplusplus
|
||||
# define NULL 0
|
||||
# else
|
||||
# define NULL (void*)0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* A basic CArray class. It is being used because C++ has no function to get the actual size in memory of a pointer. */
|
||||
template <typename T>
|
||||
class CRoseArray
|
||||
{
|
||||
private:
|
||||
CARRAY_SIZE arraysize;
|
||||
public:
|
||||
T* arr;
|
||||
CRoseArray( CARRAY_SIZE arrsize )
|
||||
{
|
||||
this->arr = NULL;
|
||||
this->arraysize = 0;
|
||||
this->arr = new T[arrsize];
|
||||
if( this->arr != NULL )
|
||||
{
|
||||
this->arraysize = arrsize;
|
||||
Clean( );
|
||||
}
|
||||
}
|
||||
|
||||
~CRoseArray( )
|
||||
{
|
||||
if( this->arr != NULL )
|
||||
{
|
||||
delete[] this->arr;
|
||||
this->arraysize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void resize( CARRAY_SIZE newsize )
|
||||
{
|
||||
if( this->arr == NULL ) return;
|
||||
T* tmp = new T[(size_t)(this->arraysize)];
|
||||
CARRAY_SIZE tmpsize = this->arraysize;
|
||||
memcpy( tmp, this->arr, (size_t)(this->arraysize*sizeof(T)) );
|
||||
delete[] this->arr;
|
||||
this->arr = new T[(size_t)(newsize)];
|
||||
if( this->arr != NULL )
|
||||
{
|
||||
memcpy( this->arr, tmp, (size_t)(tmpsize * sizeof(T)) );
|
||||
delete[] tmp;
|
||||
this->arraysize = newsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete[] tmp;
|
||||
this->arraysize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Clean( )
|
||||
{
|
||||
if( this->arr != NULL )
|
||||
{
|
||||
for( CARRAY_SIZE i = 0; i < this->arraysize; i++ )
|
||||
{
|
||||
this->arr[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
CARRAY_SIZE size()
|
||||
{
|
||||
return this->arraysize;
|
||||
}
|
||||
CARRAY_SIZE absSize()
|
||||
{
|
||||
return (this->arraysize*sizeof(T));
|
||||
}
|
||||
inline CRoseArray& operator = ( const CRoseArray& otherarr )
|
||||
{
|
||||
if( arr != NULL ) delete[] arr;
|
||||
arraysize = otherarr.arraysize;
|
||||
for( CARRAY_SIZE i = 0; i<size(); i++ ){
|
||||
arr[i] = otherarr.arr[i];
|
||||
}
|
||||
}
|
||||
inline bool operator == ( const CRoseArray& otherarr )
|
||||
{
|
||||
if( size() == otherarr.size() )
|
||||
{
|
||||
if( arr == NULL && otherarr.arr == NULL )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( CARRAY_SIZE i = 0; i<size(); i++ )
|
||||
{
|
||||
if( arr[i] != otherarr.arr[i] ) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool operator != ( const CRoseArray& otherarr )
|
||||
{
|
||||
return !(this == otherarr);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,941 @@
|
||||
//Made by Caali
|
||||
|
||||
#define __CROSEFILE_HPP__
|
||||
|
||||
/* Includes */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef __CARRAY_HPP__
|
||||
#include "CRoseArray.hpp"
|
||||
#endif
|
||||
|
||||
/* MS Visual C++ Deprecation control defines */
|
||||
#ifdef _MSC_VER
|
||||
# ifdef _CRT_SECURE_NO_DEPRECATE
|
||||
# define CFILE_NODEPRECATE
|
||||
# endif
|
||||
#else
|
||||
# define CFILE_NODEPRECATE
|
||||
#endif
|
||||
|
||||
//Debug:
|
||||
//#define CFILE_NOUNICODE
|
||||
|
||||
/* Various defines */
|
||||
#define CFILE_MAXLINELENGHT 255
|
||||
#define CFILE_UNICODE_HEADER 0xFEFF
|
||||
|
||||
/* Variable type declarations */
|
||||
/* ETC */
|
||||
typedef bool CFILE_BOOLEAN;
|
||||
typedef size_t CFILE_SIZE;
|
||||
|
||||
/* Unsigned */
|
||||
typedef unsigned long long int CFILE_ULONGLONG;
|
||||
typedef unsigned long int CFILE_ULONG;
|
||||
typedef unsigned int CFILE_UINT;
|
||||
typedef unsigned short int CFILE_USHORT;
|
||||
typedef unsigned char CFILE_UCHAR;
|
||||
|
||||
/* Signed */
|
||||
typedef signed long long int CFILE_SLONGLONG;
|
||||
typedef signed long int CFILE_SLONG;
|
||||
typedef signed int CFILE_SINT;
|
||||
typedef signed short int CFILE_SSHORT;
|
||||
typedef signed char CFILE_SCHAR;
|
||||
typedef char CFILE_CHAR;
|
||||
|
||||
/* File types */
|
||||
typedef enum
|
||||
{
|
||||
FT_NONE,
|
||||
FT_BINARY,
|
||||
FT_ASCII,
|
||||
FT_UNICODE
|
||||
} FILETYPE;
|
||||
/* File open modes */
|
||||
typedef enum
|
||||
{
|
||||
FM_NONE = 0,
|
||||
FM_READ = (1<<0),
|
||||
FM_WRITE = (1<<1),
|
||||
FM_CREATEIFDOESNTEXIST = (1<<2),
|
||||
FM_BINARY = (1<<3),
|
||||
FM_UNICODE = (1<<4),
|
||||
FM_READWRITE = (FM_READ | FM_WRITE),
|
||||
FM_RW = (FM_READWRITE | FM_CREATEIFDOESNTEXIST)
|
||||
} FILEMODE;
|
||||
|
||||
/* File errors */
|
||||
typedef enum
|
||||
{
|
||||
FE_SUCCESS = 0, //No error
|
||||
FE_EOF, //CFile::EndOfFile( ) == true
|
||||
FE_NOFILEOPENED, //fh == NULL
|
||||
FE_NORIGHTSFORACTION, //Like when trying to read when only opening in write mode
|
||||
FE_INTERNALERROR, //CFile-internal errors, supposed to destruct the CFile now!
|
||||
FE_UNICODENOTALLOWED, //#ifdef CFILE_NOUNICODE and file is unicode
|
||||
FE_UNABLETOOPENFILE //fopen( ) == NULL
|
||||
} FILEERROR;
|
||||
|
||||
/* The CFile class */
|
||||
class CRoseFile
|
||||
{
|
||||
private:
|
||||
/** The current filetype
|
||||
*/
|
||||
FILETYPE ftype;
|
||||
/** The last error occured
|
||||
*/
|
||||
FILEERROR flasterr;
|
||||
/** The current file open mode bitfield
|
||||
*/
|
||||
CFILE_CHAR fmode;
|
||||
/** Function to throw an error
|
||||
@param
|
||||
err: A FILEERROR
|
||||
*/
|
||||
void DoError( FILEERROR err )
|
||||
{
|
||||
flasterr = err;
|
||||
}
|
||||
/** Converts the bitfield file open mode to c standart ASCII open modes
|
||||
@param
|
||||
mode: A byte with the bitfield
|
||||
@result
|
||||
string with the ASCII string
|
||||
*/
|
||||
CFILE_CHAR* ConvertBitFieldFileModeToASCIIFileMode( CFILE_SCHAR mode )
|
||||
{
|
||||
CFILE_CHAR* result = new CFILE_CHAR[30];
|
||||
CFILE_UCHAR len = 0;
|
||||
for( CFILE_CHAR i = 0; i<30; i++ ) result[i] = 0x00;
|
||||
if( (mode & FM_READWRITE) == FM_READWRITE )
|
||||
{
|
||||
if( (mode & FM_CREATEIFDOESNTEXIST) == FM_CREATEIFDOESNTEXIST )
|
||||
{
|
||||
result[0] = 'w';
|
||||
result[1] = '+';
|
||||
len = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[0] = 'r';
|
||||
result[1] = '+';
|
||||
len = 2;
|
||||
}
|
||||
}
|
||||
else if( (mode & FM_READ) == FM_READ )
|
||||
{
|
||||
result[0] = 'r';
|
||||
len = 1;
|
||||
}
|
||||
else if( (mode & FM_WRITE) == FM_WRITE )
|
||||
{
|
||||
result[0] = 'w';
|
||||
len = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoError( FE_INTERNALERROR );
|
||||
delete[] result;
|
||||
return NULL;
|
||||
}
|
||||
if( (mode & FM_BINARY) == FM_BINARY )
|
||||
{
|
||||
result[len] = 'b';
|
||||
len++;
|
||||
}
|
||||
if( (mode & FM_UNICODE) == FM_UNICODE )
|
||||
{
|
||||
printf( (CFILE_CHAR*)result, 30, "%s, ccs=UNICODE", (CFILE_CHAR*)result );
|
||||
len += 13;
|
||||
}
|
||||
result[len] = 0x00;
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/** The current filehandle
|
||||
*/
|
||||
FILE* fh;
|
||||
/** Contructor
|
||||
*/
|
||||
CRoseFile( )
|
||||
{
|
||||
fh = NULL;
|
||||
ftype = FT_NONE;
|
||||
fmode = FM_NONE;
|
||||
flasterr = FE_SUCCESS;
|
||||
}
|
||||
/** Contructor opening a file after contructing
|
||||
@param
|
||||
fname: A string with the filename
|
||||
@param
|
||||
mode: A bitfield using the FILETYPE enumerator to specify the fopen mode
|
||||
*/
|
||||
CRoseFile( CFILE_CHAR* fname, CFILE_CHAR mode )
|
||||
{
|
||||
fh = NULL;
|
||||
ftype = FT_NONE;
|
||||
fmode = FM_NONE;
|
||||
flasterr = FE_SUCCESS;
|
||||
Open( fname, mode );
|
||||
}
|
||||
|
||||
//Destructors
|
||||
~CRoseFile( )
|
||||
{
|
||||
if( fh != NULL ) Close( );
|
||||
}
|
||||
|
||||
/** Opens a file
|
||||
@param
|
||||
fname: A string with the filename
|
||||
@param
|
||||
mode: A bitfield using the FILETYPE enumerator to specify the fopen mode
|
||||
@result
|
||||
Boolean indicating if Open successed or failed
|
||||
@note
|
||||
If there is already an opened file, CFile will close it and open the new file
|
||||
*/
|
||||
CFILE_BOOLEAN Open( CFILE_CHAR* fname, CFILE_CHAR mode )
|
||||
{
|
||||
if( fh != NULL ) Close( );
|
||||
fmode = mode;
|
||||
CFILE_CHAR* fasciimode = ConvertBitFieldFileModeToASCIIFileMode( fmode );
|
||||
if( fasciimode == NULL )
|
||||
{
|
||||
DoError( FE_INTERNALERROR );
|
||||
return false;
|
||||
}
|
||||
if( (fmode & FM_BINARY) == FM_BINARY )
|
||||
{
|
||||
ftype = FT_BINARY;
|
||||
}
|
||||
#ifndef CFILE_NODEPRECATE
|
||||
if( fopen_s( &fh, fname, fasciimode ) == 0 )
|
||||
{
|
||||
#else
|
||||
if( ( fh = fopen( fname, fasciimode ) ) != NULL )
|
||||
{
|
||||
#endif
|
||||
delete[] fasciimode;
|
||||
//Check if unicode or ascii
|
||||
if( ftype != FT_BINARY )
|
||||
{
|
||||
CFILE_USHORT buf;
|
||||
Read( &buf, sizeof( buf ), 1 );
|
||||
if( buf == CFILE_UNICODE_HEADER )
|
||||
{
|
||||
#ifndef CFILE_NOUNICODE
|
||||
fmode |= FM_UNICODE;
|
||||
Close( );
|
||||
CFILE_CHAR* fasciimode = ConvertBitFieldFileModeToASCIIFileMode( fmode );
|
||||
//fasciimode = ConvertBitFieldFileModeToASCIIFileMode( fmode );
|
||||
if( fasciimode == NULL )
|
||||
{
|
||||
DoError( FE_INTERNALERROR );
|
||||
return false;
|
||||
}
|
||||
#ifndef CFILE_NODEPRECATE
|
||||
fopen_s( &fh, fname, fasciimode );
|
||||
#else
|
||||
fh = fopen( fname, fasciimode );
|
||||
#endif
|
||||
SetAbsPos( 2 );
|
||||
ftype = FT_UNICODE;
|
||||
#else
|
||||
DoError( FE_UNICODENOTALLOWED );
|
||||
Close( );
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
SetAbsPos( 0 );
|
||||
ftype = FT_ASCII;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetAbsPos( 0 );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoError( FE_UNABLETOOPENFILE );
|
||||
fh = NULL;
|
||||
delete[] fasciimode;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Closes the current open file
|
||||
*/
|
||||
void Close( )
|
||||
{
|
||||
if( fh != 0 )
|
||||
{
|
||||
DoError( FE_SUCCESS );
|
||||
fclose( fh );
|
||||
fh = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CFILE_BOOLEAN IsOpen( )
|
||||
{
|
||||
return (fh != NULL)?(true):(false);
|
||||
}
|
||||
|
||||
/** Returns if the file has been opened in read mode
|
||||
@result
|
||||
Boolean indicating if the current file has been opened in read mode
|
||||
*/
|
||||
CFILE_BOOLEAN IsRead( )
|
||||
{
|
||||
return ((fmode & FM_READ) == FM_READ)?(true):(false);
|
||||
}
|
||||
/** Returns if the file has been opened in write mode
|
||||
@result
|
||||
Boolean indicating if the current file has been opened in write mode
|
||||
*/
|
||||
CFILE_BOOLEAN IsWrite( )
|
||||
{
|
||||
return ((fmode & FM_WRITE) == FM_WRITE)?(true):(false);
|
||||
}
|
||||
/** Returns filehandle
|
||||
@result
|
||||
Returns the current filehandle
|
||||
*/
|
||||
FILE* FileHandle( )
|
||||
{
|
||||
return fh;
|
||||
}
|
||||
/** Returns filetype
|
||||
@result
|
||||
returns the current filetype
|
||||
*/
|
||||
FILETYPE FileType( )
|
||||
{
|
||||
return ftype;
|
||||
}
|
||||
/** Returns last error
|
||||
@result
|
||||
Returns the last error occured
|
||||
*/
|
||||
FILEERROR GetLastError( )
|
||||
{
|
||||
return flasterr;
|
||||
}
|
||||
|
||||
/** Moves the file position to a custom point
|
||||
@param
|
||||
offset: Indicates how many bytes to move from origin
|
||||
@param
|
||||
origin: SEEK_SET -> File start | SEEK_CUR -> Current position | SEEK_END -> Last byte in file
|
||||
@result
|
||||
Boolean indicating if the seek process has successed
|
||||
@note
|
||||
Offset can be negative, too.
|
||||
*/
|
||||
CFILE_BOOLEAN Seek( CFILE_SLONG offset, signed int origin )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return false;
|
||||
}
|
||||
if( fseek( fh, offset, origin ) == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/** Moves the file position to an absolute position from the file start
|
||||
@param
|
||||
offset: Indicates how many bytes to move from SEEK_SET
|
||||
@result
|
||||
Boolean indicating if the seek process has successed
|
||||
@note
|
||||
Offset MUST NOT be negative, else it will not do anything
|
||||
*/
|
||||
CFILE_BOOLEAN SetAbsPos( CFILE_SLONG offset )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return false;
|
||||
}
|
||||
if( offset < 0 )
|
||||
{
|
||||
DoError( FE_INTERNALERROR );
|
||||
return false;
|
||||
}
|
||||
return Seek(offset,SEEK_SET);
|
||||
}
|
||||
/** Moves the file position to the start of the file
|
||||
@result
|
||||
Boolean indicating if the rewinding process has successed
|
||||
@note
|
||||
This function checks if the file has been opened in unicode mode, and if so, it sets the pointer to 2,
|
||||
the start of the file AFTER the unicode file header
|
||||
*/
|
||||
CFILE_BOOLEAN GoToStart( )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return false;
|
||||
}
|
||||
if( ftype == FT_ASCII || ftype == FT_BINARY )
|
||||
{
|
||||
return SetAbsPos( 0 );
|
||||
}
|
||||
else if( ftype == FT_UNICODE )
|
||||
{
|
||||
return SetAbsPos( 2 );
|
||||
}
|
||||
DoError( FE_INTERNALERROR );
|
||||
return false;
|
||||
}
|
||||
/** Tells the current position in the file
|
||||
@result
|
||||
Signed long indicating the absolute offset from SEEK_SET
|
||||
*/
|
||||
CFILE_SLONG Tell( )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return EOF;
|
||||
}
|
||||
return ftell( fh );
|
||||
}
|
||||
/** Checks if the file position is set after the end of the file
|
||||
@result
|
||||
Boolean indicating if the file pointer is at the end of the file
|
||||
*/
|
||||
CFILE_BOOLEAN EndOfFile( )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return true;
|
||||
}
|
||||
return (feof( fh )==0)?(false):(true);
|
||||
}
|
||||
/** Moves the file position to a custom point
|
||||
@result
|
||||
Unsigned long indicating the size of the file in bytes
|
||||
*/
|
||||
CFILE_ULONG FileSize( )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return 0;
|
||||
}
|
||||
CFILE_SLONG tmp_offset = Tell( );
|
||||
if( tmp_offset < 0 ) return 0;
|
||||
Seek( 0, SEEK_END );
|
||||
CFILE_SLONG result = Tell( );
|
||||
SetAbsPos( tmp_offset );
|
||||
return result;
|
||||
}
|
||||
/** Reads from the file starting at the current position
|
||||
@param
|
||||
buf: A pointer to a buffer where the data will be stored
|
||||
@param
|
||||
element_size: The size of each block being read
|
||||
@param
|
||||
element_count: The number of blocks being read
|
||||
@result
|
||||
Unsigned int indicating the number of bytes read
|
||||
*/
|
||||
CFILE_UINT Read( void* buf, CFILE_SIZE element_size, CFILE_SIZE element_count = 1 )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return 0;
|
||||
}
|
||||
if( !IsRead( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return 0;
|
||||
}
|
||||
return (CFILE_UINT)( fread( buf, element_size, element_count, fh ) );
|
||||
}
|
||||
/** Reads from the file starting at [offset] relative to [origin]
|
||||
@param
|
||||
buf: A pointer to a buffer where the data will be stored
|
||||
@param
|
||||
element_size: The size of each block being read
|
||||
@param
|
||||
element_count: The number of blocks being read
|
||||
@param
|
||||
offset: The offset to start reading from, relative to [origin]
|
||||
@param
|
||||
origin: Where offset is relative to, SEEK_SET -> Beginning of file | SEEK_CUR -> Current position | SEEK_END -> End of file
|
||||
@result
|
||||
Unsigned int indicating the number of bytes read
|
||||
*/
|
||||
CFILE_UINT ReadFrom( void* buf, CFILE_UINT element_size, CFILE_UINT element_count, CFILE_SLONG offset, signed int origin = SEEK_SET )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return 0;
|
||||
}
|
||||
if( !IsRead( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return 0;
|
||||
}
|
||||
if( !Seek( offset, origin ) ) return 0;
|
||||
return (CFILE_UINT)( Read( buf, element_size, element_count ) );
|
||||
}
|
||||
/** Reads from the file starting at from and ending at to
|
||||
@param
|
||||
buf: A pointer to a buffer where the data will be stored
|
||||
@param
|
||||
from: The offset to start reading from, absolute, aka relative to SEEK_SET
|
||||
@param
|
||||
to: The offset to end reading at, absolute, aka relative to SEEK_SET
|
||||
@result
|
||||
Unsigned int indicating the number of bytes read
|
||||
@note
|
||||
If (to-from) > the size of buf, it throws an exception (-> access violation), so check it before calling this function!
|
||||
*/
|
||||
CFILE_UINT ReadFromTo( void* buf, CFILE_SLONG from, CFILE_SLONG to )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return 0;
|
||||
}
|
||||
if( !IsRead( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return 0;
|
||||
}
|
||||
if( to < from ) return 0;
|
||||
if( !SetAbsPos( from ) ) return 0;
|
||||
return (CFILE_UINT)( Read( buf, (to-from), 1 ) );
|
||||
}
|
||||
/** Reads one byte and returns it as signed char
|
||||
@result
|
||||
char with the byte read
|
||||
*/
|
||||
CFILE_CHAR GetC( )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return EOF;
|
||||
}
|
||||
if( !IsRead( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return EOF;
|
||||
}
|
||||
if( ftype == FT_ASCII || ftype == FT_BINARY )
|
||||
{
|
||||
return (CFILE_CHAR)( fgetc( fh ) );
|
||||
}
|
||||
else if( ftype == FT_UNICODE )
|
||||
{
|
||||
#ifndef CFILE_NOUNICODE
|
||||
CFILE_CHAR buf;
|
||||
wchar_t buffer;
|
||||
buffer = fgetwc( fh );
|
||||
#ifndef CFILE_NODEPRECATE
|
||||
size_t tmp;
|
||||
wcstombs_s( &tmp, &buf, (size_t)(sizeof(buf)), &buffer, 1 );
|
||||
#else
|
||||
wcstombs( &buf, &buffer, 1 );
|
||||
#endif
|
||||
return (CFILE_CHAR)buf;
|
||||
#else
|
||||
DoError( FE_UNICODENOTALLOWED );
|
||||
return EOF;
|
||||
#endif
|
||||
}
|
||||
return EOF;
|
||||
}
|
||||
|
||||
/** Reads from the file starting at from and ending at to
|
||||
@param
|
||||
buf: A pointer to a buffer where the data will be stored
|
||||
@param
|
||||
max_count: The maximum number of bytes to read
|
||||
@result
|
||||
string with the string read
|
||||
@note
|
||||
GetS either reads up to [max_count] bytes or until the end of one line has been reached. (0x0D 0x0A)
|
||||
@note
|
||||
If the file is opened in binary mode, GetS throws an error and does nothing
|
||||
*/
|
||||
CFILE_CHAR* GetS( CRoseArray<CFILE_CHAR>* buf, CFILE_UINT max_count )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return NULL;
|
||||
}
|
||||
if( !IsRead( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return NULL;
|
||||
}
|
||||
if( ftype == FT_ASCII )
|
||||
{
|
||||
fgets( buf->arr, max_count, fh );
|
||||
return NULL;
|
||||
}
|
||||
else if( ftype == FT_UNICODE )
|
||||
{
|
||||
#ifndef CFILE_NOUNICODE
|
||||
wchar_t* buffer = new wchar_t[max_count];
|
||||
fgetws( buffer, max_count, fh );
|
||||
#ifndef CFILE_NODEPRECATE
|
||||
size_t tmp;
|
||||
wcstombs_s( &tmp, buf->arr, (size_t)(buf->absSize()), buffer, max_count );
|
||||
#else
|
||||
wcstombs( buf->arr, buffer, max_count );
|
||||
#endif
|
||||
return NULL;
|
||||
#else
|
||||
DoError( FE_UNICODENOTALLOWED );
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
DoError( FE_INTERNALERROR );
|
||||
return NULL;
|
||||
}
|
||||
/** Reads one line
|
||||
@result
|
||||
string with the line read
|
||||
@note
|
||||
If the file is opened in binary mode, the call to GetS throws an error and does nothing
|
||||
*/
|
||||
CFILE_CHAR* ReadLn( )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return NULL;
|
||||
}
|
||||
if( !IsRead( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return NULL;
|
||||
}
|
||||
if( EndOfFile( ) )
|
||||
{
|
||||
DoError( FE_INTERNALERROR );
|
||||
return NULL;
|
||||
}
|
||||
CRoseArray<CFILE_CHAR>* result = new CRoseArray<CFILE_CHAR>( CFILE_MAXLINELENGHT );
|
||||
result->Clean( );
|
||||
if( GetS( result, CFILE_MAXLINELENGHT ) == NULL )
|
||||
{
|
||||
CFILE_CHAR* res = new CFILE_CHAR[ (size_t)( result->size() ) ];
|
||||
for( CFILE_UINT i = 0;i<result->size(); i++ ) res[i] = 0x00;
|
||||
memcpy( res, result->arr, (size_t)(result->size()*sizeof(CFILE_CHAR)) );
|
||||
for( CFILE_UINT i = 0;i<CFILE_MAXLINELENGHT; i++ )
|
||||
{
|
||||
if( (CFILE_UCHAR)res[i] == 0x0D || (CFILE_UCHAR)res[i] == 0x0A )
|
||||
{
|
||||
if( (CFILE_ULONG)Tell( ) >= FileSize( ) - 2 )
|
||||
{
|
||||
delete result;
|
||||
delete[] res;
|
||||
return NULL;
|
||||
}
|
||||
res[i] = 0x00;
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete result;
|
||||
return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoError( FE_INTERNALERROR );
|
||||
delete result;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/** Template to read different types
|
||||
@<param>
|
||||
typename
|
||||
@result
|
||||
typename: the value being read
|
||||
*/
|
||||
template<typename T> T Get( )
|
||||
{
|
||||
T result;
|
||||
Read( &result, sizeof( T ), 1 );
|
||||
return result;
|
||||
}
|
||||
/*template<typename T> CVector2F Get( )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return CVector2F(0,0);
|
||||
}
|
||||
if( !IsRead( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return CVector2F(0,0);
|
||||
}
|
||||
CVector2F result;
|
||||
Read( &result, sizeof( CVector2F ), 1 );
|
||||
return result;
|
||||
} */
|
||||
/** Reads the whole file into the memory
|
||||
@result
|
||||
string: pointer to the memory section, NULL on error
|
||||
@note
|
||||
The current file position pointer DOES NOT get affected by this function!
|
||||
*/
|
||||
/*string ReadFileToMemory( )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return 0;
|
||||
}
|
||||
if( !IsRead( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return 0;
|
||||
}
|
||||
CFILE_SLONG curpos = Tell( );
|
||||
SetAbsPos( 0 );
|
||||
string mem = new char[(CFILE_SIZE)(FileSize( ))];
|
||||
if( mem == NULL ) //not enough RAM
|
||||
{
|
||||
SetAbsPos( curpos );
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( CFILE_UINT i = 0; i < FileSize( ); i++ ) mem[i] = 0x00;
|
||||
Read( mem, (CFILE_SIZE)(FileSize( )) );
|
||||
SetAbsPos( curpos );
|
||||
return mem;
|
||||
/*
|
||||
if( Read( mem, (CFILE_SIZE)(FileSize( )) ) != FileSize( ) )
|
||||
{
|
||||
delete[] mem;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetAbsPos( curpos );
|
||||
return mem;
|
||||
}
|
||||
*
|
||||
}
|
||||
}*/
|
||||
|
||||
/** Writes to the file
|
||||
@param
|
||||
ptr: A pointer to a buffer where the data will be read to write from
|
||||
@param
|
||||
element_size: The size of one block being written
|
||||
@param
|
||||
element_count: The number of blocks being written
|
||||
@result
|
||||
unsigned int indicating the number of bytes being written
|
||||
*/
|
||||
CFILE_UINT Write( void* ptr, CFILE_SIZE element_size, CFILE_SIZE element_count=1 )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return 0;
|
||||
}
|
||||
if( !IsWrite( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return 0;
|
||||
}
|
||||
return (CFILE_UINT)( fwrite( ptr, (size_t)( element_size ), (size_t)( element_count ), fh ) );
|
||||
}
|
||||
/** Writes to the file starting at [offset] bytes relative to [origin]
|
||||
@param
|
||||
ptr: A pointer to a buffer where the data will be read to write from
|
||||
@param
|
||||
element_size: The size of one block being written
|
||||
@param
|
||||
element_count: The number of blocks being written
|
||||
@param
|
||||
offset: The offset to start writing from, relative to [origin]
|
||||
@param
|
||||
origin: The position offset is relative to, SEEK_SET -> Start of file | SEEK_CUR -> Current position | SEEK_END -> End of file
|
||||
@result
|
||||
unsigned int indicating the number of bytes being written
|
||||
*/
|
||||
CFILE_UINT WriteFrom( void* ptr, CFILE_SIZE element_size, CFILE_SIZE element_count, CFILE_SLONG offset, signed int origin )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return 0;
|
||||
}
|
||||
if( !IsWrite( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return 0;
|
||||
}
|
||||
if( !Seek( offset, origin ) ) return 0;
|
||||
return (CFILE_UINT)( Write( ptr, element_size, element_count ) );
|
||||
}
|
||||
/** Writes to the file, from [from] to [to]
|
||||
@param
|
||||
ptr: A pointer to a buffer where the data will be read to write from
|
||||
@param
|
||||
from: The absolute (aka relative to SEEK_SET) offset to start writing from
|
||||
@param
|
||||
to: The absolute (aka relative to SEEK_SET) offset to stop writing at
|
||||
@result
|
||||
unsigned int indicating the number of bytes being written
|
||||
@note
|
||||
If (to-from) > the size of ptr, it throws an exception, so check it before calling this function!
|
||||
*/
|
||||
CFILE_UINT WriteFromTo( void* ptr, CFILE_SLONG from, CFILE_SLONG to )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return 0;
|
||||
}
|
||||
if( !IsWrite( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return 0;
|
||||
}
|
||||
if( to < from ) return 0;
|
||||
if( !SetAbsPos( from ) ) return 0;
|
||||
return (CFILE_UINT)( Write( ptr, (to-from), 1 ) );
|
||||
}
|
||||
/** Writes one byte to the file, as char
|
||||
@param
|
||||
chr: The byte to be written
|
||||
@result
|
||||
signed char: If success, it returns the char being written, else it returns EOF
|
||||
*/
|
||||
CFILE_SCHAR PutC( CFILE_SCHAR chr )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return EOF;
|
||||
}
|
||||
if( !IsWrite( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return EOF;
|
||||
}
|
||||
if( ftype == FT_ASCII || ftype == FT_BINARY )
|
||||
{
|
||||
return fputc( chr, fh );
|
||||
}
|
||||
else if( ftype == FT_UNICODE )
|
||||
{
|
||||
#ifndef CFILE_NOUNICODE
|
||||
wchar_t buffer;
|
||||
#ifndef CFILE_NODEPRECATE
|
||||
size_t tmp;
|
||||
mbstowcs_s( &tmp, &buffer, 1, (CFILE_CHAR*)(&chr), 1 );
|
||||
#else
|
||||
mbstowcs( &buffer, (CFILE_CHAR*)(&chr), 1 );
|
||||
#endif
|
||||
return (CFILE_SCHAR)(fputwc( buffer, fh ));
|
||||
#else
|
||||
DoError( FE_UNICODENOTALLOWED );
|
||||
return EOF;
|
||||
#endif
|
||||
}
|
||||
return EOF;
|
||||
}
|
||||
|
||||
/** Writes a string
|
||||
@param
|
||||
str: A string to the string to be written
|
||||
@result
|
||||
boolean indicating if writing the string has worked
|
||||
@note
|
||||
If the file is opened in binary mode, it throws an error and does nothing
|
||||
*/
|
||||
CFILE_BOOLEAN PutS( CRoseArray<CFILE_CHAR>* str )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return false;
|
||||
}
|
||||
if( !IsWrite( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return false;
|
||||
}
|
||||
if( ftype == FT_ASCII )
|
||||
{
|
||||
return (fputs( str->arr, fh )!=EOF)?(true):(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef CFILE_NOUNICODE
|
||||
wchar_t* buffer = new wchar_t[ (size_t)(str->size()) ];
|
||||
#ifndef CFILE_NODEPRECATE
|
||||
size_t tmp;
|
||||
mbstowcs_s( &tmp, buffer, (size_t)(str->size()), str->arr, (size_t)(str->size()) );
|
||||
#else
|
||||
mbstowcs( buffer, str->arr, str->size() );
|
||||
#endif
|
||||
return (fputws( buffer, fh )!=EOF)?(true):(false);
|
||||
#else
|
||||
DoError( FE_UNICODENOTALLOWED );
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
DoError( FE_INTERNALERROR );
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Template to write different types
|
||||
@<param>
|
||||
typename
|
||||
@param
|
||||
typename val: the value to be written
|
||||
@param
|
||||
cnt: times val will be written
|
||||
@result
|
||||
typename: If success, it returns the value being written, else it returns 0
|
||||
*/
|
||||
template<typename T> T Put( T val, CFILE_UINT cnt=1 )
|
||||
{
|
||||
if( fh == NULL )
|
||||
{
|
||||
DoError( FE_NOFILEOPENED );
|
||||
return 0;
|
||||
}
|
||||
if( !IsWrite( ) )
|
||||
{
|
||||
DoError( FE_NORIGHTSFORACTION );
|
||||
return 0;
|
||||
}
|
||||
return (Write( &val, sizeof( val ), cnt ) != 0)?(val):(0);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
// Props to ExJam for this code :D
|
||||
|
||||
class CStrStb
|
||||
{
|
||||
public:
|
||||
CStrStb():_Data(0){}
|
||||
CStrStb(const char* filename) : _Data(0){ LoadSTB(filename); }
|
||||
~CStrStb(){CleanUp();}
|
||||
|
||||
bool LoadSTB(const char* path)
|
||||
{
|
||||
CleanUp();
|
||||
|
||||
CRoseFile* fh = new CRoseFile((CFILE_CHAR*) path, FM_READ | FM_BINARY);
|
||||
if(!fh->IsOpen()){
|
||||
delete fh;
|
||||
return false;
|
||||
} // if is open
|
||||
|
||||
fh->Seek(4, SEEK_CUR);
|
||||
dword offset = fh->Get<dword>();
|
||||
|
||||
_Rows = fh->Get<dword>();
|
||||
_Columns = fh->Get<dword>();
|
||||
|
||||
_Rows--;
|
||||
_Columns--;
|
||||
|
||||
fh->SetAbsPos(offset);
|
||||
_Data = new char**[_Rows];
|
||||
for(dword i = 0; i < _Rows; i++)
|
||||
_Data[i] = new char*[_Columns];
|
||||
|
||||
for(dword i = 0; i < _Rows; i++){
|
||||
for(dword j = 0; j < _Columns; j++){
|
||||
dword len = fh->Get<word>();
|
||||
if(len == 0){
|
||||
_Data[i][j] = NULL;
|
||||
continue;
|
||||
} // if len
|
||||
_Data[i][j] = new char[len+1];
|
||||
fh->Read(_Data[i][j], len, 1);
|
||||
_Data[i][j][len] = 0;
|
||||
} // for j
|
||||
} // for i
|
||||
|
||||
fh->Close();
|
||||
delete fh;
|
||||
return true;
|
||||
} // bool
|
||||
|
||||
char* Data(dword Row, dword Column){
|
||||
if(Row > _Rows) return NULL;
|
||||
if(Column > _Columns) return NULL;
|
||||
|
||||
return _Data[Row][Column];
|
||||
}
|
||||
|
||||
dword Rows(){ return _Rows; }
|
||||
dword Columns(){ return _Columns; }
|
||||
|
||||
private:
|
||||
void CleanUp(){
|
||||
if(_Data != NULL){
|
||||
for(dword i = 0; i < _Rows; i++){
|
||||
for(dword j = 0; j < _Columns; j++){
|
||||
delete [] _Data[i][j];
|
||||
}
|
||||
delete [] _Data[i];
|
||||
}
|
||||
delete [] _Data;
|
||||
}
|
||||
_Rows = 0;
|
||||
_Columns = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
dword _Rows;
|
||||
dword _Columns;
|
||||
|
||||
char*** _Data;
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,242 @@
|
||||
// Props to ExJam for this code :D Ported to OSpRose by Drakia
|
||||
#pragma once
|
||||
#define QUESTREWD(reward) int QUEST_REWD_ ## reward (class CWorldServer* server, class CPlayer* client, byte* raw)
|
||||
typedef int (*fpQuestRewd)(class CWorldServer*, class CPlayer*, byte*);
|
||||
#define GETREWDDATA(rewd) STR_REWD_ ## rewd * data = (STR_REWD_ ## rewd *)raw
|
||||
|
||||
QUESTREWD(000);QUESTREWD(001);QUESTREWD(002);
|
||||
QUESTREWD(003);QUESTREWD(004);QUESTREWD(005);
|
||||
QUESTREWD(006);QUESTREWD(007);QUESTREWD(008);
|
||||
QUESTREWD(009);QUESTREWD(010);QUESTREWD(011);
|
||||
QUESTREWD(012);QUESTREWD(013);QUESTREWD(014);
|
||||
QUESTREWD(015);QUESTREWD(016);QUESTREWD(017);
|
||||
QUESTREWD(018);QUESTREWD(019);QUESTREWD(020);
|
||||
QUESTREWD(021);QUESTREWD(022);QUESTREWD(023);
|
||||
QUESTREWD(024);QUESTREWD(025);QUESTREWD(026);
|
||||
QUESTREWD(027);QUESTREWD(028);QUESTREWD(029);
|
||||
QUESTREWD(030);QUESTREWD(050);
|
||||
|
||||
struct STR_REWD_000 {
|
||||
dword iQuestSN;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_REWD_001 {
|
||||
dword uiItemSN;
|
||||
union {
|
||||
byte btOp;
|
||||
word nOp;
|
||||
};
|
||||
word nDupCNT;
|
||||
byte btPartyOpt;
|
||||
};
|
||||
|
||||
struct STR_REWD_002
|
||||
{
|
||||
dword iDataCnt;
|
||||
STR_QUEST_DATA* CheckData;
|
||||
};
|
||||
|
||||
struct STR_REWD_003
|
||||
{
|
||||
dword iDataCnt; //pos 0x00
|
||||
dword iType; //pos 0x04
|
||||
dword iValue; //pos 0x08
|
||||
byte btOp; //pos 0x0c
|
||||
//this (below original code) is just plain wrong
|
||||
//dword iDataCnt;
|
||||
//STR_ABIL_DATA* CheckData;
|
||||
//byte btPartyOpt;
|
||||
};
|
||||
|
||||
struct STR_REWD_004 {
|
||||
dword iDataCnt;
|
||||
STR_QUEST_DATA* CheckData;
|
||||
};
|
||||
|
||||
struct STR_REWD_005
|
||||
{
|
||||
byte btTarget; // pos 0x00
|
||||
//union
|
||||
//{
|
||||
// byte btEquation; // pos 0x02 apparently should be 0x01
|
||||
// word nEquation;
|
||||
//};
|
||||
byte btEquation; // pos 0x01
|
||||
word nEquation; // pos 0x02
|
||||
int iValue; // pos 0x04
|
||||
dword iItemSN; // pos 0x06
|
||||
union
|
||||
{
|
||||
byte btPartyOpt; // pos 0x0a
|
||||
word nPartyOpt;
|
||||
};
|
||||
word nItemOpt; // pos 0x0c
|
||||
};
|
||||
|
||||
struct STR_REWD_006 {
|
||||
dword iPercentOfHP;
|
||||
dword iPercentOfMP;
|
||||
byte btPartyOpt;
|
||||
};
|
||||
|
||||
struct STR_REWD_007 {
|
||||
dword iZoneSN;
|
||||
dword iX;
|
||||
dword iY;
|
||||
byte btPartyOpt;
|
||||
};
|
||||
|
||||
struct STR_REWD_008 {
|
||||
dword iMonsterSN;
|
||||
dword iHowMany;
|
||||
union {
|
||||
byte btWho;
|
||||
int iWho;
|
||||
};
|
||||
dword iZoneSN;
|
||||
dword iX;
|
||||
dword iY;
|
||||
dword iRange;
|
||||
dword iTeamNo;
|
||||
};
|
||||
|
||||
struct STR_REWD_009 {
|
||||
word shNameLen;
|
||||
string szNextTriggerSN;
|
||||
};
|
||||
|
||||
struct STR_REWD_010 {
|
||||
};
|
||||
|
||||
struct STR_REWD_011
|
||||
{
|
||||
union
|
||||
{
|
||||
byte btWho;
|
||||
word usWho;
|
||||
};
|
||||
word nVarNo;
|
||||
dword iValue;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_REWD_012 {
|
||||
union
|
||||
{
|
||||
byte btMsgType;
|
||||
int iMsgType;
|
||||
};
|
||||
dword iStrID;
|
||||
};
|
||||
|
||||
struct STR_REWD_013 {
|
||||
union {
|
||||
byte btWho;
|
||||
int iWho;
|
||||
};
|
||||
dword iSec;
|
||||
word nNameLength;
|
||||
string szTriggerName;
|
||||
dword m_HashNextTrigger;
|
||||
};
|
||||
|
||||
struct STR_REWD_014
|
||||
{
|
||||
union
|
||||
{
|
||||
byte btOp;
|
||||
int iOp;
|
||||
};
|
||||
dword iSkillNo;
|
||||
};
|
||||
|
||||
struct STR_REWD_015 {
|
||||
word nSN;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_REWD_016 {
|
||||
word nGroupSN;
|
||||
};
|
||||
|
||||
struct STR_REWD_017 {
|
||||
};
|
||||
|
||||
struct STR_REWD_018 {
|
||||
dword iStrID;
|
||||
word nCnt;
|
||||
string Data;
|
||||
};
|
||||
|
||||
struct STR_REWD_019
|
||||
{
|
||||
word nZoneNo; //pos 0x00
|
||||
word nTeamNo; //pos 0x02
|
||||
word nTriggerLength; //pos 0x04 This is definitely NOT the length of the following name. PY
|
||||
string TriggerName; //pos 0x06 + nTriggerlength more bytes. Name always appears to be 17 bytes long
|
||||
//dword m_HashTrigger;
|
||||
word m_HashTrigger; //pos 0x17 only has 2 bytes not 4. PY
|
||||
};
|
||||
|
||||
struct STR_REWD_020
|
||||
{
|
||||
byte btNoType;
|
||||
};
|
||||
|
||||
struct STR_REWD_021 {
|
||||
dword iX;
|
||||
dword iY;
|
||||
};
|
||||
|
||||
struct STR_REWD_022 {
|
||||
word nZonNo;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_REWD_023
|
||||
{
|
||||
};
|
||||
|
||||
struct STR_REWD_024 {
|
||||
int iMONEY;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_REWD_025 {
|
||||
short nPOINT;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_REWD_026 {
|
||||
word nSkillNo;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_REWD_027 {
|
||||
short nCONT;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_REWD_028 {
|
||||
dword iRange;
|
||||
word nZoneNo;
|
||||
dword iX;
|
||||
dword iY;
|
||||
};
|
||||
|
||||
struct STR_REWD_029 {
|
||||
word strLen;
|
||||
string LuaName;
|
||||
};
|
||||
|
||||
struct STR_REWD_030 //reset skills
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
struct STR_REWD_050 //respawn NPC after time delay
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,771 @@
|
||||
// Props to ExJam for this code :D Ported to OSpRose by Drakia
|
||||
#include "../WorldServer.h"
|
||||
|
||||
//Check Quest
|
||||
QUESTCOND(000)
|
||||
{
|
||||
GETCONDDATA(000);
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check quest: %i", data->iQuestSN);
|
||||
for(dword i = 0; i < 10; i++)
|
||||
{
|
||||
if(client->quest.quests[i].QuestID == data->iQuestSN)
|
||||
{
|
||||
client->CheckQuest = i;
|
||||
client->ActiveQuest = data->iQuestSN;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check Quest - Found!");
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
}
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check Quest - Not Found!");
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//Check Quest Variable
|
||||
QUESTCOND(001)
|
||||
{
|
||||
// int iDataCnt pos 0x00
|
||||
// dword iType pos 0x02
|
||||
// short m_wVarNO pos 0x02
|
||||
// word m_wVarTYPE pos 0x04
|
||||
// word nValue pos 0x06
|
||||
// byte btOp pos 0x08
|
||||
GETCONDDATA(001);
|
||||
int retVal = 1;
|
||||
if( client->questdebug )server->SendPM(client, "Data Count for this quest %i",data->iDataCnt);
|
||||
for(int i = 0; i < data->iDataCnt; i++)
|
||||
{
|
||||
dword address = i * sizeof(STR_QUEST_DATA);
|
||||
address += (dword)data;
|
||||
address += 4;
|
||||
STR_QUEST_DATA* curQst = (STR_QUEST_DATA*)address;
|
||||
word nValue = curQst->nValue;
|
||||
word tempValue = client->GetQuestVar(curQst->m_wVarTYPE, curQst->m_wVarNO);
|
||||
//word tempValue = client->quest.quests[curQst->m_wVarTYPE].Variables[curQst->m_wVarNO];
|
||||
//retVal = OperateValues<word>(curQst->btOp, &tempValue, curQst->nValue);
|
||||
switch(curQst->btOp)
|
||||
{
|
||||
case 0:
|
||||
retVal = (tempValue == curQst->nValue);
|
||||
break;
|
||||
case 1:
|
||||
retVal = (tempValue > curQst->nValue);
|
||||
break;
|
||||
case 2:
|
||||
retVal = (tempValue >= curQst->nValue);
|
||||
break;
|
||||
case 3:
|
||||
retVal = (tempValue < curQst->nValue);
|
||||
break;
|
||||
case 4:
|
||||
retVal = (tempValue <= curQst->nValue);
|
||||
break;
|
||||
case 10:
|
||||
retVal = (tempValue != curQst->nValue);
|
||||
break;
|
||||
default:
|
||||
retVal = 0;
|
||||
Log( MSG_DEBUG, "QuestCond 001 bad value for btOp %i",curQst->btOp );
|
||||
break;
|
||||
}
|
||||
if( client->questdebug )
|
||||
{
|
||||
server->SendPM(client, "is %i < %i ? retval %i",tempValue, curQst->nValue, retVal);
|
||||
server->SendPM(client, "Check Quest Var[%04x][%i] - %i (Op: %i)", curQst->m_wVarTYPE, curQst->m_wVarNO, curQst->nValue, curQst->btOp);
|
||||
}
|
||||
if(retVal == 0) return QUEST_FAILURE;
|
||||
}
|
||||
return (retVal == 0)?QUEST_FAILURE:QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Quest Variable
|
||||
QUESTCOND(002)
|
||||
{
|
||||
return QUEST_COND_001(server, client, raw);
|
||||
}
|
||||
|
||||
//Check Stats
|
||||
QUESTCOND(003)
|
||||
{
|
||||
//dword iDataCnt; pos 0x00
|
||||
//dword iType; pos 0x04
|
||||
//dword iValue; pos 0x08
|
||||
//byte btOp; pos 0x0c
|
||||
GETCONDDATA(003);
|
||||
for(int i = 0; i < data->iDataCnt; i++)
|
||||
{
|
||||
dword address = i * 0x0C;
|
||||
address += (dword)data;
|
||||
address += 4;
|
||||
STR_ABIL_DATA* curAbil = (STR_ABIL_DATA*)address;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check Stat[%i] %i (Op: %i)", curAbil->iType, curAbil->iValue, curAbil->btOp);
|
||||
int tempValue;
|
||||
switch( curAbil->iType )
|
||||
{
|
||||
case sGender:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->CharInfo->Sex, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sJob:
|
||||
tempValue = client->CharInfo->Job / 100;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Job Tempvalue = %i", tempValue);
|
||||
//if(!OperateValues<int>(curAbil->btOp, (int*)&tempValue, curAbil->iValue))
|
||||
if(!OperateValues<int>(curAbil->btOp, &tempValue, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sUnion: // Unions aren't implemented? - Drakia
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Union_s->unionvar[0], curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sStrength:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Str, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sDexterity:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Dex, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sIntelligence:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Int, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sConcentration:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Con, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sCharm:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Cha, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sSensibility:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Sen, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sLevel:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Stats->Level, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sStatPoints:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->CharInfo->StatPoints, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sMoney:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->CharInfo->Zulies, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sEXPRate:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Stats->xprate, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
default:
|
||||
Log(MSG_WARNING, "QuestCond 003: Type Unknown: '%i'", curAbil->iType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Items
|
||||
QUESTCOND(004)
|
||||
{
|
||||
GETCONDDATA(004);
|
||||
for(int i = 0; i < data->iDataCnt; i++)
|
||||
{
|
||||
dword address = i * 0x10;
|
||||
address += (dword)data;
|
||||
address += 4;
|
||||
STR_ITEM_DATA* curItem = (STR_ITEM_DATA*)address;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check Item - [Slot:%i][Type:%i]", curItem->iWhere, curItem->uiItemSN);
|
||||
// Check quest items for a specific amount
|
||||
if (curItem->iWhere == 13) // it's a quest item
|
||||
{
|
||||
// Ok, check the current Check'd quest.
|
||||
if (client->CheckQuest < 0) return QUEST_SUCCESS;
|
||||
unsigned int j = client->CheckQuest;
|
||||
bool Hasitem = false;
|
||||
bool FoundItem = false;
|
||||
for (unsigned int k = 0; k < 5; k++) //5 quest item slots
|
||||
{
|
||||
if (client->quest.quests[j].Items[k].itemnum == (curItem->uiItemSN % 1000))
|
||||
{
|
||||
FoundItem = true;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Operate - btOp: %i Val1: %i Val2: %i", curItem->btOp, client->quest.quests[j].Items[k].count, curItem->iRequestCnt);
|
||||
if(OperateValues<int>(curItem->btOp, (int*)(&client->quest.quests[j].Items[k].count), curItem->iRequestCnt))
|
||||
Hasitem = true;
|
||||
}
|
||||
}
|
||||
if(FoundItem == false) // didn't find any of the item in any of the quest slots
|
||||
{
|
||||
for (unsigned int k = 0; k < 5; k++) //Find the first empty slot
|
||||
{
|
||||
if (client->quest.quests[j].Items[k].count == 0 && Hasitem == false)
|
||||
{
|
||||
// Since the item doesn't exist, treat it as if there's 0 of them.
|
||||
int tempInt = 0;
|
||||
if(OperateValues<int>(curItem->btOp, &tempInt, curItem->iRequestCnt))
|
||||
Hasitem = true;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Requested item is not found. Comparing first empty slot instead");
|
||||
}
|
||||
}
|
||||
}
|
||||
if(Hasitem == false)
|
||||
{
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Requested item is not present. Returning quest failure");
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//item slot is not 13
|
||||
// Check equipped items for a specific item? - Drakia
|
||||
if(curItem->uiItemSN == 0 && curItem->iWhere != 0)
|
||||
{
|
||||
int itemHead = client->items[curItem->iWhere].itemtype;
|
||||
if(!OperateValues<int>(curItem->btOp, &itemHead, curItem->uiItemSN))
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "QuestCond 004 Returning Quest Success");
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Party
|
||||
QUESTCOND(005)
|
||||
{
|
||||
//byte btIsLeader pos 0x00
|
||||
//int iLevel pos 0x02
|
||||
//byte btReversed pos 0x04
|
||||
GETCONDDATA(005);
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Near Point
|
||||
QUESTCOND(006)
|
||||
{
|
||||
//int iZoneSN; pos 0x00
|
||||
//int iX; pos 0x04
|
||||
//int iY; pos 0x08
|
||||
//int iZ; pos 0x0c
|
||||
//int iRadius; pos 0x10
|
||||
GETCONDDATA(006);
|
||||
client->UpdatePosition();
|
||||
if(data->iZoneSN != 0)
|
||||
{
|
||||
if(client->Position->Map != data->iZoneSN)
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
float dx = client->Position->current.x - (float)(data->iX / 100);
|
||||
float dy = client->Position->current.y - (float)(data->iY / 100);
|
||||
float distance = sqrt((dx*dx) + (dy*dy));
|
||||
return (distance < data->iRadius)?QUEST_SUCCESS:QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//World Time
|
||||
QUESTCOND(007)
|
||||
{
|
||||
//dword ulTime pos 0x00
|
||||
//dword ulEndTime pos 0x04
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Quest Time
|
||||
QUESTCOND(008)
|
||||
{
|
||||
//dword ulTime pos 0x00
|
||||
//byte btOp pos 0x04
|
||||
GETCONDDATA(008);
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "QuestTime - ulTime: %i btOp: %i", data->ulTime, data->btOp);
|
||||
if (client->CheckQuest < 0) return QUEST_FAILURE; // Not checking a quest, don't return success
|
||||
long int Time = 0;
|
||||
if (server->STB_QUEST.rows[client->quest.quests[client->CheckQuest].QuestID][1] > 0)
|
||||
{
|
||||
Time += client->quest.quests[client->CheckQuest].StartTime; // Start time
|
||||
Time += server->STB_QUEST.rows[client->quest.quests[client->CheckQuest].QuestID][1] * 10; // Time to finish
|
||||
Time -= time(NULL); // Current time
|
||||
if (Time < 0) Time = 0; // Time is up
|
||||
Time /= 10; // Divide to get 10's of seconds
|
||||
}
|
||||
if (!OperateValues<dword>(data->btOp, (dword*)&Time, data->ulTime))
|
||||
return QUEST_FAILURE;
|
||||
else
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Skill
|
||||
QUESTCOND(009)
|
||||
{
|
||||
//int iSkillSN1 pos 0x00
|
||||
//int iSkillSN2 pos 0x02
|
||||
//byte btOp pos 0x04
|
||||
GETCONDDATA(009);
|
||||
// Line not needed, never used - Drakia
|
||||
//int checkVal = (data->btOp)?QUEST_SUCCESS:QUEST_FAILURE;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check Skill: %i", data->iSkillSN1);
|
||||
bool s1 = false;
|
||||
bool s2 = false;
|
||||
for(dword i = 0; i < 30; i++)
|
||||
{
|
||||
if(client->bskills[i] == data->iSkillSN1)
|
||||
s1 = true;
|
||||
if(client->bskills[i] == data->iSkillSN2)
|
||||
s2 = true;
|
||||
if(client->askill[i] == data->iSkillSN1)
|
||||
s1 = true;
|
||||
if(client->askill[i] == data->iSkillSN2)
|
||||
s2 = true;
|
||||
if(client->pskill[i] == data->iSkillSN1)
|
||||
s1 = true;
|
||||
if(client->pskill[i] == data->iSkillSN2)
|
||||
s2 = true;
|
||||
}
|
||||
if(data->btOp == 0)
|
||||
{
|
||||
if(s1 == false && s2 == false)
|
||||
return QUEST_SUCCESS; //true if player does not have either skill
|
||||
else
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(s1 == true || s2 == true)
|
||||
return QUEST_SUCCESS; //true if player has either skill
|
||||
else
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
//Log(MSG_DEBUG,"Questcond 009 btOp is neither 0 nor 1.");
|
||||
//return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//Unknown
|
||||
QUESTCOND(010)
|
||||
{
|
||||
GETCONDDATA(010);
|
||||
//byte btLowPcnt; pos 0x00
|
||||
//byte btHighPcnt; pos 0x01
|
||||
// I think this is a percentage chance range.
|
||||
// might be something to do with the item count though???
|
||||
|
||||
//int randv = GServer->RandNumber(0,100);
|
||||
//if(randv >= data->btLowPcnt && randv <= data->btHighPcnt)return QUEST_SUCCESS;
|
||||
|
||||
// problem is that this is only called in quest triggers from the client where it has already
|
||||
// passed the random number test.
|
||||
// we need to always return true or some quests will never finish
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Object Variable
|
||||
QUESTCOND(011)
|
||||
{
|
||||
|
||||
//byte btWho pos 0x00
|
||||
//word nVarNo pos 0x02
|
||||
//int iValue pos 0x04
|
||||
//byte btOp pos 0x09
|
||||
//if(entity->_EntityType != ENTITY_NPC) return QUEST_FAILURE;
|
||||
GETCONDDATA(011);
|
||||
/*
|
||||
if(data->btWho == 0)
|
||||
{
|
||||
CNpc* thisNpc = reinterpret_cast<CNpc*>(entity);
|
||||
thisNpc = thisNpc->SelectedNpc;
|
||||
if(thisNpc == NULL) return QUEST_FAILURE;
|
||||
|
||||
short VarValue = thisNpc->ObjVar.GetVar(data->nVarNo);
|
||||
if(OperateValues(data->btOp, &VarValue, (short)data->iValue)) return QUEST_SUCCESS;
|
||||
}*/
|
||||
if(data->btWho == 0)
|
||||
{
|
||||
|
||||
if(data->nVarNo > 19) return AI_FAILURE;
|
||||
int refVal = GServer->ObjVar[client->NPCvar][data->nVarNo];
|
||||
int tempval = 0;
|
||||
switch( data->btOp )
|
||||
{
|
||||
case 0:
|
||||
if(refVal == data->iValue)return QUEST_SUCCESS;
|
||||
break;
|
||||
case 1:
|
||||
if(refVal > data->iValue)return QUEST_SUCCESS;
|
||||
break;
|
||||
case 2:
|
||||
if(refVal >= data->iValue)return QUEST_SUCCESS;
|
||||
break;
|
||||
case 3:
|
||||
if(refVal < data->iValue)return QUEST_SUCCESS;
|
||||
break;
|
||||
case 4:
|
||||
if(refVal <= data->iValue)return QUEST_SUCCESS;
|
||||
break;
|
||||
case 10:
|
||||
if(refVal != data->iValue)return QUEST_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
Log(MSG_WARNING,"Unknown btOp in QuestCond 011");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//check Proximity and Trigger in Zone
|
||||
QUESTCOND(012)
|
||||
{
|
||||
GETCONDDATA(012);
|
||||
//word iZone; pos 0x00
|
||||
//dword ulZone; pos 0x00
|
||||
//int iX; pos 0x04
|
||||
//int iY; pos 0x06
|
||||
//int iEventID; pos 0x08
|
||||
//dword m_HashEventOBJ; pos 0x08
|
||||
//this should be easy enough - PY
|
||||
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Select NPC
|
||||
QUESTCOND(013)
|
||||
{
|
||||
//int iNpcNo pos 0x00
|
||||
GETCONDDATA(013);
|
||||
// dammit!!!! This darn function is set up to use the CCharacter class so I can't use entity (CCharacter)
|
||||
// This is going to be a serious pain in the ass
|
||||
// OK so we now have a temporary player structure so we shuold be able to do this.
|
||||
|
||||
//Log(MSG_DEBUG,"Setting NPC ref number AICOND(017). NPC Ref = %i",data->iNpcNo);
|
||||
//CMonster* monster = reinterpret_cast<CMonster*>(entity);
|
||||
//if(monster == NULL) return AI_FAILURE;
|
||||
//monster->thisnpc->refNPC = data->iNpcNo; // sets the reference variable for the correct ObjVar
|
||||
//Log(MSG_DEBUG,"Set NPC ref number %i successfully", monster->thisnpc->refNPC);
|
||||
//not sure this is going to work properly for an NPC.
|
||||
//NPC only exists as a temporary entity so refNPC may be lost after it returns from here.
|
||||
|
||||
|
||||
/*if(entity->_EntityType != ENTITY_NPC) return QUEST_FAILURE;
|
||||
|
||||
CNpc* thisNpc = reinterpret_cast<CNpc*>(entity);
|
||||
std::map<dword, CNpc*>::iterator triggerITR = server->NpcList.find(data->iNpcNo);
|
||||
if(triggerITR == server->NpcList.end()){
|
||||
thisNpc->SelectedNpc = NULL;
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
thisNpc->SelectedNpc = triggerITR->second;
|
||||
*/
|
||||
client->NPCvar = data->iNpcNo;
|
||||
//Actually that was kind of easy.. lol - PY
|
||||
|
||||
//return QUEST_SUCCESS;
|
||||
// More NPC specific stuff
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Quest Flag
|
||||
QUESTCOND(014)
|
||||
{
|
||||
GETCONDDATA(014);
|
||||
//word nSN; pos 0x00
|
||||
//byte btOp; pos 0x02
|
||||
bool dataOp = (data->btOp)?true:false;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check QFlag[%i] == %i", data->nSN, data->btOp);
|
||||
if(client->quest.GetFlag(data->nSN) == dataOp)
|
||||
{
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Quest cond 014 (check quest flag) returned Success");
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Quest cond 014 (check quest flag) returned Failure");
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
//Party member count between X and Y - PY
|
||||
QUESTCOND(015)
|
||||
{
|
||||
|
||||
//word nNumber1 pos 0x00
|
||||
//word nNumber2 pos 0x02
|
||||
GETCONDDATA(015);
|
||||
|
||||
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Zone Time
|
||||
QUESTCOND(016)
|
||||
{
|
||||
|
||||
//byte btWho pos 0x00
|
||||
//dword ulWho pos 0x00
|
||||
//dword ulTime pos 0x04
|
||||
//dword ulEndTime pos 0x08
|
||||
GETCONDDATA(016);
|
||||
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//NPC & Obj Variables?
|
||||
QUESTCOND(017)
|
||||
{
|
||||
//dword iNpcNo pos 0x00
|
||||
//word nVarNo pos 0x04
|
||||
//word unused pos 0x06
|
||||
//dword iNpcNo pos 0x08
|
||||
//word nVarNo pos 0x0c
|
||||
//word unused pos 0x0e
|
||||
//byte btOp pos 0x0f
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Time on Date
|
||||
QUESTCOND(018)
|
||||
{
|
||||
//Returns SUCCESS if the system time of the server is between the defined start and end times. Else returns FAILURE.
|
||||
//Allows for time dependent quests
|
||||
//byte btDate; pos 0x00
|
||||
//byte btHour1; pos 0x01
|
||||
//byte btMin1; pos 0x02
|
||||
//byte btHour2; pos 0x03
|
||||
//byte btMin2; pos 0x04
|
||||
GETCONDDATA(018);
|
||||
SYSTEMTIME sTIME;
|
||||
GetLocalTime(&sTIME);
|
||||
|
||||
if(data->btDate != 0)
|
||||
{
|
||||
if(sTIME.wDay != data->btDate)
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
word wMin = ((sTIME.wHour * 60) + sTIME.wMinute);
|
||||
word wFrom = (data->btHour1 * 60) + data->btMin1;
|
||||
word wTo = (data->btHour2 * 60) + data->btMin2;
|
||||
if(wMin >= wFrom && wMin <= wTo)
|
||||
return QUEST_SUCCESS;
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//Time on Day
|
||||
QUESTCOND(019)
|
||||
{
|
||||
//byte btWeekDay; pos 0x00
|
||||
//byte btHour1; pos 0x01
|
||||
//byte btMin1; pos 0x02
|
||||
//byte btHour2; pos 0x03
|
||||
//byte btMin2; pos 0x04
|
||||
GETCONDDATA(019);
|
||||
SYSTEMTIME sTIME;
|
||||
GetLocalTime(&sTIME);
|
||||
if(data->btWeekDay != 0)
|
||||
{
|
||||
if(sTIME.wDayOfWeek != data->btWeekDay)
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
word wMin = ((sTIME.wHour * 60) + sTIME.wMinute);
|
||||
word wFrom = (data->btHour1 * 60) + data->btMin1;
|
||||
word wTo = (data->btHour2 * 60) + data->btMin2;
|
||||
if(wMin >= wFrom && wMin <= wTo)
|
||||
return QUEST_SUCCESS;
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//Unknown
|
||||
QUESTCOND(020)
|
||||
{
|
||||
//int iNo1 pos 0x00
|
||||
//int iNo2 pos 0x02
|
||||
GETCONDDATA(020);
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Unknown
|
||||
QUESTCOND(021)
|
||||
{
|
||||
// byte btSelObjType pos 0x00
|
||||
// byte iRadius pos 0x04
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Server/Channel
|
||||
QUESTCOND(022)
|
||||
{
|
||||
//word nX pos 0x00
|
||||
//word nY pos 0x02
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//In Clan?
|
||||
QUESTCOND(023)
|
||||
{
|
||||
//byte btReg pos 0x00
|
||||
GETCONDDATA(023);
|
||||
int Clanid = client->Clan->clanid;
|
||||
if(data->btReg == 0)
|
||||
{
|
||||
if(Clanid == 0)return QUEST_SUCCESS;
|
||||
else return QUEST_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Clanid != 0)return QUEST_SUCCESS;
|
||||
else return QUEST_FAILURE;
|
||||
}
|
||||
//btReg should only ever be 1 or 0 but just in case......
|
||||
//return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//Clan Position
|
||||
QUESTCOND(024)
|
||||
{
|
||||
//check my current rank in the clan against data->nPOS using btOP
|
||||
GETCONDDATA(024);
|
||||
//word nPOS; pos 0x00
|
||||
//byte btOP; pos 0x02
|
||||
int myRank = client->Clan->clanrank;
|
||||
switch(data->btOP)
|
||||
{
|
||||
case 0:
|
||||
if(myRank == data->nPOS)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 1:
|
||||
if(myRank > data->nPOS)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 2:
|
||||
if(myRank >= data->nPOS)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 3:
|
||||
if(myRank < data->nPOS)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 4:
|
||||
if(myRank <= data->nPOS)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 10:
|
||||
if(myRank != data->nPOS)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Clan Contribution
|
||||
QUESTCOND(025)
|
||||
{
|
||||
//word nCONT pos 0x00
|
||||
//byte btOP pos 0x02
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Grade
|
||||
QUESTCOND(026)
|
||||
{
|
||||
//possibly compare client's current clan grade with data->nGRD using data->btOp?
|
||||
GETCONDDATA(026);
|
||||
//word nGRD;
|
||||
//byte btOP;
|
||||
int myGrade = client->Clan->grade;
|
||||
switch(data->btOP)
|
||||
{
|
||||
case 0:
|
||||
if(myGrade == data->nGRD)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 1:
|
||||
if(myGrade > data->nGRD)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 2:
|
||||
if(myGrade >= data->nGRD)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 3:
|
||||
if(myGrade < data->nGRD)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 4:
|
||||
if(myGrade <= data->nGRD)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 10:
|
||||
if(myGrade != data->nGRD)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Clan Points
|
||||
QUESTCOND(027)
|
||||
{
|
||||
//word nPOINT pos 0x00
|
||||
//byte btOP pos 0x02
|
||||
GETCONDDATA(027);
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "QUEST - Clan Points");
|
||||
switch(data->btOP)
|
||||
{
|
||||
case 0x01:
|
||||
BEGINPACKET( pak, 0x7e0 );
|
||||
ADDBYTE ( pak, 0x5C ); //action to update clan points (charserver)
|
||||
ADDWORD ( pak, client->Clan->clanid);
|
||||
ADDWORD ( pak, client->clientid );
|
||||
ADDDWORD ( pak, client->Clan->CP );
|
||||
ADDWORD ( pak, 0x00);
|
||||
GServer->SendISCPacket( &pak );
|
||||
break;
|
||||
//default:
|
||||
//break;
|
||||
}
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Funds
|
||||
QUESTCOND(028)
|
||||
{
|
||||
//word iMONEY; pos 0x00
|
||||
//byte btOP; pos 0x02
|
||||
GETCONDDATA(027);
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Members
|
||||
QUESTCOND(029)
|
||||
{
|
||||
//word nMemberCNT; pos 0x00
|
||||
//byte btOP; pos 0x02
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Skills
|
||||
QUESTCOND(030)
|
||||
{
|
||||
//word nSkill1; pos 0x00
|
||||
//word nSkill2; pos 0x02
|
||||
//byte btOP; pos 0x05
|
||||
GETCONDDATA(030);
|
||||
//not sure what to do here. Looks like we compare something with two skill ids
|
||||
if( client->questdebug )server->SendPM(client, "Clan skills. Skill1: %i Skill2 %i btOP: %i",data->nSkill1,data->nSkill2,data->btOP);
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,763 @@
|
||||
// Props to ExJam for this code :D Ported to OSpRose by Drakia
|
||||
#include "../WorldServer.h"
|
||||
|
||||
//Check Quest
|
||||
QUESTCOND(000)
|
||||
{
|
||||
GETCONDDATA(000);
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check quest: %i", data->iQuestSN);
|
||||
for(dword i = 0; i < 10; i++)
|
||||
{
|
||||
if(client->quest.quests[i].QuestID == data->iQuestSN)
|
||||
{
|
||||
client->CheckQuest = i;
|
||||
client->ActiveQuest = data->iQuestSN;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check Quest # %i Found!", data->iQuestSN);
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
}
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check Quest - Not Found!");
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//Check Quest Variable
|
||||
QUESTCOND(001)
|
||||
{
|
||||
// int iDataCnt pos 0x00
|
||||
// dword iType pos 0x02
|
||||
// short m_wVarNO pos 0x02
|
||||
// word m_wVarTYPE pos 0x04
|
||||
// word nValue pos 0x06
|
||||
// byte btOp pos 0x08
|
||||
GETCONDDATA(001);
|
||||
int retVal = 1;
|
||||
if( client->questdebug )server->SendPM(client, "Data Count for this quest %i",data->iDataCnt);
|
||||
for(int i = 0; i < data->iDataCnt; i++)
|
||||
{
|
||||
dword address = i * sizeof(STR_QUEST_DATA);
|
||||
address += (dword)data;
|
||||
address += 4;
|
||||
STR_QUEST_DATA* curQst = (STR_QUEST_DATA*)address;
|
||||
word nValue = curQst->nValue;
|
||||
word tempValue = client->GetQuestVar(curQst->m_wVarTYPE, curQst->m_wVarNO);
|
||||
//word tempValue = client->quest.quests[curQst->m_wVarTYPE].Variables[curQst->m_wVarNO];
|
||||
//retVal = OperateValues<word>(curQst->btOp, &tempValue, curQst->nValue);
|
||||
switch(curQst->btOp)
|
||||
{
|
||||
case 0:
|
||||
retVal = (tempValue == curQst->nValue);
|
||||
break;
|
||||
case 1:
|
||||
retVal = (tempValue > curQst->nValue);
|
||||
break;
|
||||
case 2:
|
||||
retVal = (tempValue >= curQst->nValue);
|
||||
break;
|
||||
case 3:
|
||||
retVal = (tempValue < curQst->nValue);
|
||||
break;
|
||||
case 4:
|
||||
retVal = (tempValue <= curQst->nValue);
|
||||
break;
|
||||
case 10:
|
||||
retVal = (tempValue != curQst->nValue);
|
||||
break;
|
||||
default:
|
||||
retVal = 0;
|
||||
Log( MSG_DEBUG, "QuestCond 001 bad value for btOp %i",curQst->btOp );
|
||||
break;
|
||||
}
|
||||
if( client->questdebug )
|
||||
{
|
||||
server->SendPM(client, "is %i < %i ? retval %i",tempValue, curQst->nValue, retVal);
|
||||
server->SendPM(client, "Check Quest Var[%04x][%i] - %i (Op: %i)", curQst->m_wVarTYPE, curQst->m_wVarNO, curQst->nValue, curQst->btOp);
|
||||
}
|
||||
if(retVal == 0) return QUEST_FAILURE;
|
||||
}
|
||||
return (retVal == 0)?QUEST_FAILURE:QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Quest Variable
|
||||
QUESTCOND(002)
|
||||
{
|
||||
return QUEST_COND_001(server, client, raw);
|
||||
}
|
||||
|
||||
//Check Stats
|
||||
QUESTCOND(003)
|
||||
{
|
||||
//dword iDataCnt; pos 0x00
|
||||
//dword iType; pos 0x04
|
||||
//dword iValue; pos 0x08
|
||||
//byte btOp; pos 0x0c
|
||||
GETCONDDATA(003);
|
||||
for(int i = 0; i < data->iDataCnt; i++)
|
||||
{
|
||||
dword address = i * 0x0C;
|
||||
address += (dword)data;
|
||||
address += 4;
|
||||
STR_ABIL_DATA* curAbil = (STR_ABIL_DATA*)address;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check Stat[%i] %i (Op: %i)", curAbil->iType, curAbil->iValue, curAbil->btOp);
|
||||
int tempValue;
|
||||
switch( curAbil->iType )
|
||||
{
|
||||
case sGender:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->CharInfo->Sex, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sJob:
|
||||
tempValue = client->CharInfo->Job / 100;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Job Tempvalue = %i", tempValue);
|
||||
//if(!OperateValues<int>(curAbil->btOp, (int*)&tempValue, curAbil->iValue))
|
||||
if(!OperateValues<int>(curAbil->btOp, &tempValue, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sUnion: // Unions aren't implemented? - Drakia
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Union_s->unionvar[0], curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sStrength:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Str, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sDexterity:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Dex, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sIntelligence:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Int, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sConcentration:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Con, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sCharm:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Cha, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sSensibility:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Attr->Sen, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sLevel:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Stats->Level, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sStatPoints:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->CharInfo->StatPoints, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sMoney:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->CharInfo->Zulies, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
case sEXPRate:
|
||||
if(!OperateValues<int>(curAbil->btOp, (int*)&client->Stats->xprate, curAbil->iValue))
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
default:
|
||||
Log(MSG_WARNING, "QuestCond 003: Type Unknown: '%i'", curAbil->iType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Items
|
||||
QUESTCOND(004)
|
||||
{
|
||||
GETCONDDATA(004);
|
||||
for(int i = 0; i < data->iDataCnt; i++)
|
||||
{
|
||||
dword address = i * 0x10;
|
||||
address += (dword)data;
|
||||
address += 4;
|
||||
STR_ITEM_DATA* curItem = (STR_ITEM_DATA*)address;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check Item - [Slot:%i][Type:%i]", curItem->iWhere, curItem->uiItemSN);
|
||||
// Check quest items for a specific amount
|
||||
if (curItem->iWhere == 13) // it's a quest item
|
||||
{
|
||||
// Ok, check the current Check'd quest.
|
||||
if (client->CheckQuest < 0) return QUEST_SUCCESS;
|
||||
unsigned int j = client->CheckQuest;
|
||||
bool Hasitem = false;
|
||||
bool FoundItem = false;
|
||||
for (unsigned int k = 0; k < 5; k++) //5 quest item slots
|
||||
{
|
||||
if (client->quest.quests[j].Items[k].itemnum == (curItem->uiItemSN % 1000))
|
||||
{
|
||||
FoundItem = true;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Operate - btOp: %i Val1: %i Val2: %i", curItem->btOp, client->quest.quests[j].Items[k].count, curItem->iRequestCnt);
|
||||
if(OperateValues<int>(curItem->btOp, (int*)(&client->quest.quests[j].Items[k].count), curItem->iRequestCnt))
|
||||
Hasitem = true;
|
||||
}
|
||||
}
|
||||
if(FoundItem == false) // didn't find any of the item in any of the quest slots
|
||||
{
|
||||
for (unsigned int k = 0; k < 5; k++) //Find the first empty slot
|
||||
{
|
||||
if (client->quest.quests[j].Items[k].count == 0 && Hasitem == false)
|
||||
{
|
||||
// Since the item doesn't exist, treat it as if there's 0 of them.
|
||||
int tempInt = 0;
|
||||
if(OperateValues<int>(curItem->btOp, &tempInt, curItem->iRequestCnt))
|
||||
Hasitem = true;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Requested item is not found. Comparing first empty slot instead");
|
||||
}
|
||||
}
|
||||
}
|
||||
if(Hasitem == false)
|
||||
{
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Requested item is not present. Returning quest failure");
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
}
|
||||
else //this needs to be recoded. It doesn't do a damn thing right now WTF?
|
||||
{
|
||||
//item slot is not 13
|
||||
// Check equipped items for a specific item? - Drakia
|
||||
if(curItem->uiItemSN == 0 && curItem->iWhere != 0)
|
||||
{
|
||||
int itemHead = client->items[curItem->iWhere].itemtype;
|
||||
if(!OperateValues<int>(curItem->btOp, &itemHead, curItem->uiItemSN))
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "QuestCond 004 Returning Quest Success");
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Party
|
||||
QUESTCOND(005)
|
||||
{
|
||||
//byte btIsLeader pos 0x00
|
||||
//int iLevel pos 0x04
|
||||
//byte btReversed pos 0x08
|
||||
GETCONDDATA(005);
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Near Point
|
||||
QUESTCOND(006)
|
||||
{
|
||||
//int iZoneSN; pos 0x00
|
||||
//int iX; pos 0x04
|
||||
//int iY; pos 0x08
|
||||
//int iZ; pos 0x0c
|
||||
//int iRadius; pos 0x10
|
||||
GETCONDDATA(006);
|
||||
client->UpdatePosition();
|
||||
if(data->iZoneSN != 0)
|
||||
{
|
||||
if(client->Position->Map != data->iZoneSN)
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
float dx = client->Position->current.x - (float)(data->iX / 100);
|
||||
float dy = client->Position->current.y - (float)(data->iY / 100);
|
||||
float distance = sqrt((dx*dx) + (dy*dy));
|
||||
return (distance < data->iRadius)?QUEST_SUCCESS:QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//World Time
|
||||
QUESTCOND(007)
|
||||
{
|
||||
//dword ulTime pos 0x00
|
||||
//dword ulEndTime pos 0x04
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Quest Time
|
||||
QUESTCOND(008)
|
||||
{
|
||||
//dword ulTime pos 0x00
|
||||
//byte btOp pos 0x04
|
||||
GETCONDDATA(008);
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "QuestTime - ulTime: %i btOp: %i", data->ulTime, data->btOp);
|
||||
if (client->CheckQuest < 0) return QUEST_FAILURE; // Not checking a quest, don't return success
|
||||
long int Time = 0;
|
||||
if (server->STB_QUEST.rows[client->quest.quests[client->CheckQuest].QuestID][1] > 0)
|
||||
{
|
||||
Time += client->quest.quests[client->CheckQuest].StartTime; // Start time
|
||||
Time += server->STB_QUEST.rows[client->quest.quests[client->CheckQuest].QuestID][1] * 10; // Time to finish
|
||||
Time -= time(NULL); // Current time
|
||||
if (Time < 0) Time = 0; // Time is up
|
||||
Time /= 10; // Divide to get 10's of seconds
|
||||
}
|
||||
if (!OperateValues<dword>(data->btOp, (dword*)&Time, data->ulTime))
|
||||
return QUEST_FAILURE;
|
||||
else
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Skill
|
||||
QUESTCOND(009)
|
||||
{
|
||||
//int iSkillSN1 pos 0x00
|
||||
//int iSkillSN2 pos 0x02
|
||||
//byte btOp pos 0x04
|
||||
GETCONDDATA(009);
|
||||
// Line not needed, never used - Drakia
|
||||
//int checkVal = (data->btOp)?QUEST_SUCCESS:QUEST_FAILURE;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check Skill: %i", data->iSkillSN1);
|
||||
bool s1 = false;
|
||||
bool s2 = false;
|
||||
for(dword i = 0; i < 30; i++)
|
||||
{
|
||||
if(client->bskills[i] == data->iSkillSN1)
|
||||
s1 = true;
|
||||
if(client->bskills[i] == data->iSkillSN2)
|
||||
s2 = true;
|
||||
if(client->askill[i] == data->iSkillSN1)
|
||||
s1 = true;
|
||||
if(client->askill[i] == data->iSkillSN2)
|
||||
s2 = true;
|
||||
if(client->pskill[i] == data->iSkillSN1)
|
||||
s1 = true;
|
||||
if(client->pskill[i] == data->iSkillSN2)
|
||||
s2 = true;
|
||||
}
|
||||
if(data->btOp == 0)
|
||||
{
|
||||
if(s1 == false && s2 == false)
|
||||
return QUEST_SUCCESS; //true if player does not have either skill
|
||||
else
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(s1 == true || s2 == true)
|
||||
return QUEST_SUCCESS; //true if player has either skill
|
||||
else
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
//Log(MSG_DEBUG,"Questcond 009 btOp is neither 0 nor 1.");
|
||||
//return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//random number in range
|
||||
QUESTCOND(010)
|
||||
{
|
||||
GETCONDDATA(010);
|
||||
//byte btLowPcnt; pos 0x00
|
||||
//byte btHighPcnt; pos 0x01
|
||||
int randv = GServer->RandNumber(0,100);
|
||||
if(randv >= data->btLowPcnt && randv <= data->btHighPcnt)return QUEST_SUCCESS;
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//Object Variable
|
||||
QUESTCOND(011)
|
||||
{
|
||||
|
||||
//byte btWho pos 0x00
|
||||
//word nVarNo pos 0x02
|
||||
//int iValue pos 0x04
|
||||
//byte btOp pos 0x09
|
||||
GETCONDDATA(011);
|
||||
/*
|
||||
if(data->btWho == 0)
|
||||
{
|
||||
CNpc* thisNpc = reinterpret_cast<CNpc*>(entity);
|
||||
thisNpc = thisNpc->SelectedNpc;
|
||||
if(thisNpc == NULL) return QUEST_FAILURE;
|
||||
|
||||
short VarValue = thisNpc->ObjVar.GetVar(data->nVarNo);
|
||||
if(OperateValues(data->btOp, &VarValue, (short)data->iValue)) return QUEST_SUCCESS;
|
||||
}*/
|
||||
if(data->btWho == 0)
|
||||
{
|
||||
|
||||
if(data->nVarNo > 19) return AI_FAILURE;
|
||||
int refVal = GServer->ObjVar[client->NPCvar][data->nVarNo];
|
||||
int tempval = 0;
|
||||
switch( data->btOp )
|
||||
{
|
||||
case 0:
|
||||
if(refVal == data->iValue)return QUEST_SUCCESS;
|
||||
break;
|
||||
case 1:
|
||||
if(refVal > data->iValue)return QUEST_SUCCESS;
|
||||
break;
|
||||
case 2:
|
||||
if(refVal >= data->iValue)return QUEST_SUCCESS;
|
||||
break;
|
||||
case 3:
|
||||
if(refVal < data->iValue)return QUEST_SUCCESS;
|
||||
break;
|
||||
case 4:
|
||||
if(refVal <= data->iValue)return QUEST_SUCCESS;
|
||||
break;
|
||||
case 10:
|
||||
if(refVal != data->iValue)return QUEST_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
Log(MSG_WARNING,"Unknown btOp in QuestCond 011");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//select event object
|
||||
QUESTCOND(012)
|
||||
{
|
||||
GETCONDDATA(012);
|
||||
//word iZone; pos 0x00
|
||||
//dword ulZone; pos 0x00
|
||||
//int iX; pos 0x04
|
||||
//int iY; pos 0x06
|
||||
//int iEventID; pos 0x08
|
||||
//dword m_HashEventOBJ; pos 0x08
|
||||
//this should be easy enough - PY
|
||||
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Select NPC
|
||||
QUESTCOND(013)
|
||||
{
|
||||
//int iNpcNo pos 0x00
|
||||
GETCONDDATA(013);
|
||||
// dammit!!!! This darn function is set up to use the CCharacter class so I can't use entity (CCharacter)
|
||||
// This is going to be a serious pain in the ass
|
||||
// OK so we now have a temporary player structure so we shuold be able to do this.
|
||||
|
||||
//Log(MSG_DEBUG,"Setting NPC ref number AICOND(017). NPC Ref = %i",data->iNpcNo);
|
||||
//CMonster* monster = reinterpret_cast<CMonster*>(entity);
|
||||
//if(monster == NULL) return AI_FAILURE;
|
||||
//monster->thisnpc->refNPC = data->iNpcNo; // sets the reference variable for the correct ObjVar
|
||||
//Log(MSG_DEBUG,"Set NPC ref number %i successfully", monster->thisnpc->refNPC);
|
||||
//not sure this is going to work properly for an NPC.
|
||||
//NPC only exists as a temporary entity so refNPC may be lost after it returns from here.
|
||||
|
||||
|
||||
/*if(entity->_EntityType != ENTITY_NPC) return QUEST_FAILURE;
|
||||
|
||||
CNpc* thisNpc = reinterpret_cast<CNpc*>(entity);
|
||||
std::map<dword, CNpc*>::iterator triggerITR = server->NpcList.find(data->iNpcNo);
|
||||
if(triggerITR == server->NpcList.end()){
|
||||
thisNpc->SelectedNpc = NULL;
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
thisNpc->SelectedNpc = triggerITR->second;
|
||||
*/
|
||||
client->NPCvar = data->iNpcNo;
|
||||
//Actually that was kind of easy.. lol - PY
|
||||
|
||||
//return QUEST_SUCCESS;
|
||||
// More NPC specific stuff
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Quest Flag
|
||||
QUESTCOND(014)
|
||||
{
|
||||
GETCONDDATA(014);
|
||||
//word nSN; pos 0x00
|
||||
//byte btOp; pos 0x02
|
||||
bool dataOp = (data->btOp)?true:false;
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Check QFlag[%i] == %i", data->nSN, data->btOp);
|
||||
if(client->quest.GetFlag(data->nSN) == dataOp)
|
||||
{
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Quest cond 014 (check quest flag) returned Success");
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "Quest cond 014 (check quest flag) returned Failure");
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
//Party member count between X and Y - PY
|
||||
QUESTCOND(015)
|
||||
{
|
||||
|
||||
//word nNumber1 pos 0x00
|
||||
//word nNumber2 pos 0x02
|
||||
GETCONDDATA(015);
|
||||
|
||||
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Zone Time
|
||||
QUESTCOND(016)
|
||||
{
|
||||
|
||||
//byte btWho pos 0x00
|
||||
//dword ulWho pos 0x00
|
||||
//dword ulTime pos 0x04
|
||||
//dword ulEndTime pos 0x08
|
||||
GETCONDDATA(016);
|
||||
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//NPC & Obj Variables?
|
||||
QUESTCOND(017)
|
||||
{
|
||||
//dword iNpcNo pos 0x00
|
||||
//word nVarNo pos 0x04
|
||||
//word unused pos 0x06
|
||||
//dword iNpcNo pos 0x08
|
||||
//word nVarNo pos 0x0c
|
||||
//word unused pos 0x0e
|
||||
//byte btOp pos 0x0f
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Time on Date
|
||||
QUESTCOND(018)
|
||||
{
|
||||
//Returns SUCCESS if the system time of the server is between the defined start and end times. Else returns FAILURE.
|
||||
//Allows for time dependent quests
|
||||
//byte btDate; pos 0x00
|
||||
//byte btHour1; pos 0x01
|
||||
//byte btMin1; pos 0x02
|
||||
//byte btHour2; pos 0x03
|
||||
//byte btMin2; pos 0x04
|
||||
GETCONDDATA(018);
|
||||
SYSTEMTIME sTIME;
|
||||
GetLocalTime(&sTIME);
|
||||
|
||||
if(data->btDate != 0)
|
||||
{
|
||||
if(sTIME.wDay != data->btDate)
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
word wMin = ((sTIME.wHour * 60) + sTIME.wMinute);
|
||||
word wFrom = (data->btHour1 * 60) + data->btMin1;
|
||||
word wTo = (data->btHour2 * 60) + data->btMin2;
|
||||
if(wMin >= wFrom && wMin <= wTo)
|
||||
return QUEST_SUCCESS;
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//Time on Day
|
||||
QUESTCOND(019)
|
||||
{
|
||||
//byte btWeekDay; pos 0x00
|
||||
//byte btHour1; pos 0x01
|
||||
//byte btMin1; pos 0x02
|
||||
//byte btHour2; pos 0x03
|
||||
//byte btMin2; pos 0x04
|
||||
GETCONDDATA(019);
|
||||
SYSTEMTIME sTIME;
|
||||
GetLocalTime(&sTIME);
|
||||
if(data->btWeekDay != 0)
|
||||
{
|
||||
if(sTIME.wDayOfWeek != data->btWeekDay)
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
word wMin = ((sTIME.wHour * 60) + sTIME.wMinute);
|
||||
word wFrom = (data->btHour1 * 60) + data->btMin1;
|
||||
word wTo = (data->btHour2 * 60) + data->btMin2;
|
||||
if(wMin >= wFrom && wMin <= wTo)
|
||||
return QUEST_SUCCESS;
|
||||
return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//Team number between iNo1 and iNo2
|
||||
QUESTCOND(020)
|
||||
{
|
||||
//int iNo1 pos 0x00
|
||||
//int iNo2 pos 0x02
|
||||
GETCONDDATA(020);
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Distance check
|
||||
QUESTCOND(021)
|
||||
{
|
||||
// byte btSelObjType pos 0x00
|
||||
// byte iRadius pos 0x04
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Check Server/Channel
|
||||
QUESTCOND(022)
|
||||
{
|
||||
//word nX pos 0x00
|
||||
//word nY pos 0x02
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//In Clan?
|
||||
QUESTCOND(023)
|
||||
{
|
||||
//byte btReg pos 0x00
|
||||
GETCONDDATA(023);
|
||||
int Clanid = client->Clan->clanid;
|
||||
if(data->btReg == 0)
|
||||
{
|
||||
if(Clanid == 0)return QUEST_SUCCESS;
|
||||
else return QUEST_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Clanid != 0)return QUEST_SUCCESS;
|
||||
else return QUEST_FAILURE;
|
||||
}
|
||||
//btReg should only ever be 1 or 0 but just in case......
|
||||
//return QUEST_FAILURE;
|
||||
}
|
||||
|
||||
//Clan Position
|
||||
QUESTCOND(024)
|
||||
{
|
||||
//check my current rank in the clan against data->nPOS using btOP
|
||||
GETCONDDATA(024);
|
||||
//word nPOS; pos 0x00
|
||||
//byte btOP; pos 0x02
|
||||
int myRank = client->Clan->clanrank;
|
||||
switch(data->btOP)
|
||||
{
|
||||
case 0:
|
||||
if(myRank == data->nPOS)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 1:
|
||||
if(myRank > data->nPOS)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 2:
|
||||
if(myRank >= data->nPOS)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 3:
|
||||
if(myRank < data->nPOS)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 4:
|
||||
if(myRank <= data->nPOS)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 10:
|
||||
if(myRank != data->nPOS)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Clan Contribution
|
||||
QUESTCOND(025)
|
||||
{
|
||||
//word nCONT pos 0x00
|
||||
//byte btOP pos 0x02
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Grade
|
||||
QUESTCOND(026)
|
||||
{
|
||||
//possibly compare client's current clan grade with data->nGRD using data->btOp?
|
||||
GETCONDDATA(026);
|
||||
//word nGRD;
|
||||
//byte btOP;
|
||||
int myGrade = client->Clan->grade;
|
||||
switch(data->btOP)
|
||||
{
|
||||
case 0:
|
||||
if(myGrade == data->nGRD)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 1:
|
||||
if(myGrade > data->nGRD)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 2:
|
||||
if(myGrade >= data->nGRD)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 3:
|
||||
if(myGrade < data->nGRD)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 4:
|
||||
if(myGrade <= data->nGRD)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
case 10:
|
||||
if(myGrade != data->nGRD)
|
||||
return QUEST_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
return QUEST_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Clan Points
|
||||
QUESTCOND(027)
|
||||
{
|
||||
//word nPOINT pos 0x00
|
||||
//byte btOP pos 0x02
|
||||
GETCONDDATA(027);
|
||||
if( client->questdebug )
|
||||
server->SendPM(client, "QUEST - Clan Points");
|
||||
switch(data->btOP)
|
||||
{
|
||||
case 0x01:
|
||||
BEGINPACKET( pak, 0x7e0 );
|
||||
ADDBYTE ( pak, 0x5C ); //action to update clan points (charserver)
|
||||
ADDWORD ( pak, client->Clan->clanid);
|
||||
ADDWORD ( pak, client->clientid );
|
||||
ADDDWORD ( pak, client->Clan->CP );
|
||||
ADDWORD ( pak, 0x00);
|
||||
GServer->SendISCPacket( &pak );
|
||||
break;
|
||||
//default:
|
||||
//break;
|
||||
}
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Funds
|
||||
QUESTCOND(028)
|
||||
{
|
||||
//word iMONEY; pos 0x00
|
||||
//byte btOP; pos 0x02
|
||||
GETCONDDATA(027);
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Members
|
||||
QUESTCOND(029)
|
||||
{
|
||||
//word nMemberCNT; pos 0x00
|
||||
//byte btOP; pos 0x02
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
|
||||
//Clan Skills
|
||||
QUESTCOND(030)
|
||||
{
|
||||
//word nSkill1; pos 0x00
|
||||
//word nSkill2; pos 0x02
|
||||
//byte btOP; pos 0x05
|
||||
GETCONDDATA(030);
|
||||
//not sure what to do here. Looks like we compare something with two skill ids
|
||||
if( client->questdebug )server->SendPM(client, "Clan skills. Skill1: %i Skill2 %i btOP: %i",data->nSkill1,data->nSkill2,data->btOP);
|
||||
return QUEST_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
// Props to ExJam for this code :D Ported to OSpRose by Drakia
|
||||
#define QUESTCOND(cond) int QUEST_COND_ ## cond (class CWorldServer* server, class CPlayer* client, byte* raw)
|
||||
typedef int (*fpQuestCond)(class CWorldServer*, class CPlayer*, byte*);
|
||||
#define GETCONDDATA(cond) STR_COND_ ## cond * data = (STR_COND_ ## cond *)raw
|
||||
|
||||
QUESTCOND(000);QUESTCOND(001);QUESTCOND(002);
|
||||
QUESTCOND(003);QUESTCOND(004);QUESTCOND(005);
|
||||
QUESTCOND(006);QUESTCOND(007);QUESTCOND(008);
|
||||
QUESTCOND(009);QUESTCOND(010);QUESTCOND(011);
|
||||
QUESTCOND(012);QUESTCOND(013);QUESTCOND(014);
|
||||
QUESTCOND(015);QUESTCOND(016);QUESTCOND(017);
|
||||
QUESTCOND(018);QUESTCOND(019);QUESTCOND(020);
|
||||
QUESTCOND(021);QUESTCOND(022);QUESTCOND(023);
|
||||
QUESTCOND(024);QUESTCOND(025);QUESTCOND(026);
|
||||
QUESTCOND(027);QUESTCOND(028);QUESTCOND(029);
|
||||
QUESTCOND(030);
|
||||
|
||||
struct STR_COND_000
|
||||
{
|
||||
int iQuestSN;
|
||||
};
|
||||
|
||||
struct STR_COND_001
|
||||
{
|
||||
dword iDataCnt;
|
||||
//int iDataCnt;
|
||||
STR_QUEST_DATA* CheckData;
|
||||
};
|
||||
|
||||
struct STR_COND_002
|
||||
{
|
||||
dword iDataCnt;
|
||||
//int iDataCnt;
|
||||
STR_QUEST_DATA* CheckData;
|
||||
};
|
||||
|
||||
struct STR_COND_003
|
||||
{
|
||||
dword iDataCnt; //pos 0x00
|
||||
STR_ABIL_DATA* CheckData; // pos = variable. it repeats iDataCnt times
|
||||
};
|
||||
|
||||
struct STR_COND_004
|
||||
{
|
||||
dword iDataCnt; //pos 0x00
|
||||
STR_ITEM_DATA* CheckData; // pos = variable. it repeats iDataCnt times
|
||||
};
|
||||
|
||||
struct STR_COND_005
|
||||
{
|
||||
byte btIsLeader;
|
||||
int iLevel;
|
||||
byte btReversed;
|
||||
};
|
||||
|
||||
struct STR_COND_006
|
||||
{
|
||||
int iZoneSN; //pos 0x00
|
||||
int iX; //pos 0x04
|
||||
int iY; //pos 0x08
|
||||
int iZ; //pos 0x0c
|
||||
int iRadius; //pos 0x10
|
||||
};
|
||||
|
||||
struct STR_COND_007
|
||||
{
|
||||
dword ulTime;
|
||||
dword ulEndTime;
|
||||
};
|
||||
|
||||
struct STR_COND_008
|
||||
{
|
||||
dword ulTime;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_COND_009
|
||||
{
|
||||
int iSkillSN1;
|
||||
int iSkillSN2;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_COND_010
|
||||
{
|
||||
byte btLowPcnt;
|
||||
byte btHighPcnt;
|
||||
};
|
||||
|
||||
struct STR_COND_011
|
||||
{
|
||||
union
|
||||
{
|
||||
byte btWho; //pos 0x00
|
||||
word usWho;
|
||||
};
|
||||
word nVarNo; //pos 0x02
|
||||
int iValue; //pos 0x04
|
||||
byte btOp; //pos 0x09
|
||||
};
|
||||
|
||||
struct STR_COND_012
|
||||
{
|
||||
union
|
||||
{
|
||||
word iZone;
|
||||
dword ulZone;
|
||||
};
|
||||
int iX;
|
||||
int iY;
|
||||
union
|
||||
{
|
||||
int iEventID;
|
||||
dword m_HashEventOBJ;
|
||||
};
|
||||
};
|
||||
|
||||
struct STR_COND_013
|
||||
{
|
||||
int iNpcNo;
|
||||
};
|
||||
|
||||
struct STR_COND_014
|
||||
{
|
||||
word nSN;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_COND_015
|
||||
{
|
||||
word nNumber1;
|
||||
word nNumber2;
|
||||
};
|
||||
|
||||
struct STR_COND_016
|
||||
{
|
||||
union
|
||||
{
|
||||
byte btWho;
|
||||
dword ulWho;
|
||||
};
|
||||
dword ulTime;
|
||||
dword ulEndTime;
|
||||
};
|
||||
|
||||
struct STR_COND_017
|
||||
{
|
||||
STR_NPCVAR NpcVar1;
|
||||
STR_NPCVAR NpcVar2;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_COND_018
|
||||
{
|
||||
byte btDate;
|
||||
byte btHour1;
|
||||
byte btMin1;
|
||||
byte btHour2;
|
||||
byte btMin2;
|
||||
};
|
||||
|
||||
struct STR_COND_019
|
||||
{
|
||||
byte btWeekDay;
|
||||
byte btHour1;
|
||||
byte btMin1;
|
||||
byte btHour2;
|
||||
byte btMin2;
|
||||
};
|
||||
|
||||
struct STR_COND_020
|
||||
{
|
||||
int iNo1;
|
||||
int iNo2;
|
||||
};
|
||||
|
||||
struct STR_COND_021
|
||||
{
|
||||
union
|
||||
{
|
||||
byte btSelObjType;
|
||||
dword ulSelObjType;
|
||||
};
|
||||
int iRadius;
|
||||
};
|
||||
|
||||
struct STR_COND_022
|
||||
{
|
||||
word nX;
|
||||
word nY;
|
||||
};
|
||||
|
||||
struct STR_COND_023
|
||||
{
|
||||
byte btReg;
|
||||
};
|
||||
|
||||
struct STR_COND_024
|
||||
{
|
||||
word nPOS;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_COND_025
|
||||
{
|
||||
word nCONT;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_COND_026
|
||||
{
|
||||
word nGRD;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_COND_027
|
||||
{
|
||||
word nPOINT;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_COND_028
|
||||
{
|
||||
word iMONEY;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_COND_029
|
||||
{
|
||||
word nMemberCNT;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_COND_030
|
||||
{
|
||||
word nSkill1;
|
||||
word nSkill2;
|
||||
byte btOP;
|
||||
};
|
||||
@@ -0,0 +1,98 @@
|
||||
// Props to ExJam for this code :D Ported to OSpRose by Drakia
|
||||
class CQuestTrigger {
|
||||
public:
|
||||
struct SQuestDatum {
|
||||
int size;
|
||||
int opcode;
|
||||
byte* data;
|
||||
};
|
||||
|
||||
strings TriggerName;
|
||||
dword TriggerHash;
|
||||
|
||||
byte CheckNext;
|
||||
dword id;
|
||||
|
||||
dword ConditionCount;
|
||||
SQuestDatum** Conditions;
|
||||
|
||||
dword ActionCount;
|
||||
SQuestDatum** Actions;
|
||||
private:
|
||||
};
|
||||
|
||||
struct STR_NPCVAR {
|
||||
dword iNpcNo;
|
||||
word nVarNo;
|
||||
word unused;
|
||||
};
|
||||
|
||||
struct STR_ITEM_DATA {
|
||||
dword uiItemSN;
|
||||
dword iWhere;
|
||||
dword iRequestCnt;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_QUEST_DATA
|
||||
{
|
||||
union
|
||||
{
|
||||
dword iType;
|
||||
struct
|
||||
{
|
||||
short m_wVarNO;
|
||||
word m_wVarTYPE;
|
||||
};
|
||||
};
|
||||
word nValue;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_ABIL_DATA
|
||||
{
|
||||
dword iType;
|
||||
int iValue;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
#define QUEST_FAILURE 6
|
||||
#define QUEST_SUCCESS 5
|
||||
template <class T> int OperateValues(byte btOp, T* value1PTR, T value2)
|
||||
{
|
||||
T value1 = *value1PTR;
|
||||
switch(btOp)
|
||||
{
|
||||
case 0:
|
||||
return (value1 == value2);
|
||||
case 1:
|
||||
return (value1 > value2);
|
||||
case 2:
|
||||
return (value1 >= value2);
|
||||
case 3:
|
||||
return (value1 < value2);
|
||||
case 4:
|
||||
return (value1 <= value2);
|
||||
case 5:
|
||||
(*value1PTR) = value2;
|
||||
return 1;
|
||||
case 6:
|
||||
(*value1PTR) = value1 + value2;
|
||||
return 1;
|
||||
case 7:
|
||||
(*value1PTR) = value1 - value2;
|
||||
return 1;
|
||||
case 9:
|
||||
(*value1PTR) += 1;
|
||||
return 1;
|
||||
case 10:
|
||||
return (value1 != value2);
|
||||
default:
|
||||
Log(MSG_WARNING, "OperateValues unknown btOp='%u'", btOp);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
dword GetRewardValue(dword function, dword amount, CPlayer* client, word nDupCNT);
|
||||
|
||||
#include "QuestConditions.h"
|
||||
#include "QuestActions.h"
|
||||
@@ -0,0 +1,157 @@
|
||||
// Props to ExJam for this code :D
|
||||
#include "../WorldServer.h"
|
||||
#include "QuestActions.h"
|
||||
|
||||
void CWorldServer::ReadQSD(strings path, dword index){
|
||||
CRoseFile* fh = new CRoseFile(path, FM_READ | FM_BINARY);
|
||||
|
||||
if(fh->IsOpen())
|
||||
{ // goto done;
|
||||
|
||||
//Log(MSG_LOAD, "Loading %s ", path);
|
||||
|
||||
fh->Seek(4, SEEK_CUR);
|
||||
dword BlockCount = fh->Get<dword>();
|
||||
fh->Seek(fh->Get<word>(), SEEK_CUR);
|
||||
for(dword i = 0; i < BlockCount; i++)
|
||||
{
|
||||
dword RecordCount = fh->Get<dword>();
|
||||
fh->Seek(fh->Get<word>(), SEEK_CUR);
|
||||
for(dword j = 0; j < RecordCount; j++)
|
||||
{
|
||||
CQuestTrigger* trigger = new CQuestTrigger();
|
||||
trigger->id=((index*0x10000)+(i*0x100)+j);
|
||||
trigger->CheckNext = fh->Get<byte>();
|
||||
trigger->ConditionCount = fh->Get<dword>();
|
||||
trigger->ActionCount = fh->Get<dword>();
|
||||
dword len = fh->Get<word>();
|
||||
trigger->TriggerName = new char[len+1];
|
||||
fh->Read(trigger->TriggerName, len, 1);
|
||||
trigger->TriggerName[len] = 0;
|
||||
|
||||
if(trigger->ConditionCount > 0)
|
||||
{
|
||||
trigger->Conditions = new CQuestTrigger::SQuestDatum*[trigger->ConditionCount];
|
||||
for(dword k = 0; k < trigger->ConditionCount; k++)
|
||||
{
|
||||
CQuestTrigger::SQuestDatum* data = new CQuestTrigger::SQuestDatum();
|
||||
data->size = fh->Get<int>();
|
||||
data->opcode = fh->Get<int>();
|
||||
data->data = new byte[data->size - 8];
|
||||
fh->Read(data->data, data->size - 8, 1);
|
||||
trigger->Conditions[k] = data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger->Conditions = NULL;
|
||||
}
|
||||
|
||||
if(trigger->ActionCount > 0)
|
||||
{
|
||||
trigger->Actions = new CQuestTrigger::SQuestDatum*[trigger->ActionCount];
|
||||
for(dword k = 0; k < trigger->ActionCount; k++)
|
||||
{
|
||||
CQuestTrigger::SQuestDatum* data = new CQuestTrigger::SQuestDatum();
|
||||
data->size = fh->Get<int>();
|
||||
data->opcode = fh->Get<int>() - 0x01000000;
|
||||
data->data = new byte[data->size - 8];
|
||||
fh->Read(data->data, data->size - 8, 1);
|
||||
trigger->Actions[k] = data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger->Actions = NULL;
|
||||
}
|
||||
|
||||
trigger->TriggerHash = MakeStrHash(trigger->TriggerName);
|
||||
TriggerList.push_back( trigger );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
Log( MSG_ERROR, "QSD File: '%s' did not load properly", path );
|
||||
|
||||
fh->Close();
|
||||
delete fh;
|
||||
}
|
||||
|
||||
void CWorldServer::LoadQuestData(){
|
||||
CStrStb* stbQuest = new CStrStb("3DDATA\\STB\\LIST_QUESTDATA.STB");
|
||||
Log(MSG_INFO, "Loading QSD files");
|
||||
for(dword i = 1; i < stbQuest->Rows(); i++){
|
||||
if(stbQuest->Data(i, 0)){
|
||||
GServer->ReadQSD(stbQuest->Data(i, 0),i);
|
||||
}
|
||||
}
|
||||
|
||||
Log(MSG_INFO, "Finished loading quest data ");
|
||||
|
||||
qstCondFunc[0] = &QUEST_COND_000;
|
||||
qstCondFunc[1] = &QUEST_COND_001;
|
||||
qstCondFunc[2] = &QUEST_COND_002;
|
||||
qstCondFunc[3] = &QUEST_COND_003;
|
||||
qstCondFunc[4] = &QUEST_COND_004;
|
||||
qstCondFunc[5] = &QUEST_COND_005;
|
||||
qstCondFunc[6] = &QUEST_COND_006;
|
||||
qstCondFunc[7] = &QUEST_COND_007;
|
||||
qstCondFunc[8] = &QUEST_COND_008;
|
||||
qstCondFunc[9] = &QUEST_COND_009;
|
||||
qstCondFunc[10] = &QUEST_COND_010;
|
||||
qstCondFunc[11] = &QUEST_COND_011;
|
||||
qstCondFunc[12] = &QUEST_COND_012;
|
||||
qstCondFunc[13] = &QUEST_COND_013;
|
||||
qstCondFunc[14] = &QUEST_COND_014;
|
||||
qstCondFunc[15] = &QUEST_COND_015;
|
||||
qstCondFunc[16] = &QUEST_COND_016;
|
||||
qstCondFunc[17] = &QUEST_COND_017;
|
||||
qstCondFunc[18] = &QUEST_COND_018;
|
||||
qstCondFunc[19] = &QUEST_COND_019;
|
||||
qstCondFunc[20] = &QUEST_COND_020;
|
||||
qstCondFunc[21] = &QUEST_COND_021;
|
||||
qstCondFunc[22] = &QUEST_COND_022;
|
||||
qstCondFunc[23] = &QUEST_COND_023;
|
||||
qstCondFunc[24] = &QUEST_COND_024;
|
||||
qstCondFunc[25] = &QUEST_COND_025;
|
||||
qstCondFunc[26] = &QUEST_COND_026;
|
||||
qstCondFunc[27] = &QUEST_COND_027;
|
||||
qstCondFunc[28] = &QUEST_COND_028;
|
||||
qstCondFunc[29] = &QUEST_COND_029;
|
||||
qstCondFunc[30] = &QUEST_COND_030;
|
||||
|
||||
qstRewdFunc[0] = &QUEST_REWD_000;
|
||||
qstRewdFunc[1] = &QUEST_REWD_001;
|
||||
qstRewdFunc[2] = &QUEST_REWD_002;
|
||||
qstRewdFunc[3] = &QUEST_REWD_003;
|
||||
qstRewdFunc[4] = &QUEST_REWD_004;
|
||||
qstRewdFunc[5] = &QUEST_REWD_005;
|
||||
qstRewdFunc[6] = &QUEST_REWD_006;
|
||||
qstRewdFunc[7] = &QUEST_REWD_007;
|
||||
qstRewdFunc[8] = &QUEST_REWD_008;
|
||||
qstRewdFunc[9] = &QUEST_REWD_009;
|
||||
qstRewdFunc[10] = &QUEST_REWD_010;
|
||||
qstRewdFunc[11] = &QUEST_REWD_011;
|
||||
qstRewdFunc[12] = &QUEST_REWD_012;
|
||||
qstRewdFunc[13] = &QUEST_REWD_013;
|
||||
qstRewdFunc[14] = &QUEST_REWD_014;
|
||||
qstRewdFunc[15] = &QUEST_REWD_015;
|
||||
qstRewdFunc[16] = &QUEST_REWD_016;
|
||||
qstRewdFunc[17] = &QUEST_REWD_017;
|
||||
qstRewdFunc[18] = &QUEST_REWD_018;
|
||||
qstRewdFunc[19] = &QUEST_REWD_019;
|
||||
qstRewdFunc[20] = &QUEST_REWD_020;
|
||||
qstRewdFunc[21] = &QUEST_REWD_021;
|
||||
qstRewdFunc[22] = &QUEST_REWD_022;
|
||||
qstRewdFunc[23] = &QUEST_REWD_023;
|
||||
qstRewdFunc[24] = &QUEST_REWD_024;
|
||||
qstRewdFunc[25] = &QUEST_REWD_025;
|
||||
qstRewdFunc[26] = &QUEST_REWD_026;
|
||||
qstRewdFunc[27] = &QUEST_REWD_027;
|
||||
qstRewdFunc[28] = &QUEST_REWD_028;
|
||||
qstRewdFunc[29] = &QUEST_REWD_029;
|
||||
qstRewdFunc[30] = &QUEST_REWD_030;
|
||||
//qstRewdFunc[50] = &QUEST_REWD_050;
|
||||
|
||||
delete stbQuest;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "../WorldServer.h"
|
||||
|
||||
unsigned long MakeStrHash(const char *str)
|
||||
{
|
||||
register unsigned long result = 0xDEADC0DE;
|
||||
register unsigned long tkey = 0x7FED7FED;
|
||||
register char c = 0;
|
||||
while (*(str)){
|
||||
c = (*str<'a'||*str>'z')?*str:*str-32;
|
||||
result += tkey;
|
||||
tkey *= 0x21;
|
||||
result ^= keys[c];
|
||||
tkey += result + c + 3;
|
||||
str++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
const unsigned long keys[256] = {
|
||||
0x697A5, 0x6045C, 0xAB4E2, 0x409E4, 0x71209, 0x32392, 0xA7292, 0xB09FC, 0x4B658, 0xAAAD5, 0x9B9CF, 0xA326A, 0x8DD12, 0x38150, 0x8E14D, 0x2EB7F,
|
||||
0xE0A56, 0x7E6FA, 0xDFC27, 0xB1301, 0x8B4F7, 0xA7F70, 0xAA713, 0x6CC0F, 0x6FEDF, 0x2EC87, 0xC0F1C, 0x45CA4, 0x30DF8, 0x60E99, 0xBC13E, 0x4E0B5,
|
||||
0x6318B, 0x82679, 0x26EF2, 0x79C95, 0x86DDC, 0x99BC0, 0xB7167, 0x72532, 0x68765, 0xC7446, 0xDA70D, 0x9D132, 0xE5038, 0x2F755, 0x9171F, 0xCB49E,
|
||||
0x6F925, 0x601D3, 0x5BD8A, 0x2A4F4, 0x9B022, 0x706C3, 0x28C10, 0x2B24B, 0x7CD55, 0xCA355, 0xD95F4, 0x727BC, 0xB1138, 0x9AD21, 0xC0ACA, 0xCD928,
|
||||
0x953E5, 0x97A20, 0x345F3, 0xBDC03, 0x7E157, 0x96C99, 0x968EF, 0x92AA9, 0xC2276, 0xA695D, 0x6743B, 0x2723B, 0x58980, 0x66E08, 0x51D1B, 0xB97D2,
|
||||
0x6CAEE, 0xCC80F, 0x3BA6C, 0xB0BF5, 0x9E27B, 0xD122C, 0x48611, 0x8C326, 0xD2AF8, 0xBB3B7, 0xDED7F, 0x4B236, 0xD298F, 0xBE912, 0xDC926, 0xC873F,
|
||||
0xD0716, 0x9E1D3, 0x48D94, 0x9BD91, 0x5825D, 0x55637, 0xB2057, 0xBCC6C, 0x460DE, 0xAE7FB, 0x81B03, 0x34D8F, 0xC0528, 0xC9B59, 0x3D260, 0x6051D,
|
||||
0x93757, 0x8027F, 0xB7C34, 0x4A14E, 0xB12B8, 0xE4945, 0x28203, 0xA1C0F, 0xAA382, 0x46ABB, 0x330B9, 0x5A114, 0xA754B, 0xC68D0, 0x9040E, 0x6C955,
|
||||
0xBB1EF, 0x51E6B, 0x9FF21, 0x51BCA, 0x4C879, 0xDFF70, 0x5B5EE, 0x29936, 0xB9247, 0x42611, 0x2E353, 0x26F3A, 0x683A3, 0xA1082, 0x67333, 0x74EB7,
|
||||
0x754BA, 0x369D5, 0x8E0BC, 0xABAFD, 0x6630B, 0xA3A7E, 0xCDBB1, 0x8C2DE, 0x92D32, 0x2F8ED, 0x7EC54, 0x572F5, 0x77461, 0xCB3F5, 0x82C64, 0x35FE0,
|
||||
0x9203B, 0xADA2D, 0xBAEBD, 0xCB6AF, 0xC8C9A, 0x5D897, 0xCB727, 0xA13B3, 0xB4D6D, 0xC4929, 0xB8732, 0xCCE5A, 0xD3E69, 0xD4B60, 0x89941, 0x79D85,
|
||||
0x39E0F, 0x6945B, 0xC37F8, 0x77733, 0x45D7D, 0x25565, 0xA3A4E, 0xB9F9E, 0x316E4, 0x36734, 0x6F5C3, 0xA8BA6, 0xC0871, 0x42D05, 0x40A74, 0x2E7ED,
|
||||
0x67C1F, 0x28BE0, 0xE162B, 0xA1C0F, 0x2F7E5, 0xD505A, 0x9FCC8, 0x78381, 0x29394, 0x53D6B, 0x7091D, 0xA2FB1, 0xBB942, 0x29906, 0xC412D, 0x3FCD5,
|
||||
0x9F2EB, 0x8F0CC, 0xE25C3, 0x7E519, 0x4E7D9, 0x5F043, 0xBBA1B, 0x6710A, 0x819FB, 0x9A223, 0x38E47, 0xE28AD, 0xB690B, 0x42328, 0x7CF7E, 0xAE108,
|
||||
0xE54BA, 0xBA5A1, 0xA09A6, 0x9CAB7, 0xDB2B3, 0xA98CC, 0x5CEBA, 0x9245D, 0x5D083, 0x8EA21, 0xAE349, 0x54940, 0x8E557, 0x83EFD, 0xDC504, 0xA6059,
|
||||
0xB85C9, 0x9D162, 0x7AEB6, 0xBED34, 0xB4963, 0xE367B, 0x4C891, 0x9E42C, 0xD4304, 0x96EAA, 0xD5D69, 0x866B8, 0x83508, 0x7BAEC, 0xD03FD, 0xDA122
|
||||
};
|
||||
|
||||
unsigned long MakeStrHash(const char *str);
|
||||
@@ -0,0 +1,196 @@
|
||||
#define QUESTREWD(reward) int QUEST_REWD_ ## reward ## (class CWorldServer* server, class CPlayer* client, byte* raw)
|
||||
typedef int (*fpQuestRewd)(class CWorldServer*, class CPlayer*, byte*);
|
||||
#define GETREWDDATA(rewd) STR_REWD_ ## rewd ## * data = (STR_REWD_ ## rewd ## *)raw;
|
||||
|
||||
QUESTREWD(000);QUESTREWD(001);QUESTREWD(002);
|
||||
QUESTREWD(003);QUESTREWD(004);QUESTREWD(005);
|
||||
QUESTREWD(006);QUESTREWD(007);QUESTREWD(008);
|
||||
QUESTREWD(009);QUESTREWD(010);QUESTREWD(011);
|
||||
QUESTREWD(012);QUESTREWD(013);QUESTREWD(014);
|
||||
QUESTREWD(015);QUESTREWD(016);QUESTREWD(017);
|
||||
QUESTREWD(018);QUESTREWD(019);QUESTREWD(020);
|
||||
QUESTREWD(021);QUESTREWD(022);QUESTREWD(023);
|
||||
QUESTREWD(024);QUESTREWD(025);QUESTREWD(026);
|
||||
QUESTREWD(027);QUESTREWD(028);
|
||||
|
||||
struct STR_REWD_000 {
|
||||
dword iQuestSN;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_REWD_001 {
|
||||
dword uiItemSN;
|
||||
union {
|
||||
byte btOp;
|
||||
word nOp;
|
||||
};
|
||||
word nDupCNT;
|
||||
byte btPartyOpt;
|
||||
};
|
||||
|
||||
struct STR_REWD_002 {
|
||||
dword iDataCnt;
|
||||
STR_QUEST_DATA* CheckData;
|
||||
};
|
||||
|
||||
struct STR_REWD_003 {
|
||||
dword iDataCnt;
|
||||
STR_ABIL_DATA* CheckData;
|
||||
byte btPartyOpt;
|
||||
};
|
||||
|
||||
struct STR_REWD_004 {
|
||||
dword iDataCnt;
|
||||
STR_QUEST_DATA* CheckData;
|
||||
};
|
||||
|
||||
struct STR_REWD_005 {
|
||||
byte btTarget;
|
||||
union {
|
||||
byte btEquation;
|
||||
word nEquation;
|
||||
};
|
||||
int iValue;
|
||||
dword iItemSN;
|
||||
union {
|
||||
byte btPartyOpt;
|
||||
word nPartyOpt;
|
||||
};
|
||||
word nItemOpt;
|
||||
};
|
||||
|
||||
struct STR_REWD_006 {
|
||||
dword iPercentOfHP;
|
||||
dword iPercentOfMP;
|
||||
byte btPartyOpt;
|
||||
};
|
||||
|
||||
struct STR_REWD_007 {
|
||||
dword iZoneSN;
|
||||
dword iX;
|
||||
dword iY;
|
||||
byte btPartyOpt;
|
||||
};
|
||||
|
||||
struct STR_REWD_008 {
|
||||
dword iMonsterSN;
|
||||
dword iHowMany;
|
||||
union {
|
||||
byte btWho;
|
||||
int iWho;
|
||||
};
|
||||
dword iZoneSN;
|
||||
dword iX;
|
||||
dword iY;
|
||||
dword iRange;
|
||||
dword iTeamNo;
|
||||
};
|
||||
|
||||
struct STR_REWD_009 {
|
||||
word shNameLen;
|
||||
string szNextTriggerSN;
|
||||
};
|
||||
|
||||
struct STR_REWD_010 {
|
||||
};
|
||||
|
||||
//UNKNOWN STRUCT FOR 11 OMG OMG OMG
|
||||
struct STR_REWD_011 {
|
||||
};
|
||||
|
||||
struct STR_REWD_012 {
|
||||
union {
|
||||
byte btMsgType;
|
||||
int iMsgType;
|
||||
};
|
||||
dword iStrID;
|
||||
};
|
||||
|
||||
struct STR_REWD_013 {
|
||||
union {
|
||||
byte btWho;
|
||||
int iWho;
|
||||
};
|
||||
dword iSec;
|
||||
word nNameLength;
|
||||
string szTriggerName;
|
||||
dword m_HashNextTrigger;
|
||||
};
|
||||
|
||||
struct STR_REWD_014 {
|
||||
union {
|
||||
byte btOp;
|
||||
int iOp;
|
||||
};
|
||||
dword iSkillNo;
|
||||
};
|
||||
|
||||
struct STR_REWD_015 {
|
||||
word nSN;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_REWD_016 {
|
||||
word nGroupSN;
|
||||
};
|
||||
|
||||
struct STR_REWD_017 {
|
||||
};
|
||||
|
||||
struct STR_REWD_018 {
|
||||
dword iStrID;
|
||||
word nCnt;
|
||||
string Data;
|
||||
};
|
||||
|
||||
struct STR_REWD_019 {
|
||||
word nZoneNo;
|
||||
word nTeamNo;
|
||||
word nTriggerLength;
|
||||
string TriggerName;
|
||||
dword m_HashTrigger;
|
||||
};
|
||||
|
||||
struct STR_REWD_020 {
|
||||
byte btNoType;
|
||||
};
|
||||
|
||||
struct STR_REWD_021 {
|
||||
dword iX;
|
||||
dword iY;
|
||||
};
|
||||
|
||||
struct STR_REWD_022 {
|
||||
word nZonNo;
|
||||
byte btOp;
|
||||
};
|
||||
|
||||
struct STR_REWD_023 {
|
||||
};
|
||||
|
||||
struct STR_REWD_024 {
|
||||
int iMONEY;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_REWD_025 {
|
||||
short nPOINT;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_REWD_026 {
|
||||
word nSkillNo;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_REWD_027 {
|
||||
short nCONT;
|
||||
byte btOP;
|
||||
};
|
||||
|
||||
struct STR_REWD_028 {
|
||||
dword iRange;
|
||||
word nZoneNo;
|
||||
dword iX;
|
||||
dword iY;
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,48 @@
|
||||
#include "worldserver.h"
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// STB reading shit
|
||||
// -----------------------------------------------------------------------------------------
|
||||
int STBStoreData( char* filename, CSTBData* data )
|
||||
{
|
||||
FILE* fh;
|
||||
fh = NULL;
|
||||
char tmpfield[512];
|
||||
unsigned dataoffset = 0;
|
||||
unsigned short fieldlen = 0;
|
||||
|
||||
if ( fopen( filename, "rb" ) != 0 )
|
||||
{
|
||||
Log( MSG_ERROR, "Could not load STB '%s'", filename );
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read the header
|
||||
fseek( fh, 4, SEEK_SET );
|
||||
fread( &dataoffset, 4, 1, fh );
|
||||
fread( &data->rowcount, 4, 1, fh );
|
||||
fread( &data->fieldcount, 4, 1, fh );
|
||||
data->rowcount--;
|
||||
data->fieldcount--;
|
||||
// Read the data
|
||||
fseek( fh, dataoffset, SEEK_SET );
|
||||
int* tmp = new int[data->rowcount*data->fieldcount];
|
||||
data->rows = new int*[data->rowcount];
|
||||
for( unsigned i = 0; i < data->rowcount; i++ ) data->rows[i] = &tmp[i*data->fieldcount];
|
||||
for( unsigned j = 0; j < data->rowcount*data->fieldcount; j++ ) {
|
||||
fread( &fieldlen, 2, 1, fh );
|
||||
fread( tmpfield, 1, fieldlen, fh );
|
||||
tmpfield[fieldlen]=0;
|
||||
tmp[j] = atoi( tmpfield );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//void STBFreeData( CSTBData* data )
|
||||
//{
|
||||
// delete [] data->rows[0];
|
||||
// delete [] data->rows;
|
||||
//}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Written by Brett19
|
||||
#include "worldserver.h"
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// STB reading shit
|
||||
// -----------------------------------------------------------------------------------------
|
||||
int STBStoreData( const char* filename, CSTBData* data )
|
||||
{
|
||||
char tmpfield[512];
|
||||
unsigned dataoffset = 0;
|
||||
unsigned short fieldlen = 0;
|
||||
|
||||
FILE* fh = fopen( filename, "rb" );
|
||||
if ( fh == 0 )
|
||||
{
|
||||
Log( MSG_ERROR, "Could not load STB '%s'", filename );
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read the header
|
||||
fseek( fh, 4, SEEK_SET );
|
||||
fread( &dataoffset, 4, 1, fh );
|
||||
fread( &data->rowcount, 4, 1, fh );
|
||||
fread( &data->fieldcount, 4, 1, fh );
|
||||
data->rowcount--;
|
||||
data->fieldcount--;
|
||||
// Read the data
|
||||
fseek( fh, dataoffset, SEEK_SET );
|
||||
int* tmp = new int[data->rowcount * data->fieldcount];
|
||||
data->rows = new int*[data->rowcount];
|
||||
for( unsigned i = 0; i < data->rowcount; i++ )
|
||||
{
|
||||
data->rows[i] = &tmp[i * data->fieldcount];
|
||||
}
|
||||
for( unsigned j = 0; j < data->rowcount * data->fieldcount; j++ )
|
||||
{
|
||||
fread( &fieldlen, 2, 1, fh );
|
||||
fread( tmpfield, 1, fieldlen, fh );
|
||||
tmpfield[fieldlen]=0;
|
||||
tmp[j] = atoi( tmpfield );
|
||||
}
|
||||
fclose( fh );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void STBFreeData( CSTBData* data )
|
||||
{
|
||||
delete [] data->rows[0];
|
||||
delete [] data->rows;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Written by Brett19
|
||||
#include "worldserver.h"
|
||||
|
||||
struct CSTBData {
|
||||
unsigned rowcount;
|
||||
unsigned fieldcount;
|
||||
int** rows;
|
||||
};
|
||||
|
||||
int STBStoreData( const char* filename, CSTBData* data );
|
||||
void STBFreeData( CSTBData* data );
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
Rose Online Server Emulator
|
||||
Copyright (C) 2006,2007 OSRose Team http://www.dev-osrose.com
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
//LMA: Code taken from IFONPCDUMPER from Drakia and adpated.
|
||||
|
||||
#include "worldserver.h"
|
||||
|
||||
|
||||
|
||||
//Reading and storing a STL in a single map :)
|
||||
int STLStoreData( const char* filename, int family)
|
||||
{
|
||||
entry** index;
|
||||
map<UINT, entry> table;
|
||||
char tempfield[512];
|
||||
UINT entryCount;
|
||||
UINT langCount;
|
||||
int temp=0;
|
||||
|
||||
|
||||
FILE* fh = fopen( filename, "rb" );
|
||||
if (fh==0)
|
||||
{
|
||||
Log( MSG_ERROR, "Could not load STL '%s'", filename );
|
||||
return 0;
|
||||
}
|
||||
|
||||
fread(&temp,1,1,fh);
|
||||
fread(tempfield,1,temp,fh);
|
||||
fread(&entryCount,4,1,fh);
|
||||
|
||||
if(family == 6)
|
||||
Log(MSG_DEBUG,"%s:: %i",filename,entryCount);
|
||||
|
||||
index = new entry*[entryCount];
|
||||
for (UINT i = 0; i < entryCount; i++)
|
||||
{
|
||||
entry item;
|
||||
temp=0;
|
||||
fread(&temp,1,1,fh);
|
||||
fread(tempfield,1,temp,fh);
|
||||
tempfield[temp]=0;
|
||||
|
||||
for(int k=0;k<temp;k++)
|
||||
{
|
||||
if(tempfield[k]>=48&&tempfield[k]<=57)
|
||||
break;
|
||||
tempfield[k]=48;
|
||||
}
|
||||
|
||||
item.stringId=atoi(tempfield);
|
||||
fread(&item.id,4,1,fh);
|
||||
table[item.stringId] = item;
|
||||
index[i] = &table[item.stringId];
|
||||
}
|
||||
|
||||
vector<UINT> langOffset;
|
||||
fread(&langCount,4,1,fh);
|
||||
|
||||
for (UINT i = 0; i < langCount; i++)
|
||||
{
|
||||
temp=0;
|
||||
fread(&temp,4,1,fh);
|
||||
langOffset.push_back(temp);
|
||||
}
|
||||
|
||||
|
||||
UINT i=STL_ENGLISH;
|
||||
fseek( fh, langOffset.at(i), SEEK_SET );
|
||||
|
||||
for (UINT j = 0; j < entryCount; j++)
|
||||
{
|
||||
entry* Entry = index[j];
|
||||
language lang;
|
||||
temp=0;
|
||||
fread(&temp,4,1,fh);
|
||||
lang.offset=temp;
|
||||
Entry->lang.push_back(lang);
|
||||
}
|
||||
|
||||
for (UINT j = 0; j < entryCount; j++)
|
||||
{
|
||||
entry* Entry = index[j];
|
||||
//language *item = &Entry->lang.at(i);
|
||||
language *item = &Entry->lang.at(0);
|
||||
fseek( fh, item->offset, SEEK_SET );
|
||||
temp=0;
|
||||
fread(&temp,1,1,fh);
|
||||
|
||||
//no text?
|
||||
if(temp==0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fread(item->text,1,temp,fh);
|
||||
item->text[temp]=0;
|
||||
|
||||
//LMA: let's do the map ^_^
|
||||
UINT mapid=family*100000+index[j]->stringId;
|
||||
if(GServer->STLNameList.find(mapid)!=GServer->STLNameList.end())
|
||||
{
|
||||
Log(MSG_WARNING,"STL ID %u (line %i) already in map %s",mapid,j,GServer->STLNameList[mapid]);
|
||||
continue;
|
||||
}
|
||||
|
||||
char* new_text=new char[temp+1];
|
||||
memcpy(new_text,item->text,temp);
|
||||
new_text[temp]=0;
|
||||
GServer->STLNameList[mapid]=new_text;
|
||||
}
|
||||
|
||||
fclose( fh );
|
||||
delete[] index;
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
Rose Online Server Emulator
|
||||
Copyright (C) 2006,2007 OSRose Team http://www.dev-osrose.com
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
//LMA: Code taken from IFONPCDUMPER from Drakia and adpated.
|
||||
#define STL_ENGLISH 1
|
||||
#include "worldserver.h"
|
||||
|
||||
|
||||
int STLStoreData(const char* filename, int family);
|
||||
|
||||
struct language
|
||||
{
|
||||
char text[512];
|
||||
UINT offset;
|
||||
};
|
||||
|
||||
struct entry
|
||||
{
|
||||
UINT id;
|
||||
UINT stringId;
|
||||
vector<language> lang;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Written by Brett19
|
||||
|
||||
#include "worldserver.h"
|
||||
|
||||
unsigned long MakeStrHash(const char *str)
|
||||
{
|
||||
register unsigned long result = 0xDEADC0DE;
|
||||
register unsigned long tkey = 0x7FED7FED;
|
||||
register char c = 0;
|
||||
while (*(str)){
|
||||
c = (*str<'a'||*str>'z')?*str:*str-32;
|
||||
result += tkey;
|
||||
tkey *= 0x21;
|
||||
result ^= keys[c];
|
||||
tkey += result + c + 3;
|
||||
str++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
const unsigned long keys[256] = {
|
||||
0x697A5, 0x6045C, 0xAB4E2, 0x409E4, 0x71209, 0x32392, 0xA7292, 0xB09FC, 0x4B658, 0xAAAD5, 0x9B9CF, 0xA326A, 0x8DD12, 0x38150, 0x8E14D, 0x2EB7F,
|
||||
0xE0A56, 0x7E6FA, 0xDFC27, 0xB1301, 0x8B4F7, 0xA7F70, 0xAA713, 0x6CC0F, 0x6FEDF, 0x2EC87, 0xC0F1C, 0x45CA4, 0x30DF8, 0x60E99, 0xBC13E, 0x4E0B5,
|
||||
0x6318B, 0x82679, 0x26EF2, 0x79C95, 0x86DDC, 0x99BC0, 0xB7167, 0x72532, 0x68765, 0xC7446, 0xDA70D, 0x9D132, 0xE5038, 0x2F755, 0x9171F, 0xCB49E,
|
||||
0x6F925, 0x601D3, 0x5BD8A, 0x2A4F4, 0x9B022, 0x706C3, 0x28C10, 0x2B24B, 0x7CD55, 0xCA355, 0xD95F4, 0x727BC, 0xB1138, 0x9AD21, 0xC0ACA, 0xCD928,
|
||||
0x953E5, 0x97A20, 0x345F3, 0xBDC03, 0x7E157, 0x96C99, 0x968EF, 0x92AA9, 0xC2276, 0xA695D, 0x6743B, 0x2723B, 0x58980, 0x66E08, 0x51D1B, 0xB97D2,
|
||||
0x6CAEE, 0xCC80F, 0x3BA6C, 0xB0BF5, 0x9E27B, 0xD122C, 0x48611, 0x8C326, 0xD2AF8, 0xBB3B7, 0xDED7F, 0x4B236, 0xD298F, 0xBE912, 0xDC926, 0xC873F,
|
||||
0xD0716, 0x9E1D3, 0x48D94, 0x9BD91, 0x5825D, 0x55637, 0xB2057, 0xBCC6C, 0x460DE, 0xAE7FB, 0x81B03, 0x34D8F, 0xC0528, 0xC9B59, 0x3D260, 0x6051D,
|
||||
0x93757, 0x8027F, 0xB7C34, 0x4A14E, 0xB12B8, 0xE4945, 0x28203, 0xA1C0F, 0xAA382, 0x46ABB, 0x330B9, 0x5A114, 0xA754B, 0xC68D0, 0x9040E, 0x6C955,
|
||||
0xBB1EF, 0x51E6B, 0x9FF21, 0x51BCA, 0x4C879, 0xDFF70, 0x5B5EE, 0x29936, 0xB9247, 0x42611, 0x2E353, 0x26F3A, 0x683A3, 0xA1082, 0x67333, 0x74EB7,
|
||||
0x754BA, 0x369D5, 0x8E0BC, 0xABAFD, 0x6630B, 0xA3A7E, 0xCDBB1, 0x8C2DE, 0x92D32, 0x2F8ED, 0x7EC54, 0x572F5, 0x77461, 0xCB3F5, 0x82C64, 0x35FE0,
|
||||
0x9203B, 0xADA2D, 0xBAEBD, 0xCB6AF, 0xC8C9A, 0x5D897, 0xCB727, 0xA13B3, 0xB4D6D, 0xC4929, 0xB8732, 0xCCE5A, 0xD3E69, 0xD4B60, 0x89941, 0x79D85,
|
||||
0x39E0F, 0x6945B, 0xC37F8, 0x77733, 0x45D7D, 0x25565, 0xA3A4E, 0xB9F9E, 0x316E4, 0x36734, 0x6F5C3, 0xA8BA6, 0xC0871, 0x42D05, 0x40A74, 0x2E7ED,
|
||||
0x67C1F, 0x28BE0, 0xE162B, 0xA1C0F, 0x2F7E5, 0xD505A, 0x9FCC8, 0x78381, 0x29394, 0x53D6B, 0x7091D, 0xA2FB1, 0xBB942, 0x29906, 0xC412D, 0x3FCD5,
|
||||
0x9F2EB, 0x8F0CC, 0xE25C3, 0x7E519, 0x4E7D9, 0x5F043, 0xBBA1B, 0x6710A, 0x819FB, 0x9A223, 0x38E47, 0xE28AD, 0xB690B, 0x42328, 0x7CF7E, 0xAE108,
|
||||
0xE54BA, 0xBA5A1, 0xA09A6, 0x9CAB7, 0xDB2B3, 0xA98CC, 0x5CEBA, 0x9245D, 0x5D083, 0x8EA21, 0xAE349, 0x54940, 0x8E557, 0x83EFD, 0xDC504, 0xA6059,
|
||||
0xB85C9, 0x9D162, 0x7AEB6, 0xBED34, 0xB4963, 0xE367B, 0x4C891, 0x9E42C, 0xD4304, 0x96EAA, 0xD5D69, 0x866B8, 0x83508, 0x7BAEC, 0xD03FD, 0xDA122
|
||||
};
|
||||
|
||||
unsigned long MakeStrHash(const char *str);
|
||||
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
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 "worldmap.h"
|
||||
|
||||
CMap::CMap( )
|
||||
{
|
||||
id = 0;
|
||||
dayperiod = 0;
|
||||
morningtime = 0;
|
||||
daytime = 0;
|
||||
eveningtime = 0;
|
||||
nighttime = 0;
|
||||
allowpvp = 0;
|
||||
allowpat = 0;
|
||||
MapTime = 0;
|
||||
LastUpdate = clock( );
|
||||
CurrentTime = 0;
|
||||
RespawnList.clear();
|
||||
MonsterSpawnList.clear();
|
||||
//if(GServer->Config.SpawnType == 0)
|
||||
MobGroupList.clear();
|
||||
MonsterList.clear();
|
||||
DropsList.clear();
|
||||
PlayerList.clear();
|
||||
NPCList.clear();
|
||||
TeleGateList.clear();
|
||||
MonsterMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
DropMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
}
|
||||
|
||||
CMap::~CMap( )
|
||||
{
|
||||
for(UINT i=0;i<RespawnList.size();i++)
|
||||
delete RespawnList.at(i);
|
||||
if(GServer->Config.SpawnType == 0)
|
||||
{
|
||||
for(UINT i = 0; i < MobGroupList.size(); i++)
|
||||
{
|
||||
CMobGroup *thisgroup = MobGroupList.at(i);
|
||||
for (UINT j = 0; j < thisgroup->basicMobs.size(); j++)
|
||||
delete thisgroup->basicMobs.at(j);
|
||||
for (UINT j = 0; j < thisgroup->tacMobs.size(); j++)
|
||||
delete thisgroup->tacMobs.at(j);
|
||||
delete thisgroup;
|
||||
}
|
||||
}
|
||||
for(UINT i=0;i<MonsterSpawnList.size();i++)
|
||||
delete MonsterSpawnList.at(i);
|
||||
for(UINT i=0;i<MonsterList.size();i++)
|
||||
delete MonsterList.at(i);
|
||||
for(UINT i=0;i<DropsList.size();i++)
|
||||
delete DropsList.at(i);
|
||||
for(UINT i=0;i<NPCList.size();i++)
|
||||
delete NPCList.at(i);
|
||||
for(UINT i=0;i<TeleGateList.size();i++)
|
||||
delete TeleGateList.at(i);
|
||||
}
|
||||
|
||||
bool CMap::AddPlayer( CPlayer* player )
|
||||
{
|
||||
PlayerList.push_back( player );
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CMap::RemovePlayer( CPlayer* player, bool clearobject )
|
||||
{
|
||||
GServer->ClearClientID( player->clientid );
|
||||
if(clearobject)
|
||||
{
|
||||
BEGINPACKET( pak, 0x794 );
|
||||
ADDWORD ( pak, player->clientid );
|
||||
GServer->SendToVisible( &pak, player, false );
|
||||
}
|
||||
for(UINT i=0;i<PlayerList.size();i++)
|
||||
{
|
||||
if(PlayerList.at(i)==player)
|
||||
{
|
||||
PlayerList.erase( PlayerList.begin()+i );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// add a new monster to this map
|
||||
CMonster* CMap::AddMonster( UINT montype, fPoint position, UINT owner, UINT spawnid , UINT aggro, bool IsTD, UINT limit, UINT AI)
|
||||
{
|
||||
//if(AI == 999) Log( MSG_WARNING, "AddMonster Spawning a TD mob" );
|
||||
// check if is a valid monster
|
||||
CNPCData* thisnpc = GServer->GetNPCDataByID( montype );
|
||||
if(thisnpc == NULL)
|
||||
{
|
||||
Log( MSG_WARNING, "Add Monster:: Invalid montype %u", montype );
|
||||
return NULL;
|
||||
}
|
||||
//if(AI == 999) Log( MSG_WARNING, "AddMonster Cast CNPCData " );
|
||||
CMonster* monster = new (nothrow) CMonster( position, montype, this->id, owner, spawnid );
|
||||
if(monster == NULL)
|
||||
{
|
||||
Log( MSG_WARNING, "Add Monster %u:: Error allocing memory",montype);
|
||||
return NULL;
|
||||
}
|
||||
//if(AI == 999) Log( MSG_WARNING, "AddMonster Cast CMonster " );
|
||||
monster->thisnpc = thisnpc;
|
||||
monster->Stats->MaxHP = monster->GetMaxHP( );
|
||||
monster->Stats->HP = monster->Stats->MaxHP;
|
||||
monster->Stats->MaxMP = monster->GetMaxMP( );
|
||||
monster->Stats->MP = monster->Stats->MaxMP;
|
||||
monster->thisnpc->_EntityType = ENTITY_MONSTER;
|
||||
monster->Status->Stance = 0; // WTF is Status->Stance even used for????
|
||||
monster->Stats->stance = 0; //mWalking
|
||||
monster->Status->spawnid = spawnid; //this way we can easily find which spawn a mob belongs to
|
||||
monster->SetStats( );
|
||||
//if(AI == 999) Log( MSG_WARNING, "AddMonster Stats set " );
|
||||
if(AI != 0 && monster->monAI != 999)
|
||||
{
|
||||
//Set monster's AI to the defined value
|
||||
//if(AI == 999) Log( MSG_WARNING, "AddMonster Setting monAI " );
|
||||
monster->monAI = AI;
|
||||
CAip* script = NULL;
|
||||
//if(AI == 999) Log( MSG_WARNING, "AddMonster Created CAip script " );
|
||||
if(monster->monAI == 999) AI = 30; //should create an AIP script that does nothing
|
||||
for(unsigned j=0; j < GServer->AipList.size(); j++)
|
||||
{
|
||||
if (GServer->AipList.at(j)->AInumber == AI)
|
||||
{
|
||||
script = GServer->AipList.at(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//if(monster->monAI == 999) Log( MSG_WARNING, "AddMonster finished scanning the AIPList " );
|
||||
if(script == NULL && monster->monAI != 999)
|
||||
{
|
||||
Log( MSG_WARNING, "Invalid AI script for monster type %i using AI %i", montype,monster->monAI );
|
||||
monster->AItimer = 4000;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Log( MSG_WARNING, "Setting timer for monster type %i using AI %i", montype,monster->monAI );
|
||||
if(monster->monAI == 999)
|
||||
{
|
||||
monster->AItimer = 4000;
|
||||
}
|
||||
else
|
||||
{
|
||||
monster->AItimer = script->minTime * 1000; //set AI timer value for this monster
|
||||
}
|
||||
//Log( MSG_WARNING, "Set the timer from the script" );
|
||||
}
|
||||
if (monster->AItimer == 0)
|
||||
monster->AItimer = 4000;
|
||||
//Log( MSG_WARNING, "AddMonster end of main clause " );
|
||||
}
|
||||
else
|
||||
{
|
||||
//Set monster's AI to the default value for this monster type
|
||||
monster->monAI = monster->thisnpc->AI;
|
||||
CAip* script = NULL;
|
||||
for(unsigned j=0; j < GServer->AipList.size(); j++)
|
||||
{
|
||||
if (GServer->AipList.at(j)->AInumber == monster->monAI)
|
||||
{
|
||||
script = GServer->AipList.at(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(script == NULL && monster->monAI != 999) //don't give error messag eif it's 999
|
||||
{
|
||||
//Log( MSG_WARNING, "Invalid AI script for monster type %i using AI %i", montype,AI );
|
||||
monster->AItimer = 4000;
|
||||
}
|
||||
else
|
||||
{
|
||||
monster->AItimer = script->minTime * 1000; //set AI timer value for this monster
|
||||
}
|
||||
if (monster->AItimer == 0)
|
||||
monster->AItimer = 4000;
|
||||
//if(AI == 999) Log( MSG_WARNING, "AddMonster end of ELSE clause " );
|
||||
}
|
||||
if(IsTD)
|
||||
{
|
||||
|
||||
//monster->thisnpc->AI = 331;
|
||||
monster->monAI = 999;
|
||||
monster->thisnpc->aggresive = 0;
|
||||
monster->NextWayPoint = 1;
|
||||
monster->Position->destiny = monster->Position->current;
|
||||
monster->AItimer = 1000; //shorter testing time because it doesn't run an AIP
|
||||
}
|
||||
|
||||
if(limit == 0)
|
||||
{
|
||||
monster->Status->dayonly = false;
|
||||
monster->Status->nightonly = false;
|
||||
}
|
||||
if(limit == 1)
|
||||
{
|
||||
monster->Status->dayonly = true;
|
||||
monster->Status->nightonly = false;
|
||||
}
|
||||
if(limit == 2)
|
||||
{
|
||||
monster->Status->dayonly = false;
|
||||
monster->Status->nightonly = true;
|
||||
}
|
||||
if(aggro != 1)
|
||||
monster->thisnpc->aggresive = aggro;
|
||||
monster->Stats->HP = monster->Stats->MaxHP;
|
||||
/* BEGINPACKET( pak, 0x799 );
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDWORD ( pak, 0x8005 );
|
||||
GServer->SendToVisible( &pak, monster);*/
|
||||
MonsterList.push_back( monster );
|
||||
if(AI == 999) Log( MSG_WARNING, "Added to monster list " );
|
||||
if(spawnid != 0)
|
||||
{
|
||||
if(GServer->Config.SpawnType == 1)
|
||||
{
|
||||
CSpawnArea* thisspawn = GServer->GetSpawnArea( spawnid, this->id );
|
||||
if(thisspawn!=NULL)
|
||||
thisspawn->amon++;
|
||||
}
|
||||
if(GServer->Config.SpawnType == 0)
|
||||
{
|
||||
CMobGroup* thisgroup = GServer->GetMobGroup( spawnid, this->id );
|
||||
if(thisgroup != NULL)
|
||||
thisgroup->active++;
|
||||
}
|
||||
}
|
||||
monster->activecycle = GServer->RandNumber(1, 10);
|
||||
monster->SpawnTime = clock( );
|
||||
monster->lastAiUpdate = clock();
|
||||
monster->hitcount = 0xFF;
|
||||
//monster->OnSpawn( false );
|
||||
monster->hitcount = 0;
|
||||
return monster;
|
||||
}
|
||||
|
||||
|
||||
// Delete a monster
|
||||
bool CMap::DeleteMonster( CMonster* monster, bool clearobject, UINT i )
|
||||
{
|
||||
if(monster == NULL) return false;
|
||||
GServer->ClearClientID( monster->clientid );
|
||||
if(monster->Position->respawn != 0)
|
||||
{
|
||||
if(GServer->Config.SpawnType == 1)
|
||||
{
|
||||
CSpawnArea* thisspawn = GServer->GetSpawnArea( monster->Position->respawn, monster->Position->Map );
|
||||
if(thisspawn != NULL)
|
||||
{
|
||||
if(thisspawn->amon >= thisspawn->max)// reset spawn timer if the spawn is full
|
||||
thisspawn->lastRespawnTime = clock();
|
||||
thisspawn->amon--;
|
||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 2);
|
||||
}
|
||||
}
|
||||
if(GServer->Config.SpawnType == 0)
|
||||
{
|
||||
CMobGroup* thisgroup = GServer->GetMobGroup( monster->Position->respawn, monster->Position->Map );
|
||||
if(thisgroup!=NULL)
|
||||
{
|
||||
if(thisgroup->active >= thisgroup->limit)// reset spawn timer if the spawn is full
|
||||
thisgroup->lastRespawnTime = clock();
|
||||
thisgroup->active--;
|
||||
thisgroup->basicKills++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(clearobject) // is this for clearing Summons?
|
||||
{
|
||||
//Log( MSG_INFO, "Deleting monster %i (clearobject)",monster->montype);
|
||||
BEGINPACKET( pak, 0x799 );
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDWORD ( pak, monster->clientid );
|
||||
ADDWORD ( pak, 0x8005 );
|
||||
//ADDDWORD ( pak, monster->Stats->HP );
|
||||
//ADDDWORD ( pak, 16 );
|
||||
GServer->SendToVisible( &pak, monster );
|
||||
}
|
||||
if(i != 0)
|
||||
{
|
||||
//Log( MSG_INFO, "Deleting monster %i from monster list. montype: %i",i,monster->montype);
|
||||
MonsterList.erase( MonsterList.begin()+i );
|
||||
//Log( MSG_INFO, "erased from list");
|
||||
delete monster;
|
||||
//Log( MSG_INFO, "deleted");
|
||||
return true;
|
||||
}
|
||||
for(UINT j=0;j<MonsterList.size();j++)
|
||||
{
|
||||
|
||||
CMonster* othermon = MonsterList.at(j);
|
||||
if(othermon == monster)
|
||||
{
|
||||
//Log( MSG_INFO, "Deleting monster %i from monster list. No specified position",monster->montype);
|
||||
MonsterList.erase( MonsterList.begin() + j );
|
||||
delete monster;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//Log( MSG_INFO, "Deleting monster %i from monster list anyway",monster->montype);
|
||||
delete monster;
|
||||
return false;
|
||||
}
|
||||
|
||||
//add a new drop
|
||||
bool CMap::AddDrop( CDrop* drop )
|
||||
{
|
||||
drop->droptime = time(NULL);
|
||||
DropsList.push_back( drop );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMap::DeleteDrop( CDrop* drop )
|
||||
{
|
||||
BEGINPACKET( pak, 0x794 );
|
||||
ADDWORD ( pak, drop->clientid );
|
||||
GServer->SendToVisible( &pak, drop );
|
||||
GServer->ClearClientID( drop->clientid );
|
||||
for(UINT j=0;j<DropsList.size();j++)
|
||||
{
|
||||
if(drop==DropsList.at(j))
|
||||
{
|
||||
DropsList.erase( DropsList.begin()+j );
|
||||
delete drop;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
delete drop;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CMap::AddNPC( CNPC* npc )
|
||||
{
|
||||
NPCList.push_back( npc );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMap::DeleteNPC( CNPC* npc )
|
||||
{
|
||||
BEGINPACKET( pak, 0x794 );
|
||||
ADDWORD ( pak, npc->clientid );
|
||||
GServer->SendToMap( &pak, npc->posMap );
|
||||
GServer->ClearClientID( npc->clientid );
|
||||
Log(MSG_INFO,"Sent message to players in map to delete NPC");
|
||||
for(UINT j=0;j<NPCList.size();j++)
|
||||
{
|
||||
if(npc == NPCList.at(j))
|
||||
{
|
||||
NPCList.erase( NPCList.begin()+j );
|
||||
delete npc;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
delete npc;
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#ifndef __ROSE_MAP__
|
||||
#define __ROSE_MAP__
|
||||
#include "worldserver.h"
|
||||
#include "player.h"
|
||||
#include "character.h"
|
||||
#include "worldmonster.h"
|
||||
#include "datatypes.h"
|
||||
class CMonster;
|
||||
|
||||
class CMap
|
||||
{
|
||||
public:
|
||||
CMap( );
|
||||
~CMap( );
|
||||
|
||||
bool AddPlayer( CPlayer* player );
|
||||
bool RemovePlayer( CPlayer* player, bool clearobject=true );
|
||||
|
||||
//CMonster* AddMonster( UINT montype, fPoint position, UINT owner=0, UINT spawnid=0, bool GetDropData=false );
|
||||
CMonster* AddMonster( UINT montype, fPoint position, UINT owner=0, UINT spawnid=0, UINT aggro = 0, bool IsTD = false, UINT limit = 0, UINT AI = 0);
|
||||
bool DeleteMonster( CMonster*, bool clearobject=false, UINT i=0 );
|
||||
bool AddDrop( CDrop* drop );
|
||||
bool DeleteDrop( CDrop* drop );
|
||||
bool AddNPC( CNPC* npc );
|
||||
bool DeleteNPC( CNPC* npc );
|
||||
|
||||
CMonster* ConverToMonster( CMonster* monster, UINT newmonttype, bool heal=true );
|
||||
CMonster* ConverToMonster( CNPC* npc, UINT newmonttype );
|
||||
|
||||
bool TeleportPlayer( CPlayer* player, fPoint coord, bool TelePassenger=true );
|
||||
bool pakTeleport( CPlayer* player, unsigned int Map, fPoint coord, bool TelePassenger );
|
||||
|
||||
CRespawnPoint* GetNearRespawn( CPlayer* player );
|
||||
CRespawnPoint* GetFirstRespawn( );
|
||||
CRespawnPoint* GetStartRespawn( );
|
||||
|
||||
CCharacter* GetCharInMap( unsigned int id );
|
||||
CPlayer* GetPlayerInMap( UINT id );
|
||||
CMonster* GetMonsterInMap( UINT id );
|
||||
CPlayer* GetCharIDInMap( UINT id );
|
||||
CDrop* GetDropInMap( UINT id );
|
||||
CNPC* GetNPCInMap( UINT id );
|
||||
void UpdateTime( );
|
||||
void CleanDrops( );
|
||||
void RespawnMonster( );
|
||||
void UpdateSpawnCounter( UINT montype, UINT spawnid, UINT key );
|
||||
bool IsNight( );
|
||||
|
||||
void LoadZon(strings zon);
|
||||
void LoadData(strings zonpath, CStrStb* stbEvent);
|
||||
void AddIfo(strings ifo, CStrStb* stbEvent);
|
||||
|
||||
UINT id; // id from map
|
||||
UINT dayperiod; // duration of 1 day
|
||||
UINT morningtime; // morning start time
|
||||
UINT daytime; // day start time
|
||||
UINT eveningtime; // evening start time
|
||||
UINT nighttime; // nighttime start time
|
||||
UINT ZoneTime; // how many 6 minute segments since start of the day
|
||||
UINT ghost; // ghosttime?
|
||||
BYTE allowpvp; // pvp allowed?
|
||||
bool dungeon; // Map is a dungeon?
|
||||
bool allowpat; // pat allowed?
|
||||
// Time values
|
||||
UINT MapTime; // Past time in this map
|
||||
UINT Date;
|
||||
UINT hour;
|
||||
UINT minute;
|
||||
UINT ObjVar[20]; //for storing map variables used in AIP
|
||||
clock_t LastUpdate;// Last time update in the map
|
||||
UINT CurrentTime; // 0 = morning | 1 = day | 2 = evening | 3 = night | 4+ = invalid
|
||||
//TDMobList
|
||||
UINT TDMobList[500]; //List of TD monsters waiting to be spawned
|
||||
UINT TDNextSpawn; //index pointer for next spawn in TDMobList
|
||||
UINT TDNextMon; //index pointer for next addition to TDMobList
|
||||
clock_t lastTDSpawnTime; //time of the last spawn
|
||||
UINT TDMobDelay; // delay between spawns (ms)
|
||||
UINT crystalhealth; // current health of the base crystal
|
||||
UINT portalhealth; // current health of the spawn portal
|
||||
UINT mapXPRate; // exp rate for this map
|
||||
|
||||
|
||||
|
||||
vector<CRespawnPoint*> RespawnList; // RespawnZones List
|
||||
vector<CSpawnArea*> MonsterSpawnList; // Monster spawn in this map
|
||||
#ifdef USEIFO
|
||||
vector<CMobGroup*> MobGroupList; // Spawn "Zones"
|
||||
#endif
|
||||
vector<CMonster*> MonsterList; // Monster List in this map
|
||||
vector<CDrop*> DropsList; // Droped Items
|
||||
vector<CPlayer*> PlayerList; // Client list in this map;
|
||||
vector<CNPC*> NPCList; // NPC in this map
|
||||
vector<CTeleGate*> TeleGateList; // Telegates from this map
|
||||
std::vector<SZonPoint*> PointList;
|
||||
|
||||
pthread_mutex_t MonsterMutex, DropMutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#ifndef __ROSE_MAP__
|
||||
#define __ROSE_MAP__
|
||||
#include "worldserver.h"
|
||||
#include "player.h"
|
||||
#include "character.h"
|
||||
#include "worldmonster.h"
|
||||
#include "datatypes.h"
|
||||
class CMonster;
|
||||
|
||||
class CMap
|
||||
{
|
||||
public:
|
||||
CMap( );
|
||||
~CMap( );
|
||||
|
||||
bool AddPlayer( CPlayer* player );
|
||||
bool RemovePlayer( CPlayer* player, bool clearobject=true );
|
||||
|
||||
//CMonster* AddMonster( UINT montype, fPoint position, UINT owner=0, UINT spawnid=0, bool GetDropData=false );
|
||||
CMonster* AddMonster( UINT montype, fPoint position, UINT owner=0, UINT spawnid=0, UINT aggro = 0, bool IsTD = false, UINT limit = 0, UINT AI = 0);
|
||||
bool DeleteMonster( CMonster*, bool clearobject=false, UINT i=0 );
|
||||
bool AddDrop( CDrop* drop );
|
||||
bool DeleteDrop( CDrop* drop );
|
||||
bool AddNPC( CNPC* npc );
|
||||
bool DeleteNPC( CNPC* npc );
|
||||
|
||||
CMonster* ConverToMonster( CMonster* monster, UINT newmonttype, bool heal=true );
|
||||
CMonster* ConverToMonster( CNPC* npc, UINT newmonttype );
|
||||
|
||||
bool TeleportPlayer( CPlayer* player, fPoint coord, bool TelePassenger=true );
|
||||
bool pakTeleport( CPlayer* player, unsigned int Map, fPoint coord, bool TelePassenger );
|
||||
|
||||
CRespawnPoint* GetNearRespawn( CPlayer* player );
|
||||
CRespawnPoint* GetFirstRespawn( );
|
||||
CRespawnPoint* GetStartRespawn( );
|
||||
|
||||
CCharacter* GetCharInMap( unsigned int id );
|
||||
CPlayer* GetPlayerInMap( UINT id );
|
||||
CMonster* GetMonsterInMap( UINT id );
|
||||
CPlayer* GetCharIDInMap( UINT id );
|
||||
CDrop* GetDropInMap( UINT id );
|
||||
CNPC* GetNPCInMap( UINT id );
|
||||
void UpdateTime( );
|
||||
void CleanDrops( );
|
||||
void RespawnMonster( );
|
||||
void UpdateSpawnCounter( UINT montype, UINT spawnid, UINT key );
|
||||
bool IsNight( );
|
||||
|
||||
void LoadZon(strings zon);
|
||||
void LoadData(strings zonpath, CStrStb* stbEvent);
|
||||
void AddIfo(strings ifo, CStrStb* stbEvent);
|
||||
|
||||
UINT id; // id from map
|
||||
UINT dayperiod; // duration of 1 day
|
||||
UINT morningtime; // morning start time
|
||||
UINT daytime; // day start time
|
||||
UINT eveningtime; // evening start time
|
||||
UINT nighttime; // nighttime start time
|
||||
UINT ZoneTime; // how many 6 minute segments since start of the day
|
||||
UINT ghost; // ghosttime?
|
||||
BYTE allowpvp; // pvp allowed?
|
||||
bool dungeon; // Map is a dungeon?
|
||||
bool allowpat; // pat allowed?
|
||||
// Time values
|
||||
UINT MapTime; // Past time in this map
|
||||
UINT Date;
|
||||
UINT hour;
|
||||
UINT minute;
|
||||
UINT ObjVar[20]; //for storing map variables used in AIP
|
||||
clock_t LastUpdate;// Last time update in the map
|
||||
UINT CurrentTime; // 0 = morning | 1 = day | 2 = evening | 3 = night | 4+ = invalid
|
||||
//TDMobList
|
||||
UINT TDMobList[100]; //List of TD monsters waiting to be spawned
|
||||
UINT TDNextSpawn; //index pointer for next spawn in TDMobList
|
||||
UINT TDNextMon; //index pointer for next addition to TDMobList
|
||||
clock_t lastTDSpawnTime; //time of the last spawn
|
||||
UINT TDMobDelay; // delay between spawns (ms)
|
||||
UINT crystalhealth; // current health of the base crystal
|
||||
UINT portalhealth; // current health of the spawn portal
|
||||
UINT mapXPRate; // exp rate for this map
|
||||
|
||||
|
||||
|
||||
vector<CRespawnPoint*> RespawnList; // RespawnZones List
|
||||
vector<CSpawnArea*> MonsterSpawnList; // Monster spawn in this map
|
||||
#ifdef USEIFO
|
||||
vector<CMobGroup*> MobGroupList; // Spawn "Zones"
|
||||
#endif
|
||||
vector<CMonster*> MonsterList; // Monster List in this map
|
||||
vector<CDrop*> DropsList; // Droped Items
|
||||
vector<CPlayer*> PlayerList; // Client list in this map;
|
||||
vector<CNPC*> NPCList; // NPC in this map
|
||||
vector<CTeleGate*> TeleGateList; // Telegates from this map
|
||||
std::vector<SZonPoint*> PointList;
|
||||
|
||||
pthread_mutex_t MonsterMutex, DropMutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user