added source folders

This commit is contained in:
purpleyouko committed 2025-08-11 11:09:09 -05:00
1 parent f23521f720
commit cfdc19df45
145 files changed
+54574

No files matched your search

+946
View File
@@ -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);
}
+847
View File
@@ -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;
}
+570
View File
@@ -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;
}
+478
View File
@@ -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);
}
+195
View File
@@ -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 );
}
+21
View File
@@ -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"
+435
View File
@@ -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;
}
+195
View File
@@ -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;
}
+375
View File
@@ -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;
}
+296
View File
@@ -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;
}
+229
View File
@@ -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;
}
+200
View File
@@ -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;
};
+61
View File
@@ -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"
+898
View File
@@ -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();
}
}
}
}
+113
View File
@@ -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);
}
}
+53
View File
@@ -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 );
}
+70
View File
@@ -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();
}
+110
View File
@@ -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
+458
View File
@@ -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
+165
View File
@@ -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
+247
View File
@@ -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
+266
View File
@@ -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
};
+167
View File
@@ -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;
}
+29
View File
@@ -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"
+975
View File
@@ -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;
}
+802
View File
@@ -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;
}
+205
View File
@@ -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();
}
+118
View File
@@ -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
+63
View File
@@ -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;
}
+606
View File
@@ -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;
}
+111
View File
@@ -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;
}
+303
View File
@@ -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
+257
View File
@@ -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
+45
View File
@@ -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;
}
+168
View File
@@ -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;
}
+28
View File
@@ -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);
+81
View File
@@ -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;
}
// -----------------------------------------------------------------------------------------
+550
View File
@@ -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;
}
+151
View File
@@ -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 );
}
+52
View File
@@ -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
+120
View File
@@ -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;
}
+519
View File
@@ -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;
}
}
}
+170
View File
@@ -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
+2
View File
@@ -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
+125
View File
@@ -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;
}*/
+55
View File
@@ -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;
}
}
};
+120
View File
@@ -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);
}
};
+941
View File
@@ -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);
}
};
+82
View File
@@ -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
+242
View File
@@ -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
{
};
+771
View File
@@ -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;
}
+239
View File
@@ -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;
};
+98
View File
@@ -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"
+157
View File
@@ -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;
}
+17
View File
@@ -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;
}
+20
View File
@@ -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);
+196
View File
@@ -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
+48
View File
@@ -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;
//}
+53
View File
@@ -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;
}
+11
View File
@@ -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 );
+137
View File
@@ -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;
}
+42
View File
@@ -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;
};
+19
View File
@@ -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;
}
+20
View File
@@ -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);
+379
View File
@@ -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;
}
+119
View File
@@ -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
+119
View File
@@ -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