2019-07-27 21:54:05 -04:00
/ *
2019-06-19 01:10:15 -04:00
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Copyright ( C ) 2015 - 2019 Project Meteor Dev Team
This file is part of Project Meteor Server .
Project Meteor Server is free software : you can redistribute it and / or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Project Meteor Server 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 Affero General Public License for more details .
You should have received a copy of the GNU Affero General Public License
along with Project Meteor Server . If not , see < https : www . gnu . org / licenses / > .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* /
using Meteor.Common ;
using System ;
using System.Collections.Generic ;
using MoonSharp.Interpreter ;
2022-02-16 15:30:32 -05:00
using Meteor.Map.DataObjects ;
using Meteor.Map.DataObjects.chara ;
2019-06-19 01:10:15 -04:00
using Meteor.Map.lua ;
using Meteor.Map.packets.WorldPackets.Send.Group ;
using Meteor.Map.utils ;
using Meteor.Map.actors.group ;
using Meteor.Map.actors.chara.player ;
using Meteor.Map.actors.director ;
using Meteor.Map.actors.chara.npc ;
using Meteor.Map.actors.chara.ai ;
using Meteor.Map.actors.chara.ai.controllers ;
using Meteor.Map.actors.chara.ai.utils ;
using Meteor.Map.actors.chara.ai.state ;
using Meteor.Map.actors.chara ;
2022-02-17 13:22:18 -05:00
using Meteor.Map.Actors.QuestNS ;
2019-06-19 01:10:15 -04:00
using Meteor.Map.packets.send ;
using Meteor.Map.packets.send.actor ;
using Meteor.Map.packets.send.events ;
using Meteor.Map.packets.send.actor.inventory ;
using Meteor.Map.packets.send.player ;
using Meteor.Map.packets.send.actor.battle ;
using Meteor.Map.packets.receive.events ;
using static Meteor . Map . LuaUtils ;
2020-02-28 00:05:46 -05:00
using Meteor.Map.packets.send.actor.events ;
2022-02-19 01:17:50 -05:00
using System.Text ;
2019-06-19 01:10:15 -04:00
namespace Meteor.Map.Actors
{
class Player : Character
{
public const int TIMER_TOTORAK = 0 ;
2019-07-27 21:54:05 -04:00
public const int TIMER_DZEMAEL = 1 ;
2019-06-19 01:10:15 -04:00
public const int TIMER_BOWL_OF_EMBERS_HARD = 2 ;
public const int TIMER_BOWL_OF_EMBERS = 3 ;
public const int TIMER_THORNMARCH = 4 ;
public const int TIMER_AURUMVALE = 5 ;
public const int TIMER_CUTTERSCRY = 6 ;
public const int TIMER_BATTLE_ALEPORT = 7 ;
public const int TIMER_BATTLE_HYRSTMILL = 8 ;
public const int TIMER_BATTLE_GOLDENBAZAAR = 9 ;
public const int TIMER_HOWLING_EYE_HARD = 10 ;
public const int TIMER_HOWLING_EYE = 11 ;
public const int TIMER_CASTRUM_TOWER = 12 ;
public const int TIMER_BOWL_OF_EMBERS_EXTREME = 13 ;
public const int TIMER_RIVENROAD = 14 ;
public const int TIMER_RIVENROAD_HARD = 15 ;
public const int TIMER_BEHEST = 16 ;
public const int TIMER_COMPANYBEHEST = 17 ;
public const int TIMER_RETURN = 18 ;
public const int TIMER_SKIRMISH = 19 ;
2019-07-27 21:54:05 -04:00
public const int NPCLS_GONE = 0 ;
2019-06-19 01:10:15 -04:00
public const int NPCLS_INACTIVE = 1 ;
2019-07-27 21:54:05 -04:00
public const int NPCLS_ACTIVE = 2 ;
public const int NPCLS_ALERT = 3 ;
2019-06-19 01:10:15 -04:00
public const int SLOT_MAINHAND = 0 ;
public const int SLOT_OFFHAND = 1 ;
public const int SLOT_THROWINGWEAPON = 4 ;
public const int SLOT_PACK = 5 ;
public const int SLOT_POUCH = 6 ;
public const int SLOT_HEAD = 8 ;
public const int SLOT_UNDERSHIRT = 9 ;
public const int SLOT_BODY = 10 ;
public const int SLOT_UNDERGARMENT = 11 ;
public const int SLOT_LEGS = 12 ;
public const int SLOT_HANDS = 13 ;
public const int SLOT_BOOTS = 14 ;
public const int SLOT_WAIST = 15 ;
public const int SLOT_NECK = 16 ;
public const int SLOT_EARS = 17 ;
public const int SLOT_WRISTS = 19 ;
public const int SLOT_RIGHTFINGER = 21 ;
public const int SLOT_LEFTFINGER = 22 ;
public static int [ ] MAXEXP = { 570 , 700 , 880 , 1100 , 1500 , 1800 , 2300 , 3200 , 4300 , 5000 , //Level <= 10
5900 , 6800 , 7700 , 8700 , 9700 , 11000 , 12000 , 13000 , 15000 , 16000 , //Level <= 20
20000 , 22000 , 23000 , 25000 , 27000 , 29000 , 31000 , 33000 , 35000 , 38000 , //Level <= 30
45000 , 47000 , 50000 , 53000 , 56000 , 59000 , 62000 , 65000 , 68000 , 71000 , //Level <= 40
74000 , 78000 , 81000 , 85000 , 89000 , 92000 , 96000 , 100000 , 100000 , 110000 } ; //Level <= 50
//Event Related
public uint currentEventOwner = 0 ;
public string currentEventName = "" ;
2019-07-26 23:24:06 -04:00
public byte currentEventType = 0 ;
2019-06-19 01:10:15 -04:00
public Coroutine currentEventRunning ;
//Player Info
public uint destinationZone ;
public ushort destinationSpawnType ;
public uint [ ] timers = new uint [ 20 ] ;
public uint currentTitle ;
public uint playTime ;
public uint lastPlayTimeUpdate ;
public bool isGM = false ;
public bool isZoneChanging = true ;
//Trading
private Player otherTrader = null ;
private ReferencedItemPackage myOfferings ;
private bool isTradeAccepted = false ;
//GC Related
public byte gcCurrent ;
public byte gcRankLimsa ;
public byte gcRankGridania ;
public byte gcRankUldah ;
//Mount Related
public bool hasChocobo ;
public bool hasGoobbue ;
public string chocoboName ;
2019-07-11 12:13:23 -04:00
public byte mountState = 0 ;
public byte chocoboAppearance ;
public uint rentalExpireTime = 0 ;
public byte rentalMinLeft = 0 ;
2019-06-19 01:10:15 -04:00
public uint achievementPoints ;
//Property Array Request Stuff
private int lastPosition = 0 ;
private int lastStep = 0 ;
//Quest Actors (MUST MATCH playerWork.questScenario/questGuildleve)
public Quest [ ] questScenario = new Quest [ 16 ] ;
public uint [ ] questGuildleve = new uint [ 8 ] ;
2022-02-16 15:30:32 -05:00
public QuestStateManager questStateManager ;
2019-06-19 01:10:15 -04:00
//Aetheryte
public uint homepoint = 0 ;
public byte homepointInn = 0 ;
//Nameplate Stuff
public uint currentLSPlate = 0 ;
public byte repairType = 0 ;
//Retainer
RetainerMeetingRelationGroup retainerMeetingGroup = null ;
public Retainer currentSpawnedRetainer = null ;
public bool sentRetainerSpawn = false ;
private List < Director > ownedDirectors = new List < Director > ( ) ;
private Director loginInitDirector = null ;
List < ushort > hotbarSlotsToUpdate = new List < ushort > ( ) ;
public PlayerWork playerWork = new PlayerWork ( ) ;
public Session playerSession ;
public Player ( Session cp , uint actorID ) : base ( actorID )
{
playerSession = cp ;
2022-02-05 17:48:49 -05:00
Name = String . Format ( "_pc{0:00000000}" , actorID ) ;
2019-06-19 01:10:15 -04:00
className = "Player" ;
moveSpeeds [ 0 ] = SetActorSpeedPacket . DEFAULT_STOP ;
moveSpeeds [ 1 ] = SetActorSpeedPacket . DEFAULT_WALK ;
moveSpeeds [ 2 ] = SetActorSpeedPacket . DEFAULT_RUN ;
moveSpeeds [ 3 ] = SetActorSpeedPacket . DEFAULT_ACTIVE ;
itemPackages [ ItemPackage . NORMAL ] = new ItemPackage ( this , ItemPackage . MAXSIZE_NORMAL , ItemPackage . NORMAL ) ;
itemPackages [ ItemPackage . KEYITEMS ] = new ItemPackage ( this , ItemPackage . MAXSIZE_KEYITEMS , ItemPackage . KEYITEMS ) ;
itemPackages [ ItemPackage . CURRENCY_CRYSTALS ] = new ItemPackage ( this , ItemPackage . MAXSIZE_CURRANCY , ItemPackage . CURRENCY_CRYSTALS ) ;
itemPackages [ ItemPackage . MELDREQUEST ] = new ItemPackage ( this , ItemPackage . MAXSIZE_MELDREQUEST , ItemPackage . MELDREQUEST ) ;
itemPackages [ ItemPackage . BAZAAR ] = new ItemPackage ( this , ItemPackage . MAXSIZE_BAZAAR , ItemPackage . BAZAAR ) ;
itemPackages [ ItemPackage . LOOT ] = new ItemPackage ( this , ItemPackage . MAXSIZE_LOOT , ItemPackage . LOOT ) ;
equipment = new ReferencedItemPackage ( this , ItemPackage . MAXSIZE_EQUIPMENT , ItemPackage . EQUIPMENT ) ;
//Set the Skill level caps of all FFXIV (classes)skills to 50
for ( int i = 0 ; i < charaWork . battleSave . skillLevelCap . Length ; i + + )
{
if ( i ! = CLASSID_PUG & &
i ! = CLASSID_MRD & &
i ! = CLASSID_GLA & &
i ! = CLASSID_MRD & &
i ! = CLASSID_ARC & &
i ! = CLASSID_LNC & &
i ! = CLASSID_THM & &
i ! = CLASSID_CNJ & &
i ! = CLASSID_CRP & &
i ! = CLASSID_BSM & &
i ! = CLASSID_ARM & &
i ! = CLASSID_GSM & &
i ! = CLASSID_LTW & &
i ! = CLASSID_WVR & &
i ! = CLASSID_ALC & &
i ! = CLASSID_CUL & &
i ! = CLASSID_MIN & &
i ! = CLASSID_BTN & &
i ! = CLASSID_FSH )
charaWork . battleSave . skillLevelCap [ i ] = 0xFF ;
else
charaWork . battleSave . skillLevelCap [ i ] = 50 ;
}
charaWork . property [ 0 ] = 1 ;
charaWork . property [ 1 ] = 1 ;
charaWork . property [ 2 ] = 1 ;
charaWork . property [ 4 ] = 1 ;
charaWork . command [ 0 ] = 0xA0F00000 | 21001 ;
charaWork . command [ 1 ] = 0xA0F00000 | 21001 ;
charaWork . command [ 2 ] = 0xA0F00000 | 21002 ;
charaWork . command [ 3 ] = 0xA0F00000 | 12004 ;
charaWork . command [ 4 ] = 0xA0F00000 | 21005 ;
charaWork . command [ 5 ] = 0xA0F00000 | 21006 ;
charaWork . command [ 6 ] = 0xA0F00000 | 21007 ;
charaWork . command [ 7 ] = 0xA0F00000 | 12009 ;
charaWork . command [ 8 ] = 0xA0F00000 | 12010 ;
charaWork . command [ 9 ] = 0xA0F00000 | 12005 ;
charaWork . command [ 10 ] = 0xA0F00000 | 12007 ;
charaWork . command [ 11 ] = 0xA0F00000 | 12011 ;
charaWork . command [ 12 ] = 0xA0F00000 | 22012 ;
charaWork . command [ 13 ] = 0xA0F00000 | 22013 ;
charaWork . command [ 14 ] = 0xA0F00000 | 29497 ;
charaWork . command [ 15 ] = 0xA0F00000 | 22015 ;
charaWork . commandAcquired [ 27150 - 26000 ] = true ;
2022-02-19 01:17:50 -05:00
2019-06-19 01:10:15 -04:00
playerWork . questGuildleveComplete [ 120050 - 120001 ] = true ;
for ( int i = 0 ; i < charaWork . additionalCommandAcquired . Length ; i + + )
charaWork . additionalCommandAcquired [ i ] = true ;
for ( int i = 0 ; i < charaWork . commandCategory . Length ; i + + )
charaWork . commandCategory [ i ] = 1 ;
charaWork . battleTemp . generalParameter [ 3 ] = 1 ;
charaWork . eventSave . bazaarTax = 5 ;
charaWork . battleSave . potencial = 6.6f ;
charaWork . battleSave . negotiationFlag [ 0 ] = true ;
charaWork . commandCategory [ 0 ] = 1 ;
charaWork . commandCategory [ 1 ] = 1 ;
charaWork . parameterSave . commandSlot_compatibility [ 0 ] = true ;
charaWork . parameterSave . commandSlot_compatibility [ 1 ] = true ;
charaWork . commandBorder = 0x20 ;
charaWork . parameterTemp . tp = 0 ;
Database . LoadPlayerCharacter ( this ) ;
lastPlayTimeUpdate = Utils . UnixTimeStampUTC ( ) ;
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
this . aiContainer = new AIContainer ( this , new PlayerController ( this ) , null , new TargetFind ( this ) ) ;
allegiance = CharacterTargetingAllegiance . Player ;
CalculateBaseStats ( ) ;
2022-02-16 15:30:32 -05:00
questStateManager = new QuestStateManager ( this ) ;
2022-02-19 01:17:50 -05:00
questStateManager . Init ( questScenario , playerWork . questScenarioComplete ) ;
2019-06-19 01:10:15 -04:00
}
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
public List < SubPacket > Create0x132Packets ( )
{
List < SubPacket > packets = new List < SubPacket > ( ) ;
2022-02-05 17:48:49 -05:00
packets . Add ( _0x132Packet . BuildPacket ( Id , 0xB , "commandForced" ) ) ;
packets . Add ( _0x132Packet . BuildPacket ( Id , 0xA , "commandDefault" ) ) ;
packets . Add ( _0x132Packet . BuildPacket ( Id , 0x6 , "commandWeak" ) ) ;
packets . Add ( _0x132Packet . BuildPacket ( Id , 0x4 , "commandContent" ) ) ;
packets . Add ( _0x132Packet . BuildPacket ( Id , 0x6 , "commandJudgeMode" ) ) ;
packets . Add ( _0x132Packet . BuildPacket ( Id , 0x100 , "commandRequest" ) ) ;
packets . Add ( _0x132Packet . BuildPacket ( Id , 0x100 , "widgetCreate" ) ) ;
packets . Add ( _0x132Packet . BuildPacket ( Id , 0x100 , "macroRequest" ) ) ;
2019-06-19 01:10:15 -04:00
return packets ;
}
/ *
* PLAYER ARGS :
* Unknown - Bool
* Unknown - Bool
* Is Init Director - Bool
* Unknown - Bool
* Unknown - Number
* Unknown - Bool
* Timer Array - 20 Number
* /
public override SubPacket CreateScriptBindPacket ( Player requestPlayer )
{
List < LuaParam > lParams ;
2022-02-05 17:48:49 -05:00
if ( IsMyPlayer ( requestPlayer . Id ) )
2019-06-19 01:10:15 -04:00
{
if ( loginInitDirector ! = null )
lParams = LuaUtils . CreateLuaParamList ( "/Chara/Player/Player_work" , false , false , true , loginInitDirector , true , 0 , false , timers , true ) ;
else
lParams = LuaUtils . CreateLuaParamList ( "/Chara/Player/Player_work" , true , false , false , true , 0 , false , timers , true ) ;
}
else
lParams = LuaUtils . CreateLuaParamList ( "/Chara/Player/Player_work" , false , false , false , false , false , true ) ;
2022-02-05 17:48:49 -05:00
ActorInstantiatePacket . BuildPacket ( Id , Name , className , lParams ) . DebugPrintSubPacket ( ) ;
2019-06-19 01:10:15 -04:00
2022-02-05 17:48:49 -05:00
return ActorInstantiatePacket . BuildPacket ( Id , Name , className , lParams ) ;
2019-06-19 01:10:15 -04:00
}
public override List < SubPacket > GetSpawnPackets ( Player requestPlayer , ushort spawnType )
{
List < SubPacket > subpackets = new List < SubPacket > ( ) ;
subpackets . Add ( CreateAddActorPacket ( 8 ) ) ;
2022-02-05 17:48:49 -05:00
if ( IsMyPlayer ( requestPlayer . Id ) )
2019-06-19 01:10:15 -04:00
subpackets . AddRange ( Create0x132Packets ( ) ) ;
subpackets . Add ( CreateSpeedPacket ( ) ) ;
subpackets . Add ( CreateSpawnPositonPacket ( this , spawnType ) ) ;
subpackets . Add ( CreateAppearancePacket ( ) ) ;
subpackets . Add ( CreateNamePacket ( ) ) ;
2022-02-05 17:48:49 -05:00
subpackets . Add ( _0xFPacket . BuildPacket ( Id ) ) ;
2019-06-19 01:10:15 -04:00
subpackets . Add ( CreateStatePacket ( ) ) ;
subpackets . Add ( CreateSubStatePacket ( ) ) ;
subpackets . Add ( CreateInitStatusPacket ( ) ) ;
subpackets . Add ( CreateSetActorIconPacket ( ) ) ;
subpackets . Add ( CreateIsZoneingPacket ( ) ) ;
2022-02-05 17:48:49 -05:00
subpackets . AddRange ( CreatePlayerRelatedPackets ( requestPlayer . Id ) ) ;
2019-07-27 21:54:05 -04:00
subpackets . Add ( CreateScriptBindPacket ( requestPlayer ) ) ;
2019-06-19 01:10:15 -04:00
return subpackets ;
}
2022-01-25 22:55:39 -05:00
public new SubPacket CreateNamePacket ( )
{
bool isMale = true ;
switch ( playerWork . tribe )
{
case 2 :
case 5 :
case 7 :
case 9 :
case 11 :
case 12 :
case 13 :
isMale = false ;
break ;
}
2022-02-05 17:48:49 -05:00
return SetActorNamePacket . BuildPacket ( Id , DisplayName ! = null ? 0 : LocalizedDisplayName , LocalizedDisplayName = = 0xFFFFFFFF | LocalizedDisplayName = = 0x0 | DisplayName ! = null ? DisplayName : "" , isMale ) ;
2022-01-25 22:55:39 -05:00
}
2019-06-19 01:10:15 -04:00
public List < SubPacket > CreatePlayerRelatedPackets ( uint requestingPlayerActorId )
{
List < SubPacket > subpackets = new List < SubPacket > ( ) ;
if ( gcCurrent ! = 0 )
2022-02-05 17:48:49 -05:00
subpackets . Add ( SetGrandCompanyPacket . BuildPacket ( Id , gcCurrent , gcRankLimsa , gcRankGridania , gcRankUldah ) ) ;
2019-06-19 01:10:15 -04:00
if ( currentTitle ! = 0 )
2022-02-05 17:48:49 -05:00
subpackets . Add ( SetPlayerTitlePacket . BuildPacket ( Id , currentTitle ) ) ;
2019-06-19 01:10:15 -04:00
if ( currentJob ! = 0 )
2022-02-05 17:48:49 -05:00
subpackets . Add ( SetCurrentJobPacket . BuildPacket ( Id , currentJob ) ) ;
2019-06-19 01:10:15 -04:00
if ( IsMyPlayer ( requestingPlayerActorId ) )
{
2022-02-05 17:48:49 -05:00
subpackets . Add ( SetSpecialEventWorkPacket . BuildPacket ( Id ) ) ;
2019-06-19 01:10:15 -04:00
if ( hasChocobo & & chocoboName ! = null & & ! chocoboName . Equals ( "" ) )
{
2022-02-05 17:48:49 -05:00
subpackets . Add ( SetChocoboNamePacket . BuildPacket ( Id , chocoboName ) ) ;
subpackets . Add ( SetHasChocoboPacket . BuildPacket ( Id , hasChocobo ) ) ;
2019-06-19 01:10:15 -04:00
}
if ( hasGoobbue )
2022-02-05 17:48:49 -05:00
subpackets . Add ( SetHasGoobbuePacket . BuildPacket ( Id , hasGoobbue ) ) ;
2019-06-19 01:10:15 -04:00
2022-02-05 17:48:49 -05:00
subpackets . Add ( SetAchievementPointsPacket . BuildPacket ( Id , achievementPoints ) ) ;
2019-06-19 01:10:15 -04:00
subpackets . Add ( Database . GetLatestAchievements ( this ) ) ;
2019-07-27 21:54:05 -04:00
subpackets . Add ( Database . GetAchievementsPacket ( this ) ) ;
2019-06-19 01:10:15 -04:00
}
if ( mountState = = 1 )
2022-02-05 17:48:49 -05:00
subpackets . Add ( SetCurrentMountChocoboPacket . BuildPacket ( Id , chocoboAppearance , rentalExpireTime , rentalMinLeft ) ) ;
2019-06-19 01:10:15 -04:00
else if ( mountState = = 2 )
2022-02-05 17:48:49 -05:00
subpackets . Add ( SetCurrentMountGoobbuePacket . BuildPacket ( Id , 1 ) ) ;
2019-07-27 21:54:05 -04:00
//Inn Packets (Dream, Cutscenes, Armoire)
2022-02-05 14:05:32 -05:00
if ( CurrentArea . isInn )
2019-07-27 21:54:05 -04:00
{
SetCutsceneBookPacket cutsceneBookPacket = new SetCutsceneBookPacket ( ) ;
2022-02-16 15:30:32 -05:00
bool [ ] testComplete = new bool [ 2048 ] ; //TODO: Change to playerwork.scenarioComplete
2019-07-27 21:54:05 -04:00
for ( int i = 0 ; i < 2048 ; i + + )
2022-02-16 15:30:32 -05:00
testComplete [ i ] = true ;
QueuePacket ( cutsceneBookPacket . BuildPacket ( Id , "<Path Companion>" , 11 , 1 , 1 , testComplete ) ) ;
2022-02-05 17:48:49 -05:00
QueuePacket ( SetPlayerDreamPacket . BuildPacket ( Id , 0x16 , GetInnCode ( ) ) ) ;
2019-07-27 21:54:05 -04:00
}
2019-06-19 01:10:15 -04:00
return subpackets ;
}
public override List < SubPacket > GetInitPackets ( )
{
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil ( "/_init" , this ) ;
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
propPacketUtil . AddProperty ( "charaWork.eventSave.bazaarTax" ) ;
propPacketUtil . AddProperty ( "charaWork.battleSave.potencial" ) ;
//Properties
for ( int i = 0 ; i < charaWork . property . Length ; i + + )
{
2019-07-27 21:54:05 -04:00
if ( charaWork . property [ i ] ! = 0 )
2019-06-19 01:10:15 -04:00
propPacketUtil . AddProperty ( String . Format ( "charaWork.property[{0}]" , i ) ) ;
}
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
//Parameters
propPacketUtil . AddProperty ( "charaWork.parameterSave.hp[0]" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterSave.hpMax[0]" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterSave.mp" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterSave.mpMax" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterTemp.tp" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterSave.state_mainSkill[0]" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterSave.state_mainSkillLevel" ) ;
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
//Status Times
for ( int i = 0 ; i < charaWork . statusShownTime . Length ; i + + )
{
if ( charaWork . statusShownTime [ i ] ! = 0 )
propPacketUtil . AddProperty ( String . Format ( "charaWork.statusShownTime[{0}]" , i ) ) ;
}
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
//General Parameters
for ( int i = 3 ; i < charaWork . battleTemp . generalParameter . Length ; i + + )
{
if ( charaWork . battleTemp . generalParameter [ i ] ! = 0 )
propPacketUtil . AddProperty ( String . Format ( "charaWork.battleTemp.generalParameter[{0}]" , i ) ) ;
}
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
propPacketUtil . AddProperty ( "charaWork.battleTemp.castGauge_speed[0]" ) ;
propPacketUtil . AddProperty ( "charaWork.battleTemp.castGauge_speed[1]" ) ;
//Battle Save Skillpoint
propPacketUtil . AddProperty ( String . Format ( "charaWork.battleSave.skillPoint[{0}]" , charaWork . parameterSave . state_mainSkill [ 0 ] - 1 ) ) ;
//Commands
propPacketUtil . AddProperty ( "charaWork.commandBorder" ) ;
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
propPacketUtil . AddProperty ( "charaWork.battleSave.negotiationFlag[0]" ) ;
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
for ( int i = 0 ; i < charaWork . command . Length ; i + + )
{
if ( charaWork . command [ i ] ! = 0 )
{
propPacketUtil . AddProperty ( String . Format ( "charaWork.command[{0}]" , i ) ) ;
//Recast Timers
2019-07-27 21:54:05 -04:00
if ( i > = charaWork . commandBorder )
2019-06-19 01:10:15 -04:00
{
propPacketUtil . AddProperty ( String . Format ( "charaWork.parameterTemp.maxCommandRecastTime[{0}]" , i - charaWork . commandBorder ) ) ;
propPacketUtil . AddProperty ( String . Format ( "charaWork.parameterSave.commandSlot_recastTime[{0}]" , i - charaWork . commandBorder ) ) ;
}
}
}
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
for ( int i = 0 ; i < charaWork . commandCategory . Length ; i + + )
{
charaWork . commandCategory [ i ] = 1 ;
if ( charaWork . commandCategory [ i ] ! = 0 )
propPacketUtil . AddProperty ( String . Format ( "charaWork.commandCategory[{0}]" , i ) ) ;
}
for ( int i = 0 ; i < charaWork . commandAcquired . Length ; i + + )
{
if ( charaWork . commandAcquired [ i ] ! = false )
propPacketUtil . AddProperty ( String . Format ( "charaWork.commandAcquired[{0}]" , i ) ) ;
}
for ( int i = 0 ; i < charaWork . additionalCommandAcquired . Length ; i + + )
{
if ( charaWork . additionalCommandAcquired [ i ] ! = false )
propPacketUtil . AddProperty ( String . Format ( "charaWork.additionalCommandAcquired[{0}]" , i ) ) ;
}
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
for ( int i = 0 ; i < charaWork . parameterSave . commandSlot_compatibility . Length ; i + + )
{
charaWork . parameterSave . commandSlot_compatibility [ i ] = true ;
if ( charaWork . parameterSave . commandSlot_compatibility [ i ] )
propPacketUtil . AddProperty ( String . Format ( "charaWork.parameterSave.commandSlot_compatibility[{0}]" , i ) ) ;
}
for ( int i = 0 ; i < charaWork . parameterSave . commandSlot_recastTime . Length ; i + + )
{
if ( charaWork . parameterSave . commandSlot_recastTime [ i ] ! = 0 )
propPacketUtil . AddProperty ( String . Format ( "charaWork.parameterSave.commandSlot_recastTime[{0}]" , i ) ) ;
}
//System
propPacketUtil . AddProperty ( "charaWork.parameterTemp.forceControl_float_forClientSelf[0]" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterTemp.forceControl_float_forClientSelf[1]" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterTemp.forceControl_int16_forClientSelf[0]" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterTemp.forceControl_int16_forClientSelf[1]" ) ;
charaWork . parameterTemp . otherClassAbilityCount [ 0 ] = 4 ;
charaWork . parameterTemp . otherClassAbilityCount [ 1 ] = 5 ;
charaWork . parameterTemp . giftCount [ 1 ] = 5 ;
propPacketUtil . AddProperty ( "charaWork.parameterTemp.otherClassAbilityCount[0]" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterTemp.otherClassAbilityCount[1]" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterTemp.giftCount[1]" ) ;
propPacketUtil . AddProperty ( "charaWork.depictionJudge" ) ;
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
//Scenario
for ( int i = 0 ; i < playerWork . questScenario . Length ; i + + )
{
if ( playerWork . questScenario [ i ] ! = 0 )
propPacketUtil . AddProperty ( String . Format ( "playerWork.questScenario[{0}]" , i ) ) ;
}
//Guildleve - Local
for ( int i = 0 ; i < playerWork . questGuildleve . Length ; i + + )
{
if ( playerWork . questGuildleve [ i ] ! = 0 )
propPacketUtil . AddProperty ( String . Format ( "playerWork.questGuildleve[{0}]" , i ) ) ;
}
//Guildleve - Regional
for ( int i = 0 ; i < work . guildleveId . Length ; i + + )
{
if ( work . guildleveId [ i ] ! = 0 )
propPacketUtil . AddProperty ( String . Format ( "work.guildleveId[{0}]" , i ) ) ;
if ( work . guildleveDone [ i ] ! = false )
propPacketUtil . AddProperty ( String . Format ( "work.guildleveDone[{0}]" , i ) ) ;
if ( work . guildleveChecked [ i ] ! = false )
propPacketUtil . AddProperty ( String . Format ( "work.guildleveChecked[{0}]" , i ) ) ;
}
//Bazaar
CheckBazaarFlags ( true ) ;
if ( charaWork . eventSave . repairType ! = 0 )
propPacketUtil . AddProperty ( "charaWork.eventSave.repairType" ) ;
if ( charaWork . eventTemp . bazaarRetail )
propPacketUtil . AddProperty ( "charaWork.eventTemp.bazaarRetail" ) ;
if ( charaWork . eventTemp . bazaarRepair )
propPacketUtil . AddProperty ( "charaWork.eventTemp.bazaarRepair" ) ;
if ( charaWork . eventTemp . bazaarMateria )
propPacketUtil . AddProperty ( "charaWork.eventTemp.bazaarMateria" ) ;
//NPC Linkshell
for ( int i = 0 ; i < playerWork . npcLinkshellChatCalling . Length ; i + + )
{
if ( playerWork . npcLinkshellChatCalling [ i ] ! = false )
propPacketUtil . AddProperty ( String . Format ( "playerWork.npcLinkshellChatCalling[{0}]" , i ) ) ;
if ( playerWork . npcLinkshellChatExtra [ i ] ! = false )
propPacketUtil . AddProperty ( String . Format ( "playerWork.npcLinkshellChatExtra[{0}]" , i ) ) ;
}
propPacketUtil . AddProperty ( "playerWork.restBonusExpRate" ) ;
//Profile
propPacketUtil . AddProperty ( "playerWork.tribe" ) ;
propPacketUtil . AddProperty ( "playerWork.guardian" ) ;
propPacketUtil . AddProperty ( "playerWork.birthdayMonth" ) ;
propPacketUtil . AddProperty ( "playerWork.birthdayDay" ) ;
propPacketUtil . AddProperty ( "playerWork.initialTown" ) ;
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
return propPacketUtil . Done ( ) ;
}
public void SendSeamlessZoneInPackets ( )
{
2022-02-05 17:48:49 -05:00
QueuePacket ( SetMusicPacket . BuildPacket ( Id , CurrentArea . bgmDay , SetMusicPacket . EFFECT_FADEIN ) ) ;
QueuePacket ( SetWeatherPacket . BuildPacket ( Id , SetWeatherPacket . WEATHER_CLEAR , 1 ) ) ;
2019-06-19 01:10:15 -04:00
}
2022-02-13 18:48:22 -05:00
public void SendZoneInPackets ( WorldManager world , ushort spawnType )
2019-06-19 01:10:15 -04:00
{
2022-02-05 17:48:49 -05:00
QueuePacket ( SetActorIsZoningPacket . BuildPacket ( Id , false ) ) ;
QueuePacket ( SetDalamudPacket . BuildPacket ( Id , 0 ) ) ;
2019-07-25 20:28:57 -04:00
//Music Packets
if ( currentMainState = = SetActorStatePacket . MAIN_STATE_MOUNTED )
{
if ( rentalExpireTime ! = 0 )
2022-02-05 17:48:49 -05:00
QueuePacket ( SetMusicPacket . BuildPacket ( Id , 64 , 0x01 ) ) ; //Rental
2019-07-25 20:28:57 -04:00
else
2019-07-25 20:35:12 -04:00
{
if ( mountState = = 1 )
2022-02-05 17:48:49 -05:00
QueuePacket ( SetMusicPacket . BuildPacket ( Id , 83 , 0x01 ) ) ; //Mount
2019-07-25 20:35:12 -04:00
else
2022-02-05 17:48:49 -05:00
QueuePacket ( SetMusicPacket . BuildPacket ( Id , 98 , 0x01 ) ) ; //Goobbue
2019-07-25 20:35:12 -04:00
}
2019-07-25 20:28:57 -04:00
}
else
2022-02-05 17:48:49 -05:00
QueuePacket ( SetMusicPacket . BuildPacket ( Id , CurrentArea . bgmDay , 0x01 ) ) ; //Zone
2019-07-25 20:28:57 -04:00
2022-02-05 17:48:49 -05:00
QueuePacket ( SetWeatherPacket . BuildPacket ( Id , SetWeatherPacket . WEATHER_CLEAR , 1 ) ) ;
2019-06-19 01:10:15 -04:00
2022-02-05 17:48:49 -05:00
QueuePacket ( SetMapPacket . BuildPacket ( Id , CurrentArea . RegionId , CurrentArea . ZoneId ) ) ;
2019-06-19 01:10:15 -04:00
2019-07-27 21:54:05 -04:00
QueuePackets ( GetSpawnPackets ( this , spawnType ) ) ;
2019-06-19 01:10:15 -04:00
#region Inventory & Equipment
2022-02-05 17:48:49 -05:00
QueuePacket ( InventoryBeginChangePacket . BuildPacket ( Id , true ) ) ;
2019-06-19 01:10:15 -04:00
itemPackages [ ItemPackage . NORMAL ] . SendFullPackage ( this ) ;
itemPackages [ ItemPackage . CURRENCY_CRYSTALS ] . SendFullPackage ( this ) ;
itemPackages [ ItemPackage . KEYITEMS ] . SendFullPackage ( this ) ;
itemPackages [ ItemPackage . BAZAAR ] . SendFullPackage ( this ) ;
itemPackages [ ItemPackage . MELDREQUEST ] . SendFullPackage ( this ) ;
itemPackages [ ItemPackage . LOOT ] . SendFullPackage ( this ) ;
2019-07-27 21:54:05 -04:00
equipment . SendUpdate ( this ) ;
2022-02-05 17:48:49 -05:00
playerSession . QueuePacket ( InventoryEndChangePacket . BuildPacket ( Id ) ) ;
2019-06-19 01:10:15 -04:00
#endregion
playerSession . QueuePacket ( GetInitPackets ( ) ) ;
2022-02-05 14:05:32 -05:00
List < SubPacket > areaMasterSpawn = CurrentArea . GetSpawnPackets ( ) ;
2019-06-19 01:10:15 -04:00
List < SubPacket > debugSpawn = world . GetDebugActor ( ) . GetSpawnPackets ( ) ;
List < SubPacket > worldMasterSpawn = world . GetActor ( ) . GetSpawnPackets ( ) ;
2019-07-27 21:54:05 -04:00
2019-06-19 01:10:15 -04:00
playerSession . QueuePacket ( areaMasterSpawn ) ;
playerSession . QueuePacket ( debugSpawn ) ;
playerSession . QueuePacket ( worldMasterSpawn ) ;
2022-02-05 14:05:32 -05:00
if ( CurrentArea . GetWeatherDirector ( ) ! = null )
2019-06-19 01:10:15 -04:00
{
2022-02-05 14:05:32 -05:00
playerSession . QueuePacket ( CurrentArea . GetWeatherDirector ( ) . GetSpawnPackets ( ) ) ;
2019-06-19 01:10:15 -04:00
}
foreach ( Director director in ownedDirectors )
{
QueuePackets ( director . GetSpawnPackets ( ) ) ;
QueuePackets ( director . GetInitPackets ( ) ) ;
}
if ( currentContentGroup ! = null )
currentContentGroup . SendGroupPackets ( playerSession ) ;
if ( currentParty ! = null )
currentParty . SendGroupPackets ( playerSession ) ;
2019-07-27 21:54:05 -04:00
SendInstanceUpdate ( ) ;
2019-06-19 01:10:15 -04:00
}
private void SendRemoveInventoryPackets ( List < ushort > slots )
{
int currentIndex = 0 ;
while ( true )
{
if ( slots . Count - currentIndex > = 64 )
2022-02-05 17:48:49 -05:00
QueuePacket ( InventoryRemoveX64Packet . BuildPacket ( Id , slots , ref currentIndex ) ) ;
2019-06-19 01:10:15 -04:00
else if ( slots . Count - currentIndex > = 32 )
2022-02-05 17:48:49 -05:00
QueuePacket ( InventoryRemoveX32Packet . BuildPacket ( Id , slots , ref currentIndex ) ) ;
2019-06-19 01:10:15 -04:00
else if ( slots . Count - currentIndex > = 16 )
2022-02-05 17:48:49 -05:00
QueuePacket ( InventoryRemoveX16Packet . BuildPacket ( Id , slots , ref currentIndex ) ) ;
2019-06-19 01:10:15 -04:00
else if ( slots . Count - currentIndex > = 8 )
2022-02-05 17:48:49 -05:00
QueuePacket ( InventoryRemoveX08Packet . BuildPacket ( Id , slots , ref currentIndex ) ) ;
2019-06-19 01:10:15 -04:00
else if ( slots . Count - currentIndex = = 1 )
2022-02-05 17:48:49 -05:00
QueuePacket ( InventoryRemoveX01Packet . BuildPacket ( Id , slots [ currentIndex ] ) ) ;
2019-06-19 01:10:15 -04:00
else
break ;
}
}
public bool IsMyPlayer ( uint otherActorId )
{
2022-02-05 17:48:49 -05:00
return Id = = otherActorId ;
2019-07-27 21:54:05 -04:00
}
2019-06-19 01:10:15 -04:00
public void QueuePacket ( SubPacket packet )
{
playerSession . QueuePacket ( packet ) ;
}
public void QueuePackets ( List < SubPacket > packets )
{
playerSession . QueuePacket ( packets ) ;
}
public void SendPacket ( string path )
{
try
{
BasePacket packet = new BasePacket ( path ) ;
2022-02-05 17:48:49 -05:00
packet . ReplaceActorID ( Id ) ;
2019-06-19 01:10:15 -04:00
var packets = packet . GetSubpackets ( ) ;
QueuePackets ( packets ) ;
}
catch ( Exception e )
{
this . SendMessage ( SendMessagePacket . MESSAGE_TYPE_SYSTEM_ERROR , "[SendPacket]" , "Unable to send packet." ) ;
this . SendMessage ( SendMessagePacket . MESSAGE_TYPE_SYSTEM_ERROR , "[SendPacket]" , e . Message ) ;
}
}
public void BroadcastPackets ( List < SubPacket > packets , bool sendToSelf )
{
foreach ( SubPacket packet in packets )
{
if ( sendToSelf )
{
2022-02-05 17:48:49 -05:00
SubPacket clonedPacket = new SubPacket ( packet , Id ) ;
2019-06-19 01:10:15 -04:00
QueuePacket ( clonedPacket ) ;
}
foreach ( Actor a in playerSession . actorInstanceList )
{
if ( a is Player )
{
Player p = ( Player ) a ;
if ( p . Equals ( this ) )
continue ;
2022-02-05 17:48:49 -05:00
SubPacket clonedPacket = new SubPacket ( packet , a . Id ) ;
2019-06-19 01:10:15 -04:00
p . QueuePacket ( clonedPacket ) ;
}
}
}
}
public void BroadcastPacket ( SubPacket packet , bool sendToSelf )
{
if ( sendToSelf )
{
2022-02-05 17:48:49 -05:00
SubPacket clonedPacket = new SubPacket ( packet , Id ) ;
2019-06-19 01:10:15 -04:00
QueuePacket ( clonedPacket ) ;
}
foreach ( Actor a in playerSession . actorInstanceList )
{
if ( a is Player )
{
Player p = ( Player ) a ;
if ( p . Equals ( this ) )
continue ;
2022-02-05 17:48:49 -05:00
SubPacket clonedPacket = new SubPacket ( packet , a . Id ) ;
2019-06-19 01:10:15 -04:00
p . QueuePacket ( clonedPacket ) ;
}
}
}
public void ChangeAnimation ( uint animId )
{
2022-02-05 14:05:32 -05:00
Actor a = CurrentArea . FindActorInArea ( currentTarget ) ;
2019-06-19 01:10:15 -04:00
if ( a is Npc )
( ( Npc ) a ) . animationId = animId ;
}
public void SetDCFlag ( bool flag )
{
if ( flag )
{
2022-02-05 17:48:49 -05:00
BroadcastPacket ( SetActorIconPacket . BuildPacket ( Id , SetActorIconPacket . DISCONNECTING ) , true ) ;
2019-06-19 01:10:15 -04:00
}
else
{
if ( isGM )
2022-02-05 17:48:49 -05:00
BroadcastPacket ( SetActorIconPacket . BuildPacket ( Id , SetActorIconPacket . ISGM ) , true ) ;
2019-06-19 01:10:15 -04:00
else
2022-02-05 17:48:49 -05:00
BroadcastPacket ( SetActorIconPacket . BuildPacket ( Id , 0 ) , true ) ;
2019-06-19 01:10:15 -04:00
}
}
public void CleanupAndSave ( )
{
playerSession . LockUpdates ( true ) ;
//Remove actor from zone and main server list
2022-02-05 14:05:32 -05:00
CurrentArea . RemoveActorFromZone ( this ) ;
2019-06-19 01:10:15 -04:00
//Set Destination to 0
this . destinationZone = 0 ;
this . destinationSpawnType = 0 ;
//Clean up parties
RemoveFromCurrentPartyAndCleanup ( ) ;
//Save Player
Database . SavePlayerPlayTime ( this ) ;
Database . SavePlayerPosition ( this ) ;
Database . SavePlayerStatusEffects ( this ) ;
2022-01-25 22:55:39 -05:00
//Save Quests
foreach ( Quest quest in questScenario )
{
if ( quest ! = null )
2022-02-17 13:22:18 -05:00
quest . GetData ( ) . Save ( ) ;
2022-01-25 22:55:39 -05:00
}
2019-06-19 01:10:15 -04:00
}
public void CleanupAndSave ( uint destinationZone , ushort spawnType , float destinationX , float destinationY , float destinationZ , float destinationRot )
{
playerSession . LockUpdates ( true ) ;
//Remove actor from zone and main server list
2022-02-05 14:05:32 -05:00
CurrentArea . RemoveActorFromZone ( this ) ;
2019-06-19 01:10:15 -04:00
//Clean up parties
RemoveFromCurrentPartyAndCleanup ( ) ;
//Set destination
this . destinationZone = destinationZone ;
this . destinationSpawnType = spawnType ;
this . positionX = destinationX ;
this . positionY = destinationY ;
this . positionZ = destinationZ ;
this . rotation = destinationRot ;
this . statusEffects . RemoveStatusEffectsByFlags ( ( uint ) StatusEffectFlags . LoseOnZoning ) ;
//Save Player
Database . SavePlayerPlayTime ( this ) ;
Database . SavePlayerPosition ( this ) ;
Database . SavePlayerStatusEffects ( this ) ;
}
public void SendMessage ( uint logType , string sender , string message )
{
2022-02-05 17:48:49 -05:00
QueuePacket ( SendMessagePacket . BuildPacket ( Id , logType , sender , message ) ) ;
2019-06-19 01:10:15 -04:00
}
2019-07-27 21:54:05 -04:00
//Only use at logout since it's intensive
private byte GetInnCode ( )
{
2022-02-05 14:05:32 -05:00
if ( CurrentArea . isInn )
2019-07-27 21:54:05 -04:00
{
Vector3 position = new Vector3 ( positionX , 0 , positionZ ) ;
if ( Utils . Distance ( position , new Vector3 ( 0 , 0 , 0 ) ) < = 20f )
return 3 ;
else if ( Utils . Distance ( position , new Vector3 ( 160 , 0 , 160 ) ) < = 20f )
return 2 ;
else if ( Utils . Distance ( position , new Vector3 ( - 160 , 0 , - 160 ) ) < = 20f )
return 1 ;
}
return 0 ;
}
public void SetSleeping ( )
{
playerSession . LockUpdates ( true ) ;
switch ( GetInnCode ( ) )
{
case 1 :
positionX = - 162.42f ;
positionY = 0f ;
positionZ = - 154.21f ;
rotation = 1.56f ;
break ;
case 2 :
positionX = 157.55f ;
positionY = 0f ;
positionZ = 165.05f ;
rotation = 1.53f ;
break ;
case 3 :
positionX = - 2.65f ;
positionY = 0f ;
positionZ = 3.94f ;
rotation = 1.52f ;
break ;
}
}
2019-06-19 01:10:15 -04:00
public void Logout ( )
{
// todo: really this should be in CleanupAndSave but we might want logout/disconnect handled separately for some effects
2022-02-05 17:48:49 -05:00
QueuePacket ( LogoutPacket . BuildPacket ( Id ) ) ;
2019-06-19 01:10:15 -04:00
statusEffects . RemoveStatusEffectsByFlags ( ( uint ) StatusEffectFlags . LoseOnLogout ) ;
CleanupAndSave ( ) ;
}
public void QuitGame ( )
{
2022-02-05 17:48:49 -05:00
QueuePacket ( QuitPacket . BuildPacket ( Id ) ) ;
2019-06-19 01:10:15 -04:00
statusEffects . RemoveStatusEffectsByFlags ( ( uint ) StatusEffectFlags . LoseOnLogout ) ;
CleanupAndSave ( ) ;
}
public uint GetPlayTime ( bool doUpdate )
{
if ( doUpdate )
{
uint curTime = Utils . UnixTimeStampUTC ( ) ;
playTime + = curTime - lastPlayTimeUpdate ;
lastPlayTimeUpdate = curTime ;
}
return playTime ;
}
public void SavePlayTime ( )
{
Database . SavePlayerPlayTime ( this ) ;
}
public void ChangeMusic ( ushort musicId )
{
2022-02-05 17:48:49 -05:00
QueuePacket ( SetMusicPacket . BuildPacket ( Id , musicId , 1 ) ) ;
2019-06-19 01:10:15 -04:00
}
2022-02-05 13:56:58 -05:00
public void ChangeMusic ( ushort musicId , ushort musicTrackMode )
{
2022-02-06 12:36:15 -05:00
QueuePacket ( SetMusicPacket . BuildPacket ( Id , musicId , musicTrackMode ) ) ;
2022-02-05 13:56:58 -05:00
}
2019-06-19 01:10:15 -04:00
public void SendMountAppearance ( )
{
if ( mountState = = 1 )
2022-02-05 17:48:49 -05:00
BroadcastPacket ( SetCurrentMountChocoboPacket . BuildPacket ( Id , chocoboAppearance , rentalExpireTime , rentalMinLeft ) , true ) ;
2019-06-19 01:10:15 -04:00
else if ( mountState = = 2 )
2022-02-05 17:48:49 -05:00
BroadcastPacket ( SetCurrentMountGoobbuePacket . BuildPacket ( Id , 1 ) , true ) ;
2019-06-19 01:10:15 -04:00
}
public void SetMountState ( byte mountState )
{
this . mountState = mountState ;
SendMountAppearance ( ) ;
}
public byte GetMountState ( )
{
return mountState ;
}
public void DoEmote ( uint targettedActor , uint animId , uint descId )
{
2022-02-05 17:48:49 -05:00
BroadcastPacket ( ActorDoEmotePacket . BuildPacket ( Id , targettedActor , animId , descId ) , true ) ;
2019-06-19 01:10:15 -04:00
}
public void SendGameMessage ( Actor sourceActor , Actor textIdOwner , ushort textId , byte log , params object [ ] msgParams )
{
if ( msgParams = = null | | msgParams . Length = = 0 )
{
2022-02-05 17:48:49 -05:00
QueuePacket ( GameMessagePacket . BuildPacket ( Server . GetWorldManager ( ) . GetActor ( ) . Id , sourceActor . Id , textIdOwner . Id , textId , log ) ) ;
2019-06-19 01:10:15 -04:00
}
else
2022-02-05 17:48:49 -05:00
QueuePacket ( GameMessagePacket . BuildPacket ( Server . GetWorldManager ( ) . GetActor ( ) . Id , sourceActor . Id , textIdOwner . Id , textId , log , LuaUtils . CreateLuaParamList ( msgParams ) ) ) ;
2019-06-19 01:10:15 -04:00
}
public void SendGameMessage ( Actor textIdOwner , ushort textId , byte log , params object [ ] msgParams )
{
if ( msgParams = = null | | msgParams . Length = = 0 )
2022-02-05 17:48:49 -05:00
QueuePacket ( GameMessagePacket . BuildPacket ( Server . GetWorldManager ( ) . GetActor ( ) . Id , textIdOwner . Id , textId , log ) ) ;
2019-06-19 01:10:15 -04:00
else
2022-02-05 17:48:49 -05:00
QueuePacket ( GameMessagePacket . BuildPacket ( Server . GetWorldManager ( ) . GetActor ( ) . Id , textIdOwner . Id , textId , log , LuaUtils . CreateLuaParamList ( msgParams ) ) ) ;
2019-06-19 01:10:15 -04:00
}
2022-02-07 05:13:34 -05:00
public void SendGameMessageDisplayName ( Actor textIdOwner , ushort textId , byte log , string customSender , params object [ ] msgParams )
2019-06-19 01:10:15 -04:00
{
if ( msgParams = = null | | msgParams . Length = = 0 )
2022-02-05 17:48:49 -05:00
QueuePacket ( GameMessagePacket . BuildPacket ( Server . GetWorldManager ( ) . GetActor ( ) . Id , textIdOwner . Id , textId , customSender , log ) ) ;
2019-06-19 01:10:15 -04:00
else
2022-02-05 17:48:49 -05:00
QueuePacket ( GameMessagePacket . BuildPacket ( Server . GetWorldManager ( ) . GetActor ( ) . Id , textIdOwner . Id , textId , customSender , log , LuaUtils . CreateLuaParamList ( msgParams ) ) ) ;
2019-06-19 01:10:15 -04:00
}
2022-02-07 05:13:34 -05:00
public void SendGameMessageLocalizedDisplayName ( Actor textIdOwner , ushort textId , byte log , uint displayId , params object [ ] msgParams )
2019-06-19 01:10:15 -04:00
{
if ( msgParams = = null | | msgParams . Length = = 0 )
2022-02-05 17:48:49 -05:00
QueuePacket ( GameMessagePacket . BuildPacket ( Server . GetWorldManager ( ) . GetActor ( ) . Id , textIdOwner . Id , textId , displayId , log ) ) ;
2019-06-19 01:10:15 -04:00
else
2022-02-05 17:48:49 -05:00
QueuePacket ( GameMessagePacket . BuildPacket ( Server . GetWorldManager ( ) . GetActor ( ) . Id , textIdOwner . Id , textId , displayId , log , LuaUtils . CreateLuaParamList ( msgParams ) ) ) ;
2019-06-19 01:10:15 -04:00
}
public void BroadcastWorldMessage ( ushort worldMasterId , params object [ ] msgParams )
{
//SubPacket worldMasterMessage =
2022-02-05 14:05:32 -05:00
//CurrentArea.BroadcastPacketAroundActor(this, worldMasterMessage);
2019-06-19 01:10:15 -04:00
}
2022-02-13 18:48:22 -05:00
public void ChangeIntoNpc ( Npc npc )
{
uint [ ] npcAppearIds = new uint [ appearanceIds . Length ] ;
for ( int i = 0 ; i < appearanceIds . Length ; i + + )
npcAppearIds [ i ] = npc . appearanceIds [ i ] ;
SetActorAppearancePacket setappearance = new SetActorAppearancePacket ( npc . modelId , npcAppearIds ) ;
BroadcastPacket ( setappearance . BuildPacket ( Id ) , true ) ;
}
2019-06-19 01:10:15 -04:00
public void GraphicChange ( uint slot , uint graphicId )
{
appearanceIds [ slot ] = graphicId ;
}
public void GraphicChange ( uint slot , uint weapId , uint equipId , uint variantId , uint colorId )
{
uint mixedVariantId ;
if ( weapId = = 0 )
mixedVariantId = ( ( variantId & 0x1F ) < < 5 ) | colorId ;
else
mixedVariantId = variantId ;
uint graphicId =
( weapId & 0x3FF ) < < 20 |
( equipId & 0x3FF ) < < 10 |
( mixedVariantId & 0x3FF ) ;
appearanceIds [ slot ] = graphicId ;
}
2020-03-29 13:33:27 -04:00
public void GraphicChange ( int slot , InventoryItem invItem )
{
if ( invItem = = null )
appearanceIds [ slot ] = 0 ;
else
{
ItemData item = Server . GetItemGamedata ( invItem . itemId ) ;
if ( item is EquipmentItem )
{
EquipmentItem eqItem = ( EquipmentItem ) item ;
uint mixedVariantId ;
if ( eqItem . graphicsWeaponId = = 0 )
mixedVariantId = ( ( eqItem . graphicsVariantId & 0x1F ) < < 5 ) | eqItem . graphicsColorId ;
else
mixedVariantId = eqItem . graphicsVariantId ;
uint graphicId =
( eqItem . graphicsWeaponId & 0x3FF ) < < 20 |
( eqItem . graphicsEquipmentId & 0x3FF ) < < 10 |
( mixedVariantId & 0x3FF ) ;
appearanceIds [ slot ] = graphicId ;
}
//Handle offhand
if ( slot = = MAINHAND & & item is WeaponItem )
{
WeaponItem wpItem = ( WeaponItem ) item ;
uint graphicId =
( wpItem . graphicsOffhandWeaponId & 0x3FF ) < < 20 |
( wpItem . graphicsOffhandEquipmentId & 0x3FF ) < < 10 |
( wpItem . graphicsOffhandVariantId & 0x3FF ) ;
if ( graphicId ! = 0 )
appearanceIds [ SetActorAppearancePacket . OFFHAND ] = graphicId ;
}
//Handle ALC offhand special case
if ( slot = = OFFHAND & & item is WeaponItem & & item . IsAlchemistWeapon ( ) )
{
WeaponItem wpItem = ( WeaponItem ) item ;
uint graphicId =
( ( wpItem . graphicsWeaponId + 1 ) & 0x3FF ) < < 20 |
( wpItem . graphicsEquipmentId & 0x3FF ) < < 10 |
( wpItem . graphicsVariantId & 0x3FF ) ;
if ( graphicId ! = 0 )
appearanceIds [ SetActorAppearancePacket . SPOFFHAND ] = graphicId ;
}
}
Database . SavePlayerAppearance ( this ) ;
BroadcastPacket ( CreateAppearancePacket ( ) , true ) ;
}
2019-06-19 01:10:15 -04:00
public void SendAppearance ( )
{
BroadcastPacket ( CreateAppearancePacket ( ) , true ) ;
}
public void SendCharaExpInfo ( )
{
if ( lastStep = = 0 )
{
int maxLength ;
if ( ( sizeof ( short ) * charaWork . battleSave . skillLevel . Length ) - lastPosition < 0x5E )
maxLength = ( sizeof ( short ) * charaWork . battleSave . skillLevel . Length ) - lastPosition ;
else
maxLength = 0x5E ;
byte [ ] skillLevelBuffer = new byte [ maxLength ] ;
Buffer . BlockCopy ( charaWork . battleSave . skillLevel , 0 , skillLevelBuffer , 0 , skillLevelBuffer . Length ) ;
SetActorPropetyPacket charaInfo1 = new SetActorPropetyPacket ( "charaWork/exp" ) ;
charaInfo1 . SetIsArrayMode ( true ) ;
if ( maxLength = = 0x5E )
{
charaInfo1 . AddBuffer ( Utils . MurmurHash2 ( "charaWork.battleSave.skillLevel" , 0 ) , skillLevelBuffer , 0 , skillLevelBuffer . Length , 0x0 ) ;
lastPosition + = maxLength ;
}
else
{
charaInfo1 . AddBuffer ( Utils . MurmurHash2 ( "charaWork.battleSave.skillLevel" , 0 ) , skillLevelBuffer , 0 , skillLevelBuffer . Length , 0x3 ) ;
lastPosition = 0 ;
lastStep + + ;
}
charaInfo1 . AddTarget ( ) ;
2022-02-05 17:48:49 -05:00
QueuePacket ( charaInfo1 . BuildPacket ( Id ) ) ;
2019-06-19 01:10:15 -04:00
}
else if ( lastStep = = 1 )
{
int maxLength ;
if ( ( sizeof ( short ) * charaWork . battleSave . skillLevelCap . Length ) - lastPosition < 0x5E )
maxLength = ( sizeof ( short ) * charaWork . battleSave . skillLevelCap . Length ) - lastPosition ;
else
maxLength = 0x5E ;
byte [ ] skillCapBuffer = new byte [ maxLength ] ;
Buffer . BlockCopy ( charaWork . battleSave . skillLevelCap , lastPosition , skillCapBuffer , 0 , skillCapBuffer . Length ) ;
SetActorPropetyPacket charaInfo1 = new SetActorPropetyPacket ( "charaWork/exp" ) ;
if ( maxLength = = 0x5E )
{
charaInfo1 . SetIsArrayMode ( true ) ;
charaInfo1 . AddBuffer ( Utils . MurmurHash2 ( "charaWork.battleSave.skillLevelCap" , 0 ) , skillCapBuffer , 0 , skillCapBuffer . Length , 0x1 ) ;
lastPosition + = maxLength ;
}
else
{
charaInfo1 . SetIsArrayMode ( false ) ;
charaInfo1 . AddBuffer ( Utils . MurmurHash2 ( "charaWork.battleSave.skillLevelCap" , 0 ) , skillCapBuffer , 0 , skillCapBuffer . Length , 0x3 ) ;
lastStep = 0 ;
lastPosition = 0 ;
}
charaInfo1 . AddTarget ( ) ;
2022-02-05 17:48:49 -05:00
QueuePacket ( charaInfo1 . BuildPacket ( Id ) ) ;
2019-06-19 01:10:15 -04:00
}
}
2022-02-19 01:17:50 -05:00
private void SendAchievedAetheryte ( ushort from , ushort to )
{
Bitstream fakeAetheryte = new Bitstream ( 512 , true ) ;
SetActorPropetyPacket completedQuestWorkUpdate = new SetActorPropetyPacket ( from , to , "work/achieveAetheryte" ) ;
completedQuestWorkUpdate . AddBitfield ( Utils . MurmurHash2 ( "work.event_achieve_aetheryte" , 0 ) , fakeAetheryte . GetSlice ( from , to ) ) ;
completedQuestWorkUpdate . AddTarget ( ) ;
QueuePacket ( completedQuestWorkUpdate . BuildPacket ( Id ) ) ;
}
private void SendCompletedQuests ( ushort from , ushort to )
{
2022-02-20 12:39:34 -05:00
byte [ ] data = questStateManager . GetCompletionSliceBytes ( from , to ) ;
2022-02-19 01:17:50 -05:00
SetActorPropetyPacket completedQuestWorkUpdate = new SetActorPropetyPacket ( from , to , "playerWork/journal" ) ;
completedQuestWorkUpdate . AddBitfield ( Utils . MurmurHash2 ( "playerWork.questScenarioComplete" , 0 ) , data ) ;
completedQuestWorkUpdate . AddTarget ( ) ;
QueuePacket ( completedQuestWorkUpdate . BuildPacket ( Id ) ) ;
}
public void OnWorkSyncRequest ( string propertyName , ushort from = 0 , ushort to = 0 )
{
switch ( propertyName )
{
case "charaWork/exp" :
SendCharaExpInfo ( ) ;
break ;
case "work/achieveAetheryte" :
SendAchievedAetheryte ( from , to ) ;
break ;
case "playerWork/questCompleteS" :
SendCompletedQuests ( from , to ) ;
break ;
}
}
2019-06-19 01:10:15 -04:00
public int GetHighestLevel ( )
{
int max = 0 ;
foreach ( short level in charaWork . battleSave . skillLevel )
{
if ( level > max )
max = level ;
}
return max ;
}
public InventoryItem [ ] GetGearset ( ushort classId )
{
return Database . GetEquipment ( this , classId ) ;
}
public void PrepareClassChange ( byte classId )
{
SendCharaExpInfo ( ) ;
}
public void DoClassChange ( byte classId )
{
//load hotbars
//Calculate stats
//Calculate hp/mp
//Get Potenciel ??????
//Set HP/MP/TP PARAMS
//Set mainskill and level
//Set Parameters
//Set current EXP
//Set Hotbar Commands 1
//Set Hotbar Commands 2
//Set Hotbar Commands 3
//Check if bonus point available... set
//Remove buffs that fall off when changing class
CommandResultContainer resultContainer = new CommandResultContainer ( ) ;
statusEffects . RemoveStatusEffectsByFlags ( ( uint ) StatusEffectFlags . LoseOnClassChange , resultContainer ) ;
resultContainer . CombineLists ( ) ;
DoBattleAction ( 0 , 0x7c000062 , resultContainer . GetList ( ) ) ;
2019-06-08 21:11:51 -07:00
//If new class, init abilties and level
if ( charaWork . battleSave . skillLevel [ classId - 1 ] < = 0 )
{
UpdateClassLevel ( classId , 1 ) ;
EquipAbilitiesAtLevel ( classId , 1 ) ;
}
2019-06-19 01:10:15 -04:00
//Set rested EXP
charaWork . parameterSave . state_mainSkill [ 0 ] = classId ;
charaWork . parameterSave . state_mainSkillLevel = charaWork . battleSave . skillLevel [ classId - 1 ] ;
playerWork . restBonusExpRate = 0.0f ;
for ( int i = charaWork . commandBorder ; i < charaWork . command . Length ; i + + )
{
charaWork . command [ i ] = 0 ;
charaWork . commandCategory [ i ] = 0 ;
}
//If new class, init abilties and level
if ( charaWork . battleSave . skillLevel [ classId - 1 ] < = 0 )
{
UpdateClassLevel ( classId , 1 ) ;
EquipAbilitiesAtLevel ( classId , 1 ) ;
}
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil ( "charaWork/stateForAll" , this ) ;
propertyBuilder . AddProperty ( "charaWork.parameterSave.state_mainSkill[0]" ) ;
propertyBuilder . AddProperty ( "charaWork.parameterSave.state_mainSkillLevel" ) ;
propertyBuilder . NewTarget ( "playerWork/expBonus" ) ;
propertyBuilder . AddProperty ( "playerWork.restBonusExpRate" ) ;
propertyBuilder . NewTarget ( "charaWork/battleStateForSelf" ) ;
propertyBuilder . AddProperty ( String . Format ( "charaWork.battleSave.skillPoint[{0}]" , classId - 1 ) ) ;
Database . LoadHotbar ( this ) ;
var time = Utils . UnixTimeStampUTC ( ) ;
for ( int i = charaWork . commandBorder ; i < charaWork . command . Length ; i + + )
{
if ( charaWork . command [ i ] ! = 0 )
{
charaWork . parameterSave . commandSlot_recastTime [ i - charaWork . commandBorder ] = time + charaWork . parameterTemp . maxCommandRecastTime [ i - charaWork . commandBorder ] ;
}
}
UpdateHotbar ( ) ;
List < SubPacket > packets = propertyBuilder . Done ( ) ;
foreach ( SubPacket packet in packets )
BroadcastPacket ( packet , true ) ;
Database . SavePlayerCurrentClass ( this ) ;
RecalculateStats ( ) ;
}
public void UpdateClassLevel ( byte classId , short level )
{
Database . PlayerCharacterUpdateClassLevel ( this , classId , level ) ;
charaWork . battleSave . skillLevel [ classId - 1 ] = level ;
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil ( "charaWork/stateForAll" , this ) ;
propertyBuilder . AddProperty ( String . Format ( "charaWork.battleSave.skillLevel[{0}]" , classId - 1 ) ) ;
List < SubPacket > packets = propertyBuilder . Done ( ) ;
QueuePackets ( packets ) ;
}
public void SetRepairRequest ( byte type )
{
charaWork . eventSave . repairType = type ;
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil ( "charaWork/bazaar" , this ) ;
propPacketUtil . AddProperty ( "charaWork.eventSave.repairType" ) ;
QueuePackets ( propPacketUtil . Done ( ) ) ;
}
public void CheckBazaarFlags ( bool noUpdate = false )
{
bool isDealing = false , isRepairing = false , seekingItem = false ;
lock ( GetItemPackage ( ItemPackage . BAZAAR ) )
{
foreach ( InventoryItem item in GetItemPackage ( ItemPackage . BAZAAR ) . GetRawList ( ) )
{
if ( item = = null )
break ;
if ( item . GetBazaarMode ( ) = = InventoryItem . MODE_SELL_SINGLE | | item . GetBazaarMode ( ) = = InventoryItem . MODE_SELL_PSTACK | | item . GetBazaarMode ( ) = = InventoryItem . MODE_SELL_FSTACK )
isDealing = true ;
if ( item . GetBazaarMode ( ) = = InventoryItem . MODE_SEEK_REPAIR )
isRepairing = true ;
if ( item . GetBazaarMode ( ) = = InventoryItem . MODE_SEEK_ITEM )
isDealing = true ;
if ( isDealing & & isRepairing & & seekingItem )
break ;
}
}
bool doUpdate = false ;
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil ( "charaWork/bazaar" , this ) ;
if ( charaWork . eventTemp . bazaarRetail ! = isDealing )
{
charaWork . eventTemp . bazaarRetail = isDealing ;
propPacketUtil . AddProperty ( "charaWork.eventTemp.bazaarRetail" ) ;
doUpdate = true ;
}
if ( charaWork . eventTemp . bazaarRepair ! = isRepairing )
{
charaWork . eventTemp . bazaarRepair = isRepairing ;
propPacketUtil . AddProperty ( "charaWork.eventTemp.bazaarRepair" ) ;
doUpdate = true ;
}
if ( charaWork . eventTemp . bazaarMateria ! = ( GetItemPackage ( ItemPackage . MELDREQUEST ) . GetCount ( ) ! = 0 ) )
{
charaWork . eventTemp . bazaarMateria = GetItemPackage ( ItemPackage . MELDREQUEST ) . GetCount ( ) ! = 0 ;
propPacketUtil . AddProperty ( "charaWork.eventTemp.bazaarMateria" ) ;
doUpdate = true ;
}
if ( ! noUpdate & & doUpdate )
BroadcastPackets ( propPacketUtil . Done ( ) , true ) ;
}
public int GetCurrentGil ( )
{
if ( HasItem ( 1000001 ) )
return GetItemPackage ( ItemPackage . CURRENCY_CRYSTALS ) . GetItemByCatelogId ( 1000001 ) . quantity ;
else
return 0 ;
}
public Actor GetActorInInstance ( uint actorId )
{
foreach ( Actor a in playerSession . actorInstanceList )
{
2022-02-05 17:48:49 -05:00
if ( a . Id = = actorId )
2019-06-19 01:10:15 -04:00
return a ;
}
return null ;
}
public void SetZoneChanging ( bool flag )
{
isZoneChanging = flag ;
}
public bool IsInZoneChange ( )
{
return isZoneChanging ;
}
public ReferencedItemPackage GetEquipment ( )
{
return equipment ;
}
public byte GetInitialTown ( )
{
return playerWork . initialTown ;
}
public uint GetHomePoint ( )
{
return homepoint ;
}
public byte GetHomePointInn ( )
{
return homepointInn ;
}
public void SetHomePoint ( uint aetheryteId )
{
homepoint = aetheryteId ;
Database . SavePlayerHomePoints ( this ) ;
}
public void SetHomePointInn ( byte townId )
{
homepointInn = townId ;
Database . SavePlayerHomePoints ( this ) ;
}
public bool HasAetheryteNodeUnlocked ( uint aetheryteId )
{
if ( aetheryteId ! = 0 )
return true ;
else
return false ;
}
public int GetFreeQuestSlot ( )
{
for ( int i = 0 ; i < questScenario . Length ; i + + )
{
if ( questScenario [ i ] = = null )
return i ;
}
return - 1 ;
}
public int GetFreeGuildleveSlot ( )
{
for ( int i = 0 ; i < work . guildleveId . Length ; i + + )
{
if ( work . guildleveId [ i ] = = 0 )
return i ;
}
return - 1 ;
}
2022-02-17 13:22:18 -05:00
#region Quests - Script Related
// Add quest from an active quest in the player's quest state. Quest scripts will use this to add a quest.
public bool AcceptQuest ( Quest instance , bool isSilent = false )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
if ( instance = = null )
return false ;
2019-06-19 01:10:15 -04:00
2022-02-17 13:22:18 -05:00
int freeSlot = GetFreeQuestSlot ( ) ;
2019-06-19 01:10:15 -04:00
if ( freeSlot = = - 1 )
2022-02-17 13:22:18 -05:00
{
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 25234 , 0x20 ) ; // "You cannot accept any more quests at this time."
return false ;
}
2019-06-19 01:10:15 -04:00
2022-02-17 13:22:18 -05:00
playerWork . questScenario [ freeSlot ] = instance . Id ;
questScenario [ freeSlot ] = instance ;
SendQuestClientUpdate ( freeSlot ) ;
if ( ! isSilent )
{
2022-02-23 23:10:11 -05:00
WorldMaster worldMaster = Server . GetWorldManager ( ) . GetActor ( ) ;
SendDataPacket ( "attention" , worldMaster , "" , 25224 , ( object ) questScenario [ freeSlot ] . GetQuestId ( ) ) ; // "<Quest> accepted."
SendGameMessage ( worldMaster , 25224 , 0x20 , ( object ) questScenario [ freeSlot ] . GetQuestId ( ) ) ; // "<Quest> accepted."
2022-02-17 13:22:18 -05:00
}
instance . OnAccept ( ) ;
2022-02-19 01:17:50 -05:00
Database . SaveQuest ( this , questScenario [ freeSlot ] , freeSlot ) ;
2022-02-17 13:22:18 -05:00
return true ;
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
// Replace a quest with another quest in the player's quest state.
2022-02-20 22:40:35 -05:00
public void ReplaceQuest ( Quest oldQuestInstance , string questName )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
for ( int i = 0 ; i < questScenario . Length ; i + + )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
if ( questScenario [ i ] ! = null & & questScenario [ i ] . Equals ( oldQuestInstance ) )
2022-02-20 22:40:35 -05:00
{
2022-02-17 13:22:18 -05:00
SendQuestClientUpdate ( i ) ;
2022-02-19 01:17:50 -05:00
oldQuestInstance . OnComplete ( ) ;
2022-02-20 23:20:45 -05:00
Database . SaveCompletedQuests ( playerSession . GetActor ( ) ) ;
2022-02-19 01:17:50 -05:00
questStateManager . UpdateQuestCompleted ( oldQuestInstance ) ;
2022-02-20 22:40:35 -05:00
Quest newQuestInstance = questStateManager . GetActiveQuest ( ( ( Quest ) Server . GetStaticActors ( questName ) ) . GetQuestId ( ) ) ;
questScenario [ i ] = newQuestInstance ;
playerWork . questScenario [ i ] = questScenario [ i ] . Id ;
2022-02-19 01:17:50 -05:00
newQuestInstance . OnAccept ( ) ;
Database . SaveQuest ( this , questScenario [ i ] , i ) ;
2022-02-17 13:22:18 -05:00
break ;
2019-06-19 01:10:15 -04:00
}
}
}
2022-02-17 13:22:18 -05:00
public void CompleteQuest ( Quest completed )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
int slot = GetQuestSlot ( completed ) ;
if ( slot > = 0 )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
// Remove the quest from the DB and update client work values
playerWork . questScenarioComplete [ completed . GetQuestId ( ) - 110001 ] = true ;
questScenario [ slot ] = null ;
playerWork . questScenario [ slot ] = 0 ;
SendQuestClientUpdate ( slot ) ;
// Reset active quest and quest state
completed . OnComplete ( ) ;
2022-02-19 01:17:50 -05:00
Database . SaveCompletedQuests ( playerSession . GetActor ( ) ) ;
Database . RemoveQuest ( this , completed . Id ) ;
2022-02-17 13:22:18 -05:00
questStateManager . UpdateQuestCompleted ( completed ) ;
// Msg Player
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 25086 , 0x20 , ( object ) completed . GetQuestId ( ) ) ; // "<Quest> complete!"
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
public bool AbandonQuest ( uint questId )
{
// Check if in an instance
if ( CurrentArea . IsPrivate ( ) )
{
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 25235 , 0x20 ) ; // "Quests cannot be abandoned while from within an instance."
return false ;
}
// Get the quest object
int slot = GetQuestSlot ( questId ) ;
Quest abandoned = questScenario [ slot ] ;
if ( abandoned = = null )
return false ;
// Check if Main Scenario
if ( abandoned . IsMainScenario ( ) )
{
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 25233 , 0x20 ) ; // "Main scenario quests cannot be abandoned."
return false ;
}
// Remove the quest from the DB and update client work values
questScenario [ slot ] = null ;
playerWork . questScenario [ slot ] = 0 ;
SendQuestClientUpdate ( slot ) ;
// Reset active quest and quest state
abandoned . OnAbandon ( ) ;
2022-02-19 01:17:50 -05:00
Database . RemoveQuest ( this , abandoned . Id ) ;
2022-02-17 13:22:18 -05:00
questStateManager . UpdateQuestAbandoned ( ) ;
// Msg Player
SendGameMessage ( this , Server . GetWorldManager ( ) . GetActor ( ) , 25236 , 0x20 , ( object ) abandoned . GetQuestId ( ) ) ; // "<Quest> abandoned."
return true ;
}
public bool HasQuest ( Quest questInstance )
{
return GetQuestSlot ( questInstance ) ! = - 1 ;
}
#endregion
#region Quests - Debug / Misc Related
// Force-Add a quest by Id. Called be debug scripts.
2022-02-20 21:30:09 -05:00
public void AddQuest ( int id , bool isSilent = false )
2019-06-19 01:10:15 -04:00
{
2022-02-20 21:30:09 -05:00
Actor actor = Server . GetStaticActors ( ( 0xA0F00000 | ( uint ) id ) ) ;
2022-02-05 17:48:49 -05:00
AddQuest ( actor . Name , isSilent ) ;
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
// Force-Add a quest by Name. Called be debug scripts. Will try to use an active quest, otherwise adds a new instance.
2019-06-19 01:10:15 -04:00
public void AddQuest ( string name , bool isSilent = false )
{
2022-02-17 13:22:18 -05:00
Quest baseQuest = ( Quest ) Server . GetStaticActors ( name ) ;
Quest activeQuest = questStateManager . GetActiveQuest ( baseQuest . GetQuestId ( ) ) ;
2019-06-19 01:10:15 -04:00
int freeSlot = GetFreeQuestSlot ( ) ;
if ( freeSlot = = - 1 )
return ;
2022-02-05 17:48:49 -05:00
playerWork . questScenario [ freeSlot ] = baseQuest . Id ;
2022-02-17 13:22:18 -05:00
questScenario [ freeSlot ] = activeQuest ? ? new Quest ( this , baseQuest ) ;
if ( activeQuest = = null )
questStateManager . ForceAddActiveQuest ( questScenario [ freeSlot ] ) ;
2022-02-20 21:30:09 -05:00
2019-06-19 01:10:15 -04:00
SendQuestClientUpdate ( freeSlot ) ;
if ( ! isSilent )
{
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 25224 , 0x20 , ( object ) questScenario [ freeSlot ] . GetQuestId ( ) ) ;
}
2022-02-17 13:22:18 -05:00
questScenario [ freeSlot ] . OnAccept ( ) ;
2022-02-20 21:30:09 -05:00
Database . SaveQuest ( this , questScenario [ freeSlot ] , freeSlot ) ;
2019-06-19 01:10:15 -04:00
}
2022-02-20 21:30:09 -05:00
public void RemoveQuest ( int id )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
for ( int i = 0 ; i < questScenario . Length ; i + + )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
if ( questScenario [ i ] ! = null & & questScenario [ i ] . Id = = ( 0xA0F00000 | id ) )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
Database . RemoveQuest ( this , questScenario [ i ] . Id ) ;
questScenario [ i ] = null ;
playerWork . questScenario [ i ] = 0 ;
SendQuestClientUpdate ( i ) ;
break ;
2019-06-19 01:10:15 -04:00
}
}
}
2022-02-17 13:22:18 -05:00
public void RemoveQuest ( string name )
2019-06-19 01:10:15 -04:00
{
2022-02-06 12:12:26 -05:00
for ( int i = 0 ; i < questScenario . Length ; i + + )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
if ( questScenario [ i ] ! = null & & questScenario [ i ] . Name . ToLower ( ) . Equals ( name . ToLower ( ) ) )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
Database . RemoveQuest ( this , questScenario [ i ] . Id ) ;
questScenario [ i ] = null ;
playerWork . questScenario [ i ] = 0 ;
2022-02-06 12:12:26 -05:00
SendQuestClientUpdate ( i ) ;
break ;
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
}
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
public bool HasQuest ( string name )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
for ( int i = 0 ; i < questScenario . Length ; i + + )
{
if ( questScenario [ i ] ! = null & & questScenario [ i ] . Name . ToLower ( ) . Equals ( name . ToLower ( ) ) )
return true ;
}
2019-06-19 01:10:15 -04:00
2022-02-17 13:22:18 -05:00
return false ;
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
public bool HasQuest ( uint id )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
for ( int i = 0 ; i < questScenario . Length ; i + + )
{
if ( questScenario [ i ] ! = null & & questScenario [ i ] . Id = = ( 0xA0F00000 | id ) )
return true ;
}
2019-06-19 01:10:15 -04:00
2022-02-17 13:22:18 -05:00
return false ;
2019-06-19 01:10:15 -04:00
}
2022-02-20 12:39:34 -05:00
public bool IsQuestCompleted ( uint id )
{
return questStateManager . IsQuestComplete ( id ) ;
}
public void SetQuestComplete ( uint id , bool flag )
{
if ( flag )
{
Quest currentQuest = GetQuest ( id ) ;
if ( currentQuest ! = null )
{
CompleteQuest ( currentQuest ) ;
return ;
}
}
questStateManager . ForceQuestCompleteFlag ( id , flag ) ;
}
2019-06-19 01:10:15 -04:00
public Quest GetQuest ( uint id )
{
for ( int i = 0 ; i < questScenario . Length ; i + + )
{
2022-02-05 17:48:49 -05:00
if ( questScenario [ i ] ! = null & & questScenario [ i ] . Id = = ( 0xA0F00000 | id ) )
2019-06-19 01:10:15 -04:00
return questScenario [ i ] ;
}
return null ;
}
public Quest GetQuest ( string name )
{
for ( int i = 0 ; i < questScenario . Length ; i + + )
{
2022-02-05 17:48:49 -05:00
if ( questScenario [ i ] ! = null & & questScenario [ i ] . Name . ToLower ( ) . Equals ( name . ToLower ( ) ) )
2019-06-19 01:10:15 -04:00
return questScenario [ i ] ;
}
return null ;
}
2022-02-17 13:22:18 -05:00
public int GetQuestSlot ( Quest quest )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
for ( int slot = 0 ; slot < questScenario . Length ; slot + + )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
if ( questScenario [ slot ] ! = null & & questScenario [ slot ] . Id = = quest . Id )
return slot ;
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
return - 1 ;
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
public int GetQuestSlot ( uint id )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
for ( int slot = 0 ; slot < questScenario . Length ; slot + + )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
if ( questScenario [ slot ] ! = null & & questScenario [ slot ] . GetQuestId ( ) = = id )
return slot ;
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
return - 1 ;
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
#endregion
2019-06-19 01:10:15 -04:00
2022-02-17 13:22:18 -05:00
#region Guildleves
public void AddGuildleve ( uint id )
2022-02-06 12:12:26 -05:00
{
2022-02-17 13:22:18 -05:00
int freeSlot = GetFreeGuildleveSlot ( ) ;
if ( freeSlot = = - 1 )
return ;
work . guildleveId [ freeSlot ] = ( ushort ) id ;
Database . SaveGuildleve ( this , id , freeSlot ) ;
SendGuildleveClientUpdate ( freeSlot ) ;
2022-02-06 12:12:26 -05:00
}
2022-02-17 13:22:18 -05:00
public void MarkGuildleve ( uint id , bool abandoned , bool completed )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
if ( HasGuildleve ( id ) )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
for ( int i = 0 ; i < work . guildleveId . Length ; i + + )
{
if ( work . guildleveId [ i ] = = id )
{
work . guildleveChecked [ i ] = completed ;
work . guildleveDone [ i ] = abandoned ;
Database . MarkGuildleve ( this , id , abandoned , completed ) ;
SendGuildleveMarkClientUpdate ( i ) ;
}
}
2019-06-19 01:10:15 -04:00
}
}
2022-02-17 13:22:18 -05:00
public void RemoveGuildleve ( uint id )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
if ( HasGuildleve ( id ) )
2019-06-19 01:10:15 -04:00
{
2022-02-17 13:22:18 -05:00
for ( int i = 0 ; i < work . guildleveId . Length ; i + + )
{
if ( work . guildleveId [ i ] = = id )
{
Database . RemoveGuildleve ( this , id ) ;
work . guildleveId [ i ] = 0 ;
SendGuildleveClientUpdate ( i ) ;
break ;
}
}
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
}
2019-06-19 01:10:15 -04:00
2022-02-17 13:22:18 -05:00
public bool HasGuildleve ( uint id )
{
for ( int i = 0 ; i < work . guildleveId . Length ; i + + )
{
if ( work . guildleveId [ i ] = = id )
return true ;
}
return false ;
2019-06-19 01:10:15 -04:00
}
2022-02-17 13:22:18 -05:00
#endregion
2019-06-19 01:10:15 -04:00
2022-01-24 23:49:10 -05:00
public Quest GetDefaultTalkQuest ( Npc npc )
{
Quest defaultTalk = null ;
2022-02-05 14:05:32 -05:00
switch ( npc . CurrentArea . RegionId )
2022-01-24 23:49:10 -05:00
{
case 101 :
defaultTalk = ( Quest ) Server . GetStaticActors ( "DftSea" ) ;
break ;
case 102 :
defaultTalk = ( Quest ) Server . GetStaticActors ( "DftRoc" ) ;
break ;
case 103 :
defaultTalk = ( Quest ) Server . GetStaticActors ( "DftFst" ) ;
break ;
case 104 :
2022-02-08 17:35:29 -05:00
case 107 :
2022-01-24 23:49:10 -05:00
defaultTalk = ( Quest ) Server . GetStaticActors ( "DftWil" ) ;
break ;
case 105 :
defaultTalk = ( Quest ) Server . GetStaticActors ( "DftLak" ) ;
break ;
case 805 :
defaultTalk = ( Quest ) Server . GetStaticActors ( "DftSrt" ) ;
break ;
}
2022-03-03 19:34:33 -05:00
if ( defaultTalk ! = null & & defaultTalk . IsQuestENPCByScript ( this , npc ) )
2022-01-24 23:49:10 -05:00
return defaultTalk ;
2022-02-07 05:13:34 -05:00
2022-01-24 23:49:10 -05:00
return null ;
}
public Quest GetTutorialQuest ( Npc npc )
{
2022-02-20 23:03:16 -05:00
if ( npc . CurrentArea . RegionId = = 101 | | npc . CurrentArea . RegionId = = 103 | | npc . CurrentArea . RegionId = = 104 )
2022-01-24 23:49:10 -05:00
{
2022-02-20 23:03:16 -05:00
switch ( npc . GetActorClassId ( ) )
{
case 1000137 :
return ( Quest ) Server . GetStaticActors ( "Trl0l1" ) ;
case 1000230 :
return ( Quest ) Server . GetStaticActors ( "Trl0g1" ) ;
case 1000841 :
return ( Quest ) Server . GetStaticActors ( "Trl0u1" ) ;
}
2022-01-24 23:49:10 -05:00
}
return null ;
}
2022-02-16 15:30:32 -05:00
public Quest [ ] GetQuestsForNpc ( Npc npc )
{
2022-02-17 13:22:18 -05:00
Quest [ ] quests = questStateManager . GetQuestsForNpc ( npc ) ;
Array . Sort ( quests , ( q1 , q2 ) = > ( q1 . HasData ( ) ? 1 : 0 ) - ( q2 . HasData ( ) ? 1 : 0 ) ) ;
return quests ;
2022-02-16 15:30:32 -05:00
}
2022-03-05 01:02:41 -05:00
public bool HandleNpcLs ( uint id )
2022-01-24 23:49:10 -05:00
{
foreach ( Quest quest in questScenario )
{
2022-03-05 01:02:41 -05:00
if ( quest ! = null & & quest . HasNpcLsMsgs ( id ) )
{
quest . OnNpcLS ( this ) ;
return true ;
}
2022-01-24 23:49:10 -05:00
}
2022-03-05 01:02:41 -05:00
return false ;
2022-01-24 23:49:10 -05:00
}
2022-03-05 01:02:41 -05:00
public void SetNpcLs ( uint npcLSId , uint state )
2019-06-19 01:10:15 -04:00
{
bool isCalling , isExtra ;
isCalling = isExtra = false ;
2022-02-07 05:13:34 -05:00
if ( npcLSId < 1 | | npcLSId > 40 )
return ;
npcLSId - - ;
2019-06-19 01:10:15 -04:00
switch ( state )
{
case NPCLS_INACTIVE :
if ( playerWork . npcLinkshellChatExtra [ npcLSId ] = = true & & playerWork . npcLinkshellChatCalling [ npcLSId ] = = false )
return ;
isExtra = true ;
break ;
case NPCLS_ACTIVE :
if ( playerWork . npcLinkshellChatExtra [ npcLSId ] = = false & & playerWork . npcLinkshellChatCalling [ npcLSId ] = = true )
return ;
isCalling = true ;
break ;
case NPCLS_ALERT :
if ( playerWork . npcLinkshellChatExtra [ npcLSId ] = = true & & playerWork . npcLinkshellChatCalling [ npcLSId ] = = true )
return ;
isExtra = isCalling = true ;
break ;
}
playerWork . npcLinkshellChatExtra [ npcLSId ] = isExtra ;
playerWork . npcLinkshellChatCalling [ npcLSId ] = isCalling ;
Database . SaveNpcLS ( this , npcLSId , isCalling , isExtra ) ;
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil ( "playerWork/npcLinkshellChat" , this ) ;
propPacketUtil . AddProperty ( String . Format ( "playerWork.npcLinkshellChatExtra[{0}]" , npcLSId ) ) ;
propPacketUtil . AddProperty ( String . Format ( "playerWork.npcLinkshellChatCalling[{0}]" , npcLSId ) ) ;
QueuePackets ( propPacketUtil . Done ( ) ) ;
}
private void SendQuestClientUpdate ( int slot )
{
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil ( "playerWork/journal" , this ) ;
propPacketUtil . AddProperty ( String . Format ( "playerWork.questScenario[{0}]" , slot ) ) ;
QueuePackets ( propPacketUtil . Done ( ) ) ;
}
private void SendGuildleveClientUpdate ( int slot )
{
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil ( "work/guildleve" , this ) ;
propPacketUtil . AddProperty ( String . Format ( "work.guildleveId[{0}]" , slot ) ) ;
QueuePackets ( propPacketUtil . Done ( ) ) ;
}
private void SendGuildleveMarkClientUpdate ( int slot )
{
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil ( "work/guildleve" , this ) ;
propPacketUtil . AddProperty ( String . Format ( "work.guildleveDone[{0}]" , slot ) ) ;
propPacketUtil . AddProperty ( String . Format ( "work.guildleveChecked[{0}]" , slot ) ) ;
QueuePackets ( propPacketUtil . Done ( ) ) ;
}
public void SendStartCastbar ( uint commandId , uint endTime )
{
playerWork . castCommandClient = commandId ;
playerWork . castEndClient = endTime ;
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil ( "playerWork/castState" , this ) ;
propPacketUtil . AddProperty ( "playerWork.castEndClient" ) ;
propPacketUtil . AddProperty ( "playerWork.castCommandClient" ) ;
QueuePackets ( propPacketUtil . Done ( ) ) ;
}
public void SendEndCastbar ( )
{
playerWork . castCommandClient = 0 ;
playerWork . castEndClient = 0 ;
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil ( "playerWork/castState" , this ) ;
propPacketUtil . AddProperty ( "playerWork.castCommandClient" ) ;
QueuePackets ( propPacketUtil . Done ( ) ) ;
}
public void SetLoginDirector ( Director director )
{
if ( ownedDirectors . Contains ( director ) )
loginInitDirector = director ;
}
public void AddDirector ( Director director , bool spawnImmediatly = false )
{
if ( ! ownedDirectors . Contains ( director ) )
{
ownedDirectors . Add ( director ) ;
director . AddMember ( this ) ;
}
}
public void SendDirectorPackets ( Director director )
{
QueuePackets ( director . GetSpawnPackets ( ) ) ;
QueuePackets ( director . GetInitPackets ( ) ) ;
}
public void RemoveDirector ( Director director )
{
if ( ownedDirectors . Contains ( director ) )
{
2022-02-05 17:48:49 -05:00
QueuePacket ( RemoveActorPacket . BuildPacket ( director . Id ) ) ;
2019-06-19 01:10:15 -04:00
ownedDirectors . Remove ( director ) ;
director . RemoveMember ( this ) ;
}
}
public GuildleveDirector GetGuildleveDirector ( )
{
foreach ( Director d in ownedDirectors )
{
if ( d is GuildleveDirector )
return ( GuildleveDirector ) d ;
}
return null ;
}
public Director GetDirector ( string directorName )
{
foreach ( Director d in ownedDirectors )
{
if ( d . GetScriptPath ( ) . Equals ( directorName ) )
return d ;
}
return null ;
}
public Director GetDirector ( uint id )
{
foreach ( Director d in ownedDirectors )
{
2022-02-05 17:48:49 -05:00
if ( d . Id = = id )
2019-06-19 01:10:15 -04:00
return d ;
}
return null ;
}
public void ExaminePlayer ( Actor examinee )
{
Player toBeExamined ;
if ( examinee is Player )
toBeExamined = ( Player ) examinee ;
else
return ;
2022-02-05 17:48:49 -05:00
QueuePacket ( InventoryBeginChangePacket . BuildPacket ( toBeExamined . Id , true ) ) ;
2019-06-19 01:10:15 -04:00
toBeExamined . GetEquipment ( ) . SendUpdateAsItemPackage ( this , ItemPackage . MAXSIZE_EQUIPMENT_OTHERPLAYER , ItemPackage . EQUIPMENT_OTHERPLAYER ) ;
2022-02-05 17:48:49 -05:00
QueuePacket ( InventoryEndChangePacket . BuildPacket ( toBeExamined . Id ) ) ;
2019-06-19 01:10:15 -04:00
}
public void SendDataPacket ( params object [ ] parameters )
{
List < LuaParam > lParams = LuaUtils . CreateLuaParamList ( parameters ) ;
2022-02-05 17:48:49 -05:00
SubPacket spacket = GenericDataPacket . BuildPacket ( Id , lParams ) ;
2019-06-19 01:10:15 -04:00
spacket . DebugPrintSubPacket ( ) ;
QueuePacket ( spacket ) ;
}
public void StartEvent ( Actor owner , EventStartPacket start )
{
2019-07-26 23:24:06 -04:00
currentEventOwner = start . ownerActorID ;
currentEventName = start . eventName ;
currentEventType = start . eventType ;
2019-06-19 01:10:15 -04:00
LuaEngine . GetInstance ( ) . EventStarted ( this , owner , start ) ;
}
public void UpdateEvent ( EventUpdatePacket update )
{
LuaEngine . GetInstance ( ) . OnEventUpdate ( this , update . luaParams ) ;
}
2019-07-26 23:24:06 -04:00
public void KickEvent ( Actor actor , string eventName , params object [ ] parameters )
2019-06-19 01:10:15 -04:00
{
if ( actor = = null )
return ;
List < LuaParam > lParams = LuaUtils . CreateLuaParamList ( parameters ) ;
2022-02-05 17:48:49 -05:00
SubPacket spacket = KickEventPacket . BuildPacket ( Id , actor . Id , eventName , 5 , lParams ) ;
2019-06-19 01:10:15 -04:00
spacket . DebugPrintSubPacket ( ) ;
QueuePacket ( spacket ) ;
}
2019-07-26 23:24:06 -04:00
public void KickEventSpecial ( Actor actor , uint unknown , string eventName , params object [ ] parameters )
2019-06-19 01:10:15 -04:00
{
if ( actor = = null )
return ;
List < LuaParam > lParams = LuaUtils . CreateLuaParamList ( parameters ) ;
2022-02-05 17:48:49 -05:00
SubPacket spacket = KickEventPacket . BuildPacket ( Id , actor . Id , eventName , 0 , lParams ) ;
2019-06-19 01:10:15 -04:00
spacket . DebugPrintSubPacket ( ) ;
QueuePacket ( spacket ) ;
}
2020-02-28 00:05:46 -05:00
public void SetEventStatus ( Actor actor , string conditionName , bool enabled , byte type )
2019-06-19 01:10:15 -04:00
{
2022-02-05 17:48:49 -05:00
QueuePacket ( SetEventStatusPacket . BuildPacket ( actor . Id , enabled , type , conditionName ) ) ;
2019-06-19 01:10:15 -04:00
}
public void RunEventFunction ( string functionName , params object [ ] parameters )
{
List < LuaParam > lParams = LuaUtils . CreateLuaParamList ( parameters ) ;
2022-02-05 17:48:49 -05:00
SubPacket spacket = RunEventFunctionPacket . BuildPacket ( Id , currentEventOwner , currentEventName , currentEventType , functionName , lParams ) ;
2019-06-19 01:10:15 -04:00
spacket . DebugPrintSubPacket ( ) ;
QueuePacket ( spacket ) ;
}
public void EndEvent ( )
{
2022-02-05 17:48:49 -05:00
SubPacket p = EndEventPacket . BuildPacket ( Id , currentEventOwner , currentEventName , currentEventType ) ;
2019-06-19 01:10:15 -04:00
p . DebugPrintSubPacket ( ) ;
QueuePacket ( p ) ;
currentEventOwner = 0 ;
currentEventName = "" ;
2019-07-26 23:24:06 -04:00
currentEventType = 0 ;
2019-06-19 01:10:15 -04:00
currentEventRunning = null ;
}
public void BroadcastCountdown ( byte countdownLength , ulong syncTime )
{
2022-02-05 17:48:49 -05:00
BroadcastPacket ( StartCountdownPacket . BuildPacket ( Id , countdownLength , syncTime , "Go!" ) , true ) ;
2019-06-19 01:10:15 -04:00
}
2019-07-27 21:54:05 -04:00
public void SendInstanceUpdate ( bool force = false )
2019-06-19 01:10:15 -04:00
{
//Server.GetWorldManager().SeamlessCheck(this);
//Update Instance
List < Actor > aroundMe = new List < Actor > ( ) ;
2022-02-05 14:05:32 -05:00
if ( CurrentArea ! = null )
aroundMe . AddRange ( CurrentArea . GetActorsAroundActor ( this , 50 ) ) ;
2019-06-19 01:10:15 -04:00
if ( zone2 ! = null )
aroundMe . AddRange ( zone2 . GetActorsAroundActor ( this , 50 ) ) ;
2019-07-27 21:54:05 -04:00
playerSession . UpdateInstance ( aroundMe , force ) ;
2019-06-19 01:10:15 -04:00
}
public bool IsInParty ( )
{
return currentParty ! = null ;
}
public bool IsPartyLeader ( )
{
if ( IsInParty ( ) )
{
Party party = ( Party ) currentParty ;
2022-02-05 17:48:49 -05:00
return party . GetLeader ( ) = = Id ;
2019-06-19 01:10:15 -04:00
}
else
return false ;
}
public void PartyOustPlayer ( uint actorId )
{
SubPacket oustPacket = PartyModifyPacket . BuildPacket ( playerSession , 1 , actorId ) ;
QueuePacket ( oustPacket ) ;
}
public void PartyOustPlayer ( string name )
{
SubPacket oustPacket = PartyModifyPacket . BuildPacket ( playerSession , 1 , name ) ;
QueuePacket ( oustPacket ) ;
}
public void PartyLeave ( )
{
SubPacket leavePacket = PartyLeavePacket . BuildPacket ( playerSession , false ) ;
QueuePacket ( leavePacket ) ;
}
public void PartyDisband ( )
{
SubPacket disbandPacket = PartyLeavePacket . BuildPacket ( playerSession , true ) ;
QueuePacket ( disbandPacket ) ;
}
public void PartyPromote ( uint actorId )
{
SubPacket promotePacket = PartyModifyPacket . BuildPacket ( playerSession , 0 , actorId ) ;
QueuePacket ( promotePacket ) ;
}
public void PartyPromote ( string name )
{
SubPacket promotePacket = PartyModifyPacket . BuildPacket ( playerSession , 0 , name ) ;
QueuePacket ( promotePacket ) ;
}
//A party member list packet came, set the party
public void SetParty ( Party group )
{
if ( group is Party & & currentParty ! = group )
{
RemoveFromCurrentPartyAndCleanup ( ) ;
currentParty = group ;
}
}
//Removes the player from the party and cleans it up if needed
public void RemoveFromCurrentPartyAndCleanup ( )
{
if ( currentParty = = null )
return ;
Party partyGroup = ( Party ) currentParty ;
for ( int i = 0 ; i < partyGroup . members . Count ; i + + )
{
2022-02-05 17:48:49 -05:00
if ( partyGroup . members [ i ] = = Id )
2019-06-19 01:10:15 -04:00
{
partyGroup . members . RemoveAt ( i ) ;
break ;
}
}
//currentParty.members.Remove(this);
if ( partyGroup . members . Count = = 0 )
Server . GetWorldManager ( ) . NoMembersInParty ( ( Party ) currentParty ) ;
currentParty = null ;
}
public void IssueChocobo ( byte appearanceId , string nameResponse )
{
Database . IssuePlayerChocobo ( this , appearanceId , nameResponse ) ;
hasChocobo = true ;
chocoboAppearance = appearanceId ;
chocoboName = nameResponse ;
2019-07-25 19:44:22 -04:00
2022-02-05 17:48:49 -05:00
QueuePacket ( SetChocoboNamePacket . BuildPacket ( Id , chocoboName ) ) ;
QueuePacket ( SetHasChocoboPacket . BuildPacket ( Id , hasChocobo ) ) ;
2019-06-19 01:10:15 -04:00
}
public void ChangeChocoboAppearance ( byte appearanceId )
{
Database . ChangePlayerChocoboAppearance ( this , appearanceId ) ;
chocoboAppearance = appearanceId ;
}
2019-07-11 12:13:23 -04:00
public void StartChocoboRental ( byte numMins )
{
rentalExpireTime = Utils . UnixTimeStampUTC ( ) + ( ( uint ) numMins * 60 ) ;
rentalMinLeft = numMins ;
}
public bool IsChocoboRentalActive ( )
{
return rentalExpireTime ! = 0 ;
}
2019-06-19 01:10:15 -04:00
public Retainer SpawnMyRetainer ( Npc bell , int retainerIndex )
{
Retainer retainer = Database . LoadRetainer ( this , retainerIndex ) ;
float distance = ( float ) Math . Sqrt ( ( ( positionX - bell . positionX ) * ( positionX - bell . positionX ) ) + ( ( positionZ - bell . positionZ ) * ( positionZ - bell . positionZ ) ) ) ;
float posX = bell . positionX - ( ( - 1.0f * ( bell . positionX - positionX ) ) / distance ) ;
float posZ = bell . positionZ - ( ( - 1.0f * ( bell . positionZ - positionZ ) ) / distance ) ;
retainer . positionX = posX ;
retainer . positionY = positionY ;
retainer . positionZ = posZ ;
retainer . rotation = ( float ) Math . Atan2 ( positionX - posX , positionZ - posZ ) ;
retainerMeetingGroup = new RetainerMeetingRelationGroup ( 5555 , this , retainer ) ;
retainerMeetingGroup . SendGroupPackets ( playerSession ) ;
currentSpawnedRetainer = retainer ;
sentRetainerSpawn = false ;
return retainer ;
}
public void DespawnMyRetainer ( )
{
if ( currentSpawnedRetainer ! = null )
{
currentSpawnedRetainer = null ;
retainerMeetingGroup . SendDeletePacket ( playerSession ) ;
retainerMeetingGroup = null ;
}
}
public override void Update ( DateTime tick )
{
2019-07-11 12:13:23 -04:00
// Chocobo Rental Expirey
if ( rentalExpireTime ! = 0 )
{
uint tickUTC = Utils . UnixTimeStampUTC ( tick ) ;
//Rental has expired, dismount
if ( rentalExpireTime < = tickUTC )
{
rentalExpireTime = 0 ;
rentalMinLeft = 0 ;
2022-02-05 14:05:32 -05:00
ChangeMusic ( CurrentArea . bgmDay ) ;
2019-07-11 12:13:23 -04:00
SetMountState ( 0 ) ;
ChangeSpeed ( 0.0f , 2.0f , 5.0f , 5.0f ) ;
ChangeState ( 0 ) ;
}
else
{
rentalMinLeft = ( byte ) ( ( rentalExpireTime - tickUTC ) / 60 ) ;
}
}
2019-06-19 01:10:15 -04:00
aiContainer . Update ( tick ) ;
2019-07-11 12:13:23 -04:00
statusEffects . Update ( tick ) ;
2019-06-19 01:10:15 -04:00
}
public override void PostUpdate ( DateTime tick , List < SubPacket > packets = null )
{
// todo: is this correct?
if ( this . playerSession . isUpdatesLocked )
2019-07-11 12:13:23 -04:00
return ;
2019-06-19 01:10:15 -04:00
// todo: should probably add another flag for battleTemp since all this uses reflection
packets = new List < SubPacket > ( ) ;
// we only want the latest update for the player
if ( ( updateFlags & ActorUpdateFlags . Position ) ! = 0 )
{
if ( positionUpdates . Count > 1 )
positionUpdates . RemoveRange ( 1 , positionUpdates . Count - 1 ) ;
}
if ( ( updateFlags & ActorUpdateFlags . HpTpMp ) ! = 0 )
{
var propPacketUtil = new ActorPropertyPacketUtil ( "charaWork/stateAtQuicklyForAll" , this ) ;
// todo: should this be using job as index?
propPacketUtil . AddProperty ( "charaWork.parameterSave.hp[0]" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterSave.hpMax[0]" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterSave.state_mainSkill[0]" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterSave.state_mainSkillLevel" ) ;
packets . AddRange ( propPacketUtil . Done ( ) ) ;
}
if ( ( updateFlags & ActorUpdateFlags . Stats ) ! = 0 )
{
var propPacketUtil = new ActorPropertyPacketUtil ( "charaWork/battleParameter" , this ) ;
for ( uint i = 0 ; i < 35 ; i + + )
{
if ( GetMod ( i ) ! = charaWork . battleTemp . generalParameter [ i ] )
{
charaWork . battleTemp . generalParameter [ i ] = ( short ) GetMod ( i ) ;
propPacketUtil . AddProperty ( String . Format ( "charaWork.battleTemp.generalParameter[{0}]" , i ) ) ;
}
}
QueuePackets ( propPacketUtil . Done ( ) ) ;
}
if ( ( updateFlags & ActorUpdateFlags . Hotbar ) ! = 0 )
{
UpdateHotbar ( hotbarSlotsToUpdate ) ;
hotbarSlotsToUpdate . Clear ( ) ;
updateFlags ^ = ActorUpdateFlags . Hotbar ;
}
2019-07-11 12:13:23 -04:00
2019-06-19 01:10:15 -04:00
base . PostUpdate ( tick , packets ) ;
}
public override void Die ( DateTime tick , CommandResultContainer actionContainer = null )
{
// todo: death timer
aiContainer . InternalDie ( tick , 60 ) ;
}
//Update commands and recast timers for the entire hotbar
public void UpdateHotbar ( )
{
for ( ushort i = charaWork . commandBorder ; i < charaWork . commandBorder + 30 ; i + + )
{
hotbarSlotsToUpdate . Add ( i ) ;
}
updateFlags | = ActorUpdateFlags . Hotbar ;
}
//Updates the hotbar and recast timers for only certain hotbar slots
public void UpdateHotbar ( List < ushort > slotsToUpdate )
{
UpdateHotbarCommands ( slotsToUpdate ) ;
UpdateRecastTimers ( slotsToUpdate ) ;
}
//Update command ids for the passed in hotbar slots
public void UpdateHotbarCommands ( List < ushort > slotsToUpdate )
{
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil ( "charaWork/command" , this ) ;
foreach ( ushort slot in slotsToUpdate )
{
propPacketUtil . AddProperty ( String . Format ( "charaWork.command[{0}]" , slot ) ) ;
propPacketUtil . AddProperty ( String . Format ( "charaWork.commandCategory[{0}]" , slot ) ) ;
}
propPacketUtil . NewTarget ( "charaWork/commandDetailForSelf" ) ;
//Enable or disable slots based on whether there is an ability in that slot
foreach ( ushort slot in slotsToUpdate )
{
charaWork . parameterSave . commandSlot_compatibility [ slot - charaWork . commandBorder ] = charaWork . command [ slot ] ! = 0 ;
propPacketUtil . AddProperty ( String . Format ( "charaWork.parameterSave.commandSlot_compatibility[{0}]" , slot - charaWork . commandBorder ) ) ;
}
QueuePackets ( propPacketUtil . Done ( ) ) ;
//QueuePackets(compatibiltyUtil.Done());
}
//Update recast timers for the passed in hotbar slots
public void UpdateRecastTimers ( List < ushort > slotsToUpdate )
{
ActorPropertyPacketUtil recastPacketUtil = new ActorPropertyPacketUtil ( "charaWork/commandDetailForSelf" , this ) ;
foreach ( ushort slot in slotsToUpdate )
{
recastPacketUtil . AddProperty ( String . Format ( "charaWork.parameterTemp.maxCommandRecastTime[{0}]" , slot - charaWork . commandBorder ) ) ;
recastPacketUtil . AddProperty ( String . Format ( "charaWork.parameterSave.commandSlot_recastTime[{0}]" , slot - charaWork . commandBorder ) ) ;
}
QueuePackets ( recastPacketUtil . Done ( ) ) ;
}
//Find the first open slot in classId's hotbar and equip an ability there.
public void EquipAbilityInFirstOpenSlot ( byte classId , uint commandId , bool printMessage = true )
{
//Find first open slot on class's hotbar slot, then call EquipAbility with that slot.
ushort hotbarSlot = 0 ;
//If the class we're equipping for is the current class, we can just look at charawork.command
if ( classId = = charaWork . parameterSave . state_mainSkill [ 0 ] )
hotbarSlot = FindFirstCommandSlotById ( 0 ) ;
//Otherwise, we need to check the database.
else
hotbarSlot = ( ushort ) ( Database . FindFirstCommandSlot ( this , classId ) + charaWork . commandBorder ) ;
EquipAbility ( classId , commandId , hotbarSlot , printMessage ) ;
}
//Add commandId to classId's hotbar at hotbarSlot.
//If classId is not the current class, do it in the database
//hotbarSlot starts at 32
public void EquipAbility ( byte classId , uint commandId , ushort hotbarSlot , bool printMessage = true )
{
var ability = Server . GetWorldManager ( ) . GetBattleCommand ( commandId ) ;
uint trueCommandId = 0xA0F00000 | commandId ;
ushort lowHotbarSlot = ( ushort ) ( hotbarSlot - charaWork . commandBorder ) ;
ushort maxRecastTime = ( ushort ) ( ability ! = null ? ability . maxRecastTimeSeconds : 5 ) ;
uint recastEnd = Utils . UnixTimeStampUTC ( ) + maxRecastTime ;
Database . EquipAbility ( this , classId , ( ushort ) ( hotbarSlot - charaWork . commandBorder ) , commandId , recastEnd ) ;
//If the class we're equipping for is the current class (need to find out if state_mainSkill is supposed to change when you're a job)
//then equip the ability in charawork.commands and save in databse, otherwise just save in database
if ( classId = = GetCurrentClassOrJob ( ) )
{
charaWork . command [ hotbarSlot ] = trueCommandId ;
charaWork . commandCategory [ hotbarSlot ] = 1 ;
charaWork . parameterTemp . maxCommandRecastTime [ lowHotbarSlot ] = maxRecastTime ;
charaWork . parameterSave . commandSlot_recastTime [ lowHotbarSlot ] = recastEnd ;
hotbarSlotsToUpdate . Add ( hotbarSlot ) ;
updateFlags | = ActorUpdateFlags . Hotbar ;
}
if ( printMessage )
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 30603 , 0x20 , 0 , commandId ) ;
}
//Doesn't take a classId because the only way to swap abilities is through the ability equip widget oe /eaction, which only apply to current class
//hotbarSlot 1 and 2 are 32-indexed.
public void SwapAbilities ( ushort hotbarSlot1 , ushort hotbarSlot2 )
{
//0 indexed hotbar slots for saving to database and recast timers
uint lowHotbarSlot1 = ( ushort ) ( hotbarSlot1 - charaWork . commandBorder ) ;
uint lowHotbarSlot2 = ( ushort ) ( hotbarSlot2 - charaWork . commandBorder ) ;
//Store information about first command
uint commandId = charaWork . command [ hotbarSlot1 ] ;
uint recastEnd = charaWork . parameterSave . commandSlot_recastTime [ lowHotbarSlot1 ] ;
ushort recastMax = charaWork . parameterTemp . maxCommandRecastTime [ lowHotbarSlot1 ] ;
//Move second command's info to first hotbar slot
charaWork . command [ hotbarSlot1 ] = charaWork . command [ hotbarSlot2 ] ;
charaWork . parameterTemp . maxCommandRecastTime [ lowHotbarSlot1 ] = charaWork . parameterTemp . maxCommandRecastTime [ lowHotbarSlot2 ] ;
charaWork . parameterSave . commandSlot_recastTime [ lowHotbarSlot1 ] = charaWork . parameterSave . commandSlot_recastTime [ lowHotbarSlot2 ] ;
//Move first command's info to second slot
charaWork . command [ hotbarSlot2 ] = commandId ;
charaWork . parameterTemp . maxCommandRecastTime [ lowHotbarSlot2 ] = recastMax ;
charaWork . parameterSave . commandSlot_recastTime [ lowHotbarSlot2 ] = recastEnd ;
//Save changes to both slots
Database . EquipAbility ( this , GetCurrentClassOrJob ( ) , ( ushort ) ( lowHotbarSlot1 ) , 0xA0F00000 ^ charaWork . command [ hotbarSlot1 ] , charaWork . parameterSave . commandSlot_recastTime [ lowHotbarSlot1 ] ) ;
Database . EquipAbility ( this , GetCurrentClassOrJob ( ) , ( ushort ) ( lowHotbarSlot2 ) , 0xA0F00000 ^ charaWork . command [ hotbarSlot2 ] , charaWork . parameterSave . commandSlot_recastTime [ lowHotbarSlot2 ] ) ;
//Update slots on client
hotbarSlotsToUpdate . Add ( hotbarSlot1 ) ;
hotbarSlotsToUpdate . Add ( hotbarSlot2 ) ;
updateFlags | = ActorUpdateFlags . Hotbar ;
}
public void UnequipAbility ( ushort hotbarSlot , bool printMessage = true )
{
2019-06-08 21:11:51 -07:00
ushort trueHotbarSlot = ( ushort ) ( hotbarSlot + charaWork . commandBorder - 1 ) ;
2019-06-19 01:10:15 -04:00
uint commandId = charaWork . command [ trueHotbarSlot ] ;
Database . UnequipAbility ( this , hotbarSlot ) ;
charaWork . command [ trueHotbarSlot ] = 0 ;
hotbarSlotsToUpdate . Add ( trueHotbarSlot ) ;
if ( printMessage & & commandId ! = 0 )
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 30604 , 0x20 , 0 , 0xA0F00000 ^ commandId ) ;
updateFlags | = ActorUpdateFlags . Hotbar ;
}
//Finds the first hotbar slot with a given commandId.
//If the returned value is outside the hotbar, it indicates it wasn't found.
public ushort FindFirstCommandSlotById ( uint commandId )
{
if ( commandId ! = 0 )
commandId | = 0xA0F00000 ;
ushort firstSlot = ( ushort ) ( charaWork . commandBorder + 30 ) ;
for ( ushort i = charaWork . commandBorder ; i < charaWork . commandBorder + 30 ; i + + )
{
if ( charaWork . command [ i ] = = commandId )
{
firstSlot = i ;
break ;
}
}
return firstSlot ;
}
private void UpdateHotbarTimer ( uint commandId , uint recastTimeMs )
{
ushort slot = FindFirstCommandSlotById ( commandId ) ;
charaWork . parameterSave . commandSlot_recastTime [ slot - charaWork . commandBorder ] = Utils . UnixTimeStampUTC ( DateTime . Now . AddMilliseconds ( recastTimeMs ) ) ;
var slots = new List < ushort > ( ) ;
slots . Add ( slot ) ;
UpdateRecastTimers ( slots ) ;
}
private uint GetHotbarTimer ( uint commandId )
{
ushort slot = FindFirstCommandSlotById ( commandId ) ;
return charaWork . parameterSave . commandSlot_recastTime [ slot - charaWork . commandBorder ] ;
}
public override void Cast ( uint spellId , uint targetId = 0 )
{
if ( aiContainer . CanChangeState ( ) )
2022-02-05 14:05:32 -05:00
aiContainer . Cast ( CurrentArea . FindActorInArea < Character > ( targetId = = 0 ? currentTarget : targetId ) , spellId ) ;
2019-06-19 01:10:15 -04:00
else if ( aiContainer . IsCurrentState < MagicState > ( ) )
// You are already casting.
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 32536 , 0x20 ) ;
else
// Please wait a moment and try again.
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 32535 , 0x20 ) ;
}
public override void Ability ( uint abilityId , uint targetId = 0 )
{
if ( aiContainer . CanChangeState ( ) )
2022-02-05 14:05:32 -05:00
aiContainer . Ability ( CurrentArea . FindActorInArea < Character > ( targetId = = 0 ? currentTarget : targetId ) , abilityId ) ;
2019-06-19 01:10:15 -04:00
else
// Please wait a moment and try again.
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 32535 , 0x20 ) ;
}
public override void WeaponSkill ( uint skillId , uint targetId = 0 )
{
if ( aiContainer . CanChangeState ( ) )
2022-02-05 14:05:32 -05:00
aiContainer . WeaponSkill ( CurrentArea . FindActorInArea < Character > ( targetId = = 0 ? currentTarget : targetId ) , skillId ) ;
2019-06-19 01:10:15 -04:00
else
// Please wait a moment and try again.
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 32535 , 0x20 ) ;
}
public override bool IsValidTarget ( Character target , ValidTarget validTarget )
{
if ( target = = null )
{
// Target does not exist.
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 32511 , 0x20 ) ;
return false ;
}
if ( target . isMovingToSpawn )
{
// That command cannot be performed on the current target.
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 32547 , 0x20 ) ;
return false ;
}
// enemy only
if ( ( validTarget & ValidTarget . Enemy ) ! = 0 )
{
// todo: this seems ambiguous
if ( target . isStatic )
{
// That command cannot be performed on the current target.
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 32547 , 0x20 ) ;
return false ;
}
if ( currentParty ! = null & & target . currentParty = = currentParty )
{
// That command cannot be performed on a party member.
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 32548 , 0x20 ) ;
return false ;
}
// todo: pvp?
if ( target . allegiance = = allegiance )
{
// That command cannot be performed on an ally.
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 32549 , 0x20 ) ;
return false ;
}
bool partyEngaged = false ;
// todo: replace with confrontation status effect? (see how dsp does it)
if ( target . aiContainer . IsEngaged ( ) )
{
if ( currentParty ! = null )
{
if ( target is BattleNpc )
{
var helpingActorId = ( ( BattleNpc ) target ) . GetMobMod ( ( uint ) MobModifier . CallForHelp ) ;
2022-02-05 17:48:49 -05:00
partyEngaged = this . Id = = helpingActorId | | ( ( ( BattleNpc ) target ) . GetMobMod ( ( uint ) MobModifier . FreeForAll ) ! = 0 ) ;
2019-06-19 01:10:15 -04:00
}
if ( ! partyEngaged )
{
foreach ( var memberId in ( ( Party ) currentParty ) . members )
{
if ( memberId = = target . currentLockedTarget )
{
partyEngaged = true ;
break ;
}
}
}
}
2022-02-05 17:48:49 -05:00
else if ( target . currentLockedTarget = = Id )
2019-06-19 01:10:15 -04:00
{
partyEngaged = true ;
}
}
else
{
partyEngaged = true ;
}
if ( ! partyEngaged )
{
// That target is already engaged.
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 32520 , 0x20 ) ;
return false ;
}
}
if ( ( validTarget & ValidTarget . Ally ) ! = 0 & & target . allegiance ! = allegiance )
{
// That command cannot be performed on the current target.
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 32547 , 0x20 ) ;
return false ;
}
// todo: isStatic seems ambiguous?
if ( ( validTarget & ValidTarget . NPC ) ! = 0 & & target . isStatic )
return true ;
// todo: why is player always zoning?
// cant target if zoning
if ( target is Player & & ( ( Player ) target ) . playerSession . isUpdatesLocked )
{
// That command cannot be performed on the current target.
SendGameMessage ( Server . GetWorldManager ( ) . GetActor ( ) , 32547 , 0x20 ) ;
return false ;
}
return true ;
}
//Do we need separate functions? they check the same things
public override bool CanUse ( Character target , BattleCommand skill , CommandResult error = null )
{
if ( ! skill . IsValidMainTarget ( this , target , error ) | | ! IsValidTarget ( target , skill . mainTarget ) )
{
// error packet is set in IsValidTarget
return false ;
}
//Might want to do these with a BattleAction instead to be consistent with the rest of command stuff
if ( GetHotbarTimer ( skill . id ) > Utils . UnixTimeStampUTC ( ) )
{
// todo: this needs confirming
// Please wait a moment and try again.
error ? . SetTextId ( 32535 ) ;
return false ;
}
if ( Utils . XZDistance ( positionX , positionZ , target . positionX , target . positionZ ) > skill . range )
{
// The target is too far away.
error ? . SetTextId ( 32539 ) ;
return false ;
}
if ( Utils . XZDistance ( positionX , positionZ , target . positionX , target . positionZ ) < skill . minRange )
{
// The target is too close.
error ? . SetTextId ( 32538 ) ;
return false ;
}
if ( target . positionY - positionY > ( skill . rangeHeight / 2 ) )
{
// The target is too far above you.
error ? . SetTextId ( 32540 ) ;
return false ;
}
if ( positionY - target . positionY > ( skill . rangeHeight / 2 ) )
{
// The target is too far below you.
error ? . SetTextId ( 32541 ) ;
return false ;
}
if ( skill . CalculateMpCost ( this ) > GetMP ( ) )
{
// You do not have enough MP.
error ? . SetTextId ( 32545 ) ;
return false ;
}
if ( skill . CalculateTpCost ( this ) > GetTP ( ) )
{
// You do not have enough TP.
error ? . SetTextId ( 32546 ) ;
return false ;
}
//Proc requirement
if ( skill . procRequirement ! = BattleCommandProcRequirement . None & & ! charaWork . battleTemp . timingCommandFlag [ ( int ) skill . procRequirement - 1 ] )
{
//Conditions for use are not met
error ? . SetTextId ( 32556 ) ;
return false ;
}
return true ;
}
public override void OnAttack ( State state , CommandResult action , ref CommandResult error )
{
var target = state . GetTarget ( ) ;
base . OnAttack ( state , action , ref error ) ;
// todo: switch based on main weap (also probably move this anim assignment somewhere else)
action . animation = 0x19001000 ;
if ( error = = null )
{
// melee attack animation
//action.animation = 0x19001000;
}
if ( target is BattleNpc )
{
( ( BattleNpc ) target ) . hateContainer . UpdateHate ( this , action . enmity ) ;
}
LuaEngine . GetInstance ( ) . OnSignal ( "playerAttack" ) ;
}
public override void OnCast ( State state , CommandResult [ ] actions , BattleCommand spell , ref CommandResult [ ] errors )
{
// todo: update hotbar timers to skill's recast time (also needs to be done on class change or equip crap)
base . OnCast ( state , actions , spell , ref errors ) ;
// todo: should just make a thing that updates the one slot cause this is dumb as hell
UpdateHotbarTimer ( spell . id , spell . recastTimeMs ) ;
//LuaEngine.GetInstance().OnSignal("spellUse");
}
public override void OnWeaponSkill ( State state , CommandResult [ ] actions , BattleCommand skill , ref CommandResult [ ] errors )
{
// todo: update hotbar timers to skill's recast time (also needs to be done on class change or equip crap)
base . OnWeaponSkill ( state , actions , skill , ref errors ) ;
// todo: should just make a thing that updates the one slot cause this is dumb as hell
UpdateHotbarTimer ( skill . id , skill . recastTimeMs ) ;
// todo: this really shouldnt be called on each ws?
lua . LuaEngine . CallLuaBattleFunction ( this , "onWeaponSkill" , this , state . GetTarget ( ) , skill ) ;
LuaEngine . GetInstance ( ) . OnSignal ( "weaponskillUse" ) ;
}
public override void OnAbility ( State state , CommandResult [ ] actions , BattleCommand ability , ref CommandResult [ ] errors )
{
base . OnAbility ( state , actions , ability , ref errors ) ;
UpdateHotbarTimer ( ability . id , ability . recastTimeMs ) ;
LuaEngine . GetInstance ( ) . OnSignal ( "abilityUse" ) ;
}
//Handles exp being added, does not handle figuring out exp bonus from buffs or skill/link chains or any of that
//Returns CommandResults that can be sent to display the EXP gained number and level ups
//exp should be a ushort single the exp graphic overflows after ~65k
public List < CommandResult > AddExp ( int exp , byte classId , byte bonusPercent = 0 )
{
List < CommandResult > actionList = new List < CommandResult > ( ) ;
exp + = ( int ) Math . Ceiling ( ( exp * bonusPercent / 100.0f ) ) ;
//You earn [exp] (+[bonusPercent]%) experience points.
//In non-english languages there are unique messages for each language, hence the use of ClassExperienceTextIds
2022-02-05 17:48:49 -05:00
actionList . Add ( new CommandResult ( Id , BattleUtils . ClassExperienceTextIds [ classId ] , 0 , ( ushort ) exp , bonusPercent ) ) ;
2019-06-19 01:10:15 -04:00
bool leveled = false ;
int diff = MAXEXP [ GetLevel ( ) - 1 ] - charaWork . battleSave . skillPoint [ classId - 1 ] ;
//While there is enough experience to level up, keep leveling up, unlocking skills and removing experience from exp until we don't have enough to level up
while ( exp > = diff & & GetLevel ( ) < charaWork . battleSave . skillLevelCap [ classId ] )
{
//Level up
LevelUp ( classId , actionList ) ;
leveled = true ;
//Reduce exp based on how much exp is needed to level
exp - = diff ;
diff = MAXEXP [ GetLevel ( ) - 1 ] ;
}
if ( leveled )
{
//Set exp to current class to 0 so that exp is added correctly
charaWork . battleSave . skillPoint [ classId - 1 ] = 0 ;
//send new level
ActorPropertyPacketUtil levelPropertyPacket = new ActorPropertyPacketUtil ( "charaWork/stateForAll" , this ) ;
levelPropertyPacket . AddProperty ( String . Format ( "charaWork.battleSave.skillLevel[{0}]" , classId - 1 ) ) ;
levelPropertyPacket . AddProperty ( "charaWork.parameterSave.state_mainSkillLevel" ) ;
QueuePackets ( levelPropertyPacket . Done ( ) ) ;
Database . SetLevel ( this , classId , GetLevel ( ) ) ;
Database . SavePlayerCurrentClass ( this ) ;
}
//Cap experience for level 50
charaWork . battleSave . skillPoint [ classId - 1 ] = Math . Min ( charaWork . battleSave . skillPoint [ classId - 1 ] + exp , MAXEXP [ GetLevel ( ) - 1 ] ) ;
ActorPropertyPacketUtil expPropertyPacket = new ActorPropertyPacketUtil ( "charaWork/battleStateForSelf" , this ) ;
expPropertyPacket . AddProperty ( String . Format ( "charaWork.battleSave.skillPoint[{0}]" , classId - 1 ) ) ;
QueuePackets ( expPropertyPacket . Done ( ) ) ;
Database . SetExp ( this , classId , charaWork . battleSave . skillPoint [ classId - 1 ] ) ;
return actionList ;
}
//Equips any abilities for the given classId at the given level. If actionList is not null, adds a "You learn Command" message
private void EquipAbilitiesAtLevel ( byte classId , short level , List < CommandResult > actionList = null )
{
//If there's any abilites that unlocks at this level, equip them.
2020-04-13 20:49:38 +00:00
List < ushort > commandIds = Server . GetWorldManager ( ) . GetBattleCommandIdByLevel ( classId , level ) ;
2019-06-19 01:10:15 -04:00
foreach ( ushort commandId in commandIds )
{
EquipAbilityInFirstOpenSlot ( classId , commandId , false ) ;
byte jobId = ConvertClassIdToJobId ( classId ) ;
if ( jobId ! = classId )
EquipAbilityInFirstOpenSlot ( jobId , commandId , false ) ;
//33926: You learn [command].
if ( actionList ! = null )
{
if ( classId = = GetCurrentClassOrJob ( ) | | jobId = = GetCurrentClassOrJob ( ) )
2022-02-05 17:48:49 -05:00
actionList . Add ( new CommandResult ( Id , 33926 , 0 , commandId ) ) ;
2019-06-19 01:10:15 -04:00
}
}
}
//Increaess level of current class and equips new abilities earned at that level
public void LevelUp ( byte classId , List < CommandResult > actionList = null )
{
if ( charaWork . battleSave . skillLevel [ classId - 1 ] < charaWork . battleSave . skillLevelCap [ classId ] )
{
//Increase level
charaWork . battleSave . skillLevel [ classId - 1 ] + + ;
charaWork . parameterSave . state_mainSkillLevel + + ;
//33909: You attain level [level].
if ( actionList ! = null )
2022-02-05 17:48:49 -05:00
actionList . Add ( new CommandResult ( Id , 33909 , 0 , ( ushort ) charaWork . battleSave . skillLevel [ classId - 1 ] ) ) ;
2019-06-19 01:10:15 -04:00
EquipAbilitiesAtLevel ( classId , GetLevel ( ) , actionList ) ;
2022-02-17 13:22:18 -05:00
questStateManager . UpdateLevel ( GetHighestLevel ( ) ) ;
2019-06-19 01:10:15 -04:00
}
}
public static byte ConvertClassIdToJobId ( byte classId )
{
byte jobId = classId ;
switch ( classId )
{
case CLASSID_PUG :
case CLASSID_GLA :
case CLASSID_MRD :
jobId + = 13 ;
break ;
case CLASSID_ARC :
case CLASSID_LNC :
jobId + = 11 ;
break ;
case CLASSID_THM :
case CLASSID_CNJ :
jobId + = 4 ;
break ;
}
return jobId ;
}
public void SetCurrentJob ( byte jobId )
{
currentJob = jobId ;
2022-02-05 17:48:49 -05:00
BroadcastPacket ( SetCurrentJobPacket . BuildPacket ( Id , jobId ) , true ) ;
2019-06-19 01:10:15 -04:00
Database . LoadHotbar ( this ) ;
SendCharaExpInfo ( ) ;
}
//Gets the id of the player's current job. If they aren't a job, gets the id of their class
public byte GetCurrentClassOrJob ( )
{
if ( currentJob ! = 0 )
return ( byte ) currentJob ;
return charaWork . parameterSave . state_mainSkill [ 0 ] ;
}
public void hpstuff ( uint hp )
{
SetMaxHP ( hp ) ;
SetHP ( hp ) ;
mpMaxBase = ( ushort ) hp ;
charaWork . parameterSave . mpMax = ( short ) hp ;
charaWork . parameterSave . mp = ( short ) hp ;
AddTP ( 3000 ) ;
updateFlags | = ActorUpdateFlags . HpTpMp ;
}
public void SetCombos ( int comboId1 = 0 , int comboId2 = 0 )
{
SetCombos ( new int [ ] { comboId1 , comboId2 } ) ;
}
public void SetCombos ( int [ ] comboIds )
{
Array . Copy ( comboIds , playerWork . comboNextCommandId , 2 ) ;
//If we're starting or continuing a combo chain, add the status effect and combo cost bonus
if ( comboIds [ 0 ] ! = 0 )
{
StatusEffect comboEffect = new StatusEffect ( this , Server . GetWorldManager ( ) . GetStatusEffect ( ( uint ) StatusEffectId . Combo ) ) ;
comboEffect . SetDuration ( 13 ) ;
comboEffect . SetOverwritable ( 1 ) ;
statusEffects . AddStatusEffect ( comboEffect , this ) ;
playerWork . comboCostBonusRate = 1 ;
}
//Otherwise we're ending a combo, remove the status
else
{
statusEffects . RemoveStatusEffect ( statusEffects . GetStatusEffectById ( ( uint ) StatusEffectId . Combo ) ) ;
playerWork . comboCostBonusRate = 0 ;
}
ActorPropertyPacketUtil comboPropertyPacket = new ActorPropertyPacketUtil ( "playerWork/combo" , this ) ;
comboPropertyPacket . AddProperty ( "playerWork.comboCostBonusRate" ) ;
comboPropertyPacket . AddProperty ( "playerWork.comboNextCommandId[0]" ) ;
comboPropertyPacket . AddProperty ( "playerWork.comboNextCommandId[1]" ) ;
QueuePackets ( comboPropertyPacket . Done ( ) ) ;
}
public override void CalculateBaseStats ( )
{
base . CalculateBaseStats ( ) ;
//Add weapon property mod
var equip = GetEquipment ( ) ;
var mainHandItem = equip . GetItemAtSlot ( SLOT_MAINHAND ) ;
var damageAttribute = 0 ;
var attackDelay = 3000 ;
var hitCount = 1 ;
if ( mainHandItem ! = null )
{
var mainHandWeapon = ( Server . GetItemGamedata ( mainHandItem . itemId ) as WeaponItem ) ;
damageAttribute = mainHandWeapon . damageAttributeType1 ;
attackDelay = ( int ) ( mainHandWeapon . damageInterval * 1000 ) ;
hitCount = mainHandWeapon . frequency ;
}
var hasShield = equip . GetItemAtSlot ( SLOT_OFFHAND ) ! = null ? 1 : 0 ;
SetMod ( ( uint ) Modifier . CanBlock , hasShield ) ;
SetMod ( ( uint ) Modifier . AttackType , damageAttribute ) ;
SetMod ( ( uint ) Modifier . Delay , attackDelay ) ;
SetMod ( ( uint ) Modifier . HitCount , hitCount ) ;
//These stats all correlate in a 3:2 fashion
//It seems these stats don't actually increase their respective stats. The magic stats do, however
AddMod ( ( uint ) Modifier . Attack , ( long ) ( GetMod ( Modifier . Strength ) * 0.667 ) ) ;
AddMod ( ( uint ) Modifier . Accuracy , ( long ) ( GetMod ( Modifier . Dexterity ) * 0.667 ) ) ;
AddMod ( ( uint ) Modifier . Defense , ( long ) ( GetMod ( Modifier . Vitality ) * 0.667 ) ) ;
//These stats correlate in a 4:1 fashion. (Unsure if MND is accurate but it would make sense for it to be)
AddMod ( ( uint ) Modifier . AttackMagicPotency , ( long ) ( ( float ) GetMod ( Modifier . Intelligence ) * 0.25 ) ) ;
AddMod ( ( uint ) Modifier . MagicAccuracy , ( long ) ( ( float ) GetMod ( Modifier . Mind ) * 0.25 ) ) ;
AddMod ( ( uint ) Modifier . HealingMagicPotency , ( long ) ( ( float ) GetMod ( Modifier . Mind ) * 0.25 ) ) ;
AddMod ( ( uint ) Modifier . MagicEvasion , ( long ) ( ( float ) GetMod ( Modifier . Piety ) * 0.25 ) ) ;
AddMod ( ( uint ) Modifier . EnfeeblingMagicPotency , ( long ) ( ( float ) GetMod ( Modifier . Piety ) * 0.25 ) ) ;
//VIT correlates to HP in a 1:1 fashion
AddMod ( ( uint ) Modifier . Hp , ( long ) ( ( float ) Modifier . Vitality ) ) ;
CalculateTraitMods ( ) ;
}
public bool HasTrait ( ushort id )
{
BattleTrait trait = Server . GetWorldManager ( ) . GetBattleTrait ( id ) ;
return HasTrait ( trait ) ;
}
public bool HasTrait ( BattleTrait trait )
{
return ( trait ! = null ) & & ( trait . job = = GetClass ( ) ) & & ( trait . level < = GetLevel ( ) ) ;
}
public void CalculateTraitMods ( )
{
var traitIds = Server . GetWorldManager ( ) . GetAllBattleTraitIdsForClass ( ( byte ) GetClass ( ) ) ;
foreach ( var traitId in traitIds )
{
var trait = Server . GetWorldManager ( ) . GetBattleTrait ( traitId ) ;
if ( HasTrait ( trait ) )
{
AddMod ( trait . modifier , trait . bonus ) ;
}
}
}
public bool HasItemEquippedInSlot ( uint itemId , ushort slot )
{
var equippedItem = equipment . GetItemAtSlot ( slot ) ;
return equippedItem ! = null & & equippedItem . itemId = = itemId ;
}
public Retainer GetSpawnedRetainer ( )
{
return currentSpawnedRetainer ;
}
public void StartTradeTransaction ( Player otherPlayer )
{
myOfferings = new ReferencedItemPackage ( this , ItemPackage . MAXSIZE_TRADE , ItemPackage . TRADE ) ;
otherTrader = otherPlayer ;
isTradeAccepted = false ;
}
public Player GetOtherTrader ( )
{
return otherTrader ;
}
public ReferencedItemPackage GetTradeOfferings ( )
{
return myOfferings ;
}
public bool IsTrading ( )
{
return otherTrader ! = null ;
}
public bool IsTradeAccepted ( )
{
return isTradeAccepted ;
}
public void AddTradeItem ( ushort slot , ItemRefParam chosenItem , int tradeQuantity )
{
if ( ! IsTrading ( ) )
return ;
//Get chosen item
InventoryItem offeredItem = itemPackages [ chosenItem . itemPackage ] . GetItemAtSlot ( chosenItem . slot ) ;
offeredItem . SetTradeQuantity ( tradeQuantity ) ;
myOfferings . Set ( slot , offeredItem ) ;
SendTradePackets ( ) ;
}
public void RemoveTradeItem ( ushort slot )
{
if ( ! IsTrading ( ) )
return ;
InventoryItem offeredItem = myOfferings . GetItemAtSlot ( slot ) ;
offeredItem . SetNormal ( ) ;
myOfferings . Clear ( slot ) ;
SendTradePackets ( ) ;
}
public void ClearTradeItems ( )
{
if ( ! IsTrading ( ) )
return ;
for ( ushort i = 0 ; i < myOfferings . GetCapacity ( ) ; i + + )
{
InventoryItem offeredItem = myOfferings . GetItemAtSlot ( i ) ;
if ( offeredItem ! = null )
offeredItem . SetNormal ( ) ;
}
myOfferings . ClearAll ( ) ;
SendTradePackets ( ) ;
}
private void SendTradePackets ( )
{
//Send to self
2022-02-05 17:48:49 -05:00
QueuePacket ( InventoryBeginChangePacket . BuildPacket ( Id , true ) ) ;
2019-06-19 01:10:15 -04:00
myOfferings . SendUpdate ( this ) ;
2022-02-05 17:48:49 -05:00
QueuePacket ( InventoryEndChangePacket . BuildPacket ( Id ) ) ;
2019-06-19 01:10:15 -04:00
//Send to other trader
2022-02-05 17:48:49 -05:00
otherTrader . QueuePacket ( InventoryBeginChangePacket . BuildPacket ( Id , true ) ) ;
2019-06-19 01:10:15 -04:00
myOfferings . SendUpdateAsItemPackage ( otherTrader ) ;
2022-02-05 17:48:49 -05:00
otherTrader . QueuePacket ( InventoryEndChangePacket . BuildPacket ( Id ) ) ;
2019-06-19 01:10:15 -04:00
}
public void AcceptTrade ( bool accepted )
{
if ( ! IsTrading ( ) )
return ;
isTradeAccepted = accepted ;
}
public void FinishTradeTransaction ( )
{
if ( myOfferings ! = null )
{
myOfferings . ClearAll ( ) ;
for ( ushort i = 0 ; i < myOfferings . GetCapacity ( ) ; i + + )
{
InventoryItem offeredItem = myOfferings . GetItemAtSlot ( i ) ;
if ( offeredItem ! = null )
offeredItem . SetNormal ( ) ;
}
2022-02-05 17:48:49 -05:00
QueuePacket ( InventoryBeginChangePacket . BuildPacket ( Id , true ) ) ;
2019-06-19 01:10:15 -04:00
myOfferings . SendUpdate ( this ) ;
2022-02-05 17:48:49 -05:00
QueuePacket ( InventoryEndChangePacket . BuildPacket ( Id ) ) ;
2019-06-19 01:10:15 -04:00
}
isTradeAccepted = false ;
myOfferings = null ;
otherTrader = null ;
}
}
}