unit ObjActor;

interface
uses
  Windows, Classes, SysUtils, Forms, StrUtils, StdCtrls, Graphics, Math, PathFind, Grobal2, SDK,
  Envir, ItemEvent, ObjBase, Common, StorageEngn, SellEngn, Guild, GroupItems, MapPoint, Mudutil, IniFiles;
type
  TMapWalkXY = record
    nWalkStep: Integer;
    nMonCount: Integer;
    nMonRange: Integer;
    nMastrRange: Integer;
    nX: Integer;
    nY: Integer;
  end;
  pTMapWalkXY = ^TMapWalkXY;
  TWalkStep = array[0..7] of TMapWalkXY;
  pTWalkStep = ^TWalkStep;

  TRunPos = record
    btDirection: Byte; //1顺时针 2逆时针
    nAttackCount: Integer;
  end;
  pTRunPos = ^TRunPos;

  TActorObject = class;
  pTActorObject = ^TActorObject;

  TPlayObject = class;
  pTPlayObject = ^TPlayObject;

  TActorObject = class(TBaseObject)
    m_sMapName: string[MAPNAMELEN];
    m_sCharName: string[ACTORNAMELEN];
    m_nCurrX: Integer; //人物所在座标X(4字节)
    m_nCurrY: Integer; //人物所在座标Y(4字节)
    m_btDirection: Byte; //人物所在方向(1字节)
    m_btGender: Byte; //人物的性别(1字节)
    m_btHair: Byte; //人物的头发(1字节)
    m_btJob: Byte; //人物的职业(1字节)
    m_nGold: Integer; //人物金币数(4字节)
    m_Abil: TAbility;
    m_nCharStatus: Integer;
    m_sHomeMap: string[MAPNAMELEN]; //回城地图
    m_nHomeX: Integer; //回城座标X
    m_nHomeY: Integer; //回城座标Y
    m_boOnHorse: Boolean;
    m_btHorseType: Byte;
    m_btDressEffType: Byte;
    m_nPkPoint: Integer; //人物的PK值(4字节)
    btB2: Byte;

    m_nGrudge: Integer; //仇恨值
    m_nIncHealth: Integer;
    m_nIncSpell: Integer;
    m_nIncHealing: Integer;
    m_nFightZoneDieCount: Integer; //在行会占争地图中死亡次数

    m_BonusAbil: TNakedAbility; //TNakedAbility
    m_CurBonusAbil: TNakedAbility;
    m_nBonusPoint: Integer;

    m_dBodyLuck: Double;
    m_nBodyLuckLevel: Integer;

    m_QuestFlag: TQuestFlag;
    m_nCharStatusEx: Integer;
    m_dwFightExp: LongWord; //怪物经验值
    m_WAbil: TAbility;
    m_AddAbil: TAddAbility;

    m_nViewRange: Integer; //可视范围大小
    m_wStatusTimeArr: TStatusTime;

    m_dwStatusArrTick: array[0..MAX_STATUS_ATTRIBUTE - 1] of LongWord;
    m_wStatusArrValue: array[0..MAX_STATUS_ATTRIBUTE - 1] of Word;
    m_dwStatusArrTimeOutTick: array[0..MAX_STATUS_ATTRIBUTE - 1] of LongWord;

    m_wNewStatusArrValue: array[0..14 - 1] of Word;
    m_dwNewStatusArrTimeOutTick: array[0..14 - 1] of LongWord;

    m_wAppr: Word;
    m_btRaceServer: Byte; //角色类型
    m_btRaceImg: Byte; //角色外形
    m_btHitPoint: Byte; //人物攻击准确度(Byte)
    m_nHitPlus: ShortInt;
    m_nHitDouble: ShortInt;
    m_dwGroupRcallTick: LongWord; //0x240  记忆使用间隔(Dword)
    m_boRecallSuite: Boolean; //0x244  记忆全套
    bo245: Boolean;
    m_boTestGa: Boolean; //0x246  //是否输入Testga 命令
    m_boGsa: Boolean; //0x247  //是否输入gsa 命令
    m_nHealthRecover: ShortInt; //0x248
    m_nSpellRecover: ShortInt; //0x249
    m_btAntiPoison: Byte; //0x24A
    m_nPoisonRecover: ShortInt; //0x24B
    m_nAntiMagic: ShortInt; //0x24C
    m_nLuck: Integer; //0x250  人物的幸运值Luck
    m_nPerHealth: Integer; //0x254
    m_nPerHealing: Integer; //0x258
    m_nPerSpell: Integer; //0x25C
    m_dwIncHealthSpellTick: LongWord; //0x260
    m_btGreenPoisoningPoint: Byte; //0x264  中绿毒降HP点数
    m_nGoldMax: Integer; //0x268  人物身上最多可带金币数(Dword)
    m_btSpeedPoint: Byte; //0x26C  人物敏捷度(Byte)
    m_btPermission: Byte; //0x26D  人物权限等级
    m_nHitSpeed: ShortInt; //0x26E  //1-18 更改数据类型
    m_btLifeAttrib: Byte; //0x26F
    m_btCoolEye: Byte; //0x270

    m_GroupLevel              :Byte;
    m_GroupExp                :LongWord;
    m_boGroupShareExp         :Boolean;

    m_Master: TActorObject; //0x288  是否被召唤(主人)
    m_dwMasterRoyaltyTick: LongWord; //0x28C  怪物叛变时间
    m_dwMasterTick: LongWord; //0x290
    n294: Integer; //0x294  杀怪计数
    m_btSlaveExpLevel: Byte; //0x298  宝宝等级 1-7
    m_btSlaveMakeLevel: Byte; //0x299  召唤等级
    m_SlaveList: TList; //0x29C  下属列表

    m_boSlaveRelax: Boolean; //0x2A0  宝宝攻击状态(休息/攻击)(Byte)
    m_btAttatckMode: Byte; //0x2A1  下属攻击状态
    m_btNameColor: Byte; //0x2A2  人物名字的颜色(Byte)
    m_nLight: Integer; //0x2A4  亮度
    m_boGuildWarArea: Boolean; //0x2A8  行会占争范围
    m_Castle: TObject; //0x2AC //所属城堡
    bo2B0: Boolean; //0x2B0
    m_dw2B4Tick: LongWord; //0x2B4
    m_boSuperMan: Boolean; //0x2B8  无敌模式
    bo2B9: Boolean; //0x2B9
    bo2BA: Boolean; //0x2BA
    m_boAnimal: Boolean; //0x2BB
    m_boNoItem: Boolean; //0x2BC
    m_boFixedHideMode: Boolean; //0x2BD
    m_boStickMode: Boolean; //0x2BE
    bo2BF: Boolean; //0x2BF
    m_boNoAttackMode: Boolean; //0x2C0
    bo2C1: Boolean; //0x2C1
    m_boSkeleton: Boolean; //是否是骨骼，是否挖过物品
    m_nMeatQuality: Integer; //0x2C4
    m_nBodyLeathery: Integer; //0x2C8
    m_boHolySeize: Boolean; //0x2CC
    m_dwHolySeizeTick: LongWord; //0x2D0
    m_dwHolySeizeInterval: LongWord; //0x2D4
    m_boCrazyMode: Boolean; //0x2D8
    m_dwCrazyModeTick: LongWord; //0x2DC
    m_dwCrazyModeInterval: LongWord; //0x2E0
    m_boShowHP: Boolean; //0x2E4
    //      nC2E6                   :Integer;      //0x2E6
    m_dwShowHPTick: LongWord; //0x2E8  心灵启示检查时间(Dword)
    m_dwShowHPInterval: LongWord; //0x2EC  心灵启示有效时长(Dword)
    bo2F0: Boolean; //0x2F0
    m_dwDupObjTick: LongWord; //0x2F4
    m_PEnvir: TEnvirnoment; //0x2F8
    m_boGhost: Boolean; //0x2FC
    m_dwGhostTick: LongWord; //0x300
    m_boDeath: Boolean; //0x304
    m_dwDeathTick: LongWord; //0x308
    m_btMonsterWeapon: Byte; //0x30C 怪物所拿的武器
    m_dwStruckTick: LongWord; //0x310
    m_boWantRefMsg: Boolean; //0x314
    m_boAddtoMapSuccess: Boolean; //0x315
    m_bo316: Boolean; //0x316

    m_MyGuild: TObject; //0x320
    m_nGuildRankNo: Integer; //0x324
    m_sGuildRankName: string; //0x328
    m_sScriptLable: string; //0x32C
    m_btAttackSkillCount: Byte; //0x330
    m_btAttackSkillPointCount: Byte; //0x334

    m_boMission: Boolean; //0x338
    m_nMissionX: Integer; //0x33C
    m_nMissionY: Integer; //0x340
    m_boHideMode: Boolean; //0x344  隐身戒指(Byte)
    m_boStoneMode: Boolean; //0x345
    m_boCoolEye: Boolean; //0x346  //是否可以看到隐身人物
    m_boUserUnLockDurg: Boolean; //0x347  //是否用了神水
    m_boTransparent: Boolean; //0x348  //魔法隐身了
    m_boAdminMode: Boolean; //0x349  管理模式(Byte)
    m_boObMode: Boolean; //0x34A  隐身模式(Byte)
    m_boTeleport: Boolean; //0x34B  传送戒指(Byte)
    m_boParalysis: Boolean; //0x34C  麻痹戒指(Byte)
    m_boUnParalysis: Boolean;
    m_boRevival: Boolean; //0x34D  复活戒指(Byte)
    m_boUnRevival: Boolean; //防复活

    m_dwRevivalTick: LongWord; //0x350  复活戒指使用间隔计数(Dword)
    m_boFlameRing: Boolean; //0x354  火焰戒指(Byte)
    m_boRecoveryRing: Boolean; //0x355  治愈戒指(Byte)
    m_boAngryRing: Boolean; //0x356  未知戒指(Byte)
    m_boMagicShield: Boolean; //0x357  护身戒指(Byte)
    m_boUnMagicShield: Boolean; //防护身
    m_boMuscleRing: Boolean; //0x358  活力戒指(Byte)
    m_boFastTrain: Boolean; //0x359  技巧项链(Byte)
    m_boProbeNecklace: Boolean; //0x35A  探测项链(Byte)
    m_boGuildMove: Boolean; //行会传送
    m_boSupermanItem: Boolean;
    m_bopirit: Boolean; //祈祷

    m_boNoDropItem: Boolean;
    m_boNoDropUseItem: Boolean;
    m_boExpItem: Boolean;
    m_boPowerItem: Boolean;
    m_boButchItem: Boolean;
    m_boButchItemEx: Boolean;
    m_sButchItem: string[ACTORNAMELEN];

    m_rExpItem: real;
    m_rPowerItem: real;
    m_dwPKDieLostExp: LongWord; //PK 死亡掉经验，不够经验就掉等级
    m_nPKDieLostLevel: Integer; //PK 死亡掉等级

    m_boAbilSeeHealGauge: Boolean; //0x35B  //心灵启示
    m_boAbilMagBubbleDefence: Boolean; //0x35C  //魔法盾
    m_btMagBubbleDefenceLevel: Byte; //0x35D
    m_dwSearchTime: LongWord; //0x360
    m_dwSearchTick: LongWord; //0x364
    m_dwRunTick: LongWord; //0x368
    m_nRunTime: Integer; //0x36C
    m_nHealthTick: Integer; //0x370    //特别指定为 此类型  此处用到 004C7CF8
    m_nSpellTick: Integer; //0x374
    m_TargetCret: TActorObject; //0x378
    m_dwTargetFocusTick: LongWord; //0x37C
    m_LastHiter: TActorObject; //0x380  人物被对方杀害时对方指针(Dword)
    m_LastHiterTick: LongWord; //0x384
    m_ExpHitter: TActorObject; //0x388
    m_ExpHitterTick: LongWord; //0x38C
    m_dwTeleportTick: LongWord; //0x390  传送戒指使用间隔(Dword)
    m_dwProbeTick: LongWord; //0x394  探测项链使用间隔(Dword)
    m_dwMapMoveTick: LongWord; //0x398
    m_boPKFlag: Boolean; //0x39C  人物攻击变色标志(Byte)
    m_dwPKTick: LongWord; //0x3A0  人物攻击变色时间长度(Dword)
    m_nMoXieSuite: Integer; //0x3A4  魔血一套(Dword)
    m_nHongMoSuite: Integer; //0x3A8 虹魔一套(Dword)

    m_boPoisonItem: Boolean; //中毒装备
    m_boDamageHPItem: Boolean; //被攻击掉对方血

    m_boVirusImmunity: Boolean; //病毒免疫

    m_db3B0: Double;
    m_dwPoisoningTick: LongWord; //中毒处理间隔时间(Dword)
    m_dwDecPkPointTick: LongWord; //减PK值时间(Dword)
    m_DecLightItemDrugTick: LongWord;
    m_dwVerifyTick: LongWord;
    m_dwCheckRoyaltyTick: LongWord;
    m_dwDecHungerPointTick: LongWord;
    m_dwHPMPTick: LongWord;

    m_MsgList: TList;
    m_VisibleActors: TList;
    m_VisibleItems: TList;
    m_VisibleHumanList: TList;

    m_SendRefMsgTick: LongWord;
    m_boInFreePKArea: Boolean; //是否在开行会战(Byte)

    m_dwHitIntervalTime :LongWord; //攻击间隔
    m_dwMagicHitIntervalTime :LongWord; //魔法间隔
    m_dwRunIntervalTime :LongWord; //走路间隔
    m_dwWalkIntervalTime :LongWord; //走路间隔
    m_dwTurnIntervalTime :LongWord; //换方向间隔
    m_dwActionIntervalTime :LongWord; //组合操作间隔
    m_dwRunLongHitIntervalTime :LongWord; //组合操作间隔
    m_dwRunHitIntervalTime :LongWord; //组合操作间隔
    m_dwWalkHitIntervalTime :LongWord; //组合操作间隔
    m_dwRunMagicIntervalTime :LongWord; //跑位魔法间隔
    m_btOldDir: Byte;
    m_dwActionTick: LongWord;
    m_wOldIdent: Word;
    m_dwHitTick: LongWord;
    m_dwWalkTick: LongWord;
    m_dwSearchEnemyTick: LongWord;
    m_boNameColorChanged: Boolean;
    m_boIsVisibleActive: Boolean; //是否在可视范围内有人物,及宝宝
    m_nProcessRunCount: ShortInt;

    m_ItemList: TList; //人物背包(Dword)数量
    m_MagicList: TList; //技能表
    m_UseItems: THumanUseItems;
    m_SayMsgList: TList;

    m_nWalkSpeed: Integer;
    m_nWalkStep: Integer;
    m_nWalkCount: Integer;
    m_dwWalkWait: LongWord;
    m_dwWalkWaitTick: LongWord;
    m_boWalkWaitLocked: Boolean;
    m_nNextHitTime: Integer;
    m_MagicOneSwordSkill: pTUserMagic; // Fencing
    m_MagicPowerHitSkill: pTUserMagic; // Slaying
    m_MagicErgumSkill: pTUserMagic; //刺杀剑法
    m_MagicBanwolSkill: pTUserMagic; //半月弯刀
    m_MagicFireSwordSkill: pTUserMagic;
    m_MagicCrsSkill: pTUserMagic;
    m_Magic31Skill: pTUserMagic;
    m_Magic41Skill: pTUserMagic; //0x528
    m_Magic42Skill: pTUserMagic; //0x528
    m_Magic43Skill: pTUserMagic; //0x528
    m_Magic58Skill: pTUserMagic;
    m_Magic60Skill: pTUserMagic;
    m_MagicZRJFSkill: pTUserMagic;

    m_Magic100Skill: pTUserMagic;
    m_Magic101Skill: pTUserMagic;
    m_Magic102Skill: pTUserMagic;
    m_Magic103Skill: pTUserMagic;

    m_btReColorIdx: Byte;
    m_dwReColorTick: LongWord;
    m_boTDBeffect             :Boolean;
    m_boPowerHit: Boolean;
    m_boUseThrusting: Boolean; //0x52D
    m_boUseHalfMoon: Boolean; //0x52E
    m_boFireHitSkill: Boolean; //0x52F
    m_boCIDHitSkill: Boolean; //0x52F
    m_boKTZHitSkill: Boolean; //0x52F
    m_boCrsHitkill: Boolean; //0x52F
    m_bo41kill: Boolean; //0x52F
    m_bo42kill: Boolean; //0x52F
    m_bo43kill: Boolean; //0x52F
    m_bo60kill: Boolean; //0x52F
    m_boZRJFHitSkill: Boolean; //0x52F

    m_dwLatestFireHitTick: LongWord; //0x530
    m_dwLatestCIDHitTick: LongWord; //0x530
    m_dwLatestKTZHitTick: LongWord; //0x530
    m_dwLatestZRJFHitTick: LongWord; //0x530

    m_dwSkillUltimateEnhancerDelayTimeTick: LongWord;

    m_dwDoMotaeboTick: LongWord; //0x534
    m_boDenyRefStatus: Boolean; //是否刷新在地图上信息；
    //m_boAddToMaped: Boolean; //是否增加地图计数
    //m_boDelFormMaped: Boolean; //是否从地图中删除计数
    m_boAutoChangeColor: Boolean; //是否自动变色
    m_dwAutoChangeColorTick: LongWord;
    m_nAutoChangeIdx: Integer;
    m_nChangeColorType: Integer;

    m_nnonfrzWalkSpeed        :Integer;
    m_nnonfrzNextHitTime      :Integer;
    m_boFixColor: Boolean; //固定颜色
    m_nFixColorIdx: Integer;
    m_nFixStatus: Integer;
    m_boFastParalysis: Boolean; //快速麻痹，受攻击后麻痹立即消失

    m_DefMsg: TDefaultMessage; //0x550
    m_nSocket: Integer; //0x59C nSocket
    m_nGSocketIdx: Integer; //0x5A0 wGateIndex 人物连接到游戏网关SOCKET ID
    m_nGateIdx: Integer; //0x5A8 nGateIdx   人物所在网关号
    m_nSoftVersionDate: Integer; //0x5AC

    m_nCSocket: Integer; //客户端网关Socket

    m_dwUserTick: array[0..6 - 1] of LongWord; //由功能插件调用

    m_nCopyHumanLevel: Integer; //复制人辈分等级
    m_dwStationTick: LongWord; //增加检测人物站立不动时间

    m_btLastOutStatus: Byte; //退出状态 1为死亡
    m_KillTargetCret: TActorObject;
    m_boRobot: Boolean;

    m_boExpGroupItem: Boolean;
    m_boDCGroupItem: Boolean;
    m_boMCGroupItem: Boolean;
    m_boSCGroupItem: Boolean;

    m_rExpGroupItem: Real;
    m_rDCGroupItem: Real;
    m_rMCGroupItem: Real;
    m_rSCGroupItem: Real;

    m_boNotDefendoof: Boolean;
    m_boStartNewShield: Boolean;
    m_dwStartNewShield: LongWord;
    m_btNewShieldType: Byte;

    m_boSpaceLock: Boolean;
    m_SpaceRect: TRect;
    m_SpaceOwner: TActorObject;
    m_LockList: TList;

    m_dwSpaceSkillDelayTick: LongWord;

    m_boDropUseItem: Boolean;
    m_dwCanUseItemTick: LongWord; //使用物品TICK
    m_boStartDuel: Boolean;
    m_DuelTargetCret: TActorObject;
    m_boDealType: Boolean;
    m_boStore: Boolean;
    m_StoreItemList: TList;

    m_NewStatus: TNewStatus; //失明状态  混乱状态
    m_dwStatusDelayTick: LongWord;
    m_nStatusDelayTime: Integer;
    m_nNewStatusX: Integer;
    m_nNewStatusY: Integer;
    m_LastSetStatus: TActorObject;
    m_dwConfusionTick: LongWord;
    m_boIsUnknowActor: Boolean;
    m_boMaskedActor: Boolean;

    m_sScatterItemName: string;
    m_sScatterItemOwnerName: string;
    m_nScatterItemX: Integer;
    m_nScatterItemY: Integer;
    m_sScatterItemMapName: string;
    m_sScatterItemMapDesc: string;

    m_GroupItem: TList; //pTGroupItem;
    m_GroupItemList: TList;
    m_boNotOnlineAddExp: Boolean; //是否是离线挂机人物
    m_boAI: Boolean; //是否是假人

    m_PoisonTarget: TActorObject;
    m_PoisonTick: LongWord;

    m_boAddToMaped: Boolean; //是否增加地图计数
    m_boDelFormMaped: Boolean; //是否从地图中删除计数
  private
    function UpdateVisibleGay(ActorObject: TActorObject): Boolean;
    function UpdateVisibleItemGay(ItemObject: TItemObject): Boolean;
    function InSafeArea: Boolean;
    function WalkToSpaceLock(nOldX, nOldY, nNewX, nNewY: Integer): Boolean;
    function Walk(nIdent: Integer): Boolean;
    function AddToMap(): Boolean;
    procedure UseLamp();
    procedure CheckPKStatus();
    procedure GetAccessory(Item: TUserItem; StdItem: pTStdItem; var AddAbility: TAddAbility);
    procedure RecalcHitSpeed();
    procedure AddItemSkill(nIndex: Integer);
    procedure DelItemSkill(nIndex: Integer);
    procedure DecPKPoint(nPoint: Integer);
    procedure HearMsg(sMsg: string);
    procedure DoDamageWeapon(nWeaponDamage: Integer);

    function GetMaster(): TActorObject;

  public
    constructor Create(); override;
    destructor Destroy; override;

    procedure AttackDir(TargeTActorObject: TActorObject; wHitMode: Word; nDir: Integer); virtual;
    function IsAddWeightAvailable(nWeight: Integer): Boolean;
    procedure DamageSpell(nSpellPoint: Integer);
    function GetCharColor(BaseObject: TActorObject): Byte;
    function GetNamecolor: Byte; virtual;
    function RecalcBagWeight(): Integer;

    function GetLevelExp(nLevel: Integer): LongWord;
    procedure SendMsg(BaseObject: TActorObject; wIdent, wParam: Integer; nParam1, nParam2, nParam3: Integer; sMsg: string);
    procedure SendFirstMsg(BaseObject: TActorObject; wIdent, wParam: Integer; lParam1, lParam2, lParam3: Integer; sMsg: string);
    procedure SendDelayMsg(BaseObject: TActorObject; wIdent, wParam: Integer; lParam1, lParam2, lParam3: Integer; sMsg: string; dwDelay: LongWord);
    procedure SendRefMsg(wIdent, wParam: Word; nParam1, nParam2, nParam3: Integer; sMsg: string); virtual;

    procedure SendUpdateMsg(BaseObject: TActorObject; wIdent, wParam: Integer; lParam1, lParam2, lParam3: Integer; sMsg: string);

    procedure SendUpdateMsgA(BaseObject: TActorObject; wIdent, wParam: Integer;
      lParam1, lParam2, lParam3: Integer; sMsg: string);

    procedure SendUpdateDelayMsg(BaseObject: TActorObject; wIdent, wParam: Integer;
      lParam1, lParam2, lParam3: Integer; sMsg: string; dwDelay: LongWord);

    procedure SendActionMsg(BaseObject: TActorObject; wIdent, wParam: Integer;
      lParam1, lParam2, lParam3: Integer; sMsg: string);
    procedure SendAttackMsg(wIdent: Word; btDir: Byte; nX, nY: Integer);
    procedure SysMsg(sMsg: string; MsgColor: TMsgColor; MsgType: TMsgType); overload; virtual;
    procedure SysMsg(sMsg: string; btFColor, btBColor: Byte; MsgType: TMsgType); overload; virtual;
    procedure MoveMsg(sMsg: string; btFColor, btBColor: Byte; nX, nY: Word; nMoveCount: Integer);
    procedure CenterMsg(sMsg: string; btFColor, btBColor: Byte; nTime: Integer);

    procedure MonsterSayMsg(AttackBaseObject: TActorObject; MonStatus: TMonStatus);

    procedure RecalcLevelAbilitys; virtual;
    function PKLevel(): Integer;
    function GetRandomHomePoint(var nX, nY: Integer): Boolean;
    function InSafeZone(): Boolean; overload;
    function InSafeZone(Envir: TEnvirnoment; nX, nY: Integer): Boolean; overload;
    procedure OpenHolySeizeMode(dwInterval: LongWord);
    procedure BreakHolySeizeMode;
    procedure OpenCrazyMode(nTime: Integer);
    procedure BreakCrazyMode();
    procedure HealthSpellChanged();
    function _Attack(var wHitMode: Word; AttackTarget: TActorObject): Boolean;
    function GetHitStruckDamage(Target: TActorObject; nDamage: Integer): Integer;
    procedure HasLevelUp(nLevel: Integer); virtual;

    procedure ClearViewRange; virtual;
    function GetFeatureEx: Word;
    procedure GoldChanged();
    procedure GameGoldChanged;
    function GetPoseCreate(): TActorObject;
    function GetGuildRelation(cert1: TActorObject; cert2: TActorObject): Integer;
    function IsGoodKilling(Cert: TActorObject): Boolean;
    procedure IncPkPoint(nPoint: Integer);
    procedure AddBodyLuck(dLuck: Double);
    procedure MakeWeaponUnlock();
    procedure ScatterGolds(GoldOfCreat: TActorObject);
    function DropGoldDown(nGold: Integer; boFalg: Boolean; GoldOfCreat, DropGoldCreat: TActorObject): Boolean;
    function DropItemDown(UserItem: pTUserItem; nScatterRange: Integer; boDieDrop: Boolean; ItemOfCreat, DropCreat: TActorObject): Boolean; overload;
    function DropItemDown(UserItem: pTUserItem; nScatterRange: Integer; DropCreat: TActorObject; Envir: TEnvirnoment; nX, nY: Integer): Boolean; overload;
    procedure DamageHealth(nDamage: Integer);
    function GetAttackPower(nBasePower, nPower: Integer): Integer;

    function CharPushed(nDir, nPushCount: Integer): Integer;
    function GetDropPosition(nOrgX, nOrgY, nRange: Integer; var nDX: Integer; var nDY: Integer): Boolean;
    function GetBackDir(nDir: Integer): Integer;
    function GetMapActorObjects(tEnvir: TEnvirnoment; nX, nY: Integer; nRage: Integer; rList: TList): Boolean;
    function GetMapEvents(tEnvir: TEnvirnoment; nX, nY, nRage: Integer; rList: TList): Boolean;
    function MagPassThroughMagic(sX, sY, tx, ty, nDir, magpwr: Integer;
      undeadattack: Boolean; btType: Byte): Integer;
    procedure KickException;
    function GetMagStruckDamage(BaseObject: TActorObject; nDamage: Integer): Integer;
    procedure DamageBubbleDefence(nInt: Integer);
    procedure BreakOpenHealth;
    function GetCharStatus: Integer;
    procedure MakeOpenHealth;
    procedure IncHealthSpell(nHP, nMP: Integer);
    procedure ItemDamageRevivalRing;
    function CalcGetExp(nLevel: Integer; nExp: Integer): Integer;
    procedure GainSlaveExp(nLevel: Integer);
    procedure MapRandomMove(sMapName: string; nInt: Integer);
    procedure TurnTo(nDir: Integer);
    procedure FeatureChanged();
    function GetFeatureToLong(): Integer;
    function GetFeature(BaseObject: TActorObject): Integer;

    function GetAddStatus(): Integer;
    function GetAddStatus2(): Integer;

    procedure AbilCopyToWAbil();
    procedure ChangePKStatus(boWarFlag: Boolean);
    procedure StruckDamage(nDamage: Integer);
    procedure StartNewShield();
    function sub_4C4CD4(sItemName: string; var nCount: Integer): pTUserItem;
    procedure StatusChanged;
    function GeTActorObjectInfo(): string;
    procedure TrainSkill(var UserMagic: pTUserMagic; nTranPoint: Integer);
    function CheckMagicLevelup(UserMagic: pTUserMagic): Boolean;
    function MagCanHitTarget(nX, nY: Integer; TargeTActorObject: TActorObject): Boolean; overload;
    function MagCanHitTarget(nX, nY, nTargetX, nTargetY: Integer): Boolean; overload;
    procedure sub_4C713C(Magic: pTUserMagic);
    function MagBubbleDefenceUp(nLevel, nSec: Integer): Boolean;
    procedure ApplyMeatQuality();
    function TakeBagItems(BaseObject: TActorObject): Boolean;
    function AddItemToBag(UserItem: pTUserItem): Boolean; virtual;
    function DelBagItem(nIndex: Integer): Boolean; overload;
    function DelBagItem(nItemIndex: Integer; sItemName: string): Boolean; overload;
    function DelBagItem(UserItem: pTUserItem): Boolean; overload;

    procedure WeightChanged();
    function IsTrainingSkill(nIndex: Integer): Boolean;
    procedure SetQuestFlagStatus(nFlag: Integer; nValue: Integer);
    function GetQuestFalgStatus(nFlag: Integer): Integer;
    function GetAttackDir(BaseObject: TActorObject; var btDir: Byte): Boolean; overload;
    function GetAttackDir(BaseObject: TActorObject; nRange: Integer; var btDir: Byte): Boolean; overload;
    function TargetInSpitRange(BaseObject: TActorObject; var btDir: Byte): Boolean;

    procedure MonsterRecalcAbilitys();
    procedure RefNameColor;
    procedure SetPKFlag(BaseObject: TActorObject); virtual;
    procedure SetLastHiter(BaseObject: TActorObject); virtual;
    function EnterAnotherMap(Envir: TEnvirnoment; nDMapX, nDMapY: Integer): Boolean;
    function sub_4DD704(): Boolean;
    function DefenceUp(nSec: Integer): Boolean;
    function MagDefenceUp(nSec: Integer): Boolean;

    function DefenceDown(nSec: Integer): Boolean;
    function MagDefenceDown(nSec: Integer): Boolean;

    procedure RefShowName;
    function MakeSlave(sMonName: string; nMakeLevel, nExpLevel, nMaxMob: Integer; dwRoyaltySec: LongWord): TActorObject;
    function MakeXYSlave(sMonName: string; nMakeLevel, nExpLevel, nMaxMob: Integer; dwRoyaltySec: LongWord; nX, nY: Integer): TActorObject;
    function MakePosion(nType, nTime, nPoint: Integer): Boolean;
    function GetFrontPosition(var nX: Integer; var nY: Integer): Boolean;
    function GetBackPosition(var nX: Integer; var nY: Integer): Boolean; overload;
    function GetBackPosition(btDirection: Byte; var nX, nY: Integer): Boolean; overload;
    function WalkTo(btDir: Byte; boFlag: Boolean): Boolean;
    procedure SpaceMove(sMAP: string; nX, nY: Integer; nInt: Integer);
    procedure SpaceMove2(nX, nY: Integer; nInt: Integer);
    function sub_4C5370(nX, nY: Integer; nRange: Integer; var nDX, nDY: Integer): Boolean;
    function CheckItems(sItemName: string): pTUserItem;
    function MagMakeDefenceArea(nX, nY, nRange, nSec: Integer; btState: Byte; boState: Boolean): Integer;
   // function MagDownAbility(nSec: Integer): Boolean;
    function MagMakeAbilityArea(nX, nY, nRange, nSec: Integer): Integer;
    procedure MagDownHealth(nType, nTime, nPoint: Integer);

    procedure IncExp(dwExp: LongWord); virtual;
    procedure GainExp(dwExp: LongWord); virtual;
    procedure GetExp(dwExp: LongWord); virtual;
    procedure WinExp(dwExp: LongWord); virtual;

    function sub_4C3538(): Integer;
    function IsGuildMaster(): Boolean;
    procedure LoadSayMsg();
    procedure DisappearA();

    function GetShowName(): string; virtual;
    function GetShowNameEx(): string;
    procedure DropUseItems(BaseObject: TActorObject); virtual;
    procedure ScatterBagItems(ItemOfCreat: TActorObject); virtual;
    function GetMessage(Msg: pTProcessMessage): Boolean; virtual; //FFFF
    function MessageCount(): Integer;
    procedure Initialize(); virtual; //FFFE
    procedure Disappear(); virtual; //FFFD
    function Operate(ProcessMsg: pTProcessMessage): Boolean; virtual; //FFFC
    procedure SearchViewRange(); virtual; //dynamic;
    procedure Run(); virtual; //dynamic;//FFFB
    procedure ProcessSayMsg(sMsg: string); virtual;
    procedure MakeGhost; virtual;
    procedure Die(); virtual;
    procedure ReAlive(); virtual; //FFF8;
    procedure RecalcAbilitys(); virtual; //FFF7
    function IsFriend(cret: TActorObject): Boolean;
    function IsProtectTarget(BaseObject: TActorObject): Boolean; virtual; //FFF6
    function IsAttackTarget(BaseObject: TActorObject): Boolean; virtual; //FFF5
    function IsProperTarget(BaseObject: TActorObject): Boolean; virtual; //FFF4
    function IsProperFriend(BaseObject: TActorObject): Boolean; virtual; //FFF3
    procedure SetTargetCreat(BaseObject: TActorObject); virtual; //FFF2
    procedure DelTargetCreat(); virtual; //FFF1
    function IsPlayMon: Boolean;
    function IsProperTargetSKILL_54(BaseObject: TActorObject): Boolean;
    function IsProperTargetSKILL_55(nLevel: Integer; BaseObject: TActorObject): Boolean;
    function IsProperTargetSKILL_57(BaseObject: TActorObject): Boolean;

    function CanRun(nX, nY: Integer; boFlag: Boolean): Boolean; overload;
    function CanRun(nCurrX, nCurrY, nX, nY: Integer; boFlag: Boolean): Boolean; overload;

    function CanMove(nX, nY: Integer; boFlag: Boolean): Boolean; overload;
    function CanMove(nCurrX, nCurrY, nX, nY: Integer; boFlag: Boolean): Boolean; overload;

    function RunTo(btDir: Byte; boFlag: Boolean; nDestX, nDestY: Integer): Boolean;
    procedure ThrustingOnOff(boSwitch: Boolean);
    procedure HalfMoonOnOff(boSwitch: Boolean);
    procedure SkillCrsOnOff(boSwitch: Boolean);
    procedure Skill42OnOff(boSwitch: Boolean);
    procedure Skill43OnOff(boSwitch: Boolean);
    procedure Skill60OnOff(boSwitch: Boolean);

    function AllowFireHitSkill(): Boolean;
    function AllowCIDHitSkill(): Boolean;
    //function AllowTwinHitSkill(): Boolean;
    function AllowKTZHitSkill(): Boolean;
    function AllowZRJFHitSkill(): Boolean;

    function CretInNearXY(TargeTActorObject: TActorObject; nX, nY: Integer): Boolean; overload;
    function CretInNearXY(TargeTActorObject: TActorObject; nX, nY: Integer; nRange: Integer): Boolean; overload;
    function MakeSelf(BaseObject: TActorObject; sMonName: string; nMaxMob: Integer; dwRoyaltySec: LongWord): TActorObject;
    function MakeMoonObj(sMonName: string; nMakeLevel, nExpLevel, nMaxMob: Integer; dwRoyaltySec: LongWord): TActorObject;
    function GetObjectCastle(): Byte;

    function _GetAtomPower(TargeTActorObject: TActorObject; btType: Byte; nPower: Integer): Integer; overload;
    function _GetAtomPower(TargeTActorObject: TActorObject; nPower: Integer): Integer; overload;

    function GetAddPowerPoint(btType: Byte; nPower: Integer): Integer; overload;
    function GetAddPowerPoint(boWarrAttack: Boolean; nPower: Integer): Integer; overload;

    function GetNotDefendoof(): Boolean;

    function GetBlind(TargeTActorObject: TActorObject): Boolean; //失明状态
    function GetConfusion(TargeTActorObject: TActorObject): Boolean; //混乱状态
    procedure SetNewStatus(Status: TNewStatus);

    procedure TakeOnGroupItem(UserItem: pTUserItem);

    procedure SetSlaveLockSpace(Rect: TRect; Owner: TActorObject);
    function InRect(nX, nY: Integer; Rect: TRect): Boolean;
    function InLockRect(Envir: TEnvirnoment; nX, nY: Integer): Boolean; overload;
    function InLockRect(Targe: TActorObject): Boolean; overload;
    function GetDropUseItem: Boolean;

    procedure AddLock(ActorObject: TActorObject);

    function GetUnknowCharName: string;
    function CheckItemBindUse(UserItem: pTUserItem; boHint: Boolean): Integer;
    procedure GetGroupItemList;
    procedure SendGroupItemMsg();

    procedure SendSerieMagicBlasthitMsg(UserMagic: pTUserMagic);
    function DoRotationMotaebo(nDir: Byte; UserMagic: pTUserMagic): Boolean;

    procedure AddMapCount;
    procedure DelMapCount;

    property Master: TActorObject read GetMaster;
  end;

  TAnimalObject = class(TActorObject)
    m_nTargetX: Integer;
    m_nTargetY: Integer;
    m_boRunAwayMode: Boolean;
    m_dwRunAwayStart: LongWord;
    m_dwRunAwayTime: LongWord;
  private

  public
    constructor Create(); override;
    procedure SearchTarget(); virtual;
    procedure SearchTarget2;
    function Operate(ProcessMsg: pTProcessMessage): Boolean; override;
    procedure Run; override;
    procedure DelTargetCreat(); override;
    procedure SetTargetXY(nX, nY: Integer); virtual;
    procedure GotoTargetXY(); virtual;
    procedure Wondering(); virtual;
    procedure Attack(TargeTActorObject: TActorObject; nDir: Integer); virtual;
    procedure Struck(hiter: TActorObject); virtual;
    procedure HitMagAttackTarget(TargeTActorObject: TActorObject; nHitPower: Integer; nMagPower: Integer; boFlag: Boolean);
  end;

  TAIObject = class(TAnimalObject)
    m_dwSearchTargetTick: LongWord;
    m_dwThinkTick: LongWord;

    m_boDupMode: Boolean;

    m_wLastHP: Word;
    //m_boCanSpell: Boolean;
    //m_dwMagicAttackInterval: LongWord;
    //m_dwMagicAttackTick: LongWord;
    m_boSelSelf: Boolean;
    m_nIncSelfHealthCount: Integer;
    m_nIncMasterHealthCount: Integer;

    //m_boIsUseMagic: Boolean;
    //m_boIsUseAttackMagic: Boolean;
    m_nDieDropUseItemRate: Integer; //死亡掉装备几率

    m_nCanMotaebo: Integer;

    m_nPowerRate: Integer; //人物攻击力倍数(此数除以 100 为真正倍数)
    m_nSelItemType: Integer;

    m_dwPickUpItemTick: LongWord;
    m_SelItemObject: TItemObject;
    //m_boCanPickUpItem: Boolean;
    m_dwEatItemTick: LongWord;
    m_wHitMode: Word;
    m_SkillUseTick: array[0..80 - 1] of LongWord; //魔法使用间隔

    //m_nAttackCount: Integer; //2007-10-11

    m_btStatus: Byte; //状态
    m_boTarget: Boolean; //是否锁定目标
    m_boProtectStatus: Boolean; //是否是守护状态
    m_nProtectTargetX: Integer; //守护坐标
    m_nProtectTargetY: Integer; //守护坐标

    m_boAutoAttack: Boolean;

    m_btTaoistUseItemType: Byte;

    m_RunPos: TRunPos;

    m_btHeroGroup: Byte; //英雄合体
    m_dwHeroGroupTick: LongWord;
    m_dwHeroGroupTime: LongWord;
    //m_FindPath: TFindPath;

    //m_boAttack: Boolean;
    m_BagItemNames: TStringList; //TBagItemNames;
    m_UseItemNames: TUseItemNames;
    m_dwAutoRepairItemTick: LongWord; //自动修理装备
    m_dwAutoAddHealthTick: LongWord; //自动增加HP MP

    m_boSuperItem: Boolean;
  private
    function Thinking: Boolean;
    function RunToNext(nX, nY: Integer): Boolean;
    function WalkToNext(nX, nY: Integer): Boolean;
    function AutoMotaebo(nDir: Byte; nMagicLevel: Integer): Boolean;
    function AutoSpell(UserMagic: pTUserMagic; nTargetX, nTargetY: Integer; BaseObject: TActorObject): Boolean;
  protected
    function GetSpellPoint(UserMagic: pTUserMagic): Integer;
    function GotoNext(nX, nY: Integer; boRun: Boolean): Boolean;
    function GotoNextOne(nX, nY: Integer; boRun: Boolean): Boolean;

    function GetStep(nCurrX, nCurrY, nTargetX, nTargetY: Integer): Integer;
    function FindObjectOfRange(AObject: TActorObject; nRange: Integer): TActorObject;
    function CanLineAttack(nStep: Integer): Boolean; overload;
    function CanLineAttack(nCurrX, nCurrY: Integer): Boolean; overload;

    function SearchMinDistance: TActorObject;

    function CanWalk(nCurrX, nCurrY, nTargetX, nTargetY: Integer; nDir: Integer; var nStep: Integer; boFlag: Boolean): Boolean;
    function IsMasterRange(nTargetX, nTargetY, nRange: Integer): Boolean;
    function GetMasterRange(nTargetX, nTargetY: Integer): Integer;

    function WalkToTargetXY(nTargetX, nTargetY: Integer): Boolean;
    function RunToTargetXY(nTargetX, nTargetY: Integer): Boolean;

    function UseSpell(UserMagic: pTUserMagic; nTargetX, nTargetY: Integer; TargeTActorObject: TActorObject): Boolean;

    function GetAttackXYDir(nX, nY: Integer; BaseObject: TActorObject; nRange: Integer; var btDir: Byte): Boolean;
    function CanAttack(BaseObject: TActorObject; nRange: Integer; var btDir: Byte): Boolean; overload;
    function CanAttack(nCurrX, nCurrY: Integer; BaseObject: TActorObject; nRange: Integer; var btDir: Byte): Boolean; overload;

    function AllowUseMagic(wMagIdx: Word): Boolean; virtual;

    function DoThink(wMagicID: Word): Integer; virtual;
    function ActThink(wMagicID: Word): Boolean; virtual;
    function FollowMaster: Boolean; virtual;
    function StartAttack(wMagIdx: Word): Boolean; virtual;
    function WarrAttackTarget(wMagIdx, wHitMode: Word): Boolean; virtual; {物理攻击}
    function WarrorAttackTarget(wMagIdx: Word): Boolean; virtual; {战士攻击}
    function WizardAttackTarget(wMagIdx: Word): Boolean; virtual; {法师攻击}
    function TaoistAttackTarget(wMagIdx: Word): Boolean; virtual; {道士攻击}

    function FindVisibleActors(ActorObject: TActorObject): Boolean;

    procedure StartEatItems(); {吃药}
    function AutoEatUseItems(btItemType: Byte): Boolean;

    function SelectMagic(): Integer; virtual; //选择魔法
    function IsUseMagic(): Boolean; virtual;
    function IsUseAttackMagic(): Boolean; virtual;
  public
    constructor Create(); override;
    destructor Destroy; override;
    procedure Run; override;
    procedure DelTargetCreat(); override;
    procedure Struck(hiter: TActorObject); override;

    function GetTargetXYCount(nX, nY, nRange: Integer): Integer;
    function GetNearTargetCount(): Integer; overload;
    function GetNearTargetCount(nCurrX, nCurrY: Integer): Integer; overload;
    function GetRangeTargetCount(nX, nY, nRange: Integer): Integer;
    function GetRangeFriendCount(nX, nY, nRange: Integer): Integer;
    function GetRangeTargetCountByDir(nDir, nX, nY, nRange: Integer): Integer;
    function GetRangeTargetXY(nDir, nRange: Integer; var nTargetX, nTargetY: Integer): Boolean;

    function IsGotoXY(X1, Y1, X2, Y2: Integer): Boolean;
    function FindMagic(wMagIdx: Word): pTUserMagic; overload;
    function FindMagic(sMagicName: string): pTUserMagic; overload;

    function GetUserItemList(nItemType, nCount: Integer): Integer;
    function CheckUserItemType(nItemType, nCount: Integer): Boolean;
    function CheckUserItem(nItemType, nCount: Integer): Boolean;
    function UseItem(nItemType, nIndex: Integer): Boolean; virtual;
    function CheckItemType(nItemType: Integer; StdItem: pTStdItem): Boolean;
  end;

  TPlayObject = class(TAIObject)
    m_sLastMapName: string[MAPNAMELEN]; //人物死亡地图
    m_nLastCurrX: Integer; //人物死亡地图所在座标X(4字节)
    m_nLastCurrY: Integer; //人物死亡地图所在座标Y(4字节)
    m_sOldSayMsg: string;
    m_nSayMsgCount: Integer; //0x560
    m_dwSayMsgTick: LongWord; //0x568
    m_boDisableSayMsg: Boolean; //0x56C
    m_dwDisableSayMsgTick: LongWord; //0x570
    m_dwCheckDupObjTick: LongWord; //0x574
    dwTick578: LongWord; //0x578
    dwTick57C: LongWord; //0x57C
    m_boInSafeArea: Boolean; //0x580

    m_sUserID: string[ACCOUNTLEN]; //0x58C    登录帐号名
    m_sIPaddr: string[15]; //0x598    人物IP地址
    m_sIPLocal: string;

    nC4: Integer;
    m_dLogonTime: TDateTime; //0x5B0  //登录时间
    m_dwLogonTick: LongWord; //0x5B8  战领沙城时间(Dword)
    m_boReadyRun: Boolean; //0x5BC  //是否进入游戏完成
    m_nSessionID: Integer; //0x5C0
    m_nPayMent: Integer; //0x5C4  人物当前模式(测试/付费模式)(Dword)
    m_nPayMode: Integer; //0x5C8
    m_SessInfo: pTSessInfo; //全局会话信息
    m_dwLoadTick: LongWord; //0x5CC
    m_nServerIndex: Integer; //0x5D0  人物当前所在服务器序号
    m_boEmergencyClose: Boolean; //0x5D4  掉线标志
    m_boSoftClose: Boolean; //0x5D5
    m_boKickFlag: Boolean; //0x5D6  断线标志(Byte)(@kick 命令)
    m_boReconnection: Boolean; //0x5D7
    m_boRcdSaved: Boolean; //0x5D8
    m_boSwitchData: Boolean; //0x5D9
    m_nWriteChgDataErrCount: Integer; //0x5DC
    m_sSwitchMapName: string; //0x5E0
    m_nSwitchMapX: Integer; //0x5E4
    m_nSwitchMapY: Integer; //0x5E8
    m_boSwitchDataSended: Boolean; //0x5EC
    m_dwChgDataWritedTick: LongWord; //0x5F0
    m_dw5D4: LongWord; //0x5F4

    m_boDealing: Boolean; //0x317
    m_DealLastTick: LongWord; //0x318 交易最后操作时间
    m_DealCreat: TPlayObject; //0x31C
    m_DealItemList: TList; //0x410
    m_nDealGolds: Integer; //0x414  交易的金币数量(Dword)
    m_boDealOK: Boolean; //0x418  确认交易标志(Byte

    m_GroupOwner: TPlayObject; //0x274
    m_GroupMembers: TStringList; //0x278  组成员
    m_boHearWhisper: Boolean; //0x27C  允许私聊
    m_boBanShout: Boolean; //0x27D  允许群聊
    m_boBanGuildChat: Boolean; //0x27E  拒绝行会聊天
    m_boAllowDeal: Boolean; //0x27F  是不允许交易
    m_BlockWhisperList: TStringList; //0x280  禁止私聊人员列表

    m_nHungerStatus: Integer; //0x0F8
    m_boAllowGuildReCall: Boolean; //0xFC
    m_wGroupRcallTime: Word; //0x10C
    m_boAllowGroupReCall: Boolean; //0x10E

    m_boAllowGroup: Boolean; //0xB0  允许组队
    m_boAllowGuild: Boolean; //0xB1  允许加入行会
    m_dwShoutMsgTick: LongWord;

    m_VisibleEvent: TList;

    m_StorageItemList: TList;

    m_dwHitIntervalTime: LongWord; //攻击间隔
    m_dwMagicHitIntervalTime: LongWord; //魔法间隔
    m_dwRunIntervalTime: LongWord; //走路间隔
    m_dwWalkIntervalTime: LongWord; //走路间隔
    m_dwTurnIntervalTime: LongWord; //换方向间隔
    m_dwActionIntervalTime: LongWord; //组合操作间隔
    m_dwRunLongHitIntervalTime: LongWord; //移动刺杀间隔
    m_dwRunHitIntervalTime: LongWord; //跑位攻击间隔
    m_dwWalkHitIntervalTime: LongWord; //走位攻击间隔
    m_dwRunMagicIntervalTime: LongWord; //跑位魔法间隔

    m_dwMagicAttackTick: LongWord; //0x600  魔法攻击时间(Dword)
    m_dwMagicAttackInterval: LongWord; //0x604  魔法攻击间隔时间(Dword)
    m_dwAttackTick: LongWord; //0x608  攻击时间(Dword)
    m_dwMoveTick: LongWord; //0x60C  人物跑动时间(Dword)
    m_dwAttackCount: LongWord; //0x610  人物攻击计数(Dword)
    m_dwAttackCountA: LongWord; //0x614  人物攻击计数(Dword)
    m_dwMagicAttackCount: LongWord; //0x618  魔法攻击计数(Dword)
    m_dwMoveCount: LongWord; //0x61C  人物跑计数(Dword)
    m_dwMoveCountA: LongWord; //0x620  人物跑计数(Dword)
    m_nOverSpeedCount: Integer; //0x624  超速计数(Dword)
    m_boDieInFight3Zone: Boolean; //0x628
    m_Script: pTScript;
    m_NPC: TActorObject;
    m_nVal: array[0..9] of Integer; //0x634 - 658
    m_nMval: array[0..99] of Integer;
    m_DyVal: array[0..9] of Integer; //0x65C - 680
    m_sPlayDiceLabel: string;
    m_boTimeRecall: Boolean; //0x684
    m_dwTimeRecallTick: LongWord;
    m_sMoveMap: string; //0x68C
    m_nMoveX: Integer; //0x690
    m_nMoveY: Integer; //0x694

    m_dwSaveRcdTick: LongWord; //0x6A0 保存人物数据时间间隔
    m_nBright: Integer; //0x6A4
    m_boNewHuman: Boolean; //0x6A8
    m_boSendNotice: Boolean; //0x6A9
    m_dwWaitLoginNoticeOKTick: LongWord;
    m_boLoginNoticeOK: Boolean; //0x6AA
    bo6AB: Boolean; //0x6AB
    m_boExpire: Boolean; //0x6AC  帐号过期
    m_dwShowLineNoticeTick: LongWord; //0x6B0
    m_nShowLineNoticeIdx: Integer; //0x6B4

    m_nShowLineNoticeIdx1: Integer; //0x6B4

    //m_AddUseItems             :array[9..12] of TUserItem;
    m_nSoftVersionDateEx: Integer;
    m_CanJmpScriptLableList: TStringList;

    m_dwTurnTick: LongWord;
    m_wOldIdent: Word;
    m_btOldDir: Byte;

    m_boFirstAction: Boolean; //第一个操作
    m_dwActionTick: LongWord; //二次操作之间间隔时间
    m_sDearName: string[ACTORNAMELEN]; //配偶名称
    m_DearHuman: TPlayObject;
    m_boCanDearRecall: Boolean; //是否允许夫妻传送
    m_boCanMasterRecall: Boolean;
    m_dwDearRecallTick: LongWord; //夫妻传送时间
    m_dwMasterRecallTick: LongWord;
    m_sMasterName: string[ACTORNAMELEN]; //师徒名称
    m_MasterHuman: TPlayObject;
    m_MasterList: TList;
    m_boMaster: Boolean;
    m_btCreditPoint: Integer; //声望点
    m_btMarryCount: Byte; //离婚次数
    m_btReLevel: Byte; //转生等级

    m_nKillMonExpMultiple: Integer; //杀怪经验倍数
    m_dwGetMsgTick: LongWord; //处理消息循环时间控制
    m_nScriptGotoCount: Integer;
    m_sScriptCurrLable: string; //用于处理 @back 脚本命令
    m_sScriptGoBackLable: string; //用于处理 @back 脚本命令
    m_boSetStoragePwd: Boolean;
    m_boReConfigPwd: Boolean;
    m_boCheckOldPwd: Boolean;
    m_boUnLockPwd: Boolean;
    m_boUnLockStoragePwd: Boolean;
    m_boPasswordLocked: Boolean; //锁密码
    m_btPwdFailCount: Byte;
    m_boLockLogon: Boolean; //是否启用锁登录功能
    m_boLockLogoned: Boolean; //是否打开登录锁
    m_sTempPwd: string[7];
    m_sStoragePwd: string[7];
    m_PoseBaseObject: TActorObject;
    m_boStartMarry: Boolean;
    m_boStartMaster: Boolean;
    m_boStartUnMarry: Boolean;
    m_boStartUnMaster: Boolean;
    m_boFilterSendMsg: Boolean; //禁止发方字(发的文字只能自己看到)
    m_nKillMonExpRate: Integer; //杀怪经验倍数(此数除以 100 为真正倍数)
    m_nPowerRate: Integer; //人物攻击力倍数(此数除以 100 为真正倍数)
    m_dwKillMonExpRateTime: LongWord;
    m_dwPowerRateTime: LongWord;
    m_dwRateTick: LongWord;

    m_boCanUseItem: Boolean; //是否允许使用物品
    m_boCanDeal: Boolean;
    m_boCanDrop: Boolean;
    m_boCanGetBackItem: Boolean;
    m_boCanWalk: Boolean;
    m_boCanRun: Boolean;
    m_boCanHit: Boolean;
    m_boCanSpell: Boolean;
    m_boCanSendMsg: Boolean;
    m_boCanDuel: Boolean;

    m_nMemberType: Integer; //会员类型
    m_nMemberLevel: Integer; //会员等级
    m_boSendMsgFlag: Boolean; //发祝福语标志
    m_boChangeItemNameFlag: Boolean;

    m_nGameGold: Integer; //游戏币
    m_boDecGameGold: Boolean; //是否自动减游戏币
    m_dwDecGameGoldTime: LongWord;
    m_dwDecGameGoldTick: LongWord;
    m_nDecGameGold: Integer; //一次减点数

    m_boIncGameGold: Boolean; //是否自动加游戏币
    m_dwIncGameGoldTime: LongWord;
    m_dwIncGameGoldTick: LongWord;
    m_nIncGameGold: Integer; //一次减点数

    m_nGamePoint: Integer; //游戏点数
    m_dwIncGamePointTick: LongWord;

    m_nPayMentPoint: Integer;
    m_dwPayMentPointTick: LongWord;

    m_dwDecHPTick: LongWord;
    m_dwIncHPTick: LongWord;

    m_GetWhisperHuman: TPlayObject;
    m_dwClearObjTick: LongWord;
    m_wContribution: Word; //贡献度
    m_sRankLevelName: string; //显示名称格式串
    m_boFilterAction: Boolean;
    m_boClientFlag: Boolean;
    m_nStep: Byte;
    m_nClientFlagMode: Integer;
    m_dwAutoGetExpTick: LongWord;
    m_nAutoGetExpTime: Integer;
    m_nAutoGetExpPoint: Integer;
    m_AutoGetExpEnvir: TEnvirnoment;
    m_boAutoGetExpInSafeZone: Boolean;
    m_DynamicVarList: TList;
    m_dwClientTick: LongWord;
    m_boTestSpeedMode: Boolean; //进入速度测试模式

    m_boRemoteMsg: Boolean; //是否允许接受消息

    m_boAllowReAlive: Boolean; //是否允许复活

    m_dwStartNotOnlineAddExpTime: LongWord; //离线挂机开始时间
    m_dwNotOnlineAddExpTime: LongWord; //离线挂机时长
    m_nNotOnlineAddExpPoint: Integer; //离线挂机每分钟增加经验值
    m_boKickAutoAddExpUser: Boolean;
    m_dwAutoAddExpPointTick: LongWord;
    m_dwAutoAddExpPointTimeTick: LongWord;

    m_boStartAutoAddExpPoint: Boolean;
    m_sAutoSendMsg: string; //自动回复信息

    m_btHearMsgFColor: Byte; //喊话字体颜色
    m_btWhisperMsgFColor: Byte; //私聊字体颜色

    m_boTimeGoto: Boolean;
    m_dwTimeGotoTick: LongWord;
    m_sTimeGotoLable: string;
    m_TimeGotoNPC: TObject;
    m_boAddSaveList: Boolean;

    m_nDealGoldPose: Integer;
    m_nBigStoragePage: Integer;
    m_dwDedingUseTick: LongWord;

    m_nShowMessagePosition: Integer;

    m_boRunPlayRobotManage: Boolean; //是否运行个人机器人
    m_RobotRunTicks: array of LongWord;
    //m_PlayRobotManage: TPlayRobotManage; //人物个人机器人

    m_boPlayOffLine: Boolean; //是否下线触发

    m_nInteger: array[0..99] of Integer; //人物变量
    m_sString: array[0..99] of string; //人物变量

    m_IntegerList: TQuickList; //N
    m_StringList: TValueList; //S


    m_wMasterCount: Word; //徒弟数

    //m_PlayOrderList: TStringList; //人物排行
    m_nSelPlayOrderType: Integer; //选择排行类型
    m_nPlayOrderPage: Integer; //选择人物排行页数
    m_boGetMyLevelOrder: Boolean;

    m_boWaitHeroDate: Boolean; //是否正在读取英雄数据
    m_boHasHero: Boolean; //是否有英雄
    m_sTempHeroName: string[ACTORNAMELEN];
    m_sHeroCharName: string[ACTORNAMELEN]; //英雄名称
    m_MyHero: TActorObject; //我的英雄
    m_boHeroVersion: Boolean;
    m_dwRecallHeroTick: LongWord;
    m_dwPickUpTick: LongWord;
    m_SuperItemBox: pTSuperItemBox;
    m_sUpgradeItemName: string;
    m_nUpgradeItemIndex: Integer;
    m_nItemIndex: Integer;
    m_boUpgradeNPC: Boolean;

    m_boSayAdvertise: Boolean;
    m_dwSayAdvertiseTick: LongWord;
    m_dwMagicMovePositionTick: LongWord; //

    m_dwSayNoticeTick: LongWord;

    m_boDueling: Boolean; //0x317 正在申请挑战
    m_DuelLastTick: LongWord; //0x318 挑战最后操作时间
    m_DuelCreat: TPlayObject; //0x31C
    m_StartDuelCreat: TPlayObject; //0x31C
    m_DuelItemList: TList; //0x410
    m_nDuelGolds: Integer; //0x414  挑战的金币数量(Dword)
    m_boDuelOK: Boolean; //0x418  确认挑战标志(Byte
    m_boAllowDuel: Boolean;

    m_nGetMonExp: Integer;

    m_dwClientLastTick: LongWord;
    m_dwServerLastTick: LongWord;

    m_TimeLabelList: TList;

    m_boSerieMagicTime: Boolean; //是否在连击
    m_dwSerieMagicTimeTick: LongWord;
    m_dwQueryItemBagTick: LongWord;

    m_nRememberItemIndex: Integer;
    m_sRememberItemName: string;

    m_dwTick30: LongWord;

    m_boShowRankLevelName: Boolean;
  private
    function UpdateVisibleEventGay(BaseObject: TBaseObject): Boolean;

    function DelStorageItem(UserItem: pTUserItem): Boolean; overload;
    function DelStorageItem(nItemIndex: Integer; sItemName: string): Boolean; overload;

    function ClientDropGold(nGold: Integer): Boolean;
    procedure ClientQueryBagItems();

    procedure ClientQueryUserState(BaseObject: TActorObject; nX, nY: Integer);
    procedure ClientQueryUserSet(ProcessMsg: pTProcessMessage);
    function ClientDropItem(sItemName: string; nItemIdx: Integer): Boolean;
    function ClientPickUpItem: Boolean;
    procedure ClientOpenDoor(nX, nY: Integer);

    procedure ClientUseItems(nItemIdx: Integer; sItemName: string);

    function UseStdmodeFunItem(StdItem: pTStdItem): Boolean;

    function ClientGetButchItem(BaseObject: TActorObject; nX, nY: Integer; btDir: Byte; var dwDelayTime: LongWord): Boolean;
    procedure ClientChangeMagicKey(nSkillIdx, nKey: Integer);
    procedure ClientClickNPC(NPC, Param: Integer);
    procedure ClientMerchantDlgSelect(nParam1: Integer; sMsg: string);
    procedure ClientMerchantQuerySellPrice(nParam1, nMakeIndex: Integer; sMsg: string);
    procedure ClientUserSellItem(nParam1, nMakeIndex: Integer; sMsg: string);
    procedure ClientUserBuyItem(nIdent, nParam1, nInt, nZz: Integer; sMsg: string);
    procedure ClientQueryRepairCost(nParam1, nInt: Integer; sMsg: string);
    procedure ClientRepairItem(nParam1, nInt: Integer; sMsg: string);

    procedure ClientUserChangeItem(nParam1, nMakeIndex: Integer; sItemName: string);

    procedure ClientUserSellOffItem(nParam1, nMakeIndex: Integer; sMsg: string); //拍卖
    procedure ClientUserBuySellOffItem(nIdent, nParam1, nInt, nZz: Integer; sMsg: string); //拍卖
    procedure ClientUserGetSellOffGold(nInt: Integer; sMsg: string); //取款

    procedure ClientBuySellOffItem(sItemName: string; nInt: Integer);

    procedure ClientGropuClose();
    procedure ClientCreateGroup(sHumName: string);
    procedure ClientAddGroupMember(sHumName: string);
    procedure ClientDelGroupMember(sHumName: string);

    procedure ClientDealTry(sHumName: string); overload;
    procedure ClientAddDealItem(nItemIdx: Integer; sItemName: string);
    procedure ClientDelDealItem(nItemIdx: Integer; sItemName: string);
    procedure ClientCancelDeal();
    procedure ClientChangeDealGold(nGold: Integer);
    procedure ClientDealEnd();

    procedure ClientDuelTry(sHumName: string); overload;
    procedure ClientAddDuelItem(nItemIdx: Integer; sItemName: string);
    procedure ClientDelDuelItem(nItemIdx: Integer; sItemName: string);
    procedure ClientCancelDuel;
    procedure ClientChangeDuelGold(nGold: Integer);
    procedure ClientDuelEnd;

    procedure ClientCancelStore;
    procedure ClientStartStore(sMsg: string; nItemCount: Integer);
    procedure ClientQueryUserStoreState(PlayObject: TActorObject; nX, nY: Integer);

    procedure ClientBuySroreItem(PlayObject: TActorObject; sMsg: string);

    procedure ClientHeroGroupAttack(BaseObject: TActorObject; nX, nY: Integer); //合击
    procedure ClientHeroTarget(BaseObject: TActorObject; nX, nY: Integer); //锁定
    procedure ClientGetSellGoodList(nType, nPage: Integer; sItemName: string);

    procedure RandomGetUserBoxItem(ItemList: TStringList);
    procedure ClientOpenBox(sItemName: string); overload;
    procedure ClientOpenBox(nItemIdx: Integer; sItemName: string); overload;
    procedure ClientGetBoxIndex();
    procedure ClientGetBoxItem(nIdx: Integer);
    procedure ClientGetBackBox();
    procedure ClientGetUpgradeItems(sData: string);

    procedure ClientQueryUserItem(nLabel: Integer; sData: string);

    procedure SendDelDuelItem(UserItem: pTUserItem);
    procedure SendAddDuelItem(UserItem: pTUserItem);
    procedure OpenDuelDlg(PlayObject: TPlayObject);

    procedure ClientQueryRanking(nTabelPage, nPageType, nPage: Integer; boSearchSelf: Boolean);
    procedure ClientStartSerieSpell(Target: TActorObject; X, Y: Integer); //开始连击
    procedure ClientStopSerieSpell(); //停止连击

    procedure ClientStorageItem(NPC: TObject; nItemIdx: Integer; sMsg: string);
    procedure ClientTakeBackStorageItem(NPC: TObject; nItemIdx: Integer; sMsg: string);
    procedure ClientGetMinMap();
    procedure ClientMakeDrugItem(NPC: TObject; nItemName: string);
    procedure ClientOpenGuildDlg();
    procedure ClientGuildHome();
    procedure ClientGuildMemberList();
    procedure ClientGuildAddMember(sHumName: string);
    procedure ClientGuildDelMember(sHumName: string);
    procedure ClientGuildUpdateNotice(sNotict: string);
    procedure ClientGuildUpdateRankInfo(sRankInfo: string);
    procedure ClientGuildAlly();
    procedure ClientGuildBreakAlly(sGuildName: string);
    procedure ClientAdjustBonus(nPoint: Integer; sMsg: string);
    function ClientChangeDir(wIdent: Word; nX, nY, nDir: Integer; var dwDelayTime: LongWord): Boolean;
    function ClientWalkXY(wIdent: Word; nX, nY: Integer; boLateDelivery: Boolean; var dwDelayTime: LongWord): Boolean;
    function ClientHorseRunXY(wIdent: Word; nX, nY: Integer; boLateDelivery: Boolean; var dwDelayTime: LongWord): Boolean;
    function ClientRunXY(wIdent: Word; nX, nY: Integer; nFlag: Integer; var dwDelayTime: LongWord): Boolean;
    function ClientHitXY(wIdent: Word; nX, nY, nDir: Integer; boLateDelivery: Boolean; var dwDelayTime: LongWord): Boolean;
    function ClientSitDownHit(nX, nY, nDir: Integer; var dwDelayTime: LongWord): Boolean;
    function ClientSpellXY(wIdent: Word; nKey: Integer; nTargetX, nTargetY: Integer; TargeTActorObject: TActorObject; boLateDelivery: Boolean; var dwDelayTime: LongWord): Boolean;

    function GetUserItemWeitht(nWhere: Integer): Integer;
    procedure SendDelDealItem(UserItem: pTUserItem);
    procedure SendAddDealItem(UserItem: pTUserItem);
    procedure OpenDealDlg(PlayObject: TPlayObject);

    function EatItems(StdItem: pTStdItem): Boolean;
    function EatUseItems(nShape: Integer): Boolean;
    function ReadBook(StdItem: pTStdItem): Boolean;
    function DayBright(): Byte;

    function RepairWeapon(): Boolean;
    function SuperRepairWeapon(): Boolean;
    function WinLottery(): Boolean;
    procedure ChangeServerMakeSlave(SalveInfo: pTSlaveInfo);
    function WeaptonMakeLuck(): Boolean;
    function PileStones(nX, nY: Integer): Boolean;
    procedure MakeMine();
    function GetRangeHumanCount(): Integer;
    procedure GetStartPoint();
    procedure MobPlace(sX, sY, sMonName, sCount: string);
    procedure LogonTimcCost;
    procedure SendNotice();
    procedure SendLogon();
    procedure SendServerConfig();
    procedure SendServerStatus();

    procedure ClientQueryUserName(Target: TActorObject; x, y: Integer);
    procedure SendUseitems();
    procedure SendUseMagic();
    procedure SendSaveItemList(nBaseObject: Integer);
    procedure SendSaveBigStorageItemList(nBaseObject: Integer; nPage: Integer);
    procedure SendDelItemList(ItemList: TStringList);
    procedure SendAdjustBonus();
    procedure SendChangeGuildName();
    procedure SendMapDescription();
    procedure SendGoldInfo(boSendName: Boolean);

    procedure ShowMapInfo(sMAP, sX, sY: string);

    function DoMotaebo(nDir: Byte; nMagicLevel: Integer): Boolean;
    function DoSpell(UserMagic: pTUserMagic; nTargetX, nTargetY: Integer; BaseObject: TActorObject): Boolean;
    procedure GetOldAbil(var OAbility: TOAbility);
    procedure ReadAllBook;
    function CheckItemsNeed(StdItem: pTStdItem): Boolean;

    function CheckActionStatus(wIdent: Word; var dwDelayTime: LongWord): Boolean;
    procedure RecalcAdjusBonus;

    procedure CheckMarry();
    procedure CheckMaster();
    procedure RefMyStatus;
    procedure ProcessClientPassword(ProcessMsg: pTProcessMessage);
    function CheckDenyLogon: Boolean;
    procedure ProcessSpiritSuite;
    function HorseRunTo(btDir: Byte; boFlag: Boolean): Boolean;

    procedure DelMember(PlayObject: TPlayObject);
    procedure SendGroupText(sMsg: string); overload;
    procedure SendGroupText(sMsg: string; boSend: Boolean); overload;
    procedure LeaveGroup();

    function UseMagicJump(MagicID: Word): Boolean;
    function AttackJump: Boolean;
  public
    constructor Create(); override;
    destructor Destroy; override;

    function GetMagicInfo(sMagicName: string): pTUserMagic; overload;
    function GetMagicInfo(nMagicID: Integer): pTUserMagic; overload;
    procedure ClearViewRange; override;

    procedure ClientTakeOnItems(btWhere: Byte; nItemIdx: Integer; sItemName: string);
    procedure ClientTakeOffItems(btWhere: Byte; nItemIdx: Integer; sItemName: string);
    procedure ClientTakeOnItemsEx(btWhere: Byte; nItemIdx: Integer; sItemName: string);
    procedure ClientTakeOffItemsEx(btWhere: Byte; nItemIdx: Integer; sItemName: string);
    procedure ClearCopyItems(ItemList: TList); overload;
    procedure AddCheckItems(ItemList: TList);

    procedure ClearCopyItems(); overload;
    procedure ClientDealTry(PlayObject: TPlayObject); overload;

    procedure ClientHeroLogOut(BaseObject: TActorObject);
    procedure ClientHeroLogOn();
    procedure ClientTakeOnItemsFormBag(nIndex: Integer; btWhere: Byte; nItemIdx: Integer; sItemName: string);
    procedure ClientTakeOffItemsToBag(nIndex: Integer; btWhere: Byte; nItemIdx: Integer; sItemName: string);

    procedure ClientMasterBagToHeroBag(nItemIdx: Integer; sItemName: string);
    procedure ClientHeroBagToMasterBag(nItemIdx: Integer; sItemName: string);
    procedure ClientHeroProtect(nX, nY: Integer);
    //procedure ClientHeroAttack(BaseObject: TActorObject; nX, nY: Integer);
    procedure ClientHeroAutoAttack(); //英雄挂机
    procedure ClientDuelTry(PlayObject: TPlayObject); overload;

    procedure MoveToHome();
    function CancelGroup(): Boolean;

    procedure BaseObjectMove(sMAP, sX, sY: string);
    function AbilityUp(UserMagic: pTUserMagic): Boolean;
    procedure SearchViewRange(); override;
    procedure PKDie(PlayObject: TPlayObject);
    procedure GameTimeChanged();
    procedure RunNotice();
    function GetMyStatus(): Integer;
    function IncGold(tGold: Integer): Boolean;
    procedure IncGamePoint(nGamePoint: Integer);
    procedure IncGameGold(nGameGold: Integer);
    procedure SendSocket(DefMsg: pTDefaultMessage; sMsg: string); virtual;
    procedure SendDefMessage(wIdent: Word; nRecog: Integer; nParam, nTag, nSeries: Word; sMsg: string); virtual;
    function IsEnoughBag(): Boolean;
    procedure SendAddItem(UserItem: pTUserItem); virtual;
    procedure SendDelItems(UserItem: pTUserItem); virtual;
    procedure Whisper(whostr, saystr: string); virtual;
    function IsBlockWhisper(sName: string): Boolean;
    function QuestCheckItem(sItemName: string; var nCount: Integer; var nParam: Integer; var nDura: Integer): pTUserItem;
    function QuestTakeCheckItem(CheckItem: pTUserItem): Boolean;
    procedure GainExp(dwExp: LongWord); override;
    procedure IncExp(dwExp: LongWord); override;
    procedure GetExp(dwExp: LongWord); override;
    procedure WinExp(dwExp: LongWord); override;
    procedure IncBeadExp(dwExp: LongWord);
    function DecGold(nGold: Integer): Boolean;
    procedure DecGamePoint(nGamePoint: Integer);
    procedure DecGameGold(nGameGold: Integer);
    procedure Run(); override;
    function Operate(ProcessMsg: pTProcessMessage): Boolean; override;
    procedure RecalcAbilitys(); override;
    procedure MakeSaveRcd(var HumanRcd: THumDataInfo);
    procedure DealCancel();
    procedure DealCancelA();
    procedure GetBackDealItems();

    procedure DuelCancel;
    procedure DuelCancelA;
    procedure GetBackDuelItems();

    procedure StoreCancel;
    procedure GetBackStoreItems();

    procedure ReAlive(); override;
    function GetShowName(): string; override;

    procedure Disappear(); override;
    procedure DisappearB();
    procedure GoldChange(sChrName: string; nGold: Integer);
    procedure ProcessUserLineMsg(sData: string);
    procedure ProcessSayMsg(sData: string); override;
    procedure ClearStatusTime();
    procedure UserLogon(); virtual;
    procedure RefRankInfo(nRankNo: Integer; sRankName: string);
    procedure RefUserState;
    procedure SendGroupMembers();
    procedure JoinGroup(PlayObject: TPlayObject);
    procedure AddGroupMember(sCharName: string; PlayObject: TPlayObject);
    function GeTActorObjectInfo(): string;
    function GetHitMsgCount(): Integer;
    function GetSpellMsgCount(): Integer;
    function GetWalkMsgCount(): Integer;
    function GetRunMsgCount(): Integer;
    function GetTurnMsgCount(): Integer;
    function GetSiteDownMsgCount(): Integer;
    function GetDigUpMsgCount(): Integer;
    procedure SetScriptLabel(sLabel: string);
    procedure GetScriptLabel(sMsg: string);
    function LableIsCanJmp(sLabel: string): Boolean;
    function GetMyInfo(): string;
    procedure MakeGhost; override;
    procedure ScatterBagItems(ItemOfCreat: TActorObject); override;
    procedure DropUseItems(BaseObject: TActorObject); override;
    procedure RecallHuman(sHumName: string);
    procedure SendAddMagic(UserMagic: pTUserMagic);
    procedure SendDelMagic(UserMagic: pTUserMagic);
    procedure ReQuestGuildWar(sGuildName: string);
    procedure SendUpdateItem(UserItem: pTUserItem); overload;

    procedure SendUpdateItem(nIndex: Integer; UserItem: pTUserItem); overload;

    procedure SendUpdateChangeItem(UserItem: pTUserItem);
    procedure GetBagUseItems(var btDc: Byte; var btSc: Byte; var btMc: Byte; var btDura: Byte);
    procedure RepairAllItem();
    procedure RandomMove();
    function IsGroupMember(Target: TActorObject): Boolean;

    procedure Hear(nIndex: Integer; sMsg: string); virtual;

    procedure CmdShowEffect(Cmd: pTGameCmd; sEffect, sTime: string);
    procedure CmdSignMapMove(Cmd: pTGameCmd);
    procedure CmdGetUserItems(Cmd: pTGameCmd; sHumanName, sItemName, sItemCount, sType: string);
    procedure CmdUserCmd(sLable: string);
    procedure CmdEndGuild();
    procedure CmdMemberFunction(sCmd, sParam: string);
    procedure CmdMemberFunctionEx(sCmd, sParam: string);

    procedure CmdSearchDear(sCmd, sParam: string);
    procedure CmdSearchMaster(sCmd, sParam: string);
    procedure CmdDearRecall(sCmd, sParam: string);
    procedure CmdMasterRecall(sCmd, sParam: string);
    procedure CmdSbkDoorControl(sCmd, sParam: string);

    procedure CmdClearBagItem(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdShowUseItemInfo(Cmd: pTGameCmd; sHumanName: string);

    procedure CmdBindUseItem(Cmd: pTGameCmd; sHumanName, sItem, sType, sLight: string);
    procedure CmdUnBindUseItem(Cmd: pTGameCmd; sHumanName, sItem: string);
    procedure CmdLockLogin(Cmd: pTGameCmd);
    procedure CmdViewDiary(sCmd: string; nFlag: Integer);
    procedure CmdUserMoveXY(sCmd, sX, sY: string);
    procedure CmdSearchHuman(sCmd, sHumanName: string);
    procedure CmdSearchMon(sCmd, sMonName: string);
    procedure CmdGroupRecall(sCmd: string);
    procedure CmdAllowGroupReCall(sCmd, sParam: string);

    procedure CmdGuildRecall(sCmd, sParam: string);

    procedure CmdChangeAttackMode(nMode: Integer; sParam1, sParam2, sParam3, sParam4, sParam5, sParam6, sParam7: string);
    procedure CmdChangeSalveStatus();
    procedure CmdTakeOnHorse(sCmd, sParam: string);
    procedure CmdTakeOffHorse(sCmd, sParam: string);

    procedure CmdPrvMsg(sCmd: string; nPermission: Integer; sHumanName: string);
    procedure CmdHumanLocal(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdMapMove(Cmd: pTGameCmd; sMapName: string);

    procedure CmdPositionMove(Cmd: pTGameCmd; sMapName, sX, sY: string);

    procedure CmdHumanInfo(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdReLoadAdmin(sCmd: string);
    procedure CmdReloadNpc(sParam: string);
    procedure CmdReloadManage(Cmd: pTGameCmd; sParam: string);
    procedure CmdReloadRobotManage;
    procedure CmdReloadRobot;
    procedure CmdReloadMonItems();
    procedure CmdAdjustExp(Human: TPlayObject; nExp: Integer);
    procedure CmdAddGuild(Cmd: pTGameCmd; sGuildName, sGuildChief: string);
    procedure CmdDelGuild(Cmd: pTGameCmd; sGuildName: string);
    procedure CmdGuildWar(sCmd, sGuildName: string);
    procedure CmdChangeSabukLord(Cmd: pTGameCmd; sCASTLENAME, sGuildName: string; boFlag: Boolean);
    procedure CmdForcedWallconquestWar(Cmd: pTGameCmd; sCASTLENAME: string);
    procedure CmdOPTraining(sHumanName, sSkillName: string; nLevel: Integer);
    procedure CmdOPDeleteSkill(sHumanName, sSkillName: string);
    procedure CmdReloadGuildAll();
    procedure CmdReAlive(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdAdjuestLevel(Cmd: pTGameCmd; sHumanName: string; nLevel: Integer);
    procedure CmdAdjuestExp(Cmd: pTGameCmd; sHumanName, sExp: string);

    procedure CmdBackStep(sCmd: string; nType, nCount: Integer);
    procedure CmdFreePenalty(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdPKpoint(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdIncPkPoint(Cmd: pTGameCmd; sHumanName: string; nPoint: Integer);
    procedure CmdHunger(sCmd, sHumanName: string; nHungerPoint: Integer);
    procedure CmdHair(Cmd: pTGameCmd; sHumanName: string; nHair: Integer);
    procedure CmdTrainingSkill(Cmd: pTGameCmd; sHumanName, sSkillName: string; nLevel: Integer);
    procedure CmdTrainingMagic(Cmd: pTGameCmd; sHumanName, sSkillName: string; nLevel: Integer);

    procedure CmdDelSkill(Cmd: pTGameCmd; sHumanName, sSkillName: string);
    procedure CmdDeleteItem(Cmd: pTGameCmd; sHumanName, sItemName: string; nCount: Integer);
    procedure CmdClearMission(Cmd: pTGameCmd; sHumanName: string);

    procedure CmdTraining(sSkillName: string; nLevel: Integer);
    procedure CmdChangeJob(Cmd: pTGameCmd; sHumanName, sJobName: string);
    procedure CmdChangeGender(Cmd: pTGameCmd; sHumanName, sSex: string);
    procedure CmdMission(Cmd: pTGameCmd; sX, sY: string);
    procedure CmdMobPlace(Cmd: pTGameCmd; sX, sY, sMonName, sCount: string);
    procedure CmdMobLevel(Cmd: pTGameCmd; Param: string);
    procedure CmdMobCount(Cmd: pTGameCmd; sMapName: string);
    procedure CmdHumanCount(Cmd: pTGameCmd; sMapName: string);

    procedure CmdDisableFilter(sCmd, sParam1: string);
    procedure CmdChangeUserFull(sCmd, sUserCount: string);
    procedure CmdChangeZenFastStep(sCmd, sFastStep: string);

    procedure CmdReconnection(sCmd, sIPaddr, sPort: string);
    procedure CmdContestPoint(Cmd: pTGameCmd; sGuildName: string);
    procedure CmdStartContest(Cmd: pTGameCmd; sParam1: string);
    procedure CmdEndContest(Cmd: pTGameCmd; sParam1: string);

    procedure CmdAnnouncement(Cmd: pTGameCmd; sGuildName: string);
    procedure CmdKill(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdMakeItem(Cmd: pTGameCmd; sItemName: string; nCount: Integer; sParam: string);
    procedure CmdSmakeItem(Cmd: pTGameCmd; nWhere, nValueType, nValue: Integer);
    procedure CmdBonuPoint(Cmd: pTGameCmd; sHumName: string; nCount: Integer);
    procedure CmdDelBonuPoint(Cmd: pTGameCmd; sHumName: string);
    procedure CmdRestBonuPoint(Cmd: pTGameCmd; sHumName: string);

    procedure CmdFireBurn(nInt, nTime, nN: Integer);
    procedure CmdTestFire(sCmd: string; nRange, nType, nTime, nPoint: Integer);

    procedure CmdTestStatus(sCmd: string; nType, nTime: Integer);

    procedure CmdDelGold(Cmd: pTGameCmd; sHumName: string; nCount: Integer);
    procedure CmdAddGold(Cmd: pTGameCmd; sHumName: string; nCount: Integer);
    procedure CmdDelGameGold(sCmd, sHumName: string; nPoint: Integer);
    procedure CmdAddGameGold(sCmd, sHumName: string; nPoint: Integer);
    procedure CmdGameGold(Cmd: pTGameCmd; sHumanName: string; sCtr: string; nGold: Integer);
    procedure CmdGamePoint(Cmd: pTGameCmd; sHumanName: string; sCtr: string; nPoint: Integer);
    procedure CmdCreditPoint(Cmd: pTGameCmd; sHumanName: string; sCtr: string; nPoint: Integer);

    procedure CmdMob(Cmd: pTGameCmd; sMonName: string; nCount, nLevel: Integer);

    procedure CmdRefineWeapon(Cmd: pTGameCmd; nDc, nMc, nSc, nHit: Integer);
    procedure CmdRecallMob(Cmd: pTGameCmd; sMonName: string; nCount, nLevel, nAutoChangeColor, nFixColor: Integer);
    procedure CmdLuckPoint(sCmd: string; nPermission: Integer; sHumanName, sCtr, sPoint: string);
    procedure CmdLotteryTicket(sCmd: string; nPermission: Integer; sParam1: string);
    procedure CmdReloadGuild(sCmd: string; nPermission: Integer; sParam1: string);
    procedure CmdReloadLineNotice(sCmd: string; nPermission: Integer; sParam1: string);
    procedure CmdReloadAbuse(sCmd: string; nPermission: Integer; sParam1: string);

    procedure CmdMobNpc(sCmd: string; nPermission: Integer; sParam1, sParam2, sParam3, sParam4: string);
    procedure CmdNpcScript(sCmd: string; nPermission: Integer; sParam1, sParam2, sParam3: string);
    procedure CmdDelNpc(sCmd: string; nPermission: Integer; sParam1: string);
    procedure CmdKickHuman(Cmd: pTGameCmd; sHumName: string);
    procedure CmdTing(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdSuperTing(Cmd: pTGameCmd; sHumanName, sRange: string);
    procedure CmdMapMoveHuman(Cmd: pTGameCmd; sSrcMap, sDenMap: string);
    procedure CmdShutup(Cmd: pTGameCmd; sHumanName, sTime: string);
    procedure CmdShowMapInfo(Cmd: pTGameCmd; sParam1: string);

    procedure CmdShutupRelease(Cmd: pTGameCmd; sHumanName: string; boAll: Boolean);
    procedure CmdShutupList(Cmd: pTGameCmd; sParam1: string);
    procedure CmdShowSbkGold(Cmd: pTGameCmd; sCASTLENAME, sCtr, sGold: string);
    procedure CmdRecallHuman(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdReGotoHuman(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdShowHumanFlag(sCmd: string; nPermission: Integer; sHumanName, sFlag: string);
    procedure CmdShowHumanUnitOpen(sCmd: string; nPermission: Integer; sHumanName, sUnit: string);
    procedure CmdShowHumanUnit(sCmd: string; nPermission: Integer; sHumanName, sUnit: string);

    procedure CmdChangeAdminMode(sCmd: string; nPermission: Integer; sParam1: string; boFlag: Boolean);
    procedure CmdChangeObMode(sCmd: string; nPermission: Integer; sParam1: string; boFlag: Boolean);
    procedure CmdChangeSuperManMode(sCmd: string; nPermission: Integer; sParam1: string; boFlag: Boolean);
    procedure CmdChangeLevel(Cmd: pTGameCmd; sParam1: string);
    procedure CmdChangeDearName(Cmd: pTGameCmd; sHumanName: string; sDearName: string);
    procedure CmdChangeMasterName(Cmd: pTGameCmd; sHumanName: string; sMasterName, sIsMaster: string);
    procedure CmdStartQuest(Cmd: pTGameCmd; sQuestName: string);
    procedure CmdSetPermission(Cmd: pTGameCmd; sHumanName, sPermission: string);
    procedure CmdClearMapMonster(Cmd: pTGameCmd; sMapName, sMonName, sItems: string);
    procedure CmdReNewLevel(Cmd: pTGameCmd; sHumanName, sLevel: string);

    procedure CmdDenyIPaddrLogon(Cmd: pTGameCmd; sIPaddr, sFixDeny: string);
    procedure CmdDelDenyIPaddrLogon(Cmd: pTGameCmd; sIPaddr, sFixDeny: string);
    procedure CmdShowDenyIPaddrLogon(Cmd: pTGameCmd; sIPaddr, sFixDeny: string);

    procedure CmdDenyAccountLogon(Cmd: pTGameCmd; sAccount, sFixDeny: string);
    procedure CmdDelDenyAccountLogon(Cmd: pTGameCmd; sAccount, sFixDeny: string);
    procedure CmdShowDenyAccountLogon(Cmd: pTGameCmd; sAccount, sFixDeny: string);

    procedure CmdDenyCharNameLogon(Cmd: pTGameCmd; sCharName, sFixDeny: string);
    procedure CmdDelDenyCharNameLogon(Cmd: pTGameCmd; sCharName, sFixDeny: string);
    procedure CmdShowDenyCharNameLogon(Cmd: pTGameCmd; sCharName, sFixDeny: string);
    procedure CmdViewWhisper(Cmd: pTGameCmd; sCharName, sParam2: string);
    procedure CmdSpirtStart(sCmd: string; sParam1: string);
    procedure CmdSpirtStop(sCmd: string; sParam1: string);
    procedure CmdSetMapMode(sCmd: string; sMapName, sMapMode, sParam1, sParam2: string);
    procedure CmdShowMapMode(sCmd: string; sMapName: string);
    procedure CmdClearHumanPassword(sCmd: string; nPermission: Integer; sHumanName: string);

    procedure CmdChangeItemName(sCmd, sMakeIndex, sItemIndex, sItemName: string);
    procedure CmdDisableSendMsg(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdEnableSendMsg(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdDisableSendMsgList(Cmd: pTGameCmd);
    procedure CmdTestGetBagItems(Cmd: pTGameCmd; sParam: string);
    procedure CmdMobFireBurn(Cmd: pTGameCmd; sMAP, sX, sY, sType, sTime, sPoint: string);
    procedure CmdTestSpeedMode(Cmd: pTGameCmd);
    procedure CmdClearCopyItem(Cmd: pTGameCmd; sHumanName: string);
    procedure CmdRangeClearCopyItem(Cmd: pTGameCmd; sRange: string);
    procedure CmdRestHero();

    procedure ProcessUserCmd(sParam1, sParam2, sParam3, sParam4, sParam5, sParam6, sParam7: string);

    function DieFunc: Boolean;
    function LevelUpFunc: Boolean;
    function KillPlayFunc: Boolean;
    function KillMonsterFunc: Boolean;
    function DieGotoLable(): Boolean;

    function IsGotoLabel(sMapName: string; nX, nY, nRange, nQuestFalgStatus: Integer; boQuestFalgStatus: Boolean; sItemName1, sItemName2: string; boNeedGroup: Boolean; nRandomCount: Integer): Boolean;
    function StartMapEventOfDropItem(sItemName: string): Boolean;
    procedure StartMapEventOfPickUpItem(sItemName: string);
    procedure StartMapEventOfMine();
    procedure StartMapEventOfWalk();
    procedure StartMapEventOfRun();

    procedure ClearTimeLabel(nType: Integer);
    procedure ClearAllDelayLabel;
    function CheckTakeOnItems(nWhere: Integer; var StdItem: TStdItem): Boolean;
    procedure SendChangeItemFail();
  end;

  TAIPlayObject = class(TPlayObject)
    m_boAIStart: Boolean;
    m_ManagedEnvir: TEnvirnoment; //挂机地图
    m_PointManager: TPointManager;
    m_Path: TPath;
    m_nPostion: Integer;
    m_nMoveFailCount: Integer;
    m_sConfigListFileName: string;
    m_sFilePath: string;
    m_sConfigFileName: string;
    m_sHeroConfigFileName: string;
  public
    constructor Create(); override;
    destructor Destroy; override;
    procedure Initialize; override;
    procedure Run; override;
    function Operate(ProcessMsg: pTProcessMessage): Boolean; override;
    function FollowMaster: Boolean; override;
    procedure Wondering(); override;
    procedure SearchTarget(); override;
    procedure Start(PathType: TPathType);
    procedure Stop;
    procedure MakeGhost; override;
    procedure ProcessSayMsg(sData: string); override;
    procedure Whisper(whostr, saystr: string); override;
    procedure Hear(nIndex: Integer; sMsg: string); override;
    procedure Die; override;
    procedure Struck(hiter: TActorObject); override;
    function IsProtectTarget(BaseObject: TActorObject): Boolean; override;
    function IsAttackTarget(BaseObject: TActorObject): Boolean; override;
    function IsProperTarget(BaseObject: TActorObject): Boolean; override;
    function IsProperFriend(BaseObject: TActorObject): Boolean; override;

    function GetRandomConfigFileName: string;
  end;

procedure AddUserLog(sMsg: string);

implementation

uses M2Share, HUtil32, EncryptUnit, ObjNpc, IdSrvClient, ItmUnit, Event,
  ObjMon, LocalDB, Castle, svMain, PlugIn, ObjHero, RunDB, UsrEngn, ObjRobot;

{ TActorObject }

constructor TActorObject.Create;
begin
  inherited;
  m_ObjType := t_Actor;

  m_boGhost := False;
  m_dwGhostTick := 0;
  m_boDeath := False;
  m_dwDeathTick := 0;
  m_SendRefMsgTick := GetTickCount();
  m_btDirection := 4;
  m_btRaceServer := RC_ANIMAL;
  m_btRaceImg := 0;
  m_btHair := 0;
  m_btJob := 0;
  m_nGold := 0;
  m_wAppr := 0;
  bo2B9 := True;
  m_nViewRange := 5;
  m_sHomeMap := '0';

  m_btPermission := 0;
  m_nLight := 0;
  m_btNameColor := 255;
  m_nHitPlus := 0;
  m_nHitDouble := 0;
  m_dBodyLuck := 0;

  m_boRecallSuite := False;
  bo245 := False;
  m_boTestGa := False;
  m_boGsa := False;
  bo2BA := False;
  m_boAbilSeeHealGauge := False;
  m_boPowerHit := False;
  m_boUseThrusting := False;
  m_boUseHalfMoon := False;
  m_boFireHitSkill := False;
  m_boCIDHitSkill := False;
  m_boKTZHitSkill := False;
  m_boTDBeffect          := False;
  m_btHitPoint := 5;
  m_btSpeedPoint := 15;
  m_nHitSpeed := 0;
  m_btLifeAttrib := 0;
  m_btAntiPoison := 0;
  m_nPoisonRecover := 0;
  m_nHealthRecover := 0;
  m_nSpellRecover := 0;
  m_nAntiMagic := 0;
  m_nLuck := 0;
  m_nIncSpell := 0;
  m_nIncHealth := 0;
  m_nIncHealing := 0;
  m_nPerHealth := 5;
  m_nPerHealing := 5;
  m_nPerSpell := 5;
  m_dwIncHealthSpellTick := GetTickCount();
  m_btGreenPoisoningPoint := 0;
  m_nFightZoneDieCount := 0;
  //  m_nGoldMax       := 5000000;
  m_nGoldMax := g_Config.nHumanMaxGold;
  m_nCharStatus := 0;
  m_nCharStatusEx := 0;
  FillChar(m_wStatusTimeArr, SizeOf(TStatusTime), #0);

  FillChar(m_wStatusArrValue, SizeOf(m_wStatusArrValue), 0);
  FillChar(m_dwStatusArrTimeOutTick, SizeOf(m_dwStatusArrTimeOutTick), #0);

  FillChar(m_wNewStatusArrValue, SizeOf(m_wNewStatusArrValue), 0);
  FillChar(m_dwNewStatusArrTimeOutTick, SizeOf(m_dwNewStatusArrTimeOutTick), #0);

  FillChar(m_BonusAbil, SizeOf(TNakedAbility), #0);
  FillChar(m_CurBonusAbil, SizeOf(TNakedAbility), #0);

  btB2 := 0;
  m_btAttatckMode := 0;
  m_boInFreePKArea := False;
  m_boGuildWarArea := False;
  bo2B0 := False;
  m_boSuperMan := False;
  m_boSkeleton := False;
  bo2BF := False;
  m_boHolySeize := False;
  m_boCrazyMode := False;
  m_boShowHP := False;
  bo2F0 := False;
  m_boAnimal := False;
  m_boNoItem := False;
  m_nBodyLeathery := 50;
  m_boFixedHideMode := False;
  m_boStickMode := False;
  m_boNoAttackMode := False;
  bo2C1 := False;
  m_boPKFlag := False;
  m_nMoXieSuite := 0;
  m_nHongMoSuite := 0;
  m_boPoisonItem := False; //中毒装备
  m_boDamageHPItem := False; //被攻击掉对方血
  m_boVirusImmunity := False; //病毒免疫

  m_db3B0 := 0;
  FillChar(m_AddAbil, SizeOf(TAddAbility), #0);

  m_MsgList := TList.Create;
  m_VisibleHumanList := TList.Create;
  m_VisibleActors := TList.Create;
  m_VisibleItems := TList.Create;

  m_ItemList := TList.Create;
  m_MagicList := TList.Create;

  m_SlaveList := TList.Create;

  m_GroupItem := TList.Create;
  m_GroupItemList := TList.Create;

  m_LockList := TList.Create;

  m_boIsVisibleActive := False;
  m_nProcessRunCount := 0;

  FillChar(m_UseItems, SizeOf(THumanUseItems), 0);
  m_MagicOneSwordSkill := nil;
  m_MagicPowerHitSkill := nil;
  m_MagicErgumSkill := nil;
  m_MagicBanwolSkill := nil;
  m_MagicFireSwordSkill := nil;
  m_MagicCrsSkill := nil;
  m_Magic41Skill := nil;
  m_Magic42Skill := nil;
  m_Magic43Skill := nil;
  m_Magic58Skill := nil;
  m_MagicZRJFSkill := nil;
  m_Magic31Skill := nil;

  m_Magic100Skill := nil;
  m_Magic101Skill := nil;
  m_Magic102Skill := nil;
  m_Magic103Skill := nil;

  m_Castle := nil;
  m_Master := nil;
  n294 := 0;
  m_btSlaveExpLevel := 0;
  m_boSlaveRelax := False;

  FillChar(m_WAbil, SizeOf(TAbility), #0);

  m_Abil.Level := 1;
  m_Abil.AC := 0;
  m_Abil.MAC := 0;
  m_Abil.DC := MakeLong(1, 4);
  m_Abil.MC := MakeLong(1, 2);
  m_Abil.SC := MakeLong(1, 2);
  m_Abil.HP := 15;
  m_Abil.MP := 15;
  m_Abil.MaxHP := 15;
  m_Abil.MaxMP := 15;
  m_Abil.Exp := 0;
  m_Abil.MaxExp := 50;
  m_Abil.Weight := 0;
  m_Abil.MaxWeight := 100;
  m_boWantRefMsg := False;

  m_MyGuild := nil;
  m_nGuildRankNo := 0;
  m_sGuildRankName := '';
  m_sScriptLable := '';
  m_boMission := False;
  m_boHideMode := False;
  m_boStoneMode := False;
  m_boCoolEye := False;
  m_boUserUnLockDurg := False;
  m_boTransparent := False;
  m_boAdminMode := False;
  m_boObMode := False;
  m_dwRunTick := GetTickCount + LongWord(Random(1500));
  m_nRunTime := 250;
  m_dwSearchTime := Random(2000) + 2000;
  m_dwSearchTick := GetTickCount;
  m_dwDecPkPointTick := GetTickCount;
  m_DecLightItemDrugTick := GetTickCount();
  m_dwPoisoningTick := GetTickCount;
  m_dwVerifyTick := GetTickCount();
  m_dwCheckRoyaltyTick := GetTickCount();
  m_dwDecHungerPointTick := GetTickCount();
  m_dwHPMPTick := GetTickCount();
  //m_dwShoutMsgTick := 0;
  m_dwTeleportTick := 0;
  m_dwProbeTick := 0;
  m_dwMapMoveTick := GetTickCount();
  m_dwMasterTick := 0;
  m_nWalkSpeed := 1400;
  m_nNextHitTime := 2000;
  m_nWalkCount := 0;
  m_dwWalkWaitTick := GetTickCount();
  m_boWalkWaitLocked := False;
  m_nHealthTick := 0;
  m_nSpellTick := 0;
  m_KillTargetCret := nil;
  m_TargetCret := nil;
  m_LastHiter := nil;
  m_ExpHitter := nil;
  m_SayMsgList := nil;
  m_boDenyRefStatus := False;
  m_btHorseType := 0;
  m_btDressEffType := 0;
  m_dwPKDieLostExp := 0;
  m_nPKDieLostLevel := 0;
  //m_boAddToMaped := True;
  m_boAutoChangeColor := False;
  m_dwAutoChangeColorTick := GetTickCount();
  m_nAutoChangeIdx := 0;
  m_nChangeColorType := -1; //是否变色

  m_boFixColor := False;
  m_nFixColorIdx := 0;
  m_nFixStatus := -1;
  m_boFastParalysis := False;

  m_nCopyHumanLevel := 0; //复制人状态

  m_dwStationTick := GetTickCount; //站的时间
  m_btLastOutStatus := 0;
  FillChar(m_dwUserTick, SizeOf(m_dwUserTick), 0);
  m_boButchItem := False;
  m_boButchItemEx := False;
  m_boNotDefendoof := False;
  m_boStartNewShield := False;
  m_btNewShieldType := 0;

  m_boSpaceLock := False;
  FillChar(m_SpaceRect, SizeOf(TRect), 0);
  m_SpaceOwner := nil;

  m_boDropUseItem := True;
  m_boStartDuel := False;
  m_DuelTargetCret := nil;

  m_NewStatus := sNone; //失明状态 混乱状态
  m_dwStatusDelayTick := GetTickCount;
  m_nStatusDelayTime := 0;

  m_LastSetStatus := nil;
  m_boIsUnknowActor := False;
  m_boMaskedActor := False;

  m_boNotOnlineAddExp := False;
  m_boAI := False;

  m_PoisonTarget := nil;
  m_PoisonTick := GetTickCount;

  m_boAddToMaped := False; //是否增加地图计数
  m_boDelFormMaped := False; //是否从地图中删除计数
end;

destructor TActorObject.Destroy;
var
  I: Integer;
  SendMessage: pTSendMessage;
  nCheckCode: Integer;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::Destroy Code: %d CharName: %s';
begin
  nCheckCode := 0;
  try
    nCheckCode := 1;
    for I := 0 to m_MsgList.Count - 1 do begin
      nCheckCode := 2;
      SendMessage := m_MsgList.Items[I];
      if (SendMessage.wIdent = RM_SENDDELITEMLIST) and (SendMessage.nParam1 <> 0) then begin
        nCheckCode := 3;
        if TStringList(SendMessage.nParam1) <> nil then begin
          TStringList(SendMessage.nParam1).Free;
          nCheckCode := 4;
        end;
      end;
      if (SendMessage.wIdent = RM_10401) and (SendMessage.nParam1 <> 0) then begin
        nCheckCode := 5;
        Dispose(pTSlaveInfo(SendMessage.nParam1));
      end;
      nCheckCode := 6;
      if (SendMessage.Buff <> nil) then begin
        nCheckCode := 7;
        FreeMem(SendMessage.Buff);
      end;
      Dispose(SendMessage);
      nCheckCode := 8;
    end;
    nCheckCode := 9;

    m_MsgList.Free;

    nCheckCode := 10;
    m_VisibleHumanList.Free;

    nCheckCode := 11;

    for I := 0 to m_VisibleActors.Count - 1 do begin
      Dispose(pTVisibleBaseObject(m_VisibleActors.Items[I]));
    end;

    nCheckCode := 12;
    m_VisibleActors.Free;

    nCheckCode := 13;
    for I := 0 to m_VisibleItems.Count - 1 do begin
      Dispose(pTVisibleMapItem(m_VisibleItems.Items[I]));
    end;

    nCheckCode := 14;
    m_VisibleItems.Free;
    nCheckCode := 15;

    nCheckCode := 17;
    for I := 0 to m_ItemList.Count - 1 do begin
      Dispose(pTUserItem(m_ItemList.Items[I]));
    end;
    nCheckCode := 18;
    m_ItemList.Free;
    nCheckCode := 19;

    nCheckCode := 20;
    for I := 0 to m_MagicList.Count - 1 do begin
      Dispose(pTUserMagic(m_MagicList.Items[I]));
    end;
    m_MagicList.Free;
    nCheckCode := 21;

    m_SlaveList.Free;
    nCheckCode := 22;

    m_LockList.Free;
    m_GroupItem.Free;
    m_GroupItemList.Free;
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg, [nCheckCode, m_sCharName]));
      MainOutMessage(E.Message);
    end;
  end;
  //g_ActorObjectList.Add('Handle:'+IntToStr(Integer(Self))+' TActorObject.Destroy:' + m_sCharName);
  inherited;
end;

function TActorObject.GetUnknowCharName: string;
begin
  Result := m_sCharName;
  if m_boIsUnknowActor then Result := g_Config.sUnKnowName;
end;

function TActorObject.InRect(nX, nY: Integer; Rect: TRect): Boolean;
begin
  Result := (nX > Rect.Left) and (nY > Rect.Top) and (nX < Rect.Right) and (nY < Rect.Bottom);
end;

procedure TActorObject.SetSlaveLockSpace(Rect: TRect; Owner: TActorObject);
var
  I: Integer;
  ActorObject: TActorObject;
begin
  for I := 0 to m_SlaveList.Count - 1 do begin
    ActorObject := TActorObject(m_SlaveList.Items[I]);
    if (m_PEnvir = Owner.m_PEnvir) and ActorObject.InRect(ActorObject.m_nCurrX, ActorObject.m_nCurrY, Rect) then begin
      ActorObject.m_SpaceRect := Rect;
      ActorObject.m_SpaceOwner := Owner;
      ActorObject.m_boSpaceLock := True;
    end;
    ActorObject.SetSlaveLockSpace(Rect, Owner);
  end;
end;

function TActorObject.InLockRect(Envir: TEnvirnoment; nX, nY: Integer): Boolean;
var
  I: Integer;
  Event: TEvent;
  BaseObjectList: TList;
  MasterObject: TActorObject;
begin
  Result := False;
  if m_boAdminMode then Exit; //锁定空间管理员不受限制
  BaseObjectList := TList.Create;
  GetMapEvents(Envir, nX, nY, 6, BaseObjectList);
  for I := 0 to BaseObjectList.Count - 1 do begin
    Event := TEvent(BaseObjectList.Items[I]);
    if (Event.m_nServerEventType = ET_MAGICLOCK) and (Event.m_OwnActorObject <> nil) then begin
      MasterObject := TActorObject(Self).Master;
      if MasterObject = nil then begin
        if InRect(nX, nY, Event.m_OwnActorObject.m_SpaceRect) then begin
          if (Event.m_OwnActorObject <> Self) then begin
            Result := True;
            Break;
          end;
        end;
      end else begin
        if InRect(nX, nY, Event.m_OwnActorObject.m_SpaceRect) then begin
          if (Event.m_OwnActorObject <> MasterObject) then begin
            if MasterObject.m_boSpaceLock and InRect(m_nCurrX, m_nCurrY, Event.m_OwnActorObject.m_SpaceRect) then begin
              Result := True;
              Break;
            end;
          end;
        end;
      end;
    end;
  end;
  BaseObjectList.Free;
end;

function TActorObject.InLockRect(Targe: TActorObject): Boolean; //检测目标是否在锁定空间中，是否可以攻击
begin
  Result := True;
  if m_boSpaceLock and Targe.m_boSpaceLock and (Targe.m_SpaceOwner <> m_SpaceOwner) then begin
    Result := False;
    Exit;
  end;

  if m_boSpaceLock and Targe.m_boSpaceLock and (Targe.m_SpaceOwner = m_SpaceOwner) then begin
    if (not InRect(m_nCurrX, m_nCurrY, m_SpaceRect)) and InRect(Targe.m_nCurrX, Targe.m_nCurrY, Targe.m_SpaceRect) then begin
      Result := False;
      Exit;
    end else
      if InRect(m_nCurrX, m_nCurrY, m_SpaceRect) and (not InRect(Targe.m_nCurrX, Targe.m_nCurrY, Targe.m_SpaceRect)) then begin
      if not g_Config.boAllowSpaceOutAttack then begin
        Result := False;
        Exit;
      end;
    end;
  end;

  if m_boSpaceLock and (not Targe.m_boSpaceLock) then begin
    if InRect(m_nCurrX, m_nCurrY, m_SpaceRect) then begin
      if (not InRect(Targe.m_nCurrX, Targe.m_nCurrY, m_SpaceRect)) then begin
        if not g_Config.boAllowSpaceOutAttack then begin
          Result := False;
          Exit;
        end;
      end;
    end else begin
      if InRect(Targe.m_nCurrX, Targe.m_nCurrY, m_SpaceRect) then begin
        Result := False;
        Exit;
      end;
    end;
  end;

  if (not m_boSpaceLock) and Targe.m_boSpaceLock then begin
    if InRect(Targe.m_nCurrX, Targe.m_nCurrY, Targe.m_SpaceRect) and
      (not InRect(m_nCurrX, m_nCurrY, Targe.m_SpaceRect)) then begin
      Result := False;
      Exit;
    end;
  end;
end;

function TActorObject.GetDropUseItem: Boolean;
var
  I: Integer;
  nDura, tDura: Integer;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
begin
  Result := True;
  m_boDropUseItem := True;
  if not (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) then Exit;
  for I := m_ItemList.Count - 1 downto 0 do begin
    UserItem := m_ItemList.Items[I];
    if (UserItem.Dura > 0) then begin

      if (UserItem.AddValue[0] = 1) and (GetDayCount(UserItem.MaxDate, Now) <= 0) then begin
        m_ItemList.Delete(I);
        Dispose(UserItem);
        Continue;
      end;

      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if StdItem <> nil then begin
        if (StdItem.Shape = 173) or (StdItem.AniCount = 173) then begin
          nDura := UserItem.Dura;
          tDura := Round(nDura / 1000);
          Dec(nDura, 1000);
          if nDura <= 0 then begin
            if m_btRaceServer = RC_PLAYOBJECT then begin
              TPlayObject(Self).SendDelItems(UserItem);
            end else
              if m_btRaceServer = RC_HEROOBJECT then begin
              THeroObject(Self).SendDelItems(UserItem);
            end;
            m_ItemList.Delete(I);
            Dispose(UserItem);
          end else begin
            UserItem.Dura := nDura;
            if tDura <> Round(nDura / 1000) then begin
              if m_btRaceServer = RC_PLAYOBJECT then begin
                TPlayObject(Self).SendUpdateItem(UserItem);
              end else
                if m_btRaceServer = RC_HEROOBJECT then begin
                THeroObject(Self).SendUpdateItem(UserItem);
              end;
            end;
            m_boDropUseItem := False;
            Result := False;
            Break;
          end;
        end;
      end;
    end;
  end;
end;

procedure TActorObject.ChangePKStatus(boWarFlag: Boolean);
begin
  if m_boInFreePKArea <> boWarFlag then begin
    m_boInFreePKArea := boWarFlag;
    m_boNameColorChanged := True;
  end;
end;

function TActorObject.GetDropPosition(nOrgX, nOrgY, nRange: Integer; var nDX: Integer; var nDY: Integer): Boolean; //004C5238
var
  I, II, III: Integer;
  nItemCount, n24, n28, n2C: Integer;
begin
  n24 := 999;
  Result := False;
  n28 := 0; //09/10
  n2C := 0; //09/10
  for I := 1 to nRange do begin
    for II := -I to I do begin
      for III := -I to I do begin
        nDX := nOrgX + III;
        nDY := nOrgY + II;
        if m_PEnvir.GetItemEx(nDX, nDY, nItemCount) = nil then begin
          if m_PEnvir.bo2C then begin
            Result := True;
            Break;
          end;
        end else begin
          if m_PEnvir.bo2C and (n24 > nItemCount) then begin
            n24 := nItemCount;
            n28 := nDX;
            n2C := nDY;
          end;
        end;
      end;
      if Result then Break;
    end;
    if Result then Break;
  end;
  if not Result then begin
    if n24 < 8 then begin
      nDX := n28;
      nDY := n2C;
    end else begin
      nDX := nOrgX;
      nDY := nOrgY;
    end;
  end;
end;

function TActorObject.DropItemDown(UserItem: pTUserItem; nScatterRange: Integer; boDieDrop: Boolean; ItemOfCreat, DropCreat: TActorObject): Boolean;
var
  dx, dy, idura: Integer;
  ItemObject: TItemObject;
  ItemObject01: TBaseObject;
  StdItem: pTStdItem;
  logcap: string;
  sUserItemName: string;
begin
  Result := False;
  StdItem := UserEngine.GetStdItem(UserItem.wIndex);
  if StdItem <> nil then begin
    if StdItem.StdMode = 40 then begin
      idura := UserItem.Dura;
      idura := idura - 2000;
      if idura < 0 then idura := 0;
      UserItem.Dura := idura;
    end;

    ItemObject := TItemObject.Create;
    ItemObject.m_UserItem := UserItem^;
    ItemObject.m_sName := StdItem.Name;
    ItemObject.m_PEnvir := m_PEnvir;
    //取自定义物品名称
    sUserItemName := '';
    if UserItem.btValue[13] = 1 then begin
      sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
    end;

    if sUserItemName <> '' then
      ItemObject.m_sName := sUserItemName;

    if m_btRaceServer = RC_PLAYOBJECT then begin
      if TPlayObject(Self).CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        ItemObject.m_sName := '(绑)' + ItemObject.m_sName;
      end;
    end else
      if (m_btRaceServer = RC_HEROOBJECT) and (m_Master <> nil) then begin
      if TPlayObject(m_Master).CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        ItemObject.m_sName := '(绑)' + ItemObject.m_sName;
      end;
    end;

    ItemObject.m_wLooks := StdItem.Looks;
    if StdItem.StdMode = 45 then begin
      ItemObject.m_wLooks := GetRandomLook(ItemObject.m_wLooks, StdItem.Shape);
    end;
    ItemObject.m_btAniCount := StdItem.AniCount;
    ItemObject.m_btReserved := 0;
    ItemObject.m_nCount := 1;
    ItemObject.m_OfActorObject := ItemOfCreat;
    ItemObject.m_dwCanPickUpTick := GetTickCount();
    ItemObject.m_DropActorObject := DropCreat;
    GetDropPosition(m_nCurrX, m_nCurrY, nScatterRange, m_nScatterItemX, m_nScatterItemY);
    ItemObject01 := m_PEnvir.AddToMap(m_nScatterItemX, m_nScatterItemY, ItemObject);
    if ItemObject01 = ItemObject then begin
      g_ItemManager.AddItem(ItemObject);

      SendRefMsg(RM_ITEMSHOW, ItemObject.m_wLooks, Integer(ItemObject), m_nScatterItemX, m_nScatterItemY, ItemObject.m_sName);
      if boDieDrop then logcap := '15'
      else logcap := '7';
      if not IsCheapStuff(StdItem.StdMode) then
        if StdItem.NeedIdentify = 1 then
          AddGameDataLog(logcap + #9 +
            m_sMapName + #9 +
            IntToStr(m_nCurrX) + #9 +
            IntToStr(m_nCurrY) + #9 +
            m_sCharName + #9 +
            //UserEngine.GetStdItemName(ui.wIndex) + #9 +
            StdItem.Name + #9 +
            IntToStr(UserItem.MakeIndex) + #9 +
            BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
            '0');
      Result := True;
    end else begin
      ItemObject.Free;
    end;
  end;
end;

function TActorObject.DropItemDown(UserItem: pTUserItem; nScatterRange: Integer; DropCreat: TActorObject; Envir: TEnvirnoment; nX, nY: Integer): Boolean;
var
  dx, dy, idura: Integer;
  ItemObject: TItemObject;
  ItemObject01: TBaseObject;
  StdItem: pTStdItem;
  logcap: string;
  sUserItemName: string;
  PlayObject: TPlayObject;
begin
  Result := False;
  StdItem := UserEngine.GetStdItem(UserItem.wIndex);
  if StdItem <> nil then begin
    if StdItem.StdMode = 40 then begin
      idura := UserItem.Dura;
      idura := idura - 2000;
      if idura < 0 then idura := 0;
      UserItem.Dura := idura;
    end;

    ItemObject := TItemObject.Create;
    ItemObject.m_UserItem := UserItem^;
    ItemObject.m_sName := StdItem.Name;
    ItemObject.m_PEnvir := Envir;
    //取自定义物品名称
    sUserItemName := '';
    if UserItem.btValue[13] = 1 then begin
      sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
    end;

    if sUserItemName <> '' then
      ItemObject.m_sName := sUserItemName;

    if m_btRaceServer = RC_PLAYOBJECT then begin
      if TPlayObject(Self).CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        ItemObject.m_sName := '(绑)' + ItemObject.m_sName;
      end;
    end else
      if (m_btRaceServer = RC_HEROOBJECT) and (m_Master <> nil) then begin
      if TPlayObject(m_Master).CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        ItemObject.m_sName := '(绑)' + ItemObject.m_sName;
      end;
    end;

    ItemObject.m_wLooks := StdItem.Looks;
    if StdItem.StdMode = 45 then begin
      ItemObject.m_wLooks := GetRandomLook(ItemObject.m_wLooks, StdItem.Shape);
    end;
    ItemObject.m_btAniCount := StdItem.AniCount;
    ItemObject.m_btReserved := 0;
    ItemObject.m_nCount := 1;
    ItemObject.m_OfActorObject := nil;
    ItemObject.m_dwCanPickUpTick := GetTickCount();
    ItemObject.m_DropActorObject := DropCreat;
    GetDropPosition(nX, nY, nScatterRange, dx, dy);
    ItemObject01 := Envir.AddToMap(dx, dy, ItemObject);
    if ItemObject01 = ItemObject then begin
      g_ItemManager.AddItem(ItemObject);
      PlayObject := TPlayObject(Envir.GetPalyObject(dx, dy, 12));
      if PlayObject <> nil then
        PlayObject.SendRefMsg(RM_ITEMSHOW, ItemObject.m_wLooks, Integer(ItemObject), dx, dy, ItemObject.m_sName);
      logcap := '7';
      if not IsCheapStuff(StdItem.StdMode) then
        if StdItem.NeedIdentify = 1 then
          AddGameDataLog(logcap + #9 +
            m_sMapName + #9 +
            IntToStr(m_nCurrX) + #9 +
            IntToStr(m_nCurrY) + #9 +
            m_sCharName + #9 +
            //UserEngine.GetStdItemName(ui.wIndex) + #9 +
            StdItem.Name + #9 +
            IntToStr(UserItem.MakeIndex) + #9 +
            BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
            '0');
      Result := True;
    end else begin
      ItemObject.Free;
    end;
  end;
end;

procedure TActorObject.GoldChanged();
begin
  if m_btRaceServer = RC_PLAYOBJECT then begin
    SendUpdateMsg(Self, RM_GOLDCHANGED, 0, 0, 0, 0, '');
  end;
end;

procedure TActorObject.GameGoldChanged();
begin
  if m_btRaceServer = RC_PLAYOBJECT then begin
    SendUpdateMsg(Self, RM_GAMEGOLDCHANGED, 0, 0, 0, 0, '');
  end;
end;

function TPlayObject.ClientPickUpItem: Boolean;
  function IsSelf(ActorObject: TActorObject): Boolean;
  var
    MasterObject: TActorObject;
  begin
    if (ActorObject = nil) or (Self = ActorObject) then Result := True
    else Result := False;
    if ActorObject <> nil then begin
      MasterObject := Master;
      if (MasterObject <> nil) and (Self = MasterObject) then Result := True;
    end;

  end;

  function IsOfGroup(ActorObject: TActorObject): Boolean;
  var
    I: Integer;
    GroupMember: TActorObject;
  begin
    Result := False;
    if m_GroupOwner = nil then Exit;
    for I := 0 to m_GroupOwner.m_GroupMembers.Count - 1 do begin
      GroupMember := TActorObject(m_GroupOwner.m_GroupMembers.Objects[I]);
      if GroupMember = ActorObject then begin
        Result := True;
        Break;
      end;
    end;
  end;
var
  UserItem: pTUserItem;
  ItemObject: TItemObject;
  StdItem: pTStdItem;
  PlayObject: TPlayObject;
  nItemBind: Integer;
begin
  Result := False;
  if m_boDealing or m_boDueling then Exit;
  ItemObject := m_PEnvir.GetItem(m_nCurrX, m_nCurrY);
  if ItemObject = nil then Exit;

  if g_Config.boStartMapEvent then begin //捡物品事件
    StartMapEventOfPickUpItem(ItemObject.m_sName);
  end;

  m_dwPickUpTick := GetTickCount;
  if (GetTickCount - ItemObject.m_dwCanPickUpTick) > g_Config.dwFloorItemCanPickUpTime {2 * 60 * 1000} then begin
    ItemObject.m_OfActorObject := nil;
  end;
  if not IsSelf(TActorObject(ItemObject.m_OfActorObject)) and not IsOfGroup(TActorObject(ItemObject.m_OfActorObject)) then begin
    SysMsg(g_sCanotPickUpItem {'在一定时间以内无法捡起此物品！！！'}, c_Red, t_Hint);
    Exit;
  end;

  if g_Config.boBindItemNoPickUp then begin
    nItemBind := CheckItemBindUse(@ItemObject.m_UserItem, False);
    if (nItemBind > 0) and (nItemBind <> 2) then begin
      SysMsg(g_sCanotPickUpBindItem {'别人的绑定物品，你无法捡起此物品！！！'}, c_Red, t_Hint);
      Exit;
    end;
  end;

  if CompareText(ItemObject.m_sName, sSTRING_GOLDNAME) = 0 then begin
    if m_PEnvir.DeleteFromMap(m_nCurrX, m_nCurrY, ItemObject) = 1 then begin
      if IncGold(ItemObject.m_nCount) then begin
        ItemObject.MakeGhost;
        SendRefMsg(RM_ITEMHIDE, 0, Integer(ItemObject), m_nCurrX, m_nCurrY, '');
        if g_boGameLogGold then
          AddGameDataLog('4' + #9 +
            m_sMapName + #9 +
            IntToStr(m_nCurrX) + #9 +
            IntToStr(m_nCurrY) + #9 +
            m_sCharName + #9 +
            sSTRING_GOLDNAME + #9 +
            IntToStr(ItemObject.m_nCount) + #9 +
            '1' + #9 +
            '0');
        GoldChanged;
      end else begin
        if m_PEnvir.AddToMap(m_nCurrX, m_nCurrY, ItemObject) <> ItemObject then ItemObject.MakeGhost;
      end;
    end;
    Exit;
  end;

  if IsEnoughBag then begin
    if m_PEnvir.DeleteFromMap(m_nCurrX, m_nCurrY, ItemObject) = 1 then begin
      New(UserItem);
      UserItem^ := ItemObject.m_UserItem;
      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if (StdItem <> nil) and IsAddWeightAvailable(UserEngine.GetStdItemWeight(UserItem.wIndex)) then begin
        ItemObject.MakeGhost;
        SendMsg(Self, RM_ITEMHIDE, 0, Integer(ItemObject), m_nCurrX, m_nCurrY, '');
        //AddItemToBag(UserItem);
        m_ItemList.Add(UserItem);
        if not IsCheapStuff(StdItem.StdMode) then
          if StdItem.NeedIdentify = 1 then
            AddGameDataLog('4' + #9 +
              m_sMapName + #9 +
              IntToStr(m_nCurrX) + #9 +
              IntToStr(m_nCurrY) + #9 +
              m_sCharName + #9 +
              //UserEngine.GetStdItemName(pu.wIndex) + #9 +
              StdItem.Name + #9 +
              IntToStr(UserItem.MakeIndex) + #9 +
              '1' + #9 +
              '0');

        SendAddItem(UserItem);

      //清除复制装备

        ClearCopyItems();
        WeightChanged();  //Added to update weight when you pick up an item.
        Result := True;

      end else begin
        Dispose(UserItem);
        if m_PEnvir.AddToMap(m_nCurrX, m_nCurrY, ItemObject) <> ItemObject then ItemObject.MakeGhost;
      end;
    end;
  end;
end;

procedure TPlayObject.RunNotice;
var
  Msg: TProcessMessage;
resourcestring
  sExceptionMsg = '[Exception] TPlayObject::RunNotice';
begin
  if m_boEmergencyClose or m_boKickFlag or m_boSoftClose then begin
    if m_boKickFlag then SendDefMessage(SM_OUTOFCONNECTION, 0, 0, 0, 0, '');
    MakeGhost();
  end else begin
    try
      if not m_boSendNotice then begin
        SendNotice();
        m_boSendNotice := True;
        m_dwWaitLoginNoticeOKTick := GetTickCount();
      end else begin
        if GetTickCount - m_dwWaitLoginNoticeOKTick > 30 * 1000 then begin
          m_boEmergencyClose := True;
        end;
        while GetMessage(@Msg) do begin
          if Msg.wIdent = CM_LOGINNOTICEOK then begin
            m_boLoginNoticeOK := True;
            m_dwClientTick := Msg.nParam1;
            //SysMsg(IntToStr(m_dwClientTick), c_Red, t_Notice);
          end;
        end;
      end;
    except
      MainOutMessage(sExceptionMsg);
    end;
  end;
end;

procedure TPlayObject.IncBeadExp(dwExp: LongWord); //聚灵珠
var
  I: Integer;
  PExp: PLongWord;
  dwItemExp: LongWord;
  dwAddExp: LongWord;
  ItemList: TList;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
begin
  if dwExp > 0 then begin
    for I := 0 to m_ItemList.Count - 1 do begin
      UserItem := m_ItemList.Items[I];
      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if (StdItem = nil) or (StdItem.StdMode <> 49) or (UserItem.Dura >= UserItem.DuraMax) then Continue;
      PExp := @UserItem.btValue;
      dwItemExp := PExp^;
      dwAddExp := Round(StdItem.Shape / 100 * dwExp);

      if dwItemExp + dwAddExp >= 10000 then begin
        UserItem.Dura := UserItem.Dura + 1;
        PExp^ := MinLong(dwItemExp + dwAddExp - 10000, High(LongWord));
        SendUpdateItem(UserItem);
      end else begin
        PExp^ := MinLong(dwItemExp + dwAddExp, High(LongWord));
      end;

      if UserItem.Dura >= UserItem.DuraMax then begin
        SysMsg(StdItem.Name + 'Experience has been together at least! ', 251, 249, t_Hint);
      end;
      break;
    end;
  end;
end;

procedure TPlayObject.WinExp(dwExp: LongWord);
begin
  if m_Abil.Level > g_Config.nLimitExpLevel then begin
    dwExp := g_Config.nLimitExpValue;
    GetExp(dwExp);
  end else
    if dwExp > 0 then begin
    dwExp := g_Config.dwKillMonExpMultiple * dwExp; //系统指定杀怪经验倍数
    dwExp := LongWord(m_nKillMonExpMultiple) * dwExp; //人物指定的杀怪经验倍数
    dwExp := Round((m_nKillMonExpRate / 100) * dwExp); //人物指定的杀怪经验倍数
    if m_PEnvir.m_boEXPRATE then
      dwExp := Round((m_PEnvir.m_nEXPRATE / 100) * dwExp); //地图上指定杀怪经验倍数
    if m_boExpItem then begin //物品经验倍数
      dwExp := Round(m_rExpItem * dwExp);
    end;
    if m_boExpGroupItem then begin //套装经验倍数
      dwExp := Round(m_rExpGroupItem * dwExp);
    end;
    GetExp(dwExp);
  end;
end;

procedure TPlayObject.IncExp(dwExp: LongWord);
var
  //lwExp: LongWord;
  dwAddExp: LongWord;
  //lwDecExp: LongWord;
  lwExp: LongWord;
label RefExp;
begin
  lwExp := dwExp;
  //MainOutMessage(Format('TPlayObject.IncExp1:%d', [dwExp]));
  //nExp := m_Abil.Exp + lwExp;
  //MainOutMessage('TPlayObject.IncExp nExp:' + IntToStr(nExp) + ' m_Abil.Exp:' + IntToStr(m_Abil.Exp) + ' m_Abil.MaxExp:' + IntToStr(m_Abil.MaxExp));
  RefExp:
  dwAddExp := m_Abil.MaxExp - m_Abil.Exp;
  if (lwExp >= dwAddExp) and (m_Abil.MaxExp > m_Abil.Exp) then begin
    if m_Abil.Level < g_Config.nMaxLevel then begin
      Inc(m_Abil.Level);
      //dwAddExp := m_Abil.MaxExp - m_Abil.Exp;
      lwExp := lwExp - dwAddExp;
      m_Abil.Exp := 0;
      //MainOutMessage(Format('TPlayObject.IncExp dwAddExp:%d', [dwAddExp]));

      //SendMsg(Self, RM_WINEXP, 0, dwAddExp, 0, 0, '');

      HasLevelUp(m_Abil.Level - 1);

      if not m_boAI then
        AddGameDataLog('12' + #9 +
          m_sMapName + #9 +
          IntToStr(m_Abil.Level) + #9 +
          IntToStr(m_Abil.Exp) + #9 +
          m_sCharName + #9 +
          '0' + #9 +
          '0' + #9 +
          '1' + #9 +
          '0');

      IncHealthSpell(2000, 2000);

      //if lwExp > 0 then IncExp(lwExp);
      if lwExp > 0 then begin
        goto RefExp;
        Exit;
      end;
    //end else begin
      //MainOutMessage(Format('TPlayObject.IncExp2:%d', [dwExp]));
      //m_Abil.Exp := 0;
    end;
  end else begin
    if m_Abil.MaxExp > m_Abil.Exp then begin
      Inc(m_Abil.Exp, lwExp);
      //SendMsg(Self, RM_WINEXP, 0, lwExp, 0, 0, '');
    end else begin
      Inc(m_Abil.Level);
      Dec(m_Abil.Exp, m_Abil.MaxExp);

      HasLevelUp(m_Abil.Level - 1);

      if not m_boAI then
        AddGameDataLog('12' + #9 +
          m_sMapName + #9 +
          IntToStr(m_Abil.Level) + #9 +
          IntToStr(m_Abil.Exp) + #9 +
          m_sCharName + #9 +
          '0' + #9 +
          '0' + #9 +
          '1' + #9 +
          '0');

      IncHealthSpell(2000, 2000);


      goto RefExp;
      Exit;
      //IncExp(lwExp);
    end;
  end;
  SendMsg(Self, RM_WINEXP, 0, dwExp, 0, 0, '');
   {
  nExp := dwExp;
  while nExp > 0 do begin
    if m_Abil.Level >= g_Config.nMaxLevel then begin
      nExp := 0;
      m_Abil.Exp := 0;
      break;
    end;
    if m_Abil.Exp + dwExp > High(LongWord) then begin
      dwAddExp := High(LongWord) - m_Abil.Exp;
      Inc(m_Abil.Exp, dwAddExp);
      Dec(nExp, dwAddExp);
      //IncBeadExp(dwAddExp);
    end else begin
      dwAddExp := dwExp;
      Inc(m_Abil.Exp, dwAddExp);
      Dec(nExp, dwAddExp);
      //IncBeadExp(dwAddExp);
    end;
  //inherited IncExp(dwExp);

    SendMsg(Self, RM_WINEXP, 0, dwAddExp, 0, 0, '');

    while m_Abil.Exp >= m_Abil.MaxExp do begin //2008-08-23增加循环调用
      Dec(m_Abil.Exp, m_Abil.MaxExp);

      if m_Abil.Level < g_Config.nMaxLevel then begin
        Inc(m_Abil.Level);
      end else begin
        m_Abil.Exp := 0;
        break;
      end;

      HasLevelUp(m_Abil.Level - 1);
      if not m_boAI then
        AddGameDataLog('12' + #9 +
          m_sMapName + #9 +
          IntToStr(m_Abil.Level) + #9 +
          IntToStr(m_Abil.Exp) + #9 +
          m_sCharName + #9 +
          '0' + #9 +
          '0' + #9 +
          '1' + #9 +
          '0');
    end;
  end;
  }
end;

procedure TPlayObject.GetExp(dwExp: LongWord);
var
  dwAddExp: LongWord;
  lwExp: LongWord;
label RefExp;
begin
  lwExp := dwExp;

  RefExp:
  dwAddExp := m_Abil.MaxExp - m_Abil.Exp;
  //MainOutMessage('TPlayObject.GetExp dwExp:' + IntToStr(dwExp) + ' dwAddExp:' + IntToStr(dwAddExp) + ' m_Abil.Exp:' + IntToStr(m_Abil.Exp) + ' m_Abil.MaxExp:' + IntToStr(m_Abil.MaxExp));
  if (lwExp >= dwAddExp) and (m_Abil.MaxExp > m_Abil.Exp) then begin
    if m_Abil.Level < g_Config.nMaxLevel then begin
      Inc(m_Abil.Level);
      lwExp := lwExp - dwAddExp;
      m_Abil.Exp := 0;

      IncBeadExp(dwAddExp);

      AddBodyLuck(dwAddExp * 0.002);

      //SendMsg(Self, RM_WINEXP, 0, dwAddExp, 0, 0, '');

      HasLevelUp(m_Abil.Level - 1);

      if not m_boAI then
        AddGameDataLog('12' + #9 +
          m_sMapName + #9 +
          IntToStr(m_Abil.Level) + #9 +
          IntToStr(m_Abil.Exp) + #9 +
          m_sCharName + #9 +
          '0' + #9 +
          '0' + #9 +
          '1' + #9 +
          '0');

      IncHealthSpell(2000, 2000);

      //if dwExp > 0 then GetExp(dwExp);
      if lwExp > 0 then begin
        goto RefExp;
        Exit;
      end;
    //end else begin
      //m_Abil.Exp := 0;
    end;
  end else begin
    if m_Abil.MaxExp > m_Abil.Exp then begin
      Inc(m_Abil.Exp, lwExp);
      IncBeadExp(lwExp);

      //SendMsg(Self, RM_WINEXP, 0, dwExp, 0, 0, '');
      AddBodyLuck(lwExp * 0.002);
    end else begin
      Inc(m_Abil.Level);
      Dec(m_Abil.Exp, m_Abil.MaxExp);

      HasLevelUp(m_Abil.Level - 1);

      if not m_boAI then
        AddGameDataLog('12' + #9 +
          m_sMapName + #9 +
          IntToStr(m_Abil.Level) + #9 +
          IntToStr(m_Abil.Exp) + #9 +
          m_sCharName + #9 +
          '0' + #9 +
          '0' + #9 +
          '1' + #9 +
          '0');

      IncHealthSpell(2000, 2000);

      goto RefExp;
      Exit;
      //GetExp(dwExp);
    end;
  end;
  SendMsg(Self, RM_WINEXP, 0, dwExp, 0, 0, '');

  {nExp := dwExp;
  while nExp > 0 do begin
    if m_Abil.Level >= g_Config.nMaxLevel then begin
      nExp := 0;
      m_Abil.Exp := 0;
      break;
    end;

    if m_Abil.Exp + dwExp > High(LongWord) then begin
      dwAddExp := High(LongWord) - m_Abil.Exp;
      Inc(m_Abil.Exp, dwAddExp);
      Dec(nExp, dwAddExp);
      IncBeadExp(dwAddExp);
    end else begin
      dwAddExp := dwExp;
      Inc(m_Abil.Exp, dwAddExp);
      Dec(nExp, dwAddExp);
      IncBeadExp(dwAddExp);
    end;

    AddBodyLuck(dwAddExp * 0.002);

    SendMsg(Self, RM_WINEXP, 0, dwAddExp, 0, 0, '');

    while m_Abil.Exp >= m_Abil.MaxExp do begin //2008-08-23增加循环调用
      Dec(m_Abil.Exp, m_Abil.MaxExp);

      if m_Abil.Level < g_Config.nMaxLevel then begin
        Inc(m_Abil.Level);
      end else begin
        m_Abil.Exp := 0;
        break;
      end;

      HasLevelUp(m_Abil.Level - 1);
      AddBodyLuck(100);
      if not m_boAI then
        AddGameDataLog('12' + #9 +
          m_sMapName + #9 +
          IntToStr(m_Abil.Level) + #9 +
          IntToStr(m_Abil.Exp) + #9 +
          m_sCharName + #9 +
          '0' + #9 +
          '0' + #9 +
          '1' + #9 +
          '0');
    end;
  end;}
end;

procedure TActorObject.RecalcLevelAbilitys();
var
  nLevel, n: Integer;
  boUpdate: Boolean;
begin
  nLevel := m_Abil.Level;
  boUpdate := False;
  case m_btJob of
    2: begin
        //m_Abil.MaxHP:=_MIN(High(Word),14 + ROUND((nLevel / 6 + 2.5) * nLevel));
        m_Abil.MaxHP := MinLong(g_Config.nMaxLevel, 14 + Round(((nLevel / g_Config.nLevelValueOfTaosHP + g_Config.nLevelValueOfTaosHPRate) * nLevel)));
        //m_Abil.MaxMP:=_MIN(High(Word),13 + ROUND((nLevel / 8)* 2.2 * nLevel));
        m_Abil.MaxMP := MinLong(g_Config.nMaxLevel, 13 + Round(((nLevel / g_Config.nLevelValueOfTaosMP) * 2.2 * nLevel)));

        m_Abil.MaxWeight := 50 + Round((nLevel / 4) * nLevel);
        m_Abil.MaxWearWeight := 15 + Round((nLevel / 50) * nLevel);
        m_Abil.MaxHandWeight := 12 + Round((nLevel / 42) * nLevel);

        n := nLevel div 7;
        m_Abil.DC := MakeLong(_MAX(n - 1, 0), _MAX(1, n));
        m_Abil.MC := 0;
        m_Abil.SC := MakeLong(_MAX(n - 1, 0), _MAX(1, n));
        m_Abil.AC := 0;

        n := Round(nLevel / 6);
        m_Abil.MAC := MakeLong(n div 2, n + 1);
      end;
    1: begin
        //m_Abil.MaxHP:=_MIN(High(Word),14 + ROUND((nLevel / 15 + 1.8) * nLevel));
        m_Abil.MaxHP := MinLong(g_Config.nMaxLevel, 14 + Round(((nLevel / g_Config.nLevelValueOfWizardHP + g_Config.nLevelValueOfWizardHPRate) * nLevel)));
        m_Abil.MaxMP := MinLong(g_Config.nMaxLevel, 13 + Round((nLevel / 5 + 2) * 2.2 * nLevel));

        m_Abil.MaxWeight := 50 + Round((nLevel / 5) * nLevel);
        m_Abil.MaxWearWeight := 15 + Round((nLevel / 100) * nLevel);
        m_Abil.MaxHandWeight := 12 + Round((nLevel / 90) * nLevel);

        n := nLevel div 7;
        m_Abil.DC := MakeLong(_MAX(n - 1, 0), _MAX(1, n));
        m_Abil.MC := MakeLong(_MAX(n - 1, 0), _MAX(1, n));
        m_Abil.SC := 0;
        m_Abil.AC := 0;
        m_Abil.MAC := 0;
      end;
    0: begin
        //m_Abil.MaxHP:=_MIN(High(Word),14 + ROUND((nLevel / 4.0 + 4.5 + nLevel / 20) * nLevel));
        m_Abil.MaxHP := MinLong(g_Config.nMaxLevel, 14 + Round(((nLevel / g_Config.nLevelValueOfWarrHP + g_Config.nLevelValueOfWarrHPRate + nLevel / 20) * nLevel)));

        m_Abil.MaxMP := MinLong(g_Config.nMaxLevel, 11 + Round(nLevel * 3.5));

        m_Abil.MaxWeight := 50 + Round((nLevel / 3) * nLevel);
        m_Abil.MaxWearWeight := 15 + Round((nLevel / 20) * nLevel);
        m_Abil.MaxHandWeight := 12 + Round((nLevel / 13) * nLevel);

        m_Abil.DC := MakeLong(_MAX((nLevel div 5) - 1, 1), _MAX(1, (nLevel div 5)));
        m_Abil.SC := 0;
        m_Abil.MC := 0;
        m_Abil.AC := MakeLong(0, (nLevel div 7));
        m_Abil.MAC := 0;
      end;
  end;
  if m_Abil.HP > m_Abil.MaxHP then begin
    m_Abil.HP := m_Abil.MaxHP;
    boUpdate := True;
  end;
  if m_Abil.MP > m_Abil.MaxMP then begin
    m_Abil.MP := m_Abil.MaxMP;
    boUpdate := True;
  end;
  if (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and boUpdate then begin
    SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
  end;
end;

procedure TActorObject.HasLevelUp(nLevel: Integer);
begin
  m_Abil.MaxExp := GetLevelExp(m_Abil.Level);

  RecalcLevelAbilitys();
  RecalcAbilitys();


  if m_btRaceServer = RC_PLAYOBJECT then begin
    SendMsg(Self, RM_LEVELUP, 0, m_Abil.Exp, 0, 0, '');
    TPlayObject(Self).LevelUpFunc();
    /////////START OF NEW BONUS///////
if (m_Abil.Level > 24 ) then begin
  Inc(m_nHealthRecover, m_AddAbil.wHealthRecover +1);
if (m_Abil.Level > 49 ) then begin
  Inc(m_nSpellRecover, m_AddAbil.wSpellRecover +1);
if (m_Abil.Level > 74 ) then begin
  Inc(m_nPoisonRecover, m_AddAbil.wPoisonRecover +1);
if (m_Abil.Level > 99 ) then begin
  Inc(m_btAntiPoison, m_AddAbil.wAntiPoison +1);
      /////////END OF NEW BONUS Quest///////
  end else begin
    SendMsg(Self, RM_LEVELUP, 0, m_Abil.Exp, 0, 0, '');
  end;
  end;
  end;
end;
end;
end;

function TPlayObject.IncGold(tGold: Integer): Boolean;
begin
  Result := False;
  //  if m_nGold + tGold <= BAGGOLD then begin
  if m_nGold + tGold <= g_Config.nHumanMaxGold then begin
    Inc(m_nGold, tGold);
    Result := True;
  end;
end;

procedure TPlayObject.IncGameGold(nGameGold: Integer);
begin
  Inc(m_nGameGold, nGameGold);
end;

procedure TPlayObject.IncGamePoint(nGamePoint: Integer);
begin
  Inc(m_nGamePoint, nGamePoint);
end;

procedure AddUserLog(sMsg: string);
begin
  MainOutMessage(sMsg);
end;

function TActorObject.WalkTo(btDir: Byte; boFlag: Boolean): Boolean;
var
  nOX, nOY, nNX, nNY, n20, n24: Integer;
  //Envir:TEnvirnoment;
  bo29: Boolean;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::WalkTo';
begin
  Result := False;
  if m_boHolySeize then Exit;
  try
    nOX := m_nCurrX;
    nOY := m_nCurrY;
    //    Envir:=m_PEnvir;
    m_btDirection := btDir;
    nNX := 0;
    nNY := 0;
    case btDir of
      DR_UP: begin
          nNX := m_nCurrX; nNY := m_nCurrY - 1; end;
      DR_UPRIGHT: begin
          nNX := m_nCurrX + 1; nNY := m_nCurrY - 1; end;
      DR_RIGHT: begin
          nNX := m_nCurrX + 1; nNY := m_nCurrY; end;
      DR_DOWNRIGHT: begin
          nNX := m_nCurrX + 1; nNY := m_nCurrY + 1; end;
      DR_DOWN: begin
          nNX := m_nCurrX; nNY := m_nCurrY + 1; end;
      DR_DOWNLEFT: begin
          nNX := m_nCurrX - 1; nNY := m_nCurrY + 1; end;
      DR_LEFT: begin
          nNX := m_nCurrX - 1; nNY := m_nCurrY; end;
      DR_UPLEFT: begin
          nNX := m_nCurrX - 1; nNY := m_nCurrY - 1; end;
    end;

    if (nNX >= 0) and ((m_PEnvir.m_nWidth - 1) >= nNX) and
      (nNY >= 0) and ((m_PEnvir.m_nHeight - 1) >= nNY) then begin
      bo29 := True;
      if bo2BA and not m_PEnvir.CanSafeWalk(nNX, nNY) then bo29 := False;
      if m_Master <> nil then begin
        m_Master.m_PEnvir.GetNextPosition(m_Master.m_nCurrX, m_Master.m_nCurrY, m_Master.m_btDirection, 1, n20, n24);
        if (nNX = n20) and (nNY = n24) then bo29 := False;
      end;
      if bo29 then begin
        if m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nNX, nNY, boFlag) > 0 then begin
          m_nCurrX := nNX;
          m_nCurrY := nNY;
        end;
      end;
    end;
    if (m_nCurrX <> nOX) or (m_nCurrY <> nOY) then begin
      if not WalkToSpaceLock(nOX, nOY, m_nCurrX, m_nCurrY) then begin
        if Walk(RM_WALK) then begin
          if m_boTransparent and m_boHideMode then m_wStatusTimeArr[STATE_TRANSPARENT {0x70}] := 1;
          Result := True;
        end else begin
          if m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nOX, nOY, True) > 0 then begin //修改
            m_nCurrX := nOX;
            m_nCurrY := nOY;
          end;
        end;
      end else begin
        if m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nOX, nOY, True) > 0 then begin //修改
          m_nCurrX := nOX;
          m_nCurrY := nOY;
        end;
      end;
    end;
  except
    MainOutMessage(sExceptionMsg);
  end;
end;

function TPlayObject.IsEnoughBag: Boolean;
begin
  Result := False;
  if m_ItemList.Count < MAXBAGITEM then
    Result := True;
end;

function TActorObject.IsAddWeightAvailable(nWeight: Integer): Boolean;
begin
  Result := False;
  if (m_WAbil.Weight + nWeight) <= m_WAbil.MaxWeight then
    Result := True;
end;

procedure TPlayObject.SendAddItem(UserItem: pTUserItem);
var
  pStdItem: pTStdItem;
  StdItem: TStdItem;
  ClientItem: TClientItem;
  OClientItem: TOClientItem;
  sUserItemName: string;
begin
  if m_boNotOnlineAddExp or m_boAI then Exit; //离线挂机不发送
  if not m_boHeroVersion then begin
    pStdItem := UserEngine.GetStdItem(UserItem.wIndex);
    if pStdItem = nil then Exit;
    StdItem := pStdItem^;
    ItemUnit.GetItemAddValue(UserItem, StdItem);
    //Move(StdItem,ClientItem.S,SizeOf(TStdItem));
    CopyStdItemToOStdItem(@StdItem, @OClientItem.s);
    //取自定义物品名称
    sUserItemName := '';
    if UserItem.btValue[13] = 1 then
      sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);

    if sUserItemName <> '' then
      OClientItem.s.Name := sUserItemName;

    OClientItem.MakeIndex := UserItem.MakeIndex;
    OClientItem.Dura := UserItem.Dura;
    OClientItem.DuraMax := UserItem.DuraMax;
    if StdItem.StdMode = 50 then begin
      OClientItem.s.Name := OClientItem.s.Name + ' #' + IntToStr(UserItem.Dura);
    end;
    if StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26] then begin
      if UserItem.btValue[8] = 0 then OClientItem.s.Shape := 0
      else OClientItem.s.Shape := 130;
    end;
    m_DefMsg := MakeDefaultMsg(SM_ADDITEM, Integer(Self), 0, 0, 1);
    SendSocket(@m_DefMsg, EncodeBuffer(@OClientItem, SizeOf(TOClientItem)));
  end else begin
    pStdItem := UserEngine.GetStdItem(UserItem.wIndex);
    if pStdItem = nil then Exit;
    StdItem := pStdItem^;
    ItemUnit.GetItemAddValue(UserItem, StdItem);
    Move(StdItem, ClientItem.s, SizeOf(TStdItem));
    //取自定义物品名称
    sUserItemName := '';
    if UserItem.btValue[13] = 1 then
      sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);

    if sUserItemName <> '' then
      ClientItem.s.Name := sUserItemName;

    if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
      ClientItem.s.Name := '(绑)' + ClientItem.s.Name;
    end;

    ClientItem.MakeIndex := UserItem.MakeIndex;
    ClientItem.Dura := UserItem.Dura;
    ClientItem.DuraMax := UserItem.DuraMax;

    ClientItem.s.AddValue := UserItem.AddValue;
    ClientItem.s.AddPoint := UserItem.AddPoint;
    ClientItem.s.MaxDate := UserItem.MaxDate;
    //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

    if StdItem.StdMode = 50 then begin
      ClientItem.s.Name := ClientItem.s.Name + ' #' + IntToStr(UserItem.Dura);
    end;
    {if StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26] then begin
      if UserItem.btValue[8] = 0 then ClientItem.s.Shape := 0
      else ClientItem.s.Shape := 130;
    end; }

    m_DefMsg := MakeDefaultMsg(SM_ADDITEM, Integer(Self), 0, 0, 1);
    SendSocket(@m_DefMsg, EncodeBuffer(@ClientItem, SizeOf(TClientItem)));
  end;
end;

function TPlayObject.IsGroupMember(Target: TActorObject): Boolean;
var
  I: Integer;
begin
  Result := False;
  if m_GroupOwner = nil then Exit;
  for I := 0 to m_GroupOwner.m_GroupMembers.Count - 1 do begin
    if m_GroupOwner.m_GroupMembers.Objects[I] = Target then begin
      Result := True;
      Break;
    end;
  end;
end;

procedure TPlayObject.Hear(nIndex: Integer; sMsg: string);
begin

end;

procedure TPlayObject.Whisper(whostr, saystr: string);
var
  PlayObject: TPlayObject;
  svidx: Integer;
  sSendStr: string;

begin
  PlayObject := UserEngine.GetPlayObject(whostr);
  if PlayObject <> nil then begin
    if not PlayObject.m_boReadyRun then begin
      SysMsg(whostr + g_sCanotSendmsg {'无法发送信息.'}, c_Red, t_Hint);
      Exit;
    end;
    if not PlayObject.m_boHearWhisper or PlayObject.IsBlockWhisper(m_sCharName) then begin
      SysMsg(whostr + g_sUserDenyWhisperMsg {' 拒绝私聊！！！'}, c_Red, t_Hint);
      Exit;
    end;

    if PlayObject.m_boNotOnlineAddExp then begin //离线挂机人物自动回复         ) and (PlayObject.m_sAutoSendMsg <> '')
        sSendStr := PlayObject.m_sAutoSendMsg;

        if m_btPermission >= 10 then begin
          SendMsg(Self, RM_WHISPER, 0, g_Config.btGMWhisperMsgFColor, g_Config.btGMWhisperMsgBColor, 0, PlayObject.GetUnknowCharName + '=>' + ' ' + sSendStr);
        end else begin
          SendMsg(Self, RM_WHISPER, 0, {g_Config.btWhisperMsgFColor} m_btWhisperMsgFColor, g_Config.btWhisperMsgBColor, 0, PlayObject.GetUnknowCharName + '=>' + ' ' + sSendStr);
        end;
      Exit;
    end;
    if m_btPermission > 0 then begin
      PlayObject.SendMsg(PlayObject, RM_WHISPER, 0, g_Config.btGMWhisperMsgFColor, g_Config.btGMWhisperMsgBColor, 0, GetUnknowCharName + '=> ' + saystr);
      //取得私聊信息
      //m_GetWhisperHuman 侦听私聊对象
      if (m_GetWhisperHuman <> nil) and (not m_GetWhisperHuman.m_boGhost) then
        m_GetWhisperHuman.SendMsg(m_GetWhisperHuman, RM_WHISPER, 0, g_Config.btGMWhisperMsgFColor, g_Config.btGMWhisperMsgBColor, 0, GetUnknowCharName + '=>' + PlayObject.GetUnknowCharName + ' ' + saystr);
      if (PlayObject.m_GetWhisperHuman <> nil) and (not PlayObject.m_GetWhisperHuman.m_boGhost) then
        PlayObject.m_GetWhisperHuman.SendMsg(PlayObject.m_GetWhisperHuman, RM_WHISPER, 0, g_Config.btGMWhisperMsgFColor, g_Config.btGMWhisperMsgBColor, 0, GetUnknowCharName + '=>' + PlayObject.GetUnknowCharName + ' ' + saystr);
    end else begin
      PlayObject.SendMsg(PlayObject, RM_WHISPER, 0, {g_Config.btWhisperMsgFColor} m_btWhisperMsgFColor, g_Config.btWhisperMsgBColor, 0, GetUnknowCharName + '=> ' + saystr);
      if (m_GetWhisperHuman <> nil) and (not m_GetWhisperHuman.m_boGhost) then
        m_GetWhisperHuman.SendMsg(m_GetWhisperHuman, RM_WHISPER, 0, {g_Config.btWhisperMsgFColor} m_btWhisperMsgFColor, g_Config.btWhisperMsgBColor, 0, GetUnknowCharName + '=>' + PlayObject.GetUnknowCharName + ' ' + saystr);

      if (PlayObject.m_GetWhisperHuman <> nil) and (not PlayObject.m_GetWhisperHuman.m_boGhost) then
        PlayObject.m_GetWhisperHuman.SendMsg(PlayObject.m_GetWhisperHuman, RM_WHISPER, 0, {g_Config.btWhisperMsgFColor} m_btWhisperMsgFColor, g_Config.btWhisperMsgBColor, 0, GetUnknowCharName + '=>' + PlayObject.GetUnknowCharName + ' ' + saystr);
    end;
  end else begin

    SysMsg(whostr + ' ' + g_sUserNotOnLine {'  没有在线！！！'}, c_Red, t_Hint);
  end;
end;

function TPlayObject.IsBlockWhisper(sName: string): Boolean;
var
  I: Integer;
begin
  Result := False;
  for I := 0 to m_BlockWhisperList.Count - 1 do begin
    if CompareText(sName, m_BlockWhisperList.Strings[I]) = 0 then begin
      Result := True;
      Break;
    end;
  end;
end;

function TActorObject.GetObjectCastle(): Byte;
begin
  Result := 0;
  if (m_btRaceServer = RC_PLAYOBJECT) and (not m_boIsUnknowActor) then begin
    if IsGuildMaster and (g_CastleManager.IsCastleMember(Self) <> nil) then begin
      Result := 1;
    end else begin
      if g_CastleManager.IsCastleMember(Self) <> nil then Result := 2;
    end;
  end;
end;

function TActorObject.PKLevel(): Integer;
begin
  Result := m_nPkPoint div 100;
end;

procedure TActorObject.HealthSpellChanged;
begin
  if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT] then
    SendUpdateMsg(Self, RM_HEALTHSPELLCHANGED, 0, 0, 0, 0, '');

  if m_boShowHP then
    SendRefMsg(RM_HEALTHSPELLCHANGED, 0, 0, 0, 0, '');
end;

procedure TActorObject.IncExp(dwExp: LongWord);
begin

end;


procedure TActorObject.GainExp(dwExp: LongWord);
begin

end;

procedure TActorObject.GetExp(dwExp: LongWord);
begin

end;

procedure TActorObject.WinExp(dwExp: LongWord);
begin

end;

function TActorObject.CalcGetExp(nLevel: Integer; nExp: Integer): Integer;
begin
  if g_Config.boHighLevelKillMonFixExp or (m_Abil.Level < (nLevel + 10)) then begin
    Result := nExp;
  end else begin
    Result := nExp - Round((nExp / 15) * (m_Abil.Level - (nLevel + 10)));
  end;
  if Result <= 0 then Result := 1;
end;

procedure TActorObject.RefNameColor();
begin
  SendRefMsg(RM_CHANGENAMECOLOR, 0, 0, 0, 0, '');
end;

procedure TActorObject.GainSlaveExp(nLevel: Integer);
  function GetUpKillCount(): Integer;
  var
    tCount: Integer;
  begin
    if m_btSlaveExpLevel < SLAVEMAXLEVEL - 2 then begin
      tCount := g_Config.MonUpLvNeedKillCount[m_btSlaveExpLevel];
    end else begin
      tCount := 0;
    end;
    //    Result:= ((m_Abil.Level shl 4) - m_Abil.Level) + 100 + tCount
    Result := ((m_Abil.Level * g_Config.nMonUpLvRate {16}) - m_Abil.Level) + g_Config.nMonUpLvNeedKillBase {100} + tCount
  end;
  {
  var
    nNeedCount:Integer;
  }
begin
  Inc(n294, nLevel);
  if GetUpKillCount() < n294 then begin
    Dec(n294, GetUpKillCount);
    if m_btSlaveExpLevel < (m_btSlaveMakeLevel * 2 + 1) then begin
      Inc(m_btSlaveExpLevel);
      RecalcAbilitys();
      RefNameColor();
    end;
  end;
end;

function TActorObject.DropGoldDown(nGold: Integer; boFalg: Boolean; GoldOfCreat, DropGoldCreat: TActorObject): Boolean;
var
  ItemObject, ItemObjectA: TItemObject;
  nX, nY: Integer;
  s20: string;
begin
  Result := False;
  ItemObject := TItemObject.Create;
  ItemObject.m_sName := sSTRING_GOLDNAME;
  ItemObject.m_btAniCount := 0;
  ItemObject.m_btReserved := 0;
  ItemObject.m_nCount := nGold;
  ItemObject.m_wLooks := GetGoldShape(nGold);
  ItemObject.m_OfActorObject := GoldOfCreat;
  ItemObject.m_dwCanPickUpTick := GetTickCount();
  ItemObject.m_DropActorObject := DropGoldCreat;
  ItemObject.m_PEnvir := m_PEnvir;
  GetDropPosition(m_nCurrX, m_nCurrY, 3, nX, nY);
  ItemObjectA := TItemObject(m_PEnvir.AddToMap(nX, nY, ItemObject));
  if ItemObjectA = ItemObject then begin
    g_ItemManager.AddItem(ItemObject);
    SendRefMsg(RM_ITEMSHOW, ItemObject.m_wLooks, Integer(ItemObject), nX, nY, ItemObject.m_sName);
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if boFalg then s20 := '15'
      else s20 := '7';
      if g_boGameLogGold then
        AddGameDataLog(s20 + #9 +
          m_sMapName + #9 +
          IntToStr(m_nCurrX) + #9 +
          IntToStr(m_nCurrY) + #9 +
          m_sCharName + #9 +
          sSTRING_GOLDNAME + #9 +
          IntToStr(nGold) + #9 +
          BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
          '0');
    end;
    Result := True;
  end else ItemObject.Free;
end;

function TActorObject.GetGuildRelation(cert1, cert2: TActorObject): Integer;
begin
  Result := 0;
  m_boGuildWarArea := False;
  if (cert1.m_MyGuild = nil) or (cert2.m_MyGuild = nil) then Exit;
  if cert1.InSafeArea or (cert2.InSafeArea) then Exit;
  if TGUild(cert1.m_MyGuild).GuildWarList.Count <= 0 then Exit;
  m_boGuildWarArea := True;
  if TGUild(cert1.m_MyGuild).IsWarGuild(TGUild(cert2.m_MyGuild)) and
    TGUild(cert2.m_MyGuild).IsWarGuild(TGUild(cert1.m_MyGuild)) then Result := 2;
  if cert1.m_MyGuild = cert2.m_MyGuild then Result := 1;
  if TGUild(cert1.m_MyGuild).IsAllyGuild(TGUild(cert2.m_MyGuild)) and
    TGUild(cert2.m_MyGuild).IsAllyGuild(TGUild(cert1.m_MyGuild)) then Result := 3;
end;

procedure TActorObject.IncPkPoint(nPoint: Integer);
var
  nOldPKLevel: Integer;
begin
  nOldPKLevel := PKLevel;
  Inc(m_nPkPoint, nPoint);
  if PKLevel <> nOldPKLevel then begin
    if PKLevel <= 2 then RefNameColor;
  end;
end;

procedure TActorObject.AddBodyLuck(dLuck: Double);
var
  n: Integer;
begin
  if (dLuck > 0) and (m_dBodyLuck < 5 * BODYLUCKUNIT) then begin
    m_dBodyLuck := m_dBodyLuck + dLuck;
  end;
  if (dLuck < 0) and (m_dBodyLuck > -(5 * BODYLUCKUNIT)) then begin
    m_dBodyLuck := m_dBodyLuck + dLuck;
  end;
  n := Trunc(m_dBodyLuck / BODYLUCKUNIT);
  if n > 5 then n := 5;
  if n < -10 then n := -10;
  m_nBodyLuckLevel := n;
end;

procedure TActorObject.MakeWeaponUnlock;
begin
  if m_UseItems[U_WEAPON].wIndex <= 0 then Exit;
  if m_UseItems[U_WEAPON].btValue[3] > 0 then begin
    Dec(m_UseItems[U_WEAPON].btValue[3]);
    SysMsg(g_sTheWeaponIsCursed, c_Red, t_Hint);
  end else begin
    if m_UseItems[U_WEAPON].btValue[4] < 10 then begin
      Inc(m_UseItems[U_WEAPON].btValue[4]);
      SysMsg(g_sTheWeaponIsCursed, c_Red, t_Hint);
    end;
  end;
  if m_btRaceServer = RC_PLAYOBJECT then begin
    RecalcAbilitys();
    SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
    SendMsg(Self, RM_SUBABILITY, 0, 0, 0, 0, '');
  end;
end;

function TActorObject.GetNotDefendoof(): Boolean;
var
  I, nDefendoof, nCount: Integer;
begin
  Result := False;
  nDefendoof := GetAddPowerPoint(3, 100);
  if nDefendoof > 0 then begin
    nCount := nDefendoof; //10 - 1;
    nDefendoof := abs(100 - nDefendoof);
    for I := 0 to nCount do begin
      if Random(nDefendoof) = 0 then begin
        Result := True;
        Exit;
      end;
    end;
  end;
end;

function TActorObject.GetAddPowerPoint(btType: Byte; nPower: Integer): Integer;
var
  I, n1C: Integer;
begin
  Result := 0;
  if g_Config.boAllowItemAddPoint then begin
    n1C := 0;
    case btType of
      1: n1C := m_WAbil.AddPoint[0];
      2: n1C := m_WAbil.AddPoint[1];
      3: n1C := m_WAbil.AddPoint[2];
      4: n1C := m_WAbil.AddPoint[3];
      5: n1C := m_WAbil.AddPoint[4];
    end;
    if n1C > 0 then
      Result := nPower * n1C div 100;
  end;
end;

function TActorObject.GetAddPowerPoint(boWarrAttack: Boolean; nPower: Integer): Integer;
var
  nDefendoof: Integer;
begin
  Result := nPower;
  if g_Config.boAllowItemAddPoint then begin
    if not m_boNotDefendoof then begin
      if boWarrAttack then begin
        Result := _MAX(nPower - GetAddPowerPoint(1, nPower), 0);
      end else begin
        Result := _MAX(nPower - GetAddPowerPoint(2, nPower), 0);
      end;
    end;
  end;
end;

function TActorObject._GetAtomPower(TargeTActorObject: TActorObject; btType: Byte; nPower: Integer): Integer; //元素攻击
var
  nAtomPower, nMaxValue: Integer;
begin
  Result := nPower;
  if g_Config.boAllowItemAddPoint then begin
    if TargeTActorObject <> nil then begin
    //if btType in [AT_FIRE, AT_PHANTOM] then begin
                                //攻击元素 + 目标弱元素 - 目标强元素
      nAtomPower := _MAX(m_WAbil.ATOM_DC[btType] + TargeTActorObject.m_WAbil.ATOM_MAC[btType] - TargeTActorObject.m_WAbil.ATOM_MC[btType], 0);
      if nAtomPower > 0 then begin
      //Result := nAtomPower * (TargeTActorObject.m_WAbil.ATOM_MAC[btType] + 1) + nAtomPower * nPower div (10 + nAtomPower) + nPower;
        case nAtomPower of
          0..50: nMaxValue := 50;
          51..100: nMaxValue := 100;
        else nMaxValue := 200;
        end;
        Result := Round(nAtomPower * nPower / nMaxValue) + nPower;
      end;
    //end;
    end else begin
    //if btType in [AT_FIRE, AT_PHANTOM] then begin
    //Result := nPower + m_WAbil.ATOM_DC[btType];
      case nAtomPower of
        0..50: nMaxValue := 50;
        51..100: nMaxValue := 100;
      else nMaxValue := 200;
      end;
      Result := Round(m_WAbil.ATOM_DC[btType] * nPower / nMaxValue) + nPower;
    //end;
    end;
  end;
end;

function TActorObject._GetAtomPower(TargeTActorObject: TActorObject; nPower: Integer): Integer; //物理攻击元素攻击
var
  I: Integer;
  n1C: Integer;
  nWhere: Integer;
begin
  Result := nPower;
  if g_Config.boAllowItemAddPoint then begin
    if (m_UseItems[U_WEAPON].wIndex > 0) and (m_UseItems[U_WEAPON].AddValue[4] > 0) then begin //优先使用武器的元素
      if m_UseItems[U_WEAPON].Dura > 0 then begin
        Result := _GetAtomPower(TargeTActorObject, m_UseItems[U_WEAPON].AddValue[1], nPower);
      end;
    end else begin
      n1C := 0;
      nWhere := -1;
      for I := Low(m_UseItems) to High(m_UseItems) do begin
        if (m_UseItems[I].wIndex > 0) and (m_UseItems[I].AddValue[4] > 0) then begin
          if m_UseItems[I].AddValue[4] < n1C then begin
            n1C := m_UseItems[I].AddValue[4];
            nWhere := I;
          end;
        end;
      end;
      if nWhere in [Low(m_UseItems)..High(m_UseItems)] then begin
        if (m_UseItems[nWhere].wIndex > 0) and (m_UseItems[nWhere].AddValue[4] > 0) then begin
          if m_UseItems[nWhere].Dura > 0 then begin
            Result := _GetAtomPower(TargeTActorObject, m_UseItems[nWhere].AddValue[1], nPower);
          end;
        end;
      end;
    end;
  end;
end;

function TActorObject.GetAttackPower(nBasePower, nPower: Integer): Integer;
var
  PlayObject: TPlayObject;
  HeroObject: THeroObject;
  CopyObject: TCopyObject;
begin
  if nPower < 0 then nPower := 0;
  if m_nLuck > 0 then begin
    if Random(10 - _MIN(9, m_nLuck)) = 0 then Result := nBasePower + nPower
    else Result := nBasePower + Random(nPower + 1);
  end else begin
    Result := nBasePower + Random(nPower + 1);
    if m_nLuck < 0 then begin
      if Random(10 - _MAX(0, -m_nLuck)) = 0 then Result := nBasePower;
    end;
  end;
  if m_btRaceServer = RC_PLAYOBJECT then begin
    PlayObject := TPlayObject(Self);
    //Result:=Result * PlayObject.m_nPowerMult + ROUND(Result * (PlayObject.m_nPowerMultPoint / 100));
    Result := Round(Result * (PlayObject.m_nPowerRate / 100));
    if PlayObject.m_boPowerItem then
      Result := Round(m_rPowerItem * Result);
    case PlayObject.m_btJob of
      0:
        if PlayObject.m_boDCGroupItem then
          Result := Round(m_rDCGroupItem * Result);
      1:
        if PlayObject.m_boMCGroupItem then
          Result := Round(m_rMCGroupItem * Result);
      2:
        if PlayObject.m_boSCGroupItem then
          Result := Round(m_rSCGroupItem * Result);
    end;
  end;

  if m_btRaceServer = RC_HEROOBJECT then begin
    HeroObject := THeroObject(Self);
    //Result:=Result * PlayObject.m_nPowerMult + ROUND(Result * (PlayObject.m_nPowerMultPoint / 100));
    Result := Round(Result * (HeroObject.m_nPowerRate / 100));
    if m_boPowerItem then
      Result := Round(m_rPowerItem * Result);

    case HeroObject.m_btJob of
      0:
        if m_boDCGroupItem then
          Result := Round(m_rDCGroupItem * Result);
      1:
        if m_boMCGroupItem then
          Result := Round(m_rMCGroupItem * Result);
      2:
        if m_boSCGroupItem then
          Result := Round(m_rSCGroupItem * Result);
    end;
  end;

  if m_btRaceServer = RC_PLAYMOSTER then begin
    CopyObject := TCopyObject(Self);
    //Result:=Result * PlayObject.m_nPowerMult + ROUND(Result * (PlayObject.m_nPowerMultPoint / 100));
    //Result := Round(Result * (CopyObject.m_nPowerRate / 100));
    if m_boPowerItem then
      Result := Round(m_rPowerItem * Result);

    case m_btJob of
      0:
        if m_boDCGroupItem then
          Result := Round(m_rDCGroupItem * Result);
      1:
        if m_boMCGroupItem then
          Result := Round(m_rMCGroupItem * Result);
      2:
        if m_boSCGroupItem then
          Result := Round(m_rSCGroupItem * Result);
    end;
  end;

  if m_boAutoChangeColor then begin
    Result := Result * m_nAutoChangeIdx + 1;
  end;
  if m_boFixColor then begin
    Result := Result * m_nFixColorIdx + 1;
  end;
end;

procedure TActorObject.SendSerieMagicBlasthitMsg(UserMagic: pTUserMagic);
begin
  SendRefMsg(RM_BLASTHHIT, UserMagic.wMagIdx, m_nCurrX, m_nCurrY, 0, '');
  if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT] then
    SysMsg(Format(g_sUseSerieMagicBlasthitMsg, [UserEngine.GetMagicName(UserMagic.wMagIdx)]), c_Green, t_Hint);
end;

procedure TActorObject.DamageHealth(nDamage: Integer); //减血
var
  nSpdam: Integer;
begin
  if ((m_LastHiter = nil) or not m_LastHiter.m_boUnMagicShield) and m_boMagicShield and (nDamage > 0) and (m_WAbil.MP > 0) then begin
    nSpdam := Round(nDamage * 1.5);
    if Integer(m_WAbil.MP) >= nSpdam then begin
      m_WAbil.MP := m_WAbil.MP - nSpdam;
      nSpdam := 0;
    end else begin
      nSpdam := nSpdam - m_WAbil.MP;
      m_WAbil.MP := 0;
    end;
    nDamage := Round(nSpdam / 1.5);
    HealthSpellChanged();
  end;
  if nDamage > 0 then begin
    if (m_WAbil.HP - nDamage) > 0 then begin
      m_WAbil.HP := m_WAbil.HP - nDamage;
    end else begin
      m_WAbil.HP := 0;
    end;
  end else begin
    if (m_WAbil.HP - nDamage) < m_WAbil.MaxHP then begin
      m_WAbil.HP := m_WAbil.HP - nDamage;
    end else begin
      m_WAbil.HP := m_WAbil.MaxHP;
    end;
  end;
end;

function TActorObject.GetBackDir(nDir: Integer): Integer;
begin
  Result := 0;
  case nDir of
    DR_UP: Result := DR_DOWN;
    DR_DOWN: Result := DR_UP;
    DR_LEFT: Result := DR_RIGHT;
    DR_RIGHT: Result := DR_LEFT;
    DR_UPLEFT: Result := DR_DOWNRIGHT;
    DR_UPRIGHT: Result := DR_DOWNLEFT;
    DR_DOWNLEFT: Result := DR_UPRIGHT;
    DR_DOWNRIGHT: Result := DR_UPLEFT;
  end;
end;

function TActorObject.CharPushed(nDir, nPushCount: Integer): Integer; //撞人
var
  I, nX, nY, olddir, oldx, oldy, nBackDir: Integer;
begin
  Result := 0;
  olddir := m_btDirection;
  oldx := m_nCurrX;
  oldy := m_nCurrY;
  m_btDirection := nDir;
  nBackDir := GetBackDir(nDir);
  for I := 0 to nPushCount - 1 do begin
    GetFrontPosition(nX, nY);
    if m_PEnvir.CanWalk(nX, nY, False) and m_PEnvir.CanWalkOfEvent(Self, nX, nY) then begin
      if m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nX, nY, False) > 0 then begin
        m_nCurrX := nX;
        m_nCurrY := nY;
        //SendRefMsg(RM_PUSH, GetBackDir(ndir), m_nCurrX, m_nCurrY, 0, '');
        SendRefMsg(RM_PUSH, nBackDir, m_nCurrX, m_nCurrY, 0, '');
        Inc(Result);
        if m_btRaceServer >= RC_ANIMAL then
          m_dwWalkTick := m_dwWalkTick + 800;
      end else Break;
    end else Break;
  end;
  //m_btDirection:=GetBackDir(ndir);
  //m_btDirection:=GetBackDir(nBackDir);
  m_btDirection := nBackDir;
  if Result = 0 then m_btDirection := olddir;
end;

function TActorObject.MagPassThroughMagic(sX, sY, tx, ty, nDir, magpwr: Integer; undeadattack: Boolean; btType: Byte): Integer;
var
  I, tCount, acpwr: Integer;
  BaseObject: TActorObject;
  //   n14,n18:integer;
begin
  tCount := 0;
  for I := 0 to 12 do begin
    BaseObject := TActorObject(m_PEnvir.GetMovingObject(sX, sY, True));
    if BaseObject <> nil then begin
      if IsProperTarget(BaseObject) then begin
        if Random(10) >= BaseObject.m_nAntiMagic then begin
          if undeadattack then
            acpwr := Round(magpwr * 1.5)
          else
            acpwr := magpwr;

          acpwr := _GetAtomPower(BaseObject, btType, acpwr);
          BaseObject.m_boNotDefendoof := GetNotDefendoof; //忽视目标防御
          acpwr := acpwr + GetAddPowerPoint(5, acpwr);

          if acpwr > 0 then
            BaseObject.SendDelayMsg(Self, RM_MAGSTRUCK, 0, acpwr, Integer(BaseObject.m_boNotDefendoof), 0, '', 600);
          Inc(tCount);
        end;
      end;
    end;
    if not ((abs(sX - tx) <= 0) and (abs(sY - ty) <= 0)) then begin
      nDir := GetNextDirection(sX, sY, tx, ty);
      if not m_PEnvir.GetNextPosition(sX, sY, nDir, 1, sX, sY) then
        Break;
    end else
      Break;
  end;
  Result := tCount;
end;

procedure TPlayObject.SendSocket(DefMsg: pTDefaultMessage; sMsg: string);
var
  MsgHdr: TMsgHeader;
  nSendBytes: Integer;
  tBuff: PChar;
resourcestring
  sExceptionMsg = '[Exception] TPlayObject::SendSocket..';
begin
  if Assigned(PlugInEngine.SendSocket) then begin
    PlugInEngine.SendSocket(Self, DefMsg, PChar(sMsg));
  end else begin
    if m_boAI then Exit;
    if m_boNotOnlineAddExp and (GetTickCount - m_dwAutoAddExpPointTimeTick > 1000 * 30) then Exit; //如果挂机超过30秒后不在发送数据到客户端
    //MainOutMessage('TPlayObject.SendSocket');
    tBuff := nil;
    try
      MsgHdr.dwCode := RUNGATECODE;
      MsgHdr.nSocket := m_nSocket;
      MsgHdr.wGSocketIdx := m_nGSocketIdx;
      MsgHdr.wIdent := GM_DATA;
      if DefMsg <> nil then begin
        if sMsg <> '' then begin
          MsgHdr.nLength := Length(sMsg) + SizeOf(TDefaultMessage) + 1;
          nSendBytes := MsgHdr.nLength + SizeOf(TMsgHeader);
          GetMem(tBuff, nSendBytes + SizeOf(Integer));
          Move(nSendBytes, tBuff^, SizeOf(Integer));
          Move(MsgHdr, tBuff[SizeOf(Integer)], SizeOf(TMsgHeader));
          Move(DefMsg^, tBuff[SizeOf(TMsgHeader) + SizeOf(Integer)], SizeOf(TDefaultMessage));
          Move(sMsg[1], tBuff[SizeOf(TDefaultMessage) + SizeOf(TMsgHeader) + SizeOf(Integer)], Length(sMsg) + 1);
        end else begin
          MsgHdr.nLength := SizeOf(TDefaultMessage);
          nSendBytes := MsgHdr.nLength + SizeOf(TMsgHeader);
          GetMem(tBuff, nSendBytes + SizeOf(Integer));
          Move(nSendBytes, tBuff^, SizeOf(Integer));
          Move(MsgHdr, tBuff[SizeOf(Integer)], SizeOf(TMsgHeader));
          Move(DefMsg^, tBuff[SizeOf(TMsgHeader) + SizeOf(Integer)], SizeOf(TDefaultMessage));
        end;
      end else begin
        if sMsg <> '' then begin
          MsgHdr.nLength := -(Length(sMsg) + 1);
          nSendBytes := abs(MsgHdr.nLength) + SizeOf(TMsgHeader);
          GetMem(tBuff, nSendBytes + SizeOf(Integer));
          Move(nSendBytes, tBuff^, SizeOf(Integer));
          Move(MsgHdr, tBuff[SizeOf(Integer)], SizeOf(TMsgHeader));
          Move(sMsg[1], tBuff[SizeOf(TMsgHeader) + SizeOf(Integer)], Length(sMsg) + 1);
        end;
      end;
      if not RunSocket.AddGateBuffer(m_nGateIdx, tBuff) then begin
        FreeMem(tBuff);
      end;
    except
      MainOutMessage(sExceptionMsg);
    end;
  end;
end;

procedure TPlayObject.SendDefMessage(wIdent: Word; nRecog: Integer; nParam, nTag, nSeries: Word; sMsg: string);
begin
  if m_boAI then Exit;
  if m_boNotOnlineAddExp and (GetTickCount - m_dwAutoAddExpPointTimeTick > 1000 * 30) then Exit; //如果挂机超过30秒后不在发送数据到客户端
  m_DefMsg := MakeDefaultMsg(wIdent, nRecog, nParam, nTag, nSeries);
  if sMsg <> '' then SendSocket(@m_DefMsg, EncodeString(sMsg))
  else SendSocket(@m_DefMsg, '');
end;

procedure TPlayObject.ClientQueryUserName(Target: TActorObject; x, y: Integer);
var
  uname: string;
  TagColor: Integer;
  Def: TDefaultMessage;
begin
  if CretInNearXY(Target, x, y) then begin
    TagColor := GetCharColor(Target);
    Def := MakeDefaultMsg(SM_USERNAME, Integer(Target), TagColor, 0, 0);
    uname := Target.GetShowName;
    SendSocket(@Def, EncodeString(uname));
  end else
    SendDefMessage(SM_GHOST, Integer(Target), x, y, 0, '');
end;

function TActorObject.GetShowNameEx: string;
var
  sShowName: string;
begin
  if m_boIsUnknowActor then begin
    sShowName := g_Config.sUnKnowName;
  end else begin
    sShowName := m_sCharName;
  end;
  if (m_btRaceServer <> RC_PLAYOBJECT) and (m_btRaceServer <> RC_HEROOBJECT) and (m_btRaceServer <> RC_PLAYMOSTER) then begin
    Result := FilterShowName(sShowName);
  end else begin
    Result := sShowName;
  end;
end;

function TActorObject.GetShowName: string;
var
  sShowName: string;
begin
  if m_boIsUnknowActor then begin
    sShowName := g_Config.sUnKnowName;
  end else begin
    sShowName := m_sCharName;
  end;
  if (m_btRaceServer <> RC_HEROOBJECT) and (m_btRaceServer <> RC_PLAYMOSTER) then begin
    Result := FilterShowName(sShowName);
  end else begin
    Result := sShowName;
  end;
  if m_nCopyHumanLevel in [0, 1] then begin
    if (m_Master <> nil) and not m_Master.m_boObMode then begin
      if m_Master.m_boIsUnknowActor then begin
        Result := Result + '(' + g_Config.sUnKnowName + ')';
      end else begin
        Result := Result + '(' + m_Master.m_sCharName + ')';
      end;
    end;
  end else begin
    if (m_nCopyHumanLevel = 2) and (m_Master <> nil) and not m_Master.m_boObMode then begin
      if g_Config.boAddMasterName then begin
        if m_Master.m_boIsUnknowActor then begin
          Result := g_Config.sUnKnowName + g_Config.sCopyHumName;
        end else begin
          Result := m_Master.m_sCharName + g_Config.sCopyHumName;
        end;
      end else begin
        Result := g_Config.sCopyHumName;
      end;
    end;
  end;
  {
  if m_btRaceServer <> RC_PLAYOBJECT then begin
    sShowName:=m_sCharName;
    Result:=FilterShowName(sShowName);
    if (m_Master <> nil) and not m_Master.m_boObMode then begin
      Result:=Result + '(' + m_Master.m_sCharName + ')';
    end;
  end else begin//004C1340
    Result:=m_sCharName;
    if m_MyGuild <> nil then begin
      if UserCastle.IsMasterGuild(TGuild(m_MyGuild)) then begin
        Result:=Result + '\' + TGuild(m_MyGuild).sGuildName + '(' + UserCastle.sName + ')';
      end else begin
        if g_boShowGuildName or (UserCastle.boUnderWar and (m_boInFreePKArea or UserCastle.IsCastleWarArea(m_PEnvir,m_nCurrX,m_nCurrY))) then begin
          Result:=Result + '\' + TGuild(m_MyGuild).sGuildName + '[' + m_sGuildRankName + ']';
        end;
      end;
    end;
  end;
  }
end;

{------------------------------------------------------------------------------}

constructor TAnimalObject.Create;
begin
  inherited;
  //m_nNotProcessCount := 0;
  m_nTargetX := -1;
  //dwTick3F0 := Random(4) * 500 + 1000;
  //dwTick3F4 := GetTickCount();
  m_btRaceServer := RC_ANIMAL;
  m_dwHitTick := GetTickCount - LongWord(Random(3000));
  m_dwWalkTick := GetTickCount - LongWord(Random(3000));
  m_dwSearchEnemyTick := GetTickCount();
  m_boRunAwayMode := False;
  m_dwRunAwayStart := GetTickCount();
  m_dwRunAwayTime := 0;
  m_nCopyHumanLevel := 0;
end;

procedure TAnimalObject.GotoTargetXY;
var
  I: Integer;
  nDir: Integer;
  n10: Integer;
  n14: Integer;
  n20: Integer;
  nOldX: Integer;
  nOldY: Integer;
begin
  if ((m_nCurrX <> m_nTargetX) or (m_nCurrY <> m_nTargetY)) then begin
    n10 := m_nTargetX;
    n14 := m_nTargetY;
    //dwTick3F4 := GetTickCount();
    nDir := DR_DOWN;
    if n10 > m_nCurrX then begin
      nDir := DR_RIGHT;
      if n14 > m_nCurrY then nDir := DR_DOWNRIGHT;
      if n14 < m_nCurrY then nDir := DR_UPRIGHT;
    end else begin
      if n10 < m_nCurrX then begin
        nDir := DR_LEFT;
        if n14 > m_nCurrY then nDir := DR_DOWNLEFT;
        if n14 < m_nCurrY then nDir := DR_UPLEFT;
      end else begin
        if n14 > m_nCurrY then nDir := DR_DOWN
        else if n14 < m_nCurrY then nDir := DR_UP;
      end;
    end;
    nOldX := m_nCurrX;
    nOldY := m_nCurrY;
    WalkTo(nDir, False);
    n20 := Random(3);
    for I := DR_UP to DR_UPLEFT do begin
      if (nOldX = m_nCurrX) and (nOldY = m_nCurrY) then begin
        if n20 <> 0 then Inc(nDir)
        else if nDir > 0 then Dec(nDir)
        else nDir := DR_UPLEFT;
        if (nDir > DR_UPLEFT) then nDir := DR_UP;
        WalkTo(nDir, False);
      end;
    end;
  end;
end;

function TAnimalObject.Operate(ProcessMsg: pTProcessMessage): Boolean;
var
  nError: Integer;
const
  sExceptionMsg0 = '[Exception] TAnimalObject::Operate %s Ident:%d Sender:%d wP:%d nP1:%d nP2:%d np3:%d Msg:%s';
begin
  //  Result:=False;
  if ProcessMsg.wIdent = RM_STRUCK then begin
    //try
    nError := 0;
    if (ProcessMsg.BaseObject = Self) and (TActorObject(ProcessMsg.nParam3 {AttackBaseObject}) <> nil) then begin
      nError := 1;
      SetLastHiter(TActorObject(ProcessMsg.nParam3 {AttackBaseObject}));
      nError := 2;
      Struck(TActorObject(ProcessMsg.nParam3 {AttackBaseObject})); {0FFEC}
      nError := 3;
      BreakHolySeizeMode();
      nError := 4;
      if (m_Master <> nil) and
        (not m_Master.m_boGhost) and
        (TActorObject(ProcessMsg.nParam3) <> m_Master) and
        (TActorObject(ProcessMsg.nParam3).m_btRaceServer = RC_PLAYOBJECT) then begin
        nError := 5;
        m_Master.SetPKFlag(TActorObject(ProcessMsg.nParam3));
        nError := 6;
      end;
      nError := 7;
      if g_Config.boMonSayMsg then MonsterSayMsg(TActorObject(ProcessMsg.nParam3), s_UnderFire);
      nError := 8;
    end;
    Result := True;
    {except
      MainOutMessage('TAnimalObject::Operate:' + IntToStr(nError));
      MainOutMessage(Format(sExceptionMsg0, [m_sCharName,
        ProcessMsg.wIdent,
          Integer(ProcessMsg.BaseObject),
          ProcessMsg.wParam,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          ProcessMsg.sMsg]));
    end;}
  end else begin
    Result := inherited Operate(ProcessMsg);
  end;
end;

procedure TAnimalObject.Run;
begin
  inherited;
end;

procedure TAnimalObject.Struck(hiter: TActorObject);
var
  btDir: Byte;
begin
  m_dwStruckTick := GetTickCount;
  if hiter <> nil then begin
    if (m_TargetCret = nil) or GetAttackDir(m_TargetCret, btDir) or (Random(6) = 0) then begin
      if IsProperTarget(hiter) then
        SetTargetCreat(hiter);
    end;
  end;
  if m_boAnimal then begin
    m_nMeatQuality := m_nMeatQuality - Random(300);
    if m_nMeatQuality < 0 then m_nMeatQuality := 0;
  end;
  //if m_Abil.Level < 50 then
  m_dwHitTick := m_dwHitTick + LongWord(150 - _MIN(130, m_Abil.Level * 4));
  //WalkTime := WalkTime + (300 - _MIN(200, (Abil.Level div 5) * 20));
end;

procedure TAnimalObject.Attack(TargeTActorObject: TActorObject; nDir: Integer);
begin
  inherited AttackDir(TargeTActorObject, 0, nDir);
end;

procedure TAnimalObject.HitMagAttackTarget(TargeTActorObject: TActorObject; nHitPower,
  nMagPower: Integer; boFlag: Boolean);
var
  I: Integer;
  nDamage: Integer;
  BaseObjectList: TList;
  BaseObject: TActorObject;
begin
  m_btDirection := GetNextDirection(m_nCurrX, m_nCurrY, TargeTActorObject.m_nCurrX, TargeTActorObject.m_nCurrY);
  BaseObjectList := TList.Create;
  m_PEnvir.GeTActorObjects(TargeTActorObject.m_nCurrX, TargeTActorObject.m_nCurrY, False, BaseObjectList);
  for I := 0 to BaseObjectList.Count - 1 do begin
    BaseObject := TActorObject(BaseObjectList.Items[I]);
    if IsProperTarget(BaseObject) then begin
      nDamage := 0;
      nHitPower := nHitPower + GetAddPowerPoint(5, nHitPower);
      nMagPower := nMagPower + GetAddPowerPoint(5, nMagPower);

      Inc(nDamage, BaseObject.GetHitStruckDamage(Self, nHitPower));
      Inc(nDamage, BaseObject.GetMagStruckDamage(Self, nMagPower));

      BaseObject.StartNewShield();
      if nDamage > 0 then begin
        BaseObject.StruckDamage(nDamage);
        BaseObject.SendDelayMsg(TActorObject(RM_STRUCK), RM_10101, nDamage, BaseObject.m_WAbil.HP, BaseObject.m_WAbil.MaxHP, Integer(Self), '', 200);
      end;
    end;
  end;
  BaseObjectList.Free;
  SendRefMsg(RM_HIT, m_btDirection, m_nCurrX, m_nCurrY, 0, '');
end;

procedure TAnimalObject.DelTargetCreat;
begin
  inherited;
  m_nTargetX := -1;
  m_nTargetY := -1;
end;

procedure TAnimalObject.SearchTarget;
var
  ActorObject, ActorObject18: TActorObject;
  I, nC, n10: Integer;
begin
  if m_NewStatus <> sNone then Exit;

  ActorObject18 := nil;
  n10 := 9999;
  for I := 0 to m_VisibleActors.Count - 1 do begin
    ActorObject := TActorObject(pTVisibleBaseObject(m_VisibleActors.Items[I]).BaseObject);
    if ActorObject <> nil then begin
      if (not ActorObject.m_boDeath) and (not ActorObject.m_boGhost) then begin
        if IsProperTarget(ActorObject) and
          (not ActorObject.m_boHideMode or m_boCoolEye) then begin
          nC := abs(m_nCurrX - ActorObject.m_nCurrX) + abs(m_nCurrY - ActorObject.m_nCurrY);
          if nC < n10 then begin
            n10 := nC;
            ActorObject18 := ActorObject;
          end;
        end;
      end;
    end;
  end;
  if ActorObject18 <> nil then SetTargetCreat(ActorObject18);
end;

procedure TAnimalObject.SearchTarget2;
var
  I, nC, n10: Integer;
  Creat, ActorObject: TActorObject;
begin
  if m_NewStatus <> sNone then Exit;
  Creat := nil;
  n10 := 9999;
  for I := 0 to m_VisibleActors.Count - 1 do begin
    ActorObject := TActorObject(pTVisibleBaseObject(m_VisibleActors.Items[I]).BaseObject);
    if ActorObject <> nil then begin
      if ActorObject.m_boDeath or ActorObject.m_boGhost then Continue;
      if IsProperTarget(ActorObject) then begin
        nC := abs(m_nCurrX - ActorObject.m_nCurrX) + abs(m_nCurrY - ActorObject.m_nCurrY);
        if nC < n10 then begin
          n10 := nC;
          Creat := ActorObject;
        end;
      end;
    end;
  end; // for
  if Creat <> nil then
    SetTargetCreat(Creat);
end;

procedure TAnimalObject.SetTargetXY(nX, nY: Integer);
begin
  m_nTargetX := nX;
  m_nTargetY := nY;
end;

procedure TAnimalObject.Wondering;
begin
  if (Random(20) = 0) then
    if (Random(4) = 1) then TurnTo(Random(8))
    else WalkTo(m_btDirection, False);
end;

{------------------------------------------------------------------------------}

constructor TAIObject.Create;
begin
  inherited;
  m_nTargetX := -1;
  m_btRaceServer := RC_ANIMAL;
  m_nCopyHumanLevel := 0;
  m_wLastHP := 0;
  m_boSlaveRelax := False;
  m_dwSearchTargetTick := GetTickCount;

  m_boFixedHideMode := False;


  m_nDieDropUseItemRate := 100; //死亡掉装备几率
  m_nSelItemType := 1;
  m_dwPickUpItemTick := GetTickCount;
  m_SelItemObject := nil;

  m_dwEatItemTick := GetTickCount;
  FillChar(m_SkillUseTick, SizeOf(m_SkillUseTick), 0); //魔法使用间隔

  m_boDupMode := False;

  m_dwThinkTick := GetTickCount();
  m_nViewRange := 10;
  m_nRunTime := 250;
  m_dwSearchTime := 1000;
  m_dwSearchTick := GetTickCount();

  m_dwWalkWait := 0;
  m_nWalkSpeed := 0;
  m_nWalkStep := 1;
  m_boSelSelf := False;
  m_nIncSelfHealthCount := 0;
  m_nIncMasterHealthCount := 0;

  m_boProtectStatus := False; //是否是守护状态
  m_nProtectTargetX := -1; //守护坐标
  m_nProtectTargetY := -1; //守护坐标
  m_boAutoAttack := False;
  m_nPowerRate := 100;

  m_btTaoistUseItemType := 0;
  FillChar(m_RunPos, SizeOf(TRunPos), #0);

  m_btHeroGroup := 0;

  m_boAI := False;

  m_dwAutoRepairItemTick := GetTickCount;
  m_dwAutoAddHealthTick := GetTickCount;
  FillChar(m_UseItemNames, SizeOf(TUseItemNames), #0);
  m_BagItemNames := TStringList.Create;

  m_boSuperItem := False;
end;

destructor TAIObject.Destroy;
begin
  m_BagItemNames.Free;
  inherited;
end;

procedure TAIObject.Run;
var
  nWalkTime: Integer;

  I, II, nWhere, nPercent, nValue: Integer;
  sCheckItemName: string;
  StdItem: pTStdItem;
  UserItem: pTUserItem;
  boRecalcAbilitys: Boolean;
  boFind: Boolean;
begin
  if (m_btRaceServer in [RC_MOONOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) or m_boAI then begin

//自动修理装备
    if m_boAI and (not m_boGhost) and (not m_boDeath) then begin
      if g_Config.boAutoRepairItem then begin
        if GetTickCount - m_dwAutoRepairItemTick > 3000 then begin
          m_dwAutoRepairItemTick := GetTickCount;
          boRecalcAbilitys := False;
          for nWhere := Low(THumanUseItems) to High(THumanUseItems) do begin
            if (m_UseItemNames[nWhere] <> '') and (m_UseItems[nWhere].wIndex <= 0) then begin
              StdItem := UserEngine.GetStdItem(m_UseItemNames[nWhere]);
              if StdItem <> nil then begin
                New(UserItem);
                if UserEngine.CopyToUserItemFromName(m_UseItemNames[nWhere], UserItem) then begin
                  boRecalcAbilitys := True;

                  if m_boSuperItem or (Random(g_Config.nScriptRandomAddValue {10}) = 0) then //几率控制
                    UserEngine.RandomUpgradeItem(UserItem); //生成极品装备

                  UserEngine._RandomItemLimitDay(UserItem, g_Config.nScriptRandomNotLimit);

                  if m_boSuperItem or (Random(g_Config.nScriptRandomNewAddValue) = 0) then
                    UserEngine._RandomUpgradeItem(UserItem);

                  if m_boSuperItem or (Random(g_Config.nScriptRandomAddPoint) = 0) then
                    UserEngine.RandomItemAddPoint(UserItem);


                  UserEngine._RandomItemLimitDay(UserItem, g_Config.nMonRandomNotLimit);

                  if StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26] then begin
                    if (StdItem.Shape = 130) or (StdItem.Shape = 131) or (StdItem.Shape = 132) then begin
                      UserEngine.GetUnknowItemValue(UserItem);

                      UserEngine._GetUnknowItemValue(UserItem);
                    end;
                  end;
                end;
                m_UseItems[nWhere] := UserItem^;
                Dispose(UserItem);
              end;
            end;
          end;

          for I := 0 to m_ItemList.Count - 1 do begin
            UserItem := m_ItemList.Items[I];
            StdItem := UserEngine.GetStdItem(UserItem.wIndex);
            if StdItem <> nil then begin
              boFind := False;
              for II := 0 to m_BagItemNames.Count - 1 do begin
                if CompareText(StdItem.Name, m_BagItemNames.Strings[II]) = 0 then begin
                  boFind := True;
                  break;
                end;
              end;

              if not boFind then begin
                New(UserItem);
                if UserEngine.CopyToUserItemFromName(m_UseItemNames[nWhere], UserItem) then begin

                  if m_boSuperItem or (Random(g_Config.nScriptRandomAddValue {10}) = 0) then //几率控制
                    UserEngine.RandomUpgradeItem(UserItem); //生成极品装备

                  UserEngine._RandomItemLimitDay(UserItem, g_Config.nScriptRandomNotLimit);

                  if m_boSuperItem or (Random(g_Config.nScriptRandomNewAddValue) = 0) then
                    UserEngine._RandomUpgradeItem(UserItem);

                  if m_boSuperItem or (Random(g_Config.nScriptRandomAddPoint) = 0) then
                    UserEngine.RandomItemAddPoint(UserItem);

                  UserEngine._RandomItemLimitDay(UserItem, g_Config.nMonRandomNotLimit);

                  if StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26] then begin
                    if (StdItem.Shape = 130) or (StdItem.Shape = 131) or (StdItem.Shape = 132) then begin
                      UserEngine.GetUnknowItemValue(UserItem);

                      UserEngine._GetUnknowItemValue(UserItem);
                    end;
                  end;
                  if not AddItemToBag(UserItem) then begin
                    Dispose(UserItem);
                    break;
                  end;
                end else Dispose(UserItem);

              end;
            end;
          end;

          for nWhere := Low(THumanUseItems) to High(THumanUseItems) do begin
            if m_UseItems[nWhere].wIndex > 0 then begin
              StdItem := UserEngine.GetStdItem(m_UseItems[nWhere].wIndex);
              if StdItem <> nil then begin
                if (m_UseItems[nWhere].DuraMax > m_UseItems[nWhere].Dura) and (StdItem.StdMode <> 43) then begin
                  if Assigned(PlugInEngine.CheckCanRepairItem) then begin
                    sCheckItemName := StdItem.Name;
                    if not PlugInEngine.CheckCanRepairItem(Self, PChar(sCheckItemName), False) then Continue;
                  end;
                  m_UseItems[nWhere].Dura := m_UseItems[nWhere].DuraMax;
              //SendMsg(Self, RM_DURACHANGE, nWhere, m_UseItems[nWhere].Dura, m_UseItems[nWhere].DuraMax, 0, '');
                end;
              end;
            end;
          end;
          if boRecalcAbilitys then RecalcAbilitys;
        end;
      end;

//自动增加HP MP
      if g_Config.boRenewHealth then begin
        if GetTickCount - m_dwAutoAddHealthTick > 1000 then begin
          m_dwAutoAddHealthTick := GetTickCount;
          nPercent := m_WAbil.HP * 100 div m_WAbil.MaxHP;
          nValue := m_WAbil.MaxHP div 10;
          if nPercent < g_Config.nRenewPercent then begin
            if m_WAbil.HP + nValue >= m_WAbil.MaxHP then begin
              m_WAbil.HP := m_WAbil.MaxHP;
            end else begin
              Inc(m_WAbil.HP, nValue);
            end;
          end;

          nValue := m_WAbil.MaxMP div 10;
          nPercent := m_WAbil.MP * 100 div m_WAbil.MaxMP;
          if nPercent < g_Config.nRenewPercent then begin
            if m_WAbil.MP + nValue >= m_WAbil.MaxMP then begin
              m_WAbil.MP := m_WAbil.MaxMP;
            end else begin
              Inc(m_WAbil.MP, nValue);
            end;
          end;
        end;
      end;
    end;

    if (not m_boGhost) and
      (not m_boDeath) and
      ((not m_boFixedHideMode) or (m_btHeroGroup > 0)) and
      (not m_boStoneMode) and
      (m_wStatusTimeArr[POISON_STONE] = 0) then begin

      if Thinking then begin
        inherited;
        Exit;
      end;

      nWalkTime := 500;
      if m_btRaceServer = RC_HEROOBJECT then begin
        case m_btJob of
          0: nWalkTime := g_Config.dwHeroWarrorWalkTime;
          1: nWalkTime := g_Config.dwHeroWizardWalkTime;
          2: nWalkTime := g_Config.dwHeroTaoistWalkTime;
        end;
      end else begin
        case m_btJob of
          0: nWalkTime := g_Config.dwWarrorWalkTime;
          1: nWalkTime := g_Config.dwWizardWalkTime;
          2: nWalkTime := g_Config.dwTaoistWalkTime;
        end;
      end;

      if GetTickCount - m_dwWalkTick > nWalkTime then begin
        m_dwWalkTick := GetTickCount;
        if (m_Master <> nil) then
          FollowMaster
        else Wondering();
      end;
    end;
  end;
  inherited;
end;

function TAIObject.RunToNext(nX, nY: Integer): Boolean;
begin
  Result := RunTo(GetNextDirection(m_nCurrX, m_nCurrY, nX, nY), False, nX, nY);
end;

function TAIObject.WalkToNext(nX, nY: Integer): Boolean;
begin
  Result := WalkTo(GetNextDirection(m_nCurrX, m_nCurrY, nX, nY), False);
end;

function TAIObject.GotoNextOne(nX, nY: Integer; boRun: Boolean): Boolean;
var
  I, nStep: Integer;
  Path: TPath;
begin
  Result := False;
  if (abs(nX - m_nCurrX) <= 2) and (abs(nY - m_nCurrY) <= 2) then begin
    if (abs(nX - m_nCurrX) <= 1) and (abs(nY - m_nCurrY) <= 1) then begin
      Result := WalkToNext(nX, nY);
    end else begin
      Result := RunToNext(nX, nY);
    end;
  end;

  if not Result then begin
    g_FindPath.BaseObject := Self;
    Path := g_FindPath.FindPath(m_PEnvir, m_nCurrX, m_nCurrY, nX, nY, boRun, False);
  //MainOutMessage(Format('%s TAIObject.GotoNextOne:(%d)', [m_sCharName, Length(Path)]));
    if Length(Path) > 0 then begin
      for I := 0 to Length(Path) - 1 do begin
        if (Path[I].X <> m_nCurrX) or (Path[I].Y <> m_nCurrY) then begin
          if (abs(Path[I].X - m_nCurrX) >= 2) or (abs(Path[I].Y - m_nCurrY) >= 2) then begin
            Result := RunToNext(Path[I].X, Path[I].Y);
          end else begin
            Result := WalkToNext(Path[I].X, Path[I].Y);
          end;
          break;
        end;
      end;
      Path := nil;
    end;
  end;

  m_RunPos.nAttackCount := 0;
end;

function TAIObject.GotoNext(nX, nY: Integer; boRun: Boolean): Boolean;
var
  I, nStep: Integer;
  Path: TPath;
begin
  Result := False;
  nStep := 0;
  if (abs(nX - m_nCurrX) <= 2) and (abs(nY - m_nCurrY) <= 2) then begin
    if (abs(nX - m_nCurrX) <= 1) and (abs(nY - m_nCurrY) <= 1) then begin
      Result := WalkToNext(nX, nY);
    end else begin
      Result := RunToNext(nX, nY);
    end;
    nStep := 1;
  end;

  if not Result then begin
    g_FindPath.BaseObject := Self;
    Path := g_FindPath.FindPath(m_PEnvir, m_nCurrX, m_nCurrY, nX, nY, boRun, False);
  //Path := FindWalkPath(m_nCurrX, m_nCurrY, nX, nY, boRun, False);
    if Length(Path) > 0 then begin
      for I := 0 to Length(Path) - 1 do begin
        if (Path[I].X <> m_nCurrX) or (Path[I].Y <> m_nCurrY) then begin
          if (abs(Path[I].X - m_nCurrX) >= 2) or (abs(Path[I].Y - m_nCurrY) >= 2) then begin
            Result := RunToNext(Path[I].X, Path[I].Y);
          end else begin
            Result := WalkToNext(Path[I].X, Path[I].Y);
          end;
          if Result then Inc(nStep) else break;
          if nStep >= 3 then break;
        end;
      end;
      Path := nil;
    end;
  end;
  m_RunPos.nAttackCount := 0;
end;

procedure TAIObject.Struck(hiter: TActorObject);
  function MINXY(AObject, BObject: TActorObject): TActorObject;
  var
    nA, nB: Integer;
  begin
    nA := abs(m_nCurrX - AObject.m_nCurrX) + abs(m_nCurrY - AObject.m_nCurrY);
    nB := abs(m_nCurrX - BObject.m_nCurrX) + abs(m_nCurrY - BObject.m_nCurrY);
    if nA > nB then Result := BObject else Result := AObject;
  end;
var
  btDir: Byte;
begin
  m_dwStruckTick := GetTickCount;
  if (m_btRaceServer in [RC_MOONOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) or m_boAI then begin
    if hiter <> nil then begin
      if {IsMasterRange(m_nCurrX, m_nCurrY, 6) and }(not m_boTarget) then begin
        if (m_TargetCret = nil) or ((m_TargetCret <> nil) and (MINXY(m_TargetCret, hiter) = hiter)) or (Random(6) = 0) then begin
          //if (m_TargetCret = nil) or (m_btJob > 0) or ((m_TargetCret <> nil) and (GetTickCount() - m_dwTargetFocusTick > 1000 * 3)) then
          if IsProperTarget(hiter) then begin
            SetTargetCreat(hiter);

          end;
        end;
      end;
    end;
  end else begin
    if hiter <> nil then begin
      if (m_TargetCret = nil) or GetAttackDir(m_TargetCret, btDir) or (Random(6) = 0) then begin
        if IsProperTarget(hiter) then begin
          SetTargetCreat(hiter);
        end;
      end;
    end;
  end;
  if m_boAnimal then begin
    m_nMeatQuality := m_nMeatQuality - Random(300);
    if m_nMeatQuality < 0 then m_nMeatQuality := 0;
  end;
  m_dwHitTick := m_dwHitTick + LongWord(150 - _MIN(130, m_Abil.Level * 4));
end;

function TAIObject.IsGotoXY(X1, Y1, X2, Y2: Integer): Boolean;
var
  I, nStep: Integer;
  Path: TPath;
begin
  Result := False;
  if (not CanWalk(X1, Y1, X2, Y2, -1, nStep, m_btRaceServer <> RC_MOONOBJECT)) then begin
    g_FindPath.BaseObject := Self;
    Path := g_FindPath.FindPath(m_PEnvir, X1, Y1, X2, Y2, False, False);
    if Length(Path) <= 0 then Exit;
    Path := nil;
    Result := True;
  end else Result := True;
end;

function TAIObject.SearchMinDistance: TActorObject;
var
  ActorObject, ActorObject18: TActorObject;
  I, nC, n10: Integer;
begin
  Result := nil;
  ActorObject18 := nil;
  n10 := 9999;
  for I := 0 to m_VisibleActors.Count - 1 do begin
    ActorObject := TActorObject(pTVisibleBaseObject(m_VisibleActors.Items[I]).BaseObject);
    if (not ActorObject.m_boDeath) and (not ActorObject.m_boGhost) then begin
      if IsProperTarget(ActorObject) and
        (not ActorObject.m_boHideMode or m_boCoolEye) then begin
        nC := abs(m_nCurrX - ActorObject.m_nCurrX) + abs(m_nCurrY - ActorObject.m_nCurrY);
        if nC < n10 then begin
          n10 := nC;
          ActorObject18 := ActorObject;
        end;
      end;
    end;
  end;
  Result := ActorObject18;
end;

function TAIObject.Thinking: Boolean;
var
  nOldX, nOldY: Integer;
  nX, nY: Integer;
begin
  Result := False;
  if (m_Master <> nil) and (m_Master.m_boGhost) then Exit;

  if m_NewStatus <> sNone then begin
    DelTargetCreat;
    if (m_NewStatus = sConfusion) and (not m_boDeath) then begin //混乱状态
      if GetTickCount - m_dwConfusionTick > 400 then begin
        m_dwConfusionTick := GetTickCount;
        if Random(10) = 0 then begin
          nX := m_nNewStatusX + Random(2);
        end else begin
          nX := m_nNewStatusX - Random(2);
        end;
        if Random(10) = 0 then begin
          nY := m_nNewStatusY + Random(2);
        end else begin
          nY := m_nNewStatusY - Random(2);
        end;
        if m_PEnvir.CanWalkEx(nX, nY, False) then begin
          if (m_btRaceServer <> RC_MOONOBJECT) then begin
            if (abs(m_nNewStatusX - nX) <= 2) and (abs(m_nNewStatusY - nY) <= 2) then begin
              if (abs(m_nCurrX - nX) <= 1) and (abs(m_nCurrY - nY) <= 1) then
                WalkToNext(nX, nY)
              else RunToNext(nX, nY);
            end else begin
              WalkToNext(nX, nY);
            end;
          end;
        end;
      end;
    end;
    Result := True;
    Exit;
  end;

  if (m_btRaceServer = RC_HEROOBJECT) and m_Master.m_boNotOnlineAddExp and m_Master.InSafeZone and InSafeZone then begin
    if (abs(m_nCurrX - m_Master.m_nCurrX) <= 3) and (abs(m_nCurrY - m_Master.m_nCurrY) <= 3) then begin
      Result := True;
      Exit;
    end;
  end;

  if (GetTickCount - m_dwThinkTick) > 3 * 1000 then begin
    m_dwThinkTick := GetTickCount();
    if m_PEnvir.GetXYObjCount(m_nCurrX, m_nCurrY) >= 2 then m_boDupMode := True;
    if not IsProperTarget(m_TargetCret) then DelTargetCreat();
  end;
  if m_boDupMode then begin
    nOldX := m_nCurrX;
    nOldY := m_nCurrY;
    WalkTo(Random(8), False);
    if (nOldX <> m_nCurrX) or (nOldY <> m_nCurrY) then begin
      m_boDupMode := False;
      Result := True;
    end;
  end;
end;

{
================================================================================
================================================================================
================================================================================
}

function TAIObject.WalkToTargetXY(nTargetX, nTargetY: Integer): Boolean;
var
  I: Integer;
  nDir: Integer;
  n20: Integer;
  nOldX: Integer;
  nOldY: Integer;
begin
  Result := False;
  if (nTargetX <> m_nCurrX) or (nTargetY <> m_nCurrY) then begin
    nDir := GetNextDirection(m_nCurrX, m_nCurrY, nTargetX, nTargetY);
    nOldX := m_nCurrX;
    nOldY := m_nCurrY;
    Result := WalkTo(nDir, False);
    //if (nTargetX = m_nCurrX) and (nTargetY = m_nCurrY) then Result := True;
    if not Result then begin
      n20 := Random(3);
      for I := DR_UP to DR_UPLEFT do begin
        if (nOldX = m_nCurrX) and (nOldY = m_nCurrY) then begin
          if n20 <> 0 then Inc(nDir)
          else if nDir > 0 then Dec(nDir)
          else nDir := DR_UPLEFT;
          if (nDir > DR_UPLEFT) then nDir := DR_UP;
          Result := WalkTo(nDir, False);
          if Result then Break;
          {if (nTargetX = m_nCurrX) and (nTargetY = m_nCurrY) then begin
            Result := True;
            Break;
          end;}
        end;
      end;
    end;
  end else Result := True;
end;

function TAIObject.RunToTargetXY(nTargetX, nTargetY: Integer): Boolean;
begin
  Result := RunTo(GetNextDirection(m_nCurrX, m_nCurrY, nTargetX, nTargetY), False, nTargetX, nTargetY);
end;

function TAIObject.GetRangeTargetXY(nDir, nRange: Integer; var nTargetX, nTargetY: Integer): Boolean;
var
  I: Integer;
  nCurrX, nCurrY: Integer;
begin
  Result := True;
  nCurrX := nTargetX;
  nCurrY := nTargetY;
  for I := 1 to nRange do begin
    if m_PEnvir.GetNextPosition(nCurrX, nCurrY, nDir, 1, nCurrX, nCurrY) then begin
      if not m_PEnvir.CanWalk(nCurrX, nCurrY, True) or not m_PEnvir.CanWalkOfEvent(Self, nCurrX, nCurrY) then begin
        nCurrX := nTargetX;
        nCurrY := nTargetY;
        Result := False;
      end;
    end else begin
      nCurrX := nTargetX;
      nCurrY := nTargetY;
      Result := False;
      Break;
    end;
  end;
  nTargetX := nCurrX;
  nTargetY := nCurrY;
end;

function TAIObject.GetRangeTargetCountByDir(nDir, nX, nY, nRange: Integer): Integer;
var
  I: Integer;
  BaseObject: TActorObject;
  nCurrX, nCurrY: Integer;
begin
  Result := 0;
  nCurrX := nX;
  nCurrY := nY;
  for I := 1 to nRange do begin
    if m_PEnvir.GetNextPosition(nCurrX, nCurrY, nDir, 1, nCurrX, nCurrY) then begin
      BaseObject := TActorObject(m_PEnvir.GetMovingObject(nCurrX, nCurrY, True));
      if (BaseObject <> nil) and (not BaseObject.m_boDeath) and (not BaseObject.m_boGhost) and (not BaseObject.m_boHideMode or m_boCoolEye) and IsProperTarget(BaseObject) then begin
        //if (m_Master <> nil) and (not Master.IsProperTarget(BaseObject)) then Continue;
        Inc(Result);
      end;
    end;
  end;
end;

function TAIObject.GetNearTargetCount(nCurrX, nCurrY: Integer): Integer;
var
  nC, n10, I: Integer;
  nX, nY: Integer;
  BaseObject: TActorObject;
begin
  Result := 0;
  BaseObject := TActorObject(m_PEnvir.GetMovingObject(nCurrX, nCurrY, True));
  if (BaseObject <> nil) and (not BaseObject.m_boDeath) and (not BaseObject.m_boGhost) and IsProperTarget(BaseObject) then
    Inc(Result);

  for n10 := 0 to 7 do begin
    if m_PEnvir.GetNextPosition(nCurrX, nCurrY, n10, 1, nX, nY) then begin
      BaseObject := TActorObject(m_PEnvir.GetMovingObject(nX, nY, True));
      if (BaseObject <> nil) and (not BaseObject.m_boDeath) and (not BaseObject.m_boGhost) and IsProperTarget(BaseObject) then
        Inc(Result);
    end;
  end;
end;

function TAIObject.GetNearTargetCount(): Integer;
var
  nC, n10, I: Integer;
  nX, nY: Integer;
  BaseObject: TActorObject;
begin
  Result := 0;
  for n10 := 0 to 7 do begin
    if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, n10, 1, nX, nY) then begin
      BaseObject := TActorObject(m_PEnvir.GetMovingObject(nX, nY, True));
      if (BaseObject <> nil) and (not BaseObject.m_boDeath) and (not BaseObject.m_boGhost) and IsProperTarget(BaseObject) then begin
        Inc(Result);
      end;
    end;
  end;
end;

function TAIObject.GetRangeTargetCount(nX, nY, nRange: Integer): Integer;
var
  BaseObjectList: TList;
  BaseObject: TActorObject;
  I: Integer;
begin
  Result := 0;
  BaseObjectList := TList.Create;
  if GetMapActorObjects(m_PEnvir, nX, nY, nRange, BaseObjectList) then begin
    for I := BaseObjectList.Count - 1 downto 0 do begin
      BaseObject := TActorObject(BaseObjectList.Items[I]);
      if (BaseObject.m_boHideMode and not m_boCoolEye) or (not IsProperTarget(BaseObject)) then begin
        BaseObjectList.Delete(I);
      end;
    end;
    Result := BaseObjectList.Count;
  end;
  BaseObjectList.Free;
end;

function TAIObject.GetRangeFriendCount(nX, nY, nRange: Integer): Integer;
var
  BaseObjectList: TList;
  BaseObject: TActorObject;
  I: Integer;
begin
  Result := 0;
  BaseObjectList := TList.Create;
  if GetMapActorObjects(m_PEnvir, nX, nY, nRange, BaseObjectList) then begin
    for I := BaseObjectList.Count - 1 downto 0 do begin
      BaseObject := TActorObject(BaseObjectList.Items[I]);
      if (BaseObject.m_boHideMode and not m_boCoolEye) or (not IsProperFriend(BaseObject)) then begin
        BaseObjectList.Delete(I);
      end;
    end;
    Result := BaseObjectList.Count;
  end;
  BaseObjectList.Free;
end;

{检测自己与 X Y 坐标范围之间的怪物数量}

function TAIObject.GetTargetXYCount(nX, nY, nRange: Integer): Integer;
var
  BaseObject: TActorObject;
  I, nDir: Integer;
  nCurrX, nCurrY: Integer;
begin
  Result := 0;
  nDir := GetNextDirection(m_nCurrX, m_nCurrY, nX, nY);
  nCurrX := m_nCurrX;
  nCurrY := m_nCurrY;
  while True do begin
    if (nCurrX = nX) and (nCurrY = nY) then Break;
    if GetNextDirection(nCurrX, nCurrY, nX, nY) <> nDir then break;
    if m_PEnvir.GetNextPosition(nCurrX, nCurrY, nDir, 1, nCurrX, nCurrY) then begin
      BaseObject := TActorObject(m_PEnvir.GetMovingObject(nCurrX, nCurrY, True));
      if (BaseObject <> nil) and (not BaseObject.m_boDeath) and (not BaseObject.m_boGhost) and (not BaseObject.m_boHideMode or m_boCoolEye) and IsProperTarget(BaseObject) then begin
        //if (m_Master <> nil) and (not Master.IsProperTarget(BaseObject)) then Continue;
        Inc(Result);
      end;
    end else Break;
  end;
end;

function TAIObject.CanWalk(nCurrX, nCurrY, nTargetX, nTargetY: Integer; nDir: Integer; var nStep: Integer; boFlag: Boolean): Boolean;
var
  I: Integer;
  btDir: Byte;
  nX, nY, nCount: Integer;
begin
  Result := False;
  nStep := 0;
  nCount := 0;
  if (nDir >= 0) and (nDir <= 7) then btDir := nDir
  else btDir := GetNextDirection(nCurrX, nCurrY, nTargetX, nTargetY);
  {nX := nCurrX;
  nY := nCurrY;}
  if boFlag then begin
    if (abs(nCurrX - nTargetX) <= 1) and (abs(nCurrY - nTargetY) <= 1) then begin
      if m_PEnvir.GetNextPosition(nCurrX, nCurrY, btDir, 1, nX, nY) and (nX = nTargetX) and (nY = nTargetY) then begin
        nStep := 1;
        Result := True;
      end;
    end else begin
      if m_PEnvir.GetNextPosition(nCurrX, nCurrY, btDir, 2, nX, nY) and (nX = nTargetX) and (nY = nTargetY) then begin
        nStep := 1;
        Result := True;
      end;
    end;
  end else begin
    if m_PEnvir.GetNextPosition(nCurrX, nCurrY, btDir, 1, nX, nY) and (nX = nTargetX) and (nY = nTargetY) then begin
      nStep := nStep + 1;
      Result := True;
      Exit;
    end else Exit;
    if m_PEnvir.GetNextPosition(nX, nY, btDir, 1, nX, nY) and (nX = nTargetX) and (nY = nTargetY) then begin
      nStep := nStep + 1;
      Result := True;
      Exit;
    end;
  end;
  {while True do begin
    if (nX = nTargetX) and (nY = nTargetY) then Break;
    if GetNextDirection(nX, nY, nTargetX, nTargetY) <> btDir then Break;
    if m_PEnvir.GetNextPosition(nX, nY, btDir, 1, nX, nY) then begin
      if (not m_PEnvir.CanWalkEx(nX, nY, boFlag)) or (not m_PEnvir.CanWalkOfEvent(Self, nX, nY)) then begin
        Result := False;
        Break;
      end;
      Inc(nStep);
    end else begin
      Result := False;
      Break;
    end;
    Inc(nCount);
  end;}
end;

function TAIObject.GetMasterRange(nTargetX, nTargetY: Integer): Integer;
var
  nCurrX, nCurrY: Integer;
begin
  Result := 0;
  if m_boAutoAttack then Exit;
  if (m_Master <> nil) then begin
    if (m_btRaceServer = RC_HEROOBJECT) and (THeroObject(Self).m_boProtectStatus) then begin
      nCurrX := THeroObject(Self).m_nProtectTargetX;
      nCurrY := THeroObject(Self).m_nProtectTargetY;
    end else begin
      nCurrX := m_Master.m_nCurrX;
      nCurrY := m_Master.m_nCurrY;
    end;
    Result := abs(nCurrX - nTargetX) + abs(nCurrY - nTargetY);
  end;
end;

function TAIObject.IsMasterRange(nTargetX, nTargetY, nRange: Integer): Boolean; //判断是否超过主人6格范围
var
  nCurrX, nCurrY: Integer;
begin
  Result := True;
  if m_boAutoAttack then Exit;
  if (m_Master <> nil) then begin
    if (m_btRaceServer = RC_HEROOBJECT) and (THeroObject(Self).m_boProtectStatus) then begin
      nCurrX := THeroObject(Self).m_nProtectTargetX;
      nCurrY := THeroObject(Self).m_nProtectTargetY;
    end else begin
      nCurrX := m_Master.m_nCurrX;
      nCurrY := m_Master.m_nCurrY;
    end;
    Result := not ((abs(nCurrX - nTargetX) > nRange) or (abs(nCurrY - nTargetY) > nRange));
  end;
end;

function TAIObject.FindObjectOfRange(AObject: TActorObject; nRange: Integer): TActorObject;
var
  III: Integer;
  I, II: Integer;
  nStartX, nStartY, nEndX, nEndY: Integer;
  MapCellInfo: pTMapCellinfo;
  BaseObject: TBaseObject;
  ActorObject: TActorObject;
  bo06: Boolean;
begin
  Result := nil;
  nStartX := m_nCurrX - nRange;
  nEndX := m_nCurrX + nRange;
  nStartY := m_nCurrY - nRange;
  nEndY := m_nCurrY + nRange;
  for I := nStartX to nEndX do begin
    for II := nStartY to nEndY do begin
      if m_PEnvir.GetMapCellInfo(I, II, MapCellInfo) and (MapCellInfo.ObjList <> nil) then begin
        for III := 0 to MapCellInfo.ObjList.Count - 1 do begin
          BaseObject := TBaseObject(MapCellInfo.ObjList.Items[III]);
          if BaseObject.m_ObjType = t_Actor then begin
            ActorObject := TActorObject(MapCellInfo.ObjList.Items[III]);
            if (ActorObject <> nil) and (not ActorObject.m_boGhost) and (not ActorObject.m_boFixedHideMode) then begin
              if (ActorObject = AObject) and IsProperTarget(ActorObject) then begin
                Result := ActorObject;
                Exit;
              end;
            end;
          end;
        end;
      end;
    end;
  end;
end;

function TAIObject.GetStep(nCurrX, nCurrY, nTargetX, nTargetY: Integer): Integer;
var
  I: Integer;
  btDir: Byte;
  nX, nY: Integer;
  BaseObject: TBaseObject;
begin
  Result := 0;
  nX := nCurrX;
  nY := nCurrY;
  btDir := GetNextDirection(nX, nY, nTargetX, nTargetY);
  while True do begin
    if (nTargetX = nX) and (nTargetY = nY) then begin
      Break;
    end;
    btDir := GetNextDirection(nX, nY, nTargetX, nTargetY);
    if not m_PEnvir.GetNextPosition(nX, nY, btDir, 1, nX, nY) then Break;
    if not m_PEnvir.CanWalkEx(nX, nY, True) then Break;
    Inc(Result);
  end;
end;

function TAIObject.CanLineAttack(nCurrX, nCurrY: Integer): Boolean; //目标是否和自己在一条线上，用来检测直线攻击的魔法是否可以攻击到目标
var
  I: Integer;
  btDir: Byte;
  nX, nY: Integer;
begin
  Result := False;
  nX := nCurrX;
  nY := nCurrY;
  btDir := GetNextDirection(nCurrX, nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);
  while True do begin
    if (m_TargetCret.m_nCurrX = nX) and (m_TargetCret.m_nCurrY = nY) then begin
      Result := True;
      Break;
    end;
    btDir := GetNextDirection(nX, nY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);
    if not m_PEnvir.GetNextPosition(nX, nY, btDir, 1, nX, nY) then Break;
    if not m_PEnvir.CanWalkEx(nX, nY, True) then Break;
  end;
end;

function TAIObject.CanLineAttack(nStep: Integer): Boolean;
var
  I: Integer;
  btDir: Byte;
  nX, nY: Integer;
begin
  Result := False;
  nX := m_nCurrX;
  nY := m_nCurrY;
  btDir := GetNextDirection(nX, nY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);
  for I := 1 to nStep do begin
    if (m_TargetCret.m_nCurrX = nX) and (m_TargetCret.m_nCurrY = nY) then begin
      Result := True;
      Break;
    end;
    btDir := GetNextDirection(nX, nY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);
    if not m_PEnvir.GetNextPosition(nX, nY, btDir, 1, nX, nY) then Break;
    if not m_PEnvir.CanWalkEx(nX, nY, True) then Break;
  end;
end;

function TAIObject.CanAttack(nCurrX, nCurrY: Integer; BaseObject: TActorObject; nRange: Integer; var btDir: Byte): Boolean;
var
  I: Integer;
  nX, nY: Integer;
begin
  Result := False;
  btDir := GetNextDirection(nCurrX, nCurrY, BaseObject.m_nCurrX, BaseObject.m_nCurrY);
  for I := 1 to nRange do begin
    if not m_PEnvir.GetNextPosition(nCurrX, nCurrY, btDir, I, nX, nY) then Break;
    if (BaseObject.m_nCurrX = nX) and (BaseObject.m_nCurrY = nY) then begin
      Result := True;
      Break;
    end;
  end;
end;

function TAIObject.CanAttack(BaseObject: TActorObject; nRange: Integer; var btDir: Byte): Boolean;
var
  I: Integer;
  nX, nY: Integer;
begin
  Result := False;
  btDir := GetNextDirection(m_nCurrX, m_nCurrY, BaseObject.m_nCurrX, BaseObject.m_nCurrY);
  for I := 1 to nRange do begin
    if not m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, btDir, I, nX, nY) then Break;
    if (BaseObject.m_nCurrX = nX) and (BaseObject.m_nCurrY = nY) then begin
      Result := True;
      Break;
    end;
  end;
end;

function TAIObject.DoThink(wMagicID: Word): Integer;
  function TargetNeedRunPos(): Boolean;
  begin
    Result := (m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_MOONOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT)
      or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER) { or (not InMasterRange(8))};
  end;

  function CanRunPos(nAttackCount: Integer): Boolean;
  begin
    Result := (m_RunPos.nAttackCount >= nAttackCount) {and (m_RunPos.nCurrX = m_nCurrX) and (m_RunPos.nCurrY = m_nCurrY)};
  end;

  function MotaeboPos(): Boolean; //获取野蛮冲撞
  var
    nTargetX, nTargetY: Integer;
    btNewDir: Byte;
  begin
    Result := False;
    if (wMagicID = 27) and (not m_boAutoAttack) and (m_Master <> nil) and (m_TargetCret <> nil) and AllowUseMagic(27) and (m_TargetCret.m_Abil.Level < m_Abil.Level) { and (GetPoseCreate = m_TargetCret)} and (GetTickCount - m_SkillUseTick[27] > 1000 * 10) then begin
      //if (m_TargetCret = nil) or (m_Master = nil) then Exit;
      btNewDir := GetNextDirection(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, m_Master.m_nCurrX, m_Master.m_nCurrY);
      if m_PEnvir.GetNextPosition(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, GetBackDir(btNewDir), 1, nTargetX, nTargetY) then begin
        //if m_PEnvir.CanWalk(nTargetX, nTargetY, True) then begin
        Result := m_PEnvir.CanWalk(nTargetX, nTargetY, True) and m_PEnvir.CanWalkOfEvent(Self, nTargetX, nTargetY);
        //if Result then self.m_btDirection:=btNewDir;
        //end;
      end;
    end;
  end;

  function MagPushArround(MagicID: Integer): Boolean;
  var
    I: Integer;
    ActorObject: TActorObject;
    BaseObject: TBaseObject;
    btNewDir: Byte;
    nTargetX, nTargetY: Integer;
  begin
    Result := False;
    if (m_TargetCret <> nil) and (m_Abil.Level > m_TargetCret.m_Abil.Level) and
      (abs(m_nCurrX - m_TargetCret.m_nCurrX) <= 1) and (abs(m_nCurrY - m_TargetCret.m_nCurrY) <= 1) then begin
      btNewDir := GetNextDirection(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, m_nCurrX, m_nCurrY);
      if m_PEnvir.GetNextPosition(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, GetBackDir(btNewDir), 1, nTargetX, nTargetY) then begin
        Result := m_PEnvir.CanWalk(nTargetX, nTargetY, True) and m_PEnvir.CanWalkOfEvent(Self, nTargetX, nTargetY);
      end;
      if Result then Exit;
    end;
    if wMagicID = MagicID then begin
      for I := 0 to m_VisibleActors.Count - 1 do begin
        ActorObject := TActorObject(pTVisibleBaseObject(m_VisibleActors.Items[I]).BaseObject);
        if (abs(m_nCurrX - ActorObject.m_nCurrX) <= 1) and (abs(m_nCurrY - ActorObject.m_nCurrY) <= 1) then begin
          if (not ActorObject.m_boDeath) and (ActorObject <> Self) and IsProperTarget(ActorObject) then begin
            if (m_Abil.Level > ActorObject.m_Abil.Level) and (not ActorObject.m_boStickMode) then begin
              btNewDir := GetNextDirection(ActorObject.m_nCurrX, ActorObject.m_nCurrY, m_nCurrX, m_nCurrY);
              if m_PEnvir.GetNextPosition(ActorObject.m_nCurrX, ActorObject.m_nCurrY, GetBackDir(btNewDir), 1, nTargetX, nTargetY) then begin
                if m_PEnvir.CanWalk(nTargetX, nTargetY, True) and m_PEnvir.CanWalkOfEvent(Self, nTargetX, nTargetY) then begin
                  Result := True;
                  Break;
                end;
              end;
            end;
          end;
        end;
      end;
    end;
  end;

var
  I: Integer;
  btDir: Byte;
  nStep: Integer;
  nRange: Integer;
begin
  Result := -1;

  case m_btJob of
    0: begin
         //1=野蛮冲撞 2=无法攻击到目标需要移动 3=走位
        if MotaeboPos then begin
          Result := 1;
        end else begin
          nRange := 1;
          if (wMagicID = 58) or (wMagicID = 77) then nRange := 4;
          if (wMagicID = 12) then nRange := 2;
          //if (wMagicID in [60]) then nRange := 6;
          //if (wMagicID in [60, 61, 62]) then nRange := 12;
          if (wMagicID in [60]) then nRange := 6;

          Result := 2;
          if (wMagicID in [61, 62]) or CanAttack(m_TargetCret, nRange, btDir) then begin
            Result := 0;
          end;

          if (Result = 0) and (not (wMagicID in [60, 61, 62])) then begin
            if TargetNeedRunPos then begin
              if CanRunPos(5) then
                Result := 5;
            end else begin
              if CanRunPos(20) then
                Result := 5;
            end;
          end;
        end;
      end;
    1: begin
        if (wMagicID = 8) and MagPushArround(wMagicID) then Exit;
        //1=躲避 2=追击 3=魔法直线攻击不到目标 4=无法攻击到目标需要移动 5=走位
        if IsUseAttackMagic then begin
          if GetNearTargetCount > 0 then begin
            Result := 1;
          end else
            if ((abs(m_TargetCret.m_nCurrX - m_nCurrX) > 6) or (abs(m_TargetCret.m_nCurrY - m_nCurrY) > 6)) then begin
            Result := 2;
          end else
            //if (wMagicID in [SKILL_FIREBALL, SKILL_FIREBALL2]) and (not CanWalk(m_nCurrX, m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, -1, nStep, True)) then begin
            if ((wMagicID in [SKILL_FIREBALL, SKILL_GREATFIREBALL]) or (m_btRaceServer = RC_MOONOBJECT)) and (not CanAttack(m_TargetCret, 10, btDir)) then begin
            Result := 3;
          end else
            if {((GetTickCount - m_dwStationTick) > 1000 * 10) and}  TargetNeedRunPos and CanRunPos(5) then begin
            Result := 5;
          end;
        end else begin
          if (not GetAttackDir(m_TargetCret, 1, btDir)) then begin
            Result := 4;
          end;
        end;
        //end;
      end;
    2: begin
        if (wMagicID = 48) and MagPushArround(wMagicID) then Exit;
        if IsUseAttackMagic then begin
          if GetNearTargetCount > 0 then begin
            Result := 1;
          end else
            if ((abs(m_TargetCret.m_nCurrX - m_nCurrX) > 6) or (abs(m_TargetCret.m_nCurrY - m_nCurrY) > 6)) then begin
            Result := 2;
          end else
            //if (wMagicID = SKILL_FIRECHARM) and (not CanWalk(m_nCurrX, m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, -1, nStep, True)) then begin
            if (wMagicID = SKILL_SOULFIREBALL) and (not CanAttack(m_TargetCret, 10, btDir)) then begin
            Result := 3;
          end else
            if {((GetTickCount - m_dwStationTick) > 1000 * 10) and}  TargetNeedRunPos and CanRunPos(5) then begin
            Result := 5;
          end;
        end else begin
          if (not GetAttackDir(m_TargetCret, 1, btDir)) then begin
            Result := 4;
          end;
        end;
      end;
  end;
end;

function TAIObject.ActThink(wMagicID: Word): Boolean;
  function FindGoodPathA(WalkStep: TWalkStep; nRange, nType: Integer): TMapWalkXY;
  var
    I: Integer;
    n10: Integer;
    n1C: Integer;
    nMastrRange: Integer;
    nMonCount: Integer;
    MapWalkXY, MapWalkXYA: pTMapWalkXY;
  begin
    n10 := High(Integer);
    MapWalkXY := nil;
    FillChar(Result, SizeOf(TMapWalkXY), 0);

    for I := DR_UP to DR_UPLEFT do begin
      if (WalkStep[I].nWalkStep > 0) and
        (abs(WalkStep[I].nX - m_TargetCret.m_nCurrX) >= nRange) and (abs(WalkStep[I].nY - m_TargetCret.m_nCurrY) >= nRange) then begin
        if (WalkStep[I].nMonCount < n10) then begin
          n10 := WalkStep[I].nMonCount;
          MapWalkXY := @WalkStep[I];
        end;
      end;
    end;

    if (MapWalkXY <> nil) and (m_Master <> nil) then begin
      nMonCount := MapWalkXY.nMonCount;
      nMastrRange := MapWalkXY.nMastrRange;
      n10 := High(Integer);
      MapWalkXYA := MapWalkXY;
      MapWalkXY := nil;
      for I := DR_UP to DR_UPLEFT do begin
        if (WalkStep[I].nWalkStep > 0) and (WalkStep[I].nMonCount <= nMonCount) and (abs(WalkStep[I].nX - m_TargetCret.m_nCurrX) >= nRange) and (abs(WalkStep[I].nY - m_TargetCret.m_nCurrY) >= nRange) then begin
          if (WalkStep[I].nMastrRange < n10) and (WalkStep[I].nMastrRange < nMastrRange) then begin
            n10 := WalkStep[I].nMastrRange;
            MapWalkXY := @WalkStep[I];
          end;
        end;
      end;
      if MapWalkXY = nil then MapWalkXY := MapWalkXYA;
    end;

    {if nType <> 0 then begin
      if MapWalkXY <> nil then begin
        nMonCount := MapWalkXY.nMonCount;
        n10 := High(Integer);
        MapWalkXY := nil;
        for I := DR_UP to DR_UPLEFT do begin
          if (WalkStep[I].nWalkStep > 0) and (WalkStep[I].nMonCount <= nMonCount) and

          (abs(WalkStep[I].nX - m_TargetCret.m_nCurrX) >= nRange) and
            (abs(WalkStep[I].nY - m_TargetCret.m_nCurrY) >= nRange) then begin
            if (WalkStep[I].nMastrRange < n10) then begin
              n10 := WalkStep[I].nMastrRange;
              MapWalkXY := @WalkStep[I];
            end;
          end;
        end;
      end;
    end; }
    if MapWalkXY <> nil then Result := MapWalkXY^;
  end;

  function FindGoodPathB(WalkStep: TWalkStep; nType: Integer): TMapWalkXY;
  var
    I: Integer;
    n10: Integer;
    n1C: Integer;
    nMastrRange: Integer;
    nMonCount: Integer;
    MapWalkXY, MapWalkXYA: pTMapWalkXY;
  begin
    n10 := High(Integer);
    MapWalkXY := nil;
    FillChar(Result, SizeOf(TMapWalkXY), 0);

    for I := DR_UP to DR_UPLEFT do begin
      if (WalkStep[I].nWalkStep > 0) then begin
        if (WalkStep[I].nMonCount < n10) then begin
          n10 := WalkStep[I].nMonCount;
          MapWalkXY := @WalkStep[I];
        end;
      end;
    end;

    if (MapWalkXY <> nil) and (m_Master <> nil) then begin
      nMonCount := MapWalkXY.nMonCount;
      nMastrRange := MapWalkXY.nMastrRange;
      n10 := High(Integer);
      MapWalkXYA := MapWalkXY;
      MapWalkXY := nil;
      for I := DR_UP to DR_UPLEFT do begin
        if (WalkStep[I].nWalkStep > 0) and (WalkStep[I].nMonCount <= nMonCount) then begin
          if (WalkStep[I].nMastrRange < n10) and (WalkStep[I].nMastrRange < nMastrRange) then begin
            n10 := WalkStep[I].nMastrRange;
            MapWalkXY := @WalkStep[I];
          end;
        end;
      end;
      if MapWalkXY = nil then MapWalkXY := MapWalkXYA;
    end;


    {if nType <> 0 then begin
      if MapWalkXY <> nil then begin
        nMonCount := MapWalkXY.nMonCount;
        n10 := High(Integer);
        MapWalkXY := nil;
        for I := DR_UP to DR_UPLEFT do begin
          if (WalkStep[I].nWalkStep > 0) and (WalkStep[I].nMonCount <= nMonCount) then begin
            if (WalkStep[I].nMastrRange < n10) then begin
              n10 := WalkStep[I].nMastrRange;
              MapWalkXY := @WalkStep[I];
            end;
          end;
        end;
      end;
    end;}

    if MapWalkXY <> nil then Result := MapWalkXY^;
  end;

  function FindMinRange(WalkStep: TWalkStep): TMapWalkXY;
  var
    I: Integer;
    n10: Integer;
    n1C: Integer;
    n2C: Integer;
    nMonCount: Integer;
    MapWalkXY, MapWalkXYA: pTMapWalkXY;
  begin
    n10 := High(Integer);
    n1C := 0;
    MapWalkXY := nil;
    FillChar(Result, SizeOf(TMapWalkXY), 0);

    for I := DR_UP to DR_UPLEFT do begin
      if (WalkStep[I].nWalkStep > 0) then begin
        n1C := abs(WalkStep[I].nX - m_TargetCret.m_nCurrX) + abs(WalkStep[I].nY - m_TargetCret.m_nCurrY);
        if (n1C < n10) then begin
          n10 := n1C;
          MapWalkXY := @WalkStep[I];
        end;
      end;
    end;

    if MapWalkXY <> nil then begin
      nMonCount := MapWalkXY.nMonCount;
      MapWalkXYA := MapWalkXY;
      MapWalkXY := nil;
      for I := DR_UP to DR_UPLEFT do begin
        if (WalkStep[I].nWalkStep > 0) and (WalkStep[I].nMonCount <= nMonCount) then begin
          n1C := abs(WalkStep[I].nX - m_TargetCret.m_nCurrX) + abs(WalkStep[I].nY - m_TargetCret.m_nCurrY);
          if (n1C <= n10) then begin
            n10 := n1C;
            MapWalkXY := @WalkStep[I];
          end;
        end;
      end;
      if MapWalkXY = nil then MapWalkXY := MapWalkXYA;
    end;

    if MapWalkXY <> nil then Result := MapWalkXY^;
  end;

  function CanWalkNextPosition(nX, nY, nRange: Integer; btDir: Byte; boFlag: Boolean): Boolean; //检测下一步在不在攻击位
  var
    nCurrX: Integer;
    nCurrY: Integer;
  begin
    Result := False;
    if m_PEnvir.GetNextPosition(nX, nY, btDir, 1, nCurrX, nCurrY) and
      CanMove(nX, nY, nCurrX, nCurrY, False) and
      (not boFlag) or CanAttack(nCurrX, nCurrY, m_TargetCret, nRange, btDir) then begin
      Result := True;
      Exit;
    end;

    if m_PEnvir.GetNextPosition(nX, nY, btDir, 2, nCurrX, nCurrY) and
      CanMove(nX, nY, nCurrX, nCurrY, False) and
      (not boFlag) or CanAttack(nCurrX, nCurrY, m_TargetCret, nRange, btDir) then begin
      Result := True;
      Exit;
    end;
  end;

  function FindPosOfSelf(WalkStep: pTWalkStep; nRange: Integer; boFlag: Boolean): Boolean;
  var
    I: Integer;
    btDir: Byte;

    nCurrX: Integer;
    nCurrY: Integer;
  begin
    Result := False;

    FillChar(WalkStep^, SizeOf(TMapWalkXY) * 8, 0);
    for I := DR_UP to DR_UPLEFT do begin
      if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, I, nRange, nCurrX, nCurrY) and CanMove(nCurrX, nCurrY, False) then begin
        if (not boFlag) or CanAttack(nCurrX, nCurrY, m_TargetCret, nRange, btDir) then begin
          WalkStep[I].nWalkStep := nRange;
          WalkStep[I].nX := nCurrX;
          WalkStep[I].nY := nCurrY;
          WalkStep[I].nMonRange := abs(nCurrX - m_TargetCret.m_nCurrX) + abs(nCurrY - m_TargetCret.m_nCurrY);
          WalkStep[I].nMonCount := GetNearTargetCount(nCurrX, nCurrY);
          WalkStep[I].nMastrRange := GetMasterRange(nCurrX, nCurrY);
          Result := True;
        end;
      end;
    end;
  end;

  function _FindPosOfSelf(WalkStep: pTWalkStep; nRange: Integer; boFlag: Boolean): Boolean;
  var
    I: Integer;
    btDir: Byte;

    nCurrX: Integer;
    nCurrY: Integer;
  begin
    Result := False;

    FillChar(WalkStep^, SizeOf(TMapWalkXY) * 8, 0);
    for I := DR_UP to DR_UPLEFT do begin
      if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, I, nRange, nCurrX, nCurrY) and CanMove(nCurrX, nCurrY, False) then begin
        if (not boFlag) or CanAttack(nCurrX, nCurrY, m_TargetCret, nRange, btDir) or CanWalkNextPosition(nCurrX, nCurrY, nRange, I, boFlag) then begin
          WalkStep[I].nWalkStep := nRange;
          WalkStep[I].nX := nCurrX;
          WalkStep[I].nY := nCurrY;
          WalkStep[I].nMonRange := abs(nCurrX - m_TargetCret.m_nCurrX) + abs(nCurrY - m_TargetCret.m_nCurrY);
          WalkStep[I].nMonCount := GetNearTargetCount(nCurrX, nCurrY);
          WalkStep[I].nMastrRange := GetMasterRange(nCurrX, nCurrY);
          Result := True;
        end;
      end;
    end;
  end;

  function FindPosOfTarget(WalkStep: pTWalkStep; nTargetX, nTargetY, nRange: Integer; boFlag: Boolean): Boolean;
  var
    I: Integer;
    btDir: Byte;
    nCurrX: Integer;
    nCurrY: Integer;
  begin
    Result := False;
    FillChar(WalkStep^, SizeOf(TMapWalkXY) * 8, 0);
    for I := DR_UP to DR_UPLEFT do begin
      if m_PEnvir.GetNextPosition(nTargetX, nTargetY, I, nRange, nCurrX, nCurrY) and
        m_PEnvir.CanWalkEx(nCurrX, nCurrY, False) and
        m_PEnvir.CanWalkOfEvent(Self, nCurrX, nCurrY) then begin
        if ((not boFlag) or CanAttack(nCurrX, nCurrY, m_TargetCret, nRange, btDir)) and IsGotoXY(m_nCurrX, m_nCurrY, nCurrX, nCurrY) then begin
          WalkStep[I].nWalkStep := nRange;
          WalkStep[I].nX := nCurrX;
          WalkStep[I].nY := nCurrY;
          WalkStep[I].nMonRange := abs(nCurrX - nTargetX) + abs(nCurrY - nTargetY);
          WalkStep[I].nMastrRange := GetMasterRange(nCurrX, nCurrY);
          WalkStep[I].nMonCount := GetRangeTargetCount(nCurrX, nCurrY, 2); //GetNearTargetCount(nCurrX, nCurrY);
          Result := True;
        end;
      end;
    end;
  end;

  function FindPos(WalkStep: pTWalkStep; nRange: Integer; boFlag: Boolean): Boolean;
  var
    I: Integer;
    btDir: Byte;

    nCurrX: Integer;
    nCurrY: Integer;
  begin
    Result := False;

    FillChar(WalkStep^, SizeOf(TMapWalkXY) * 8, 0);
    for I := DR_UP to DR_UPLEFT do begin
      if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, I, 2, nCurrX, nCurrY) and CanMove(nCurrX, nCurrY, False) then begin
        if (not boFlag) or CanAttack(nCurrX, nCurrY, m_TargetCret, nRange, btDir) then begin
          WalkStep[I].nWalkStep := nRange;
          WalkStep[I].nX := nCurrX;
          WalkStep[I].nY := nCurrY;
          WalkStep[I].nMonRange := abs(nCurrX - m_TargetCret.m_nCurrX) + abs(nCurrY - m_TargetCret.m_nCurrY);
          WalkStep[I].nMonCount := GetNearTargetCount(nCurrX, nCurrY);
          WalkStep[I].nMastrRange := GetMasterRange(nCurrX, nCurrY);
          Result := True;
        end;
      end;
    end;
    if Result then Exit;

    FillChar(WalkStep^, SizeOf(TMapWalkXY) * 8, 0);
    for I := DR_UP to DR_UPLEFT do begin
      if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, I, 1, nCurrX, nCurrY) and CanMove(nCurrX, nCurrY, False) then begin
        if (not boFlag) or CanAttack(nCurrX, nCurrY, m_TargetCret, nRange, btDir) then begin
          WalkStep[I].nWalkStep := nRange;
          WalkStep[I].nX := nCurrX;
          WalkStep[I].nY := nCurrY;
          WalkStep[I].nMonRange := abs(nCurrX - m_TargetCret.m_nCurrX) + abs(nCurrY - m_TargetCret.m_nCurrY);
          WalkStep[I].nMonCount := GetNearTargetCount(nCurrX, nCurrY);
          WalkStep[I].nMastrRange := GetMasterRange(nCurrX, nCurrY);
          Result := True;
        end;
      end;
    end;
  end;

  function _FindPos(WalkStep: pTWalkStep; nRange: Integer; boFlag: Boolean): Boolean;
  var
    I: Integer;
    btDir: Byte;

    nCurrX: Integer;
    nCurrY: Integer;
  begin
    Result := False;

    FillChar(WalkStep^, SizeOf(TMapWalkXY) * 8, 0);
    for I := DR_UP to DR_UPLEFT do begin
      if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, I, 1, nCurrX, nCurrY) and CanMove(nCurrX, nCurrY, False) then begin
        if (not boFlag) or CanAttack(nCurrX, nCurrY, m_TargetCret, nRange, btDir) or CanWalkNextPosition(nCurrX, nCurrY, nRange, I, boFlag) then begin
          WalkStep[I].nWalkStep := nRange;
          WalkStep[I].nX := nCurrX;
          WalkStep[I].nY := nCurrY;
          WalkStep[I].nMonRange := abs(nCurrX - m_TargetCret.m_nCurrX) + abs(nCurrY - m_TargetCret.m_nCurrY);
          WalkStep[I].nMonCount := GetNearTargetCount(nCurrX, nCurrY);
          WalkStep[I].nMastrRange := GetMasterRange(nCurrX, nCurrY);
          Result := True;
        end;
      end;
    end;
    if Result then Exit;

    FillChar(WalkStep^, SizeOf(TMapWalkXY) * 8, 0);
    for I := DR_UP to DR_UPLEFT do begin
      if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, I, 2, nCurrX, nCurrY) and
        CanMove(nCurrX, nCurrY, False) then begin
        if (not boFlag) or CanAttack(nCurrX, nCurrY, m_TargetCret, nRange, btDir) or CanWalkNextPosition(nCurrX, nCurrY, nRange, I, boFlag) then begin
          WalkStep[I].nWalkStep := nRange;
          WalkStep[I].nX := nCurrX;
          WalkStep[I].nY := nCurrY;
          WalkStep[I].nMonRange := abs(nCurrX - m_TargetCret.m_nCurrX) + abs(nCurrY - m_TargetCret.m_nCurrY);
          WalkStep[I].nMonCount := GetNearTargetCount(nCurrX, nCurrY);
          WalkStep[I].nMastrRange := GetMasterRange(nCurrX, nCurrY);
          Result := True;
        end;
      end;
    end;
  end;

  function WalkToRightPos(): Boolean;
  var
    I: Integer;
    boFlag: Boolean;
    nRange: Integer;
    btDir: Byte;
    WalkStep: TWalkStep;
    MapWalkXY: TMapWalkXY;
    nError: Integer;
  begin
    Result := False;
    try
      nError := 0;
      boFlag := (m_btRaceServer = RC_MOONOBJECT) or (wMagicID in [SKILL_FIREBALL, SKILL_GREATFIREBALL, SKILL_SOULFIREBALL]) or (m_btJob = 0);
      if (m_btJob = 0) or (wMagicID <= 0) then begin
        nRange := 1;
        if (wMagicID = 58) or (wMagicID = 77) then nRange := 4;
        if (wMagicID = 12) then nRange := 2;
        if (wMagicID in [60, 61, 62]) then nRange := 6;
        nError := 1;
     { if not (m_TargetCret.m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) then begin
        nError := 2;
        if FindPos(@WalkStep, nRange, boFlag) then begin
          nError := 3;
          if m_Master <> nil then begin
            MapWalkXY := FindGoodPathB(WalkStep, 1);
          end else begin
            MapWalkXY := FindGoodPathB(WalkStep, 0);
          end;
          nError := 4;
          if (MapWalkXY.nWalkStep > 0) then begin
            nError := 5;
            if GotoNext(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
              nError := 6;
              m_RunPos.btDirection := 0;
              Result := True;
              Exit;
            end;
          end;
        end;

        nError := 7;
        if _FindPos(@WalkStep, nRange, boFlag) then begin
          nError := 8;
          MapWalkXY := FindMinRange(WalkStep);
          nError := 9;
          if (MapWalkXY.nWalkStep > 0) then begin
            nError := 10;
            if GotoNext(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
              nError := 11;
              m_RunPos.btDirection := 0;
              Result := True;
              Exit;
            end;
          end;
        end;
      end;

      nError := 12;
      for I := 2 downto 1 do begin
        nError := 13;
        if FindPosOfSelf(@WalkStep, I, boFlag) then begin
          nError := 14;
          if m_Master <> nil then begin
            MapWalkXY := FindGoodPathB(WalkStep, 1);
          end else begin
            MapWalkXY := FindGoodPathB(WalkStep, 0);
          end;
          nError := 15;
          if (MapWalkXY.nWalkStep > 0) then begin
            nError := 16;
            if GotoNext(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
              nError := 17;
              m_RunPos.btDirection := 0;
              Result := True;
              Exit;
            end;
          end;
        end;
      end;

      nError := 18;
      for I := 2 downto 1 do begin
        nError := 19;
        if _FindPosOfSelf(@WalkStep, I, boFlag) then begin
          nError := 20;
          MapWalkXY := FindMinRange(WalkStep);
          nError := 21;
          if (MapWalkXY.nWalkStep > 0) then begin
            nError := 22;
            if GotoNext(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
              nError := 23;
              m_RunPos.btDirection := 0;
              Result := True;
              Exit;
            end;
          end;
        end;
      end;}

        nError := 24;
        for I := nRange downto 1 do begin
          nError := 25;
          if FindPosOfTarget(@WalkStep, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, I, boFlag) then begin
            nError := 26;
          {if m_Master <> nil then begin
            MapWalkXY := FindGoodPathB(WalkStep, 1);
          end else begin
            MapWalkXY := FindGoodPathB(WalkStep, 0);
          end;}
            MapWalkXY := FindGoodPathB(WalkStep, 0);
            nError := 27;
            if (MapWalkXY.nWalkStep > 0) then begin
              nError := 28;
            //if m_btRaceServer <> RC_MOONOBJECT then begin
              if GotoNext(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
                nError := 29;
                m_RunPos.btDirection := 0;
                Result := True;
                Exit;
              end;
            {end else begin
              if GotoNextOne(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
                nError := 29;
                m_RunPos.btDirection := 0;
                Result := True;
                break;
              end;
            end;}
            end;
          end;
        end;

        nError := 12;
        for I := 2 downto 1 do begin
          nError := 13;
          if FindPosOfSelf(@WalkStep, I, boFlag) then begin
            nError := 14;
            if m_Master <> nil then begin
              MapWalkXY := FindGoodPathB(WalkStep, 1);
            end else begin
              MapWalkXY := FindGoodPathB(WalkStep, 0);
            end;
            nError := 15;
            if (MapWalkXY.nWalkStep > 0) then begin
              nError := 16;
              if GotoNext(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
                nError := 17;
                m_RunPos.btDirection := 0;
                Result := True;
                Exit;
              end;
            end;
          end;
        end;
      end else begin
        if wMagicID > 0 then
          nRange := _MAX(Random(3), 2)
        else
          nRange := 1;

      //boFlag := nRange = 1;
        boFlag := (m_btRaceServer = RC_MOONOBJECT) or (wMagicID in [SKILL_FIREBALL, SKILL_GREATFIREBALL, SKILL_SOULFIREBALL]) or (nRange = 1);

        for I := 2 downto 1 do begin
          if FindPosOfSelf(@WalkStep, I, boFlag) then begin
          {if m_Master <> nil then begin
            MapWalkXY := FindGoodPathA(WalkStep, nRange, 1);
          end else begin
            MapWalkXY := FindGoodPathA(WalkStep, nRange, 0);
          end;}
            MapWalkXY := FindGoodPathA(WalkStep, nRange, 0);
            if (MapWalkXY.nWalkStep > 0) then begin
              if GotoNextOne(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
                m_RunPos.btDirection := 0;
                Result := True;
                Exit;
              end;
            end;
          end;
        end;

        for I := 2 downto 1 do begin
          if _FindPosOfSelf(@WalkStep, I, boFlag) then begin
            MapWalkXY := FindMinRange(WalkStep);
            if (MapWalkXY.nWalkStep > 0) then begin
              if GotoNextOne(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
                m_RunPos.btDirection := 0;
                Result := True;
                Exit;
              end;
            end;
          end;
        end;

        for I := nRange downto 1 do begin
          if FindPosOfTarget(@WalkStep, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, I, boFlag) then begin
          {if m_Master <> nil then begin
            MapWalkXY := FindGoodPathB(WalkStep, 1);
          end else begin
            MapWalkXY := FindGoodPathB(WalkStep, 0);
          end; }
            MapWalkXY := FindGoodPathB(WalkStep, 0);
            if (MapWalkXY.nWalkStep > 0) then begin
              if GotoNextOne(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
                m_RunPos.btDirection := 0;
                Result := True;
                Exit;
              end;
            end;
          end;
        end;
      end;
    except
      MainOutMessage('WalkToRightPos:' + m_sCharName + ' ' + IntToStr(nError));
    end;
  end;

  function AvoidTarget: Boolean;
  var
    I, II: Integer;
    nRange: Integer;
    btDir: Byte;
    nX, nY: Integer;
    boFlag: Boolean;
    WalkStep: TWalkStep;
    MapWalkXY: TMapWalkXY;
  begin
    Result := False;
    nRange := _MAX(Random(3), 2);

    boFlag := (m_btRaceServer = RC_MOONOBJECT) or (wMagicID in [SKILL_FIREBALL, SKILL_GREATFIREBALL, SKILL_SOULFIREBALL]);
    for I := nRange downto 1 do begin
        //if FindPosOfTarget(@WalkStep, ActorObject.m_nCurrX, ActorObject.m_nCurrY, I, boFlag) then begin
      if FindPosOfSelf(@WalkStep, I, boFlag) then begin
        {if m_Master <> nil then begin
          MapWalkXY := FindGoodPathB(WalkStep, 1);
        end else begin
          MapWalkXY := FindGoodPathB(WalkStep, 0);
        end;}
        MapWalkXY := FindGoodPathB(WalkStep, 0);
        if (MapWalkXY.nWalkStep > 0) then begin
          btDir := GetNextDirection(m_nCurrX, m_nCurrY, MapWalkXY.nX, MapWalkXY.nY);
          if GotoNextOne(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
            if (m_btRaceServer <> RC_MOONOBJECT) then begin
              for II := nRange downto 1 do begin //再跑1次
                if m_PEnvir.GetNextPosition(MapWalkXY.nX, MapWalkXY.nY, btDir, II, nX, nY) and m_PEnvir.CanWalkEx(nX, nY, True) and m_PEnvir.CanWalkOfEvent(Self, nX, nY) and
                  (GetNearTargetCount(nX, nY) <= MapWalkXY.nMonCount) then begin
                  GotoNextOne(nX, nY, m_btRaceServer <> RC_MOONOBJECT);
                  break;
                end;
              end;
            end;
            m_RunPos.btDirection := 0;
            Result := True;
            Exit;
          end;
        end;
      end;
    end;

    for I := nRange downto 1 do begin
        //if FindPosOfTarget(@WalkStep, ActorObject.m_nCurrX, ActorObject.m_nCurrY, I, boFlag) then begin
      if _FindPosOfSelf(@WalkStep, I, boFlag) then begin
        {if m_Master <> nil then begin
          MapWalkXY := FindGoodPathB(WalkStep, 1);
        end else begin
          MapWalkXY := FindGoodPathB(WalkStep, 0);
        end;}
        MapWalkXY := FindGoodPathB(WalkStep, 0);
        if (MapWalkXY.nWalkStep > 0) then begin
          btDir := GetNextDirection(m_nCurrX, m_nCurrY, MapWalkXY.nX, MapWalkXY.nY);
          if GotoNextOne(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
            for II := nRange downto 1 do begin //再跑1次
              if m_PEnvir.GetNextPosition(MapWalkXY.nX, MapWalkXY.nY, btDir, II, nX, nY) and m_PEnvir.CanWalkEx(nX, nY, True) and m_PEnvir.CanWalkOfEvent(Self, nX, nY) and
                (GetNearTargetCount(nX, nY) <= MapWalkXY.nMonCount) then begin
                MapWalkXY.nX := nX;
                MapWalkXY.nY := nY;
                GotoNextOne(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT);
                break;
              end;
            end;
            m_RunPos.btDirection := 0;
            Result := True;
            Exit;
          end;
        end;
      end;
    end;
  end;

  function FollowTarget: Boolean;
  var
    I: Integer;
    nRange: Integer;
    boFlag: Boolean;
    WalkStep: TWalkStep;
    MapWalkXY: TMapWalkXY;
  begin
    Result := False;
    nRange := 2;
    boFlag := (m_btRaceServer = RC_MOONOBJECT) or (wMagicID in [SKILL_FIREBALL, SKILL_GREATFIREBALL, SKILL_SOULFIREBALL]);
    for I := nRange downto 1 do begin
      if FindPosOfSelf(@WalkStep, I, boFlag) then begin
        {if m_Master <> nil then begin
          MapWalkXY := FindGoodPathB(WalkStep, 1);
        end else begin                                 inner power
          MapWalkXY := FindGoodPathB(WalkStep, 0);
        end;}
        MapWalkXY := FindMinRange(WalkStep);

        if (MapWalkXY.nWalkStep > 0) then begin
          if GotoNextOne(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
            m_RunPos.btDirection := 0;
            Result := True;
            Exit;
          end;
        end;
      end;
    end;

    for I := nRange downto 1 do begin
      if _FindPosOfSelf(@WalkStep, I, boFlag) then begin
        MapWalkXY := FindMinRange(WalkStep);
        if (MapWalkXY.nWalkStep > 0) then begin
          if GotoNextOne(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
            m_RunPos.btDirection := 0;
            Result := True;
            Exit;
          end;
        end;
      end;
    end;
  end;

  function MotaeboPos(): Boolean; //获取野蛮冲撞
  var
    nTargetX, nTargetY: Integer;
    btNewDir: Byte;
  begin
    Result := False;
    if (m_TargetCret = nil) or (m_Master = nil) then Exit;

    if (GetPoseCreate = m_TargetCret) or (m_TargetCret.GetPoseCreate = Self) then begin
      btNewDir := GetNextDirection(m_nCurrX, m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);
      if m_PEnvir.GetNextPosition(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, btNewDir, 1, nTargetX, nTargetY) then begin
        if m_PEnvir.CanWalk(nTargetX, nTargetY, True) then begin
          Result := True;
          Exit;
        end;
      end;
    end;
    Result := WalkToRightPos;
  end;

  function FindPosOfDir(nDir, nRange: Integer; boFlag: Boolean): TMapWalkXY;
  var
    I: Integer;
    btDir: Byte;

    nCurrX: Integer;
    nCurrY: Integer;
  begin
    FillChar(Result, SizeOf(TMapWalkXY), 0);
    if m_PEnvir.GetNextPosition(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, nDir, nRange, nCurrX, nCurrY) and
      CanMove(nCurrX, nCurrY, False) and ((boFlag and CanLineAttack(nCurrX, nCurrY)) or (not boFlag)) and IsGotoXY(m_nCurrX, m_nCurrY, nCurrX, nCurrY) then begin
      Result.nWalkStep := nRange;
      Result.nX := nCurrX;
      Result.nY := nCurrY;
      Result.nMonRange := abs(nCurrX - m_TargetCret.m_nCurrX) + abs(nCurrY - m_TargetCret.m_nCurrY);
      Result.nMonCount := GetNearTargetCount(nCurrX, nCurrY);
      Result.nMastrRange := GetMasterRange(nCurrX, nCurrY);
    end;
  end;

  function RunPosAttack(): Boolean;
    function GetNextRunPos(btDir: Byte; boTurn: Boolean): Byte;
    begin
      if boTurn then begin
        case btDir of
          DR_UP: Result := DR_RIGHT;
          DR_UPRIGHT: Result := DR_DOWNRIGHT;
          DR_RIGHT: Result := DR_DOWN;
          DR_DOWNRIGHT: Result := DR_DOWNLEFT;
          DR_DOWN: Result := DR_LEFT;
          DR_DOWNLEFT: Result := DR_UPLEFT;
          DR_LEFT: Result := DR_UP;
          DR_UPLEFT: Result := DR_UPRIGHT;
        end;
      end else begin
        case btDir of
          DR_UP: Result := DR_LEFT;
          DR_UPRIGHT: Result := DR_UPLEFT;
          DR_RIGHT: Result := DR_UP;
          DR_DOWNRIGHT: Result := DR_UPRIGHT;
          DR_DOWN: Result := DR_RIGHT;
          DR_DOWNLEFT: Result := DR_DOWNRIGHT;
          DR_LEFT: Result := DR_DOWN;
          DR_UPLEFT: Result := DR_DOWNLEFT;
        end;
      end;
    end;
  var
    WalkStep: array[0..1] of TMapWalkXY;
    MapWalkXY: pTMapWalkXY;
    btNewDir1: Byte;
    btNewDir2: Byte;
    nRange: Integer;
    boFlag: Boolean;
    btDir: Byte;
    nNearTargetCount: Integer;
  begin
    Result := False;

    btDir := GetNextDirection(m_nCurrX, m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);

    btNewDir1 := GetNextRunPos(btDir, True);
    btNewDir2 := GetNextRunPos(btDir, False);
    FillChar(WalkStep, SizeOf(TMapWalkXY) * 2, 0);

    if m_btJob = 0 then begin
      nRange := 1;
      if (wMagicID = 58) or (wMagicID = 77) then nRange := 2;
      if (wMagicID = 12) then nRange := 2;
      if (wMagicID in [60, 61, 62]) then nRange := 6;
      //nRange := 2;
      WalkStep[0] := FindPosOfDir(btNewDir1, nRange, True);
      WalkStep[1] := FindPosOfDir(btNewDir2, nRange, True);
    end else begin
      //nRange := GetStep(m_nCurrX, m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);
      nRange := 2;
      boFlag := False; // (m_btRaceServer = RC_MOONOBJECT) or (wMagicID in [SKILL_FIREBALL, SKILL_FIREBALL2, SKILL_FIRECHARM]);
      WalkStep[0] := FindPosOfDir(btNewDir1, nRange, boFlag);
      WalkStep[1] := FindPosOfDir(btNewDir2, nRange, boFlag);
    end;

    nNearTargetCount := GetNearTargetCount(m_nCurrX, m_nCurrY);
    MapWalkXY := nil;
    if (WalkStep[0].nWalkStep > 0) and (WalkStep[1].nWalkStep > 0) then begin
      if m_RunPos.btDirection > 0 then begin
        MapWalkXY := @WalkStep[1];
      end else begin
        MapWalkXY := @WalkStep[0];
      end;
      if (nNearTargetCount < WalkStep[0].nMonCount) and (nNearTargetCount < WalkStep[1].nMonCount) then
        MapWalkXY := nil
      else
        if (m_RunPos.btDirection > 0) and (nNearTargetCount < WalkStep[1].nMonCount) then
        MapWalkXY := nil
      else
        if (m_RunPos.btDirection <= 0) and (nNearTargetCount < WalkStep[0].nMonCount) then
        MapWalkXY := nil;

      if (nNearTargetCount > 0) and (MapWalkXY <> nil) and (MapWalkXY.nMonCount > nNearTargetCount) then
        MapWalkXY := nil;
    end else
      if (WalkStep[0].nWalkStep > 0) then begin
      MapWalkXY := @WalkStep[0];
      if (nNearTargetCount < WalkStep[0].nMonCount) then
        MapWalkXY := nil;
      m_RunPos.btDirection := 0;
    end else
      if (WalkStep[1].nWalkStep > 0) then begin
      MapWalkXY := @WalkStep[1];
      if (nNearTargetCount < WalkStep[1].nMonCount) then
        MapWalkXY := nil;
      m_RunPos.btDirection := 1;
    end;
    if (MapWalkXY <> nil) then begin
      if GotoNextOne(MapWalkXY.nX, MapWalkXY.nY, m_btRaceServer <> RC_MOONOBJECT) then begin
        Result := True;
      end;
    end;
    if not Result then begin
      m_RunPos.nAttackCount := 0;
    end;
  end;
var
  nCode, nError, nThinkCount: Integer;
begin
  Result := False;
  nError := 0;
  nThinkCount := 0;
  try
    while True do begin
      if m_TargetCret = nil then break;
      nThinkCount := nThinkCount + 1;
      nCode := DoThink(wMagicID);
      nError := 1;
      //if m_btRaceServer = RC_HEROOBJECT then
        //MainOutMessage('nCode:' + m_TargetCret.m_sCharName + ' ' + IntToStr(nCode) + ' ' + IntToStr(Integer(m_TargetCret)));
      case m_btJob of
        0: begin
            case nCode of
              2: begin
                  nError := 2;
                  if WalkToRightPos then begin
                    Result := True;
                    //MainOutMessage('WalkToRightPos:' + m_TargetCret.m_sCharName + ' ' + IntToStr(nCode) + ' ' + IntToStr(Integer(m_TargetCret)));
                  end else begin //无法走到正确的攻击坐标
                    //MainOutMessage('WalkToRightPos=FALSE:' + m_TargetCret.m_sCharName + ' ' + IntToStr(nCode) + ' ' + IntToStr(Integer(m_TargetCret)));
                    nError := 3;
                    DelTargetCreat;
                    if nThinkCount < 2 then begin
                      nError := 4;
                      SearchTarget;
                      nError := 5;
                      Continue;
                    end;
                  end;
                end;
              5: begin
                  nError := 6;
                  if RunPosAttack then begin
                    Result := True;
                  end;
                  nError := 7;
                end;
            end;
          end;
        1, 2: begin
            case nCode of
              1: begin
                  nError := 8;
                  Result := AvoidTarget;
                  nError := 9;
                end;
              2: begin
                  nError := 10;
                  if FollowTarget then begin
                    nError := 11;
                    Result := True;
                  end else begin //无法走到正确的攻击坐标
                    nError := 12;
                    DelTargetCreat;
                    nError := 13;
                    if nThinkCount < 2 then begin
                      nError := 14;
                      SearchTarget;
                      nError := 15;
                      Continue;
                    end;
                  end;
                end;
              3, 4: begin
                  nError := 16;
                  if WalkToRightPos then begin
                    Result := True;
                  end else begin //无法走到正确的攻击坐标
                    nError := 3;
                    DelTargetCreat;
                    if nThinkCount < 2 then begin
                      nError := 4;
                      SearchTarget;
                      nError := 5;
                      Continue;
                    end;
                  end;
                  //Result := AvoidTarget;
                  nError := 17;
                end;
              {4: begin
                  nError := 18;
                  if WalkToRightPos then begin
                    Result := True;
                    nError := 19;
                  end else begin //无法走到正确的攻击坐标
                    nError := 20;
                    DelTargetCreat;
                    nError := 21;
                    if nThinkCount < 2 then begin
                      nError := 22;
                      SearchTarget;
                      nError := 23;
                      Continue;
                    end;
                  end;
                end;}
              5: begin
                  nError := 24;
                  Result := RunPosAttack;
                  nError := 25;
                end;
            end;
          end;
      end;
      break;
    end;
  except
    MainOutMessage('TAIObject::ActThink Error:' + m_sCharName + ' Code:' + IntToStr(nCode) + ' Error:' + IntToStr(nError));
  end;
end;

function TAIObject.GetSpellPoint(UserMagic: pTUserMagic): Integer;
begin
  Result := Round(UserMagic.MagicInfo.wSpell / (UserMagic.MagicInfo.btTrainLv + 1) * (UserMagic.btLevel + 1)) + UserMagic.MagicInfo.btDefSpell;
end;

function TAIObject.AutoMotaebo(nDir: Byte; nMagicLevel: Integer): Boolean;
  function CanMotaebo(BaseObject: TActorObject): Boolean;
  var
    nC: Integer;
  begin
    Result := False;
    if (m_Abil.Level > BaseObject.m_Abil.Level) and (not BaseObject.m_boStickMode) then begin
      nC := m_Abil.Level - BaseObject.m_Abil.Level;
      //m_nCanMotaebo:=Random(20);
      if (m_nCanMotaebo < ((nMagicLevel * 4) + 6 + nC)) and IsProperTarget(BaseObject) then Result := True;
    end;
  end;
var
  bo35: Boolean;
  I, n20, n24, n28: Integer;
  PoseCreate: TActorObject;
  BaseObject_30: TActorObject;
  BaseObject_34: TActorObject;
  nX, nY: Integer;
begin
  Result := False;
  bo35 := True;
  m_btDirection := nDir;
  BaseObject_34 := nil;
  n24 := nMagicLevel + 1;
  n28 := n24;
  //m_btDirection := GetNextDirection(m_nCurrX, m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);
  PoseCreate := GetPoseCreate();
  //MainOutMessage('TAIObject.DoMotaebo1');
  if (PoseCreate <> nil) and (PoseCreate.GetPoseCreate = Self) then begin
    //MainOutMessage('TAIObject.DoMotaebo2');
    for I := 0 to _MAX(2, nMagicLevel + 1) do begin
      PoseCreate := GetPoseCreate();
      if PoseCreate <> nil then begin
        n28 := 0;
        if not CanMotaebo(PoseCreate) then Break;
        if nMagicLevel >= 3 then begin
          if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, m_btDirection, 2, nX, nY) then begin
            BaseObject_30 := TActorObject(m_PEnvir.GetMovingObject(nX, nY, True));
            if (BaseObject_30 <> nil) and CanMotaebo(BaseObject_30) then
              BaseObject_30.CharPushed(m_btDirection, 1);
          end;
        end;
        BaseObject_34 := PoseCreate;
        if PoseCreate.CharPushed(m_btDirection, 1) <> 1 then Break;
        GetFrontPosition(nX, nY);
        if m_PEnvir.CanWalkOfEvent(Self, nX, nY) and (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nX, nY, False) > 0) then begin
          m_nCurrX := nX;
          m_nCurrY := nY;
          SendRefMsg(RM_RUSH, nDir, m_nCurrX, m_nCurrY, 0, '');
          bo35 := False;
          Result := True;
        end;
        Dec(n24);
      end; //004C32D7  if PoseCreate <> nil  then begin
    end; //004C32DD for i:=0 to _MAX(2,nMagicLevel + 1) do begin
  {end else begin //004C32E8 if PoseCreate <> nil  then begin
    bo35 := False;
    for I := 0 to _MAX(2, nMagicLevel + 1) do begin
      GetFrontPosition(nX, nY); //sub_004B2790
      if m_PEnvir.CanWalkOfEvent(Self, nX, nY) and (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nX, nY, False) > 0) then begin
        m_nCurrX := nX;
        m_nCurrY := nY;
        SendRefMsg(RM_RUSH, nDir, m_nCurrX, m_nCurrY, 0, '');
        Dec(n28);
      end else begin
        if m_PEnvir.CanWalk(nX, nY, True) and m_PEnvir.CanWalkOfEvent(Self, nX, nY) then n28 := 0
        else begin
          bo35 := True;
          Break;
        end;
      end;
    end;}
  end;
  if (BaseObject_34 <> nil) then begin
    if n24 < 0 then n24 := 0;
    n20 := Random((n24 + 1) * 10) + ((n24 + 1) * 10);

    n20 := n20 + GetAddPowerPoint(5, n20);

    n20 := BaseObject_34.GetHitStruckDamage(Self, n20);

    BaseObject_34.StruckDamage(n20);
    BaseObject_34.StartNewShield();
    BaseObject_34.SendRefMsg(RM_STRUCK, n20, BaseObject_34.m_WAbil.HP, BaseObject_34.m_WAbil.MaxHP, Integer(Self), '');
    if BaseObject_34.m_btRaceServer <> RC_PLAYOBJECT then begin
      BaseObject_34.SendMsg(BaseObject_34, RM_STRUCK, n20, BaseObject_34.m_WAbil.HP, BaseObject_34.m_WAbil.MaxHP, Integer(Self), '');
    end;
  end;

  if n28 > 0 then begin
    if n24 < 0 then n24 := 0;
    n20 := Random(n24 * 10) + ((n24 + 1) * 3);

    n20 := n20 + GetAddPowerPoint(5, n20);

    n20 := GetHitStruckDamage(Self, n20);
    StruckDamage(n20);
    StartNewShield();
    SendRefMsg(RM_STRUCK, n20, m_WAbil.HP, m_WAbil.MaxHP, 0, '');
  end;
end;

function TAIObject.UseSpell(UserMagic: pTUserMagic; nTargetX, nTargetY: Integer;
  TargeTActorObject: TActorObject): Boolean;
var
  nSpellPoint: Integer;
  n14: Integer;
  BaseObject: TActorObject;
  dwCheckTime: LongWord;
  boIsWarrSkill: Boolean;
resourcestring
  sDisableMagicCross = 'DisableMagicCross %s';
begin
  Result := False;
  //MainOutMessage(m_sCharName + ' TAIObject.UseSpell');
  //if not m_boCanSpell then Exit;
  if m_boDeath or ((m_wStatusTimeArr[POISON_STONE] <> 0) and not g_Config.ClientConf.boParalyCanSpell) then Exit; //防麻

  if m_PEnvir <> nil then begin
    if not m_PEnvir.AllowMagics(UserMagic.MagicInfo.sMagicName) then begin
      SysMsg(Format(sDisableMagicCross, [UserMagic.MagicInfo.sMagicName]), c_Red, t_Notice);
      Exit;
    end;
  end;

  boIsWarrSkill := MagicManager.IsWarrSkill(UserMagic.wMagIdx);


  Dec(m_nSpellTick, 450);
  m_nSpellTick := _MAX(0, m_nSpellTick);

 { if boIsWarrSkill then begin
    //m_dwMagicAttackInterval:=0;
    //m_dwMagicAttackInterval:=g_Config.dwMagicHitIntervalTime;
  end else begin
    m_dwMagicAttackInterval := UserMagic.MagicInfo.dwDelayTime + g_Config.dwMagicHitIntervalTime;
  end;
  m_dwMagicAttackTick := GetTickCount();  }
  case UserMagic.wMagIdx of //
    SKILL_THRUSTING {12}: begin //刺杀剑法
        if m_MagicErgumSkill <> nil then begin
          if not m_boUseThrusting then begin
            ThrustingOnOff(True);
            //SendSocket(nil, '+LNG');
          end else begin
            ThrustingOnOff(False);
            //SendSocket(nil, '+ULNG');
          end;
        end;
        Result := True;
      end;
    SKILL_BANWOL {25}: begin //半月弯刀
        if m_MagicBanwolSkill <> nil then begin
          if not m_boUseHalfMoon then begin
            HalfMoonOnOff(True);
            //SendSocket(nil, '+WID');
          end else begin
            HalfMoonOnOff(False);
            //SendSocket(nil, '+UWID');
          end;
        end;
        Result := True;
      end;
    SKILL_FIRESWORD {26}: begin //烈火剑法
        if m_MagicFireSwordSkill <> nil then begin
          if AllowFireHitSkill then begin
            nSpellPoint := GetSpellPoint(UserMagic);
            if m_WAbil.MP >= nSpellPoint then begin
              if nSpellPoint > 0 then begin
                DamageSpell(nSpellPoint);
                HealthSpellChanged();
              end;
              //SendSocket(nil, '+FIR');
            end;
          end;
        end;
        Result := True;
      end;
    SKILL_MOOTEBO {27}: begin //野蛮冲撞
        Result := True;
        //MainOutMessage('TAIObject.ClientSpellXY1');
        if GetTickCount - m_SkillUseTick[27] > 1000 * 10 then begin
          m_SkillUseTick[27] := GetTickCount;
          //MainOutMessage('TAIObject.ClientSpellXY2');
          //m_btDirection := nTargetX;
          nSpellPoint := GetSpellPoint(UserMagic);
          if m_WAbil.MP >= nSpellPoint then begin
            if nSpellPoint > 0 then begin
              DamageSpell(nSpellPoint);
              HealthSpellChanged();
            end;
            m_btDirection := GetNextDirection(m_nCurrX, m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);
            if AutoMotaebo(m_btDirection, UserMagic.btLevel) then begin
              if UserMagic.btLevel < 3 then begin
                if UserMagic.MagicInfo.TrainLevel[UserMagic.btLevel] < m_Abil.Level then begin
                  TrainSkill(UserMagic, Random(3) + 1);
                  if not CheckMagicLevelup(UserMagic) then begin
                    SendDelayMsg(Self,
                      RM_MAGIC_LVEXP,
                      0,
                      UserMagic.MagicInfo.wMagicId,
                      UserMagic.btLevel,
                      UserMagic.nTranPoint,
                      '', 1000);
                  end;
                end;
              end;
            end;
          end;
        end;
      end;
    SKILL_CROSSHALFMOON: begin // Cross Half Moon
        if m_MagicCrsSkill <> nil then begin
          if not m_boCrsHitkill then begin
            SkillCrsOnOff(True);
          end else begin
            SkillCrsOnOff(False);
          end;
        end;
        Result := True;
      end;
    SKILL_TWINDRAKEBLADE: begin //狂风斩
        if m_Magic42Skill <> nil then begin
         if not m_bo42kill then begin
              nSpellPoint:=GetSpellPoint(UserMagic);
              if m_WAbil.MP >= nSpellPoint then begin
                if nSpellPoint > 0 then begin
                  DamageSpell(nSpellPoint);
                  HealthSpellChanged();
                end;
              end;
            Skill42OnOff(True);
         //   SendSocket(nil, '+TWN/' + IntToStr(GetTickCount));
          end else begin
            Skill42OnOff(False);
        //   SendSocket(nil, '+UTWN/' + IntToStr(GetTickCount));
             end;
            end;
        Result := True;
      end;

    SKILL_43: begin //破空剑
        if m_Magic43Skill <> nil then begin
          if AllowCIDHitSkill then begin
            nSpellPoint := GetSpellPoint(UserMagic);
            if m_WAbil.MP >= nSpellPoint then begin
              if nSpellPoint > 0 then begin
                DamageSpell(nSpellPoint);
                HealthSpellChanged();
              end;
              //SendSocket(nil, '+CID');
            end;
          end;
          Result := True;
        end;
      end;
    SKILL_58: begin //开天斩
        if m_Magic58Skill <> nil then begin
          if AllowKTZHitSkill then begin
            nSpellPoint := GetSpellPoint(UserMagic);
            if m_WAbil.MP >= nSpellPoint then begin
              if nSpellPoint > 0 then begin
                DamageSpell(nSpellPoint);
                HealthSpellChanged();
              end;
              //SendSocket(nil, '+KTZ');
            end;
          end;
          Result := True;
        end;
      end;
    SKILL_77: begin //逐日剑法
        if m_MagicZRJFSkill <> nil then begin
          if AllowZRJFHitSkill then begin
            nSpellPoint := GetSpellPoint(UserMagic);
            if m_WAbil.MP >= nSpellPoint then begin
              if nSpellPoint > 0 then begin
                DamageSpell(nSpellPoint);
                HealthSpellChanged();
              end;
              //SendSocket(nil, '+FIR');
            end;
          end;
        end;
        Result := True;
      end;
  else begin
      n14 := GetNextDirection(m_nCurrX, m_nCurrY, nTargetX, nTargetY);
      m_btDirection := n14;
      BaseObject := nil;
      //检查目标角色，与目标座标误差范围，如果在误差范围内则修正目标座标
      if UserMagic.wMagIdx in [60..65] then begin //如果是合击锁定目标
        if CretInNearXY(TargeTActorObject, nTargetX, nTargetY, 6) then begin
          BaseObject := TargeTActorObject;
          nTargetX := BaseObject.m_nCurrX;
          nTargetY := BaseObject.m_nCurrY;
        end;
      end else begin
        case UserMagic.wMagIdx of
            SKILL_HEALLING,
            SKILL_SOULSHIELD,
            SKILL_DEJIWONHO {15},
            SKILL_BIGHEALLING {29},
            SKILL_SINSU, {30}
            SKILL_PURIFICATION,
            SKILL_46,
            SKILL_SUMMONHOLYDEVA: begin
              if m_boSelSelf then begin
                BaseObject := Self;
                nTargetX := m_nCurrX;
                nTargetY := m_nCurrY;
              end else begin
                if m_Master <> nil then begin
                  BaseObject := m_Master;
                  nTargetX := m_Master.m_nCurrX;
                  nTargetY := m_Master.m_nCurrY;
                end else begin
                  BaseObject := Self;
                  nTargetX := m_nCurrX;
                  nTargetY := m_nCurrY;
                end;
              end;
            end;
        else begin
            if CretInNearXY(TargeTActorObject, nTargetX, nTargetY) then begin
              BaseObject := TargeTActorObject;
              nTargetX := BaseObject.m_nCurrX;
              nTargetY := BaseObject.m_nCurrY;
            end;
          end;
        end;
      end;
      if not AutoSpell(UserMagic, nTargetX, nTargetY, BaseObject) then begin
        SendRefMsg(RM_MAGICFIREFAIL, 0, 0, 0, 0, '');
        //MainOutMessage('if not DoSpell');
      end;
      Result := True;
    end;
  end;
end;

function TAIObject.AutoSpell(UserMagic: pTUserMagic; nTargetX,
  nTargetY: Integer; BaseObject: TActorObject): Boolean;
var
  nSpellPoint: Integer;
begin
  Result := False;
  try
    //MainOutMessage(m_sCharName + ' TAIObject.AutoSpell');
    if not MagicManager.IsWarrSkill(UserMagic.wMagIdx) then begin
      nSpellPoint := GetSpellPoint(UserMagic);
      if nSpellPoint > 0 then begin
        //MainOutMessage(m_sCharName + ' nSpellPoint > 0');
        if m_WAbil.MP < nSpellPoint then Exit;
        //MainOutMessage(m_sCharName + ' if m_WAbil.MP < nSpellPoint then Exit;');
        DamageSpell(nSpellPoint);
        HealthSpellChanged();
      end;
      Result := MagicManager.DoSpell(Self, UserMagic, nTargetX, nTargetY, BaseObject);
    end;
  except
    on E: Exception do begin
      MainOutMessage(Format('[Exception] TAIObject.AutoSpell MagID:%d X:%d Y:%d', [UserMagic.wMagIdx, nTargetX, nTargetY]));
      MainOutMessage(E.Message);
    end;
  end;
end;

function TAIObject.FindMagic(wMagIdx: Word): pTUserMagic;
var
  UserMagic: pTUserMagic;
  I: Integer;
begin
  Result := nil;
  for I := 0 to m_MagicList.Count - 1 do begin
    UserMagic := m_MagicList.Items[I];
    if UserMagic.MagicInfo.wMagicId = wMagIdx then begin
      Result := UserMagic;
      Break;
    end;
  end;
end;

function TAIObject.FindMagic(sMagicName: string): pTUserMagic;
var
  UserMagic: pTUserMagic;
  I: Integer;
begin
  Result := nil;
  for I := 0 to m_MagicList.Count - 1 do begin
    UserMagic := m_MagicList.Items[I];
    if CompareText(UserMagic.MagicInfo.sMagicName, sMagicName) = 0 then begin
      Result := UserMagic;
      Break;
    end;
  end;
end;

function TAIObject.GetAttackXYDir(nX, nY: Integer; BaseObject: TActorObject; nRange: Integer; var btDir: Byte): Boolean;
begin
  Result := False;
  if (nX - nRange <= BaseObject.m_nCurrX) and
    (nX + nRange >= BaseObject.m_nCurrX) and
    (nY - nRange <= BaseObject.m_nCurrY) and
    (nY + nRange >= BaseObject.m_nCurrY) and
    ((nX <> BaseObject.m_nCurrX) or
    (nY <> BaseObject.m_nCurrY)) then begin
    //Result := True;
    if ((nX - nRange) = BaseObject.m_nCurrX) and (nY = BaseObject.m_nCurrY) then begin
      btDir := DR_LEFT;
      Result := True;
      Exit;
    end;
    if ((nX + nRange) = BaseObject.m_nCurrX) and (nY = BaseObject.m_nCurrY) then begin
      btDir := DR_RIGHT;
      Result := True;
      Exit;
    end;
    if (nX = BaseObject.m_nCurrX) and ((nY - nRange) = BaseObject.m_nCurrY) then begin
      btDir := DR_UP;
      Result := True;
      Exit;
    end;
    if (nX = BaseObject.m_nCurrX) and ((nY + nRange) = BaseObject.m_nCurrY) then begin
      btDir := DR_DOWN;
      Result := True;
      Exit;
    end;
    if ((nX - nRange) = BaseObject.m_nCurrX) and ((nY - nRange) = BaseObject.m_nCurrY) then begin
      btDir := DR_UPLEFT;
      Result := True;
      Exit;
    end;
    if ((nX + nRange) = BaseObject.m_nCurrX) and ((nY - nRange) = BaseObject.m_nCurrY) then begin
      btDir := DR_UPRIGHT;
      Result := True;
      Exit;
    end;
    if ((nX - nRange) = BaseObject.m_nCurrX) and ((nY + nRange) = BaseObject.m_nCurrY) then begin
      btDir := DR_DOWNLEFT;
      Result := True;
      Exit;
    end;
    if ((nX + nRange) = BaseObject.m_nCurrX) and ((nY + nRange) = BaseObject.m_nCurrY) then begin
      btDir := DR_DOWNRIGHT;
      Result := True;
      Exit;
    end;
    btDir := 0;
  end;
end;

function TAIObject.StartAttack(wMagIdx: Word): Boolean;
resourcestring
  sExceptionMsg = '[Exception] TAIObject::StartAttack Race=%d MagIdx=%d';
begin
  Result := False;
  try
    m_dwTargetFocusTick := GetTickCount();
    //MainOutMessage(Format('%s TAIObject.StartAttack %d', [m_sCharName, wMagIdx]));
    case m_btJob of
      0: begin
          Result := WarrorAttackTarget(wMagIdx);
        end;
      1: begin
          Result := WizardAttackTarget(wMagIdx);
        end;
      2: begin
          Result := TaoistAttackTarget(wMagIdx);
        end;
    end;
    if Result then Inc(m_RunPos.nAttackCount);
  except
    MainOutMessage(Format(sExceptionMsg, [m_btRaceServer, wMagIdx]));
  end;
end;

function TAIObject.WarrAttackTarget(wMagIdx, wHitMode: Word): Boolean; {物理攻击}
var
  I: Integer;
  bt06: Byte;
  boAttack: Boolean;
  nRange: Integer;
  UserMagic: pTUserMagic;
begin
  Result := False;
  if m_TargetCret <> nil then begin
    nRange := 1;
    if (wMagIdx = 58) or (wMagIdx = 77) then nRange := 4;
    if (wMagIdx = 12) then nRange := 2;
    //if (wMagIdx in [60]) then nRange := 6;
    if (wMagIdx in [60, 61, 62]) then nRange := 12;
    //boAttack := CanAttack(m_TargetCret, nRange, bt06);

   { boAttack := GetAttackDir(m_TargetCret, 1, bt06);
    if (not boAttack) then begin
      nRange := 1;
      if (wMagIdx = 58) or (wMagIdx = 77) then nRange := 4;
      if (wMagIdx = 12) then nRange := 2;
      if (wMagIdx in [60, 61, 62]) then nRange := 5;

      boAttack := CanAttack(m_TargetCret, nRange, bt06);
    end;
   }
    //if (wMagIdx in [60, 61, 62]) then SysMsg(' TAIObject.WarrAttackTarget UseGroupSpell1', c_Red, t_Hint);
    if CanAttack(m_TargetCret, nRange, bt06) then begin
      m_dwTargetFocusTick := GetTickCount();
      //if (wMagIdx in [60, 61, 62]) then SysMsg(' TAIObject.WarrAttackTarget UseGroupSpell2', c_Red, t_Hint);
      if m_btRaceServer = RC_HEROOBJECT then begin //合击
        case wMagIdx of
          60: begin
              m_Master.m_btDirection := GetNextDirection(m_Master.m_nCurrX, m_Master.m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);
              m_Master.AttackDir(m_TargetCret, wHitMode, m_Master.m_btDirection);
            end;
          61: begin
              if m_btJob = 0 then begin
                UserMagic := THeroObject(Self).FindGroupMagic;
                if UserMagic = nil then Exit;
                MagicManager.DoSpell(m_Master, UserMagic, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, m_TargetCret);
              end;
            end;
          62: begin
              if m_btJob = 0 then begin
                UserMagic := THeroObject(Self).FindGroupMagic;
                if UserMagic = nil then Exit;
                MagicManager.DoSpell(m_Master, UserMagic, m_nCurrX, m_nCurrY, m_TargetCret);
              end;
            end;
        end;
        //if (wMagIdx in [60, 61, 62]) then SysMsg(' TAIObject.WarrAttackTarget UseGroupSpell3', c_Red, t_Hint);
      end;

      //StartAttack(m_TargetCret, bt06);
      AttackDir(m_TargetCret, wHitMode, bt06);

      {if (m_MagicPowerHitSkill <> nil) and (m_UseItems[U_WEAPON].Dura > 0) then begin
        Dec(m_btAttackSkillCount);
        if m_btAttackSkillPointCount = m_btAttackSkillCount then begin
          m_boPowerHit := True;
        end;
        if m_btAttackSkillCount <= 0 then begin
          m_btAttackSkillCount := 7 - m_MagicPowerHitSkill.btLevel;
          m_btAttackSkillPointCount := Random(m_btAttackSkillCount);
        end;
      end;}

      BreakHolySeizeMode();
      Result := True;
    end else begin
      if m_TargetCret.m_PEnvir = m_PEnvir then begin
        SetTargetXY(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);
      end else begin
        DelTargetCreat();
      end;
    end;
  end;
end;

function TAIObject.WarrorAttackTarget(wMagIdx: Word): Boolean; {战士攻击}
var
  //wHitMode: Word;
  nSelectMagic: Integer;
  UserMagic: pTUserMagic;
  dwDelayTime: LongWord;
  nDir: Byte;
  boAttack: Boolean;
begin
  Result := False;
  m_wHitMode := 0;
  //MainOutMessage('WarrorAttackTarget1:' + IntToStr(m_nSelectMagic));
  if wMagIdx > 0 then begin
    UserMagic := FindMagic(wMagIdx);
    if UserMagic <> nil then begin
      case wMagIdx of
        27, 39, 41: begin
            Result := UseSpell(UserMagic, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, m_TargetCret); //战士魔法
            //MainOutMessage('WarrorAttackTarget2:' + IntToStr(m_nSelectMagic));
            //m_nSelectMagic := 0;
            Exit;
          end;
        61..65: begin //合击
            if m_btJob <> 0 then begin
              Result := UseSpell(UserMagic, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, m_TargetCret);
              Exit;
            end;
          end;
      end;
      case wMagIdx of
        7: m_wHitMode := 3; //攻杀
        12: m_wHitMode := 4; //使用刺杀
        25: m_wHitMode := 5; //使用半月
        26: m_wHitMode := 7; //使用烈火
        40: m_wHitMode := 8; //抱月刀法
        43: m_wHitMode := 11; //破空剑
        58: m_wHitMode := 12; //开天斩
        60: m_wHitMode := 13; //破魂斩
        61: m_wHitMode := 14; //劈星斩
        62: m_wHitMode := 15; //雷霆一击
        77: m_wHitMode := 16; //逐日剑法
      end;
    end;
  end;
  Result := WarrAttackTarget(wMagIdx, m_wHitMode);
end;

function TAIObject.WizardAttackTarget(wMagIdx: Word): Boolean; {法师攻击}
var
  wHitMode: Word;
  nSelectMagic: Integer;
  UserMagic: pTUserMagic;
  dwDelayTime: LongWord;
begin
  Result := False;
  m_wHitMode := 0;
  //MainOutMessage(m_sCharName + ' TAIObject.WizardAttackTarget 0');
  if wMagIdx > 0 then begin
    UserMagic := FindMagic(wMagIdx);
    //MainOutMessage(m_sCharName + ' TAIObject.WizardAttackTarget 1');
    if UserMagic <> nil then begin
      //MainOutMessage(m_sCharName + ' TAIObject.WizardAttackTarget 2');
      Result := UseSpell(UserMagic, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, m_TargetCret); //使用魔法
      Exit;
    end;
  end;
  Result := WarrAttackTarget(wMagIdx, m_wHitMode);
end;

function TAIObject.TaoistAttackTarget(wMagIdx: Word): Boolean; {道士攻击}
var
  wHitMode: Word;
  nSelectMagic: Integer;
  UserMagic: pTUserMagic;
  nIndex, nCount: Integer;
  dwDelayTime: LongWord;
begin
  Result := False;
  m_wHitMode := 0;
  if wMagIdx > 0 then begin
    case wMagIdx of
        SKILL_SOULFIREBALL,
        SKILL_SOULSHIELD,
        SKILL_DEJIWONHO {15},
        SKILL_HOLYSHIELD {16},
        SKILL_SKELLETON {17},
        SKILL_CLOAK {18},
        SKILL_BIGCLOAK {19},
        SKILL_SINSU, {30}
        SKILL_76,
        SKILL_72: begin
          {if m_nSelectMagic = SKILL_FIRECHARM then begin
            if not MagCanHitTarget(m_nCurrX, m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY) then Exit;
          end; }
          m_btTaoistUseItemType := 5;
          nCount := 1;
          case wMagIdx of
            SKILL_SOULFIREBALL: nCount := 1;
            SKILL_SOULSHIELD: nCount := 1;
            SKILL_DEJIWONHO {15}: nCount := 1;
            SKILL_HOLYSHIELD {16}: nCount := 1;
            SKILL_SKELLETON {17}: nCount := 1;
            SKILL_CLOAK {18}: nCount := 1;
            SKILL_BIGCLOAK {19}: nCount := 1;
            SKILL_SINSU: {30} nCount := 5;
            SKILL_76: nCount := 5;
            SKILL_72: nCount := 5;
          end;

          if (m_btRaceServer = RC_HEROOBJECT) then begin
            if not g_Config.boHeroUseBagItem then begin
              if not CheckUserItemType(5, nCount) then begin //换符
                nIndex := GetUserItemList(5, nCount);
                if nIndex >= 0 then begin
                  UseItem(5, nIndex);
                end;
              end;
            end;
          end else begin
            if m_Master <> nil then begin
              if not g_Config.boMonUseBagItem then begin
                if not CheckUserItemType(5, nCount) then begin //换符
                  nIndex := GetUserItemList(5, nCount);
                  if nIndex >= 0 then begin
                    UseItem(5, nIndex);
                  end;
                end;
              end;
            end else begin
              if m_boAI then begin
                if not CheckUserItemType(5, nCount) then begin //换符
                  nIndex := GetUserItemList(5, nCount);
                  if nIndex >= 0 then begin
                    UseItem(5, nIndex);
                  end;
                end;
              end;
            end;
          end;
        end;
      SKILL_52: {52} begin
          m_btTaoistUseItemType := 3;
          if (m_btRaceServer = RC_HEROOBJECT) then begin
            if not g_Config.boHeroUseBagItem then begin
              if not CheckUserItemType(3, 3) then begin //换符
                nIndex := GetUserItemList(3, 3);
                if nIndex >= 0 then begin
                  UseItem(3, nIndex);
                end;
              end;
            end;
          end else begin
            if m_Master <> nil then begin
              if not g_Config.boMonUseBagItem then begin
                if not CheckUserItemType(3, 3) then begin //换符
                  nIndex := GetUserItemList(3, 3);
                  if nIndex >= 0 then begin
                    UseItem(3, nIndex);
                  end;
                end;
              end;
            end else begin
              if m_boAI then begin
                if not CheckUserItemType(3, 3) then begin //换符
                  nIndex := GetUserItemList(3, 3);
                  if nIndex >= 0 then begin
                    UseItem(3, nIndex);
                  end;
                end;
              end;
            end;
          end;
        end;
      SKILL_POISONING, SKILL_GROUPAMYOUNSUL: begin //换毒
          nCount := 1;
          case wMagIdx of
            SKILL_POISONING: nCount := 1;
            SKILL_GROUPAMYOUNSUL: nCount := 2;
          end;
          m_btTaoistUseItemType := m_nSelItemType;
          if (m_btRaceServer = RC_HEROOBJECT) then begin
            if not g_Config.boHeroUseBagItem then begin
              if not CheckUserItemType(m_nSelItemType, nCount) then begin //换毒
                nIndex := GetUserItemList(m_nSelItemType, nCount);
                if nIndex >= 0 then begin
                  UseItem(m_nSelItemType, nIndex);
                end;
              end;
            end;
          end else begin
            if m_Master <> nil then begin
              if not g_Config.boMonUseBagItem then begin
                if not CheckUserItemType(m_nSelItemType, nCount) then begin //换毒
                  nIndex := GetUserItemList(m_nSelItemType, nCount);
                  if nIndex >= 0 then begin
                    UseItem(m_nSelItemType, nIndex);
                  end;
                end;
              end;
            end else begin
              if m_boAI then begin
                if not CheckUserItemType(m_nSelItemType, nCount) then begin //换毒
                  nIndex := GetUserItemList(m_nSelItemType, nCount);
                  if nIndex >= 0 then begin
                    UseItem(m_nSelItemType, nIndex);
                  end;
                end;
              end;
            end;
          end;
        end;
    end;
    UserMagic := FindMagic(wMagIdx);
    if UserMagic <> nil then begin
      Result := UseSpell(UserMagic, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, m_TargetCret); //使用魔法
      Exit;
    end;
  end;
  Result := WarrAttackTarget(wMagIdx, m_wHitMode);
end;

{道士}

function TAIObject.CheckItemType(nItemType: Integer; StdItem: pTStdItem): Boolean;
begin
  Result := False;
  case nItemType of
    1: begin
        if (StdItem.StdMode = 25) and (StdItem.Shape = 1) then Result := True;
      end;
    2: begin
        if (StdItem.StdMode = 25) and (StdItem.Shape = 2) then Result := True;
      end;
    3: begin
        if (StdItem.StdMode = 25) and (StdItem.Shape = 3) then Result := True;
      end;
    5: begin
        if (StdItem.StdMode = 25) and (StdItem.Shape = 5) then Result := True;
      end;
  end;
end;

function TAIObject.CheckUserItem(nItemType, nCount: Integer): Boolean;
begin
  Result := CheckUserItemType(nItemType, nCount) or (GetUserItemList(nItemType, nCount) >= 0);
end;

function TAIObject.CheckUserItemType(nItemType, nCount: Integer): Boolean;
var
  UserItem: pTUserItem;
  StdItem: pTStdItem;
begin
  Result := False;
  if (m_UseItems[U_ARMRINGL {U_BUJUK}].wIndex > 0) and (Round(m_UseItems[U_ARMRINGL {U_BUJUK}].Dura / 100) >= nCount) then begin
    StdItem := UserEngine.GetStdItem(m_UseItems[U_ARMRINGL {U_BUJUK}].wIndex);
    if StdItem <> nil then begin
      Result := CheckItemType(nItemType, StdItem);
    end;
  end;
end;

function TAIObject.UseItem(nItemType, nIndex: Integer): Boolean; //自动换毒符
var
  UserItem: pTUserItem;
  AddUserItem: pTUserItem;
  StdItem: pTStdItem;
begin
  Result := False;
  if (nIndex >= 0) and (nIndex < m_ItemList.Count) then begin
    UserItem := m_ItemList.Items[nIndex];
    if m_UseItems[U_ARMRINGL {U_BUJUK}].wIndex > 0 then begin
      StdItem := UserEngine.GetStdItem(m_UseItems[U_ARMRINGL {U_BUJUK}].wIndex);
      if StdItem <> nil then begin
        if CheckItemType(nItemType, StdItem) then begin
          Result := True;
        end else begin
          m_ItemList.Delete(nIndex);
          New(AddUserItem);
          AddUserItem^ := m_UseItems[U_ARMRINGL {U_BUJUK}];
          if AddItemToBag(AddUserItem) then begin
            m_UseItems[U_ARMRINGL {U_BUJUK}] := UserItem^;
            Dispose(UserItem);
            Result := True;
          end else begin
            m_ItemList.Add(UserItem);
            Dispose(AddUserItem);
          end;
        end;
      end else begin
        m_ItemList.Delete(nIndex);
        m_UseItems[U_ARMRINGL {U_BUJUK}] := UserItem^;
        Dispose(UserItem);
        Result := True;
      end;
    end else begin
      m_ItemList.Delete(nIndex);
      m_UseItems[U_ARMRINGL {U_BUJUK}] := UserItem^;
      Dispose(UserItem);
      Result := True;
    end;
  end;
end;

//检测包裹中是否有符和毒
//nType 为指定类型 5 为护身符 1,2 为毒药   3,诅咒术专用

function TAIObject.GetUserItemList(nItemType, nCount: Integer): Integer;
var
  I: Integer;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
begin
  Result := -1;
  for I := 0 to m_ItemList.Count - 1 do begin
    UserItem := m_ItemList.Items[I];
    StdItem := UserEngine.GetStdItem(UserItem.wIndex);
    if StdItem <> nil then begin
      if CheckItemType(nItemType, StdItem) and (Round(UserItem.Dura / 100) >= nCount) then begin
        Result := I;
        Break;
      end;
    end;
  end;
end;

function TAIObject.FollowMaster: Boolean;
var
  I, II, III, nX, nY, nX1, nY1, nCurrX, nCurrY, nStep: Integer;
  btDir: Byte;
  boNeed: Boolean;
begin
  Result := False;
  boNeed := False;
  if (not m_Master.m_boSlaveRelax) then begin
    if (m_PEnvir <> m_Master.m_PEnvir) or (abs(m_nCurrX - m_Master.m_nCurrX) > 20) or (abs(m_nCurrY - m_Master.m_nCurrY) > 20) then begin
      boNeed := True;
    end;
  end;

  if boNeed then begin
    if m_boProtectStatus then begin
      nX := m_nProtectTargetX;
      nY := m_nProtectTargetY;
    end else begin
      m_Master.GetBackPosition(nX, nY);
      if not m_Master.m_PEnvir.CanWalk(nX, nY, True) then begin
        for I := 0 to 7 do begin
          if m_Master.m_PEnvir.GetNextPosition(m_Master.m_nCurrX, m_Master.m_nCurrY, I, 1, nX, nY) then begin
            if m_Master.m_PEnvir.CanWalk(nX, nY, True) then begin
              break;
            end;
          end;
        end;
      end;
    end;

    DelTargetCreat;
    m_nTargetX := nX;
    m_nTargetY := nY;

    if not m_boProtectStatus then begin
      SpaceMove(m_Master.m_PEnvir.sMapName, m_nTargetX, m_nTargetY, 1);
    end else begin
      SpaceMove(m_PEnvir.sMapName, m_nTargetX, m_nTargetY, 1);
    end;
    Result := True;
    Exit;
  end;

  if m_boProtectStatus then begin
    nCurrX := m_nProtectTargetX;
    nCurrY := m_nProtectTargetY;
  end else begin
    //nCurrX := m_Master.m_nCurrX;
    //nCurrY := m_Master.m_nCurrY;
    m_Master.GetBackPosition(nCurrX, nCurrY);
  end;

  if (m_TargetCret = nil) and (not m_Master.m_boSlaveRelax) then begin
    //if (abs(m_nCurrX - nCurrX) >= 1) or (abs(m_nCurrY - nCurrY) >= 1) then begin
   { if StartPickUpItem(500) then begin //捡物
      //Result := inherited FollowMaster;
      Result := True;
      Exit;
    end; }
    if not m_boProtectStatus then begin
      //MainOutMessage('THeroObject.FollowMaster');
      for I := 1 to 2 do begin //判断主人是否在英雄对面
        if m_Master.m_PEnvir.GetNextPosition(m_Master.m_nCurrX, m_Master.m_nCurrY, m_Master.m_btDirection, I, nX, nY) then begin
          if (m_nCurrX = nX) and (m_nCurrY = nY) then begin

            if m_Master.GetBackPosition(m_Master.m_btDirection, nX, nY) and
              GotoNext(nX, nY, True) then begin
              Result := True;
              Exit;
            end;

            //btDir := GetBackDir(m_Master.m_btDirection);
            for III := 1 to 2 do begin
              for II := 0 to 7 do begin
                if II <> m_Master.m_btDirection then begin
                  if m_Master.m_PEnvir.GetNextPosition(m_Master.m_nCurrX, m_Master.m_nCurrY, II, III, nX, nY) and
                  //if m_Master.GetBackPosition(II, nX1, nY1) and
                  GotoNext(nX, nY, True) then begin
                    Result := True;
                    Exit;
                  end;
                end;
              end;
            end;
            Break;
          end;
        end;
      end;
      //end;
      if m_btRaceServer = RC_MOONOBJECT then nStep := 0 else nStep := 1;
      if (abs(m_nCurrX - nCurrX) > nStep) or (abs(m_nCurrY - nCurrY) > nStep) then begin
        if GotoNextOne(nCurrX, nCurrY, True) then Exit;
        if GotoNextOne(nX, nY, True) then Exit;
        for III := 1 to 2 do begin
          for II := 0 to 7 do begin
            if II <> m_Master.m_btDirection then begin
              if m_Master.m_PEnvir.GetNextPosition(m_Master.m_nCurrX, m_Master.m_nCurrY, II, III, nX, nY) and
                  //if m_Master.GetBackPosition(II, nX1, nY1) and
              GotoNextOne(nX, nY, True) then begin
                Result := True;
                Exit;
              end;
            end;
          end;
        end;
      end;
    end else begin
      if GotoNextOne(nCurrX, nCurrY, True) then Exit;
      for III := 1 to 2 do begin
        for II := 0 to 7 do begin
          if m_Master.m_PEnvir.GetNextPosition(nCurrX, nCurrY, II, III, nX, nY) and
                  //if m_Master.GetBackPosition(II, nX1, nY1) and
          GotoNextOne(nX, nY, True) then begin
            Result := True;
            Exit;
          end;
        end;
      end;
    end;
  end;
end;
 { Result := False;
  boNeed := False;

  if (not m_Master.m_boSlaveRelax) then begin
    if (m_PEnvir <> m_Master.m_PEnvir) or (abs(m_nCurrX - m_Master.m_nCurrX) > 20) or (abs(m_nCurrY - m_Master.m_nCurrY) > 20) then begin
      boNeed := True;
    end;
  end;

  if boNeed then begin
    if m_boProtectStatus then begin
      nX := m_nProtectTargetX;
      nY := m_nProtectTargetY;
    end else begin
      m_Master.GetBackPosition(nX, nY);
      if not m_Master.m_PEnvir.CanWalk(nX, nY, True) then begin
        for I := 0 to 7 do begin
          if m_Master.m_PEnvir.GetNextPosition(m_Master.m_nCurrX, m_Master.m_nCurrY, I, 1, nX, nY) then begin
            if m_Master.m_PEnvir.CanWalk(nX, nY, True) then begin
              break;
            end;
          end;
        end;
      end;
    end;

    DelTargetCreat;
    m_nTargetX := nX;
    m_nTargetY := nY;
    SpaceMove(m_Master.m_PEnvir.sMapName, m_nTargetX, m_nTargetY, 1);
    Result := True;
    Exit;
  end;

  if (m_TargetCret = nil) and (not m_Master.m_boSlaveRelax) then begin
    for I := 1 to 2 do begin //判断主人是否在英雄对面
      if m_Master.m_PEnvir.GetNextPosition(m_Master.m_nCurrX, m_Master.m_nCurrY, m_Master.m_btDirection, I, nX, nY) then begin
        if (m_nCurrX = nX) and (m_nCurrY = nY) then begin

          if m_Master.GetBackPosition(m_Master.m_btDirection, nX1, nY1) and
            GotoNext(nX1, nY1, m_btRaceServer <> RC_MOONOBJECT) then begin
            Result := True;
            Exit;
          end;
          for III := 1 to 2 do begin
            for II := 0 to 7 do begin
              if II <> m_Master.m_btDirection then begin
                if m_Master.m_PEnvir.GetNextPosition(m_Master.m_nCurrX, m_Master.m_nCurrY, II, III, nX, nY) and
                  //if m_Master.GetBackPosition(II, nX1, nY1) and
                GotoNext(nX, nY, m_btRaceServer <> RC_MOONOBJECT) then begin
                  Result := True;
                  Exit;
                end;
              end;
            end;
          end;

          Break;
        end;
      end;
    end;
    if (abs(m_nCurrX - m_Master.m_nCurrX) > 1) or (abs(m_nCurrY - m_Master.m_nCurrY) > 1) then begin
      m_Master.GetBackPosition(nX, nY);
      if GotoNextOne(nX, nY, m_btRaceServer <> RC_MOONOBJECT) then Exit;
      for III := 1 to 2 do begin
        for II := 0 to 7 do begin
          if II <> m_Master.m_btDirection then begin
            if m_Master.m_PEnvir.GetNextPosition(m_Master.m_nCurrX, m_Master.m_nCurrY, II, III, nX, nY) and
                  //if m_Master.GetBackPosition(II, nX1, nY1) and
            GotoNextOne(nX, nY, m_btRaceServer <> RC_MOONOBJECT) then begin
              Result := True;
              Exit;
            end;
          end;
        end;
      end;
      Result := GotoNextOne(nX, nY, m_btRaceServer <> RC_MOONOBJECT);
    end;
    //end;
  end;
end; }

function TAIObject.AllowUseMagic(wMagIdx: Word): Boolean;
var
  UserMagic: pTUserMagic;
  nSpellPoint: Integer;
begin
  Result := False;
  if (wMagIdx = 22) and g_Config.boDisableInSafeZoneFireCross and (m_TargetCret <> nil) and InSafeZone(m_PEnvir, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY) then begin
    Exit;
  end;
  if m_PEnvir.AllowMagics(wMagIdx) then begin
    UserMagic := FindMagic(wMagIdx);
    if UserMagic <> nil then begin
      if not MagicManager.IsWarrSkill(UserMagic.wMagIdx) then begin
        if (UserMagic.btKey > 0) or m_boAI or (m_btRaceServer = RC_PLAYMOSTER) then begin
          nSpellPoint := GetSpellPoint(UserMagic);
          if (nSpellPoint > 0) and (nSpellPoint <= m_WAbil.MP) then Result := True;
        end;
      end else begin
        Result := (UserMagic.btKey > 0) or m_boAI or (m_btRaceServer = RC_PLAYMOSTER);
      end;
    end;
  end;
end;

function TAIObject.FindVisibleActors(ActorObject: TActorObject): Boolean;
var
  I: Integer;
begin
  //Result := False;
  //Result := m_PEnvir.GetMovingObject(ActorObject.m_nCurrX, ActorObject.m_nCurrY, ActorObject, True) = ActorObject;
  for I := 0 to m_VisibleActors.Count - 1 do begin
    if (pTVisibleBaseObject(m_VisibleActors.Items[I]).BaseObject = ActorObject) then begin
      Result := True;
      Break;
    end;
  end;
end;

function TAIObject.SelectMagic(): Integer;

  function SelKTZandCID(var nSelectMagic: Integer): Boolean;
  var
    boKTZHitSkill, boCIDHitSkill: Boolean;
  begin
    Result := False;
    boKTZHitSkill := False;
    boCIDHitSkill := False;
    if m_boCIDHitSkill then begin
      nSelectMagic := 43;
      Result := True;
      Exit;
    end;
    if m_boKTZHitSkill then begin
      nSelectMagic := 58;
      Result := True;
      Exit;
    end;
    if AllowUseMagic(43) and (not m_boCIDHitSkill) and ((GetTickCount - m_dwLatestCIDHitTick) > g_Config.nSkill43DelayTime * 1000) then begin
      boCIDHitSkill := True;
    end;
    if AllowUseMagic(58) and (not m_boKTZHitSkill) and ((GetTickCount - m_dwLatestKTZHitTick) > g_Config.nSkill58DelayTime * 1000) then begin
      boKTZHitSkill := True;
    end;

    if boKTZHitSkill and boCIDHitSkill then begin
      //Randomize;
      case Random(2) of
        1: begin
            AllowCIDHitSkill;
            nSelectMagic := 43;
            Result := True;
          end;
        2: begin
            AllowKTZHitSkill;
            nSelectMagic := 58;
            Result := True;
          end;
      else begin
          AllowCIDHitSkill;
          nSelectMagic := 43;
          Result := True;
        end;
      end;
      Exit;
    end;
    if boCIDHitSkill then begin
      AllowCIDHitSkill;
      nSelectMagic := 43;
      Result := True;
    end;
    if boKTZHitSkill then begin
      AllowKTZHitSkill;
      nSelectMagic := 58;
      Result := True;
    end;
  end;

  function GetMoonSlaveCount: Integer;
  var
    I: Integer;
    BaseObject: TActorObject;
  begin
    Result := 0;
    if m_SlaveList.Count <= 0 then Exit;
    for I := 0 to m_SlaveList.Count - 1 do begin
      BaseObject := m_SlaveList.Items[I];
      if (not BaseObject.m_boDeath) and (not BaseObject.m_boGhost) and (BaseObject.m_btRaceServer = 60) then Inc(Result);
    end;
  end;

  function GetMoonSlaveDeath: Boolean;
  var
    I: Integer;
    BaseObject: TActorObject;
  begin
    Result := False;
    if m_SlaveList.Count <= 0 then Exit;
    for I := 0 to m_SlaveList.Count - 1 do begin
      BaseObject := m_SlaveList.Items[I];
      if (BaseObject.m_btRaceServer = 60) and ((BaseObject.m_boDeath) or (BaseObject.m_boGhost)) then begin
        Result := True;
        Break;
      end;
    end;
  end;

  function GetCopySlaveCount: Integer;
  var
    I: Integer;
    BaseObject: TActorObject;
  begin
    Result := 0;
    if m_SlaveList.Count <= 0 then Exit;
    for I := 0 to m_SlaveList.Count - 1 do begin
      BaseObject := m_SlaveList.Items[I];
      if (not BaseObject.m_boDeath) and (not BaseObject.m_boGhost) and (BaseObject.m_nCopyHumanLevel = 2) then Inc(Result);
    end;
  end;

  function GetCopySlaveDeath: Boolean;
  var
    I: Integer;
    BaseObject: TActorObject;
  begin
    Result := False;
    if m_SlaveList.Count <= 0 then Exit;
    for I := 0 to m_SlaveList.Count - 1 do begin
      BaseObject := m_SlaveList.Items[I];
      if (BaseObject.m_nCopyHumanLevel = 2) and ((BaseObject.m_boDeath) or (BaseObject.m_boGhost)) then begin
        Result := True;
        Break;
      end;
    end;
  end;

  function GetSlaveCount: Integer;
  var
    I: Integer;
    BaseObject: TActorObject;
  begin
    Result := 0;
    if m_SlaveList.Count <= 0 then Exit;
    for I := 0 to m_SlaveList.Count - 1 do begin
      BaseObject := m_SlaveList.Items[I];
      if (not BaseObject.m_boDeath) and (not BaseObject.m_boGhost) {and (BaseObject.m_nCopyHumanLevel = 0)} then Inc(Result);
    end;
  end;

  function GetSlaveDeath: Boolean;
  var
    I: Integer;
    BaseObject: TActorObject;
  begin
    Result := False;
    if m_SlaveList.Count <= 0 then Exit;
    for I := 0 to m_SlaveList.Count - 1 do begin
      BaseObject := m_SlaveList.Items[I];
      if (BaseObject.m_nCopyHumanLevel = 0) and ((BaseObject.m_boDeath) or (BaseObject.m_boGhost)) then begin
        Result := True;
        Break;
      end;
    end;
  end;

  function IsUseCopySelf: Boolean;
  begin
    Result := False;
    if (m_Master <> nil) and (m_Master.m_btRaceServer = RC_PLAYOBJECT) and g_Config.boAllowRecallSelf then begin
      if AllowUseMagic(46) { and (GetTickCount - m_SkillUseTick[46] > 1000 * 10)} and ((GetCopySlaveCount <= 0) or GetCopySlaveDeath) then begin
        //m_SkillUseTick[46] := GetTickCount; //分身术
        Result := True;
      end;
    end else
      if m_Master = nil then begin
      if AllowUseMagic(46) { and (GetTickCount - m_SkillUseTick[46] > 1000 * 10) } and ((GetCopySlaveCount <= 0) or GetCopySlaveDeath) then begin
        //m_SkillUseTick[46] := GetTickCount; //分身术
        Result := True;
      end;
    end;
  end;

  function IsUseSkill8(): Boolean; //抗拒火环
  var
    BaseObjectList: TList;
    BaseObject: TActorObject;
    btNewDir: Byte;
    nTargetX, nTargetY: Integer;
    I: Integer;
  begin
    Result := False;
    if AllowUseMagic(8) and (GetTickCount - m_SkillUseTick[8] > 1000 * 5) then begin
      //m_SkillUseTick[8] := GetTickCount;
      if (m_TargetCret <> nil) and (m_Abil.Level > m_TargetCret.m_Abil.Level) and
        (abs(m_nCurrX - m_TargetCret.m_nCurrX) <= 1) and (abs(m_nCurrY - m_TargetCret.m_nCurrY) <= 1) then begin
        btNewDir := GetNextDirection(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, m_nCurrX, m_nCurrY);
        if m_PEnvir.GetNextPosition(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, GetBackDir(btNewDir), 1, nTargetX, nTargetY) then begin
          Result := m_PEnvir.CanWalk(nTargetX, nTargetY, True) and m_PEnvir.CanWalkOfEvent(Self, nTargetX, nTargetY);
        end;
        if Result then Exit;
      end;
      BaseObjectList := TList.Create;
      if GetMapActorObjects(m_PEnvir, m_nCurrX, m_nCurrY, 1, BaseObjectList) then begin
        for I := 0 to BaseObjectList.Count - 1 do begin
          BaseObject := TActorObject(BaseObjectList.Items[I]);
          if (m_TargetCret = BaseObject) and (BaseObject.m_boHideMode and not m_boCoolEye) or BaseObject.m_boDeath or BaseObject.m_boGhost or (not IsProperTarget(BaseObject)) then Continue;
          if BaseObject.m_Abil.Level < m_Abil.Level then begin
            btNewDir := GetNextDirection(BaseObject.m_nCurrX, BaseObject.m_nCurrY, m_nCurrX, m_nCurrY);
            if m_PEnvir.GetNextPosition(BaseObject.m_nCurrX, BaseObject.m_nCurrY, GetBackDir(btNewDir), 1, nTargetX, nTargetY) then begin
              if m_PEnvir.CanWalk(nTargetX, nTargetY, True) and m_PEnvir.CanWalkOfEvent(Self, nTargetX, nTargetY) then begin
                Result := True;
                Break;
              end;
            end;
          end;
        end;
      end;
      BaseObjectList.Free;
    end;
  end;

  function IsUseSkill48: Boolean; //气功波
  var
    BaseObjectList: TList;
    BaseObject: TActorObject;
    btNewDir: Byte;
    nTargetX, nTargetY: Integer;
    I: Integer;
  begin
    Result := False;
    if AllowUseMagic(48) and (GetTickCount - m_SkillUseTick[48] > 1000 * 5) then begin
      //m_SkillUseTick[48] := GetTickCount;
      if (m_TargetCret <> nil) and (m_Abil.Level > m_TargetCret.m_Abil.Level) and
        (abs(m_nCurrX - m_TargetCret.m_nCurrX) <= 1) and (abs(m_nCurrY - m_TargetCret.m_nCurrY) <= 1) then begin
        btNewDir := GetNextDirection(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, m_nCurrX, m_nCurrY);
        if m_PEnvir.GetNextPosition(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, GetBackDir(btNewDir), 1, nTargetX, nTargetY) then begin
          Result := m_PEnvir.CanWalk(nTargetX, nTargetY, True) and m_PEnvir.CanWalkOfEvent(Self, nTargetX, nTargetY);
        end;
        if Result then Exit;
      end;
      BaseObjectList := TList.Create;
      if GetMapActorObjects(m_PEnvir, m_nCurrX, m_nCurrY, 1, BaseObjectList) then begin
        for I := 0 to BaseObjectList.Count - 1 do begin
          BaseObject := TActorObject(BaseObjectList.Items[I]);
          if (m_TargetCret = BaseObject) and (BaseObject.m_boHideMode and not m_boCoolEye) or BaseObject.m_boDeath or BaseObject.m_boGhost or (not IsProperTarget(BaseObject)) then Continue;
          if BaseObject.m_Abil.Level < m_Abil.Level then begin

            btNewDir := GetNextDirection(BaseObject.m_nCurrX, BaseObject.m_nCurrY, m_nCurrX, m_nCurrY);
            if m_PEnvir.GetNextPosition(BaseObject.m_nCurrX, BaseObject.m_nCurrY, GetBackDir(btNewDir), 1, nTargetX, nTargetY) then begin
              if m_PEnvir.CanWalk(nTargetX, nTargetY, True) and m_PEnvir.CanWalkOfEvent(Self, nTargetX, nTargetY) then begin
                Result := True;
                Break;
              end;
            end;

          end;
        end;
      end;
      BaseObjectList.Free;
    end;
  end;

  function IsUseSkill27: Boolean; //野蛮冲撞
    function CanMotaebo(BaseObject: TActorObject; nMagicLevel: Integer): Boolean;
    var
      nC: Integer;
    begin
      Result := False;
      if (m_Abil.Level > BaseObject.m_Abil.Level) and (not BaseObject.m_boStickMode) then begin
        nC := m_Abil.Level - BaseObject.m_Abil.Level;
        m_nCanMotaebo := Random(20);
        if m_nCanMotaebo < ((nMagicLevel * 4) + 6 + nC) then Result := True;
      end;
    end;
  var
    nX, nY: Integer;
    nDir: Integer;
    nMagicLevel: Integer;
    UserMagic: pTUserMagic;
  begin
    Result := False;
    {if m_nSelectMagic = 27 then begin
      Result := True;
      Exit;
    end;    }
    //nDir := GetNextDirection(m_nCurrX, m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);
    if AllowUseMagic(27) and (m_TargetCret.m_Abil.Level < m_Abil.Level) and ((GetPoseCreate = m_TargetCret) or (m_TargetCret.GetPoseCreate = Self)) and (GetTickCount - m_SkillUseTick[27] > 1000 * 10) then begin
      UserMagic := FindMagic(27);
      if CanMotaebo(m_TargetCret, UserMagic.btLevel) then begin
        nDir := GetNextDirection(m_nCurrX, m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY);
     // nMagicLevel := _MAX(2, UserMagic.btLevel + 1);
      {  nX := m_TargetCret.m_nCurrX;
        nY := m_TargetCret.m_nCurrY;  }
        if m_PEnvir.GetNextPosition(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, nDir, 1, nX, nY) then begin
        //m_TargetCret.GetBackPosition(nX, nY);
        //if (nX <> m_TargetCret.m_nCurrX) or (nY <> m_TargetCret.m_nCurrY) then begin
          Result := m_TargetCret.m_PEnvir.CanWalk(nX, nY, False) and m_TargetCret.m_PEnvir.CanWalkOfEvent(Self, nX, nY);
      //m_SkillUseTick[27] := GetTickCount;
        //Result := True;
       { if m_Master <> nil then begin
          m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, m_btDirection, nMagicLevel, nX, nY);
          if (abs(m_Master.m_nCurrX - nX) > 8) or (abs(m_Master.m_nCurrY - nY) > 8) then begin
            Result := False;
          end else begin

          end;
        end; }
        end;
      end;
    end;
  end;

  function UseFireCross: Boolean;
  begin
    if (m_TargetCret <> nil) and AllowUseMagic(22) and (GetTickCount - m_SkillUseTick[22] > 1000 * 5) then begin
      //m_SkillUseTick[22] := GetTickCount;
      Result := True;
      if (not m_PEnvir.CanWalk(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY - 1, False)) or (m_PEnvir.GetEvent(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY - 1) <> nil) then
        if (not m_PEnvir.CanWalk(m_TargetCret.m_nCurrX - 1, m_TargetCret.m_nCurrY, False)) or (m_PEnvir.GetEvent(m_TargetCret.m_nCurrX - 1, m_TargetCret.m_nCurrY) <> nil) then
          if (not m_PEnvir.CanWalk(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, False)) or (m_PEnvir.GetEvent(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY) <> nil) then
            if (not m_PEnvir.CanWalk(m_TargetCret.m_nCurrX + 1, m_TargetCret.m_nCurrY, False)) or (m_PEnvir.GetEvent(m_TargetCret.m_nCurrX + 1, m_TargetCret.m_nCurrY) <> nil) then
              if (not m_PEnvir.CanWalk(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY + 1, False)) or (m_PEnvir.GetEvent(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY + 1) <> nil) then
                Result := False;
    end else Result := False;
  end;

  function QuickLighting: Boolean;
  begin
    Result := False;
    if AllowUseMagic(10) and CanLineAttack(6) then begin
      Result := True;
    end;
  end;

  function IsUseSkill41: Boolean; //狮子吼
  var
    BaseObjectList: TList;
    BaseObject: TActorObject;
    btNewDir: Byte;
    nTargetX, nTargetY: Integer;
    I, nCount: Integer;
  begin
    Result := False;
    if (m_TargetCret <> nil) and (not (m_TargetCret.m_btRaceServer in [RC_PLAYOBJECT, RC_MOONOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER])) and AllowUseMagic(41) and (GetTickCount - m_SkillUseTick[41] > 1000 * 10) then begin
      nCount := 0;
      BaseObjectList := TList.Create;
      if GetMapActorObjects(m_PEnvir, m_nCurrX, m_nCurrY, 3, BaseObjectList) then begin
        for I := 0 to BaseObjectList.Count - 1 do begin
          BaseObject := TActorObject(BaseObjectList.Items[I]);
          if (BaseObject.m_boHideMode and not m_boCoolEye) or BaseObject.m_boDeath or BaseObject.m_boGhost or (not IsProperTarget(BaseObject)) then Continue;
          if BaseObject.m_Abil.Level < m_Abil.Level then Inc(nCount);
          if nCount >= 3 then begin
            Result := True;
            Break;
          end;
        end;
      end;
      BaseObjectList.Free;
    end;
  end;

  function IsUseSkill38: Boolean; //群毒术
  var
    BaseObjectList: TList;
    BaseObject: TActorObject;
    btNewDir: Byte;
    nTargetX, nTargetY: Integer;
    I, n01, n02: Integer;
  begin
    Result := False;
    if AllowUseMagic(38) then begin
      n01 := 0;
      n02 := 0;
      BaseObjectList := TList.Create;
      if GetMapActorObjects(m_PEnvir, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, 3, BaseObjectList) then begin
        for I := 0 to BaseObjectList.Count - 1 do begin
          BaseObject := TActorObject(BaseObjectList.Items[I]);
          if (BaseObject.m_boHideMode and not m_boCoolEye) or BaseObject.m_boDeath or BaseObject.m_boGhost or BaseObject.m_boVirusImmunity or (not IsProperTarget(BaseObject)) then Continue;
          if (BaseObject.m_wStatusTimeArr[POISON_DECHEALTH] = 0) then Inc(n01);
          if (BaseObject.m_wStatusTimeArr[POISON_DAMAGEARMOR] = 0) then Inc(n02);
        end;
      end;
      BaseObjectList.Free;
      if (n01 > 1) and (n02 = 0) then begin
        if CheckUserItem(1, 2) then begin
          m_nSelItemType := 1;
          Result := True;
        end;
      end else
        if (n01 = 0) and (n02 > 1) then begin
        if CheckUserItem(2, 2) then begin
          m_nSelItemType := 2;
          Result := True;
        end;
      end else
        if (n01 > 1) and (n02 > 1) then begin
        if n01 > n02 then begin
          if CheckUserItem(1, 2) then begin
            m_nSelItemType := 1;
            Result := True;
          end else
            if CheckUserItem(2, 2) then begin
            m_nSelItemType := 2;
            Result := True;
          end;
        end else begin
          if CheckUserItem(2, 2) then begin
            m_nSelItemType := 2;
            Result := True;
          end else
            if CheckUserItem(1, 2) then begin
            m_nSelItemType := 1;
            Result := True;
          end;
        end;
      end;
    end;
  end;

var
  wHitMode: Word;
  btDir: Byte;
  nSelectMagic: Integer;
  UserMagic: pTUserMagic;
  nRangeTargetCount: Integer;
  nSelfRangeTargetCount: Integer;
  nRangeTargetCountByDir: Integer;
  boVisibleActors: Boolean;
  nStep: Integer;
  nCode: Integer;
begin
  Result := 0;
  case m_btJob of
    0: begin //战士
        if IsUseSkill27 then begin
          Result := 27;
          Exit;
        end;

        if m_boFireHitSkill then begin
          Result := 26;
          Exit;
        end;

        if m_boZRJFHitSkill then begin
          Result := 77;
          Exit;
        end;

        if AllowUseMagic(26) and (not m_boFireHitSkill) and ((GetTickCount - m_dwLatestFireHitTick) > 10 * 1000) then begin {烈火}
          AllowFireHitSkill;
          Result := 26;
          Exit;
        end;

        if SelKTZandCID(nSelectMagic) then begin
          Result := nSelectMagic;
          Exit;
        end;

        if AllowUseMagic(77) and (not m_boZRJFHitSkill) and ((GetTickCount - m_dwLatestZRJFHitTick) > g_Config.nSkillZRJFDelayTime * 1000) then begin
          //m_boZRJFHitSkill := True;
          AllowZRJFHitSkill;
          Result := 77;
          Exit;
        end;

        //if (abs(m_nCurrX - m_TargetCret.m_nCurrX) > 1) or (abs(m_nCurrY - m_TargetCret.m_nCurrY) > 1) then begin
        if GetAttackDir(m_TargetCret, 2, btDir) then begin //英雄隔位刺杀剑术
          if AllowUseMagic(12) then begin
            if not m_boUseThrusting then begin
              ThrustingOnOff(True);
            end;
            Result := 12;
            Exit;
          end;
        end;
        //end;

        if (Random(20) = 0) {and CanAttack(m_TargetCret, 1, btDir)} and AllowUseMagic(7) then begin //英雄攻杀剑术
          m_boPowerHit := True;
          Result := 7;
          Exit;
        end;

        if IsUseSkill41 then begin
          Result := 41;
          Exit;
        end;

        nRangeTargetCount := GetNearTargetCount;

        nRangeTargetCountByDir := GetRangeTargetCountByDir(GetNextDirection(m_nCurrX, m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY), m_nCurrX, m_nCurrY, 4);
        if (nRangeTargetCountByDir > 1) and (nRangeTargetCount < 3) then begin //多个怪物
          m_wLastHP := m_WAbil.HP;

          if AllowUseMagic(12) then begin
            if not m_boUseThrusting then begin
              ThrustingOnOff(True);
            end;
            Result := 12;
            Exit;
          end;

          if SelKTZandCID(nSelectMagic) then begin
            Result := nSelectMagic;
            Exit;
          end;
          if AllowUseMagic(39) and (GetTickCount - m_SkillUseTick[39] > 1000 * 10) then begin
            m_SkillUseTick[39] := GetTickCount; //英雄彻地钉
            Result := 39;
            Exit;
          end;
        end;

        if (nRangeTargetCount >= 5) or ((m_wLastHP - m_WAbil.HP) > (m_WAbil.MaxHP div 5)) then begin //被怪物包围 >=5
          if AllowUseMagic(40) then begin //英雄抱月刀法
            if not m_boCrsHitkill then begin
              SkillCrsOnOff(True);
            end;
            Result := 40;
            Exit;
          end;

          if AllowUseMagic(39) and (GetTickCount - m_SkillUseTick[39] > 1000 * 10) then begin
            m_SkillUseTick[39] := GetTickCount; //英雄彻地钉
            Result := 39;
            Exit;
          end;
        end;

        if (nRangeTargetCount >= 2) then begin //被怪物包围 >=2
          if AllowUseMagic(25) then begin //半月
            if not m_boUseHalfMoon then begin
              HalfMoonOnOff(True);
            end;
            Result := 25;
            Exit;
          end;
          if AllowUseMagic(39) and (GetTickCount - m_SkillUseTick[39] > 1000 * 10) then begin
            m_SkillUseTick[39] := GetTickCount; //英雄彻地钉
            Result := 39;
            Exit;
          end;
        end;

        if (Random(10) = 0) and AllowUseMagic(7) then begin //英雄攻杀剑术
          m_boPowerHit := True;
          Result := 7;
          Exit;
        end;

        if AllowUseMagic(12) then begin //英雄刺杀剑术
          if not m_boUseThrusting then begin
            ThrustingOnOff(True);
          end;
          Result := 12;
          Exit;
        end;

        if AllowUseMagic(25) then begin //半月
          if not m_boUseHalfMoon then begin
            HalfMoonOnOff(True);
          end;
          Result := 25;
          Exit;
        end;

        if AllowUseMagic(40) then begin //英雄抱月刀法
          if not m_boCrsHitkill then begin
            SkillCrsOnOff(True);
          end;
          Result := 40;
          Exit;
        end;

        if AllowUseMagic(39) and (GetTickCount - m_SkillUseTick[39] > 1000 * 10) then begin //英雄彻地钉
          Result := 39;
          Exit;
        end;

        if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(7) then begin //英雄攻杀剑术
          Result := 7;
          Exit;
        end;

        if SelKTZandCID(nSelectMagic) then begin
          Result := nSelectMagic;
          Exit;
        end;
      end;
    1: begin //法师
        if (AllowUseMagic(31)) and (m_wStatusTimeArr[STATE_BUBBLEDEFENCEUP {0x76}] = 0) then begin {使用 魔法盾}
          Result := 31;
          Exit;
        end;
        nRangeTargetCount := GetRangeTargetCount(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, 3);
        nSelfRangeTargetCount := GetNearTargetCount;
        nRangeTargetCountByDir := GetRangeTargetCountByDir(GetNextDirection(m_nCurrX, m_nCurrY, m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY), m_nCurrX, m_nCurrY, 6);
        if IsUseCopySelf { and ((nSelfRangeTargetCount > 1) or (nRangeTargetCount > 1))} then begin
          //m_TargetCret := nil;
          Result := 46; //分身术
          Exit;
        end;

        if nRangeTargetCountByDir > 5 then begin
          if (AllowUseMagic(75)) and (GetTickCount - m_SkillUseTick[75] > 1000 * 5) then begin {使用 流星火雨}
            Result := 75;
            Exit;
          end;
        end;

        if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
          if UseFireCross then begin
            Result := 22; //火墙
            Exit;
          end;
        end else begin
          if nRangeTargetCountByDir > 1 then begin
            if UseFireCross then begin
              Result := 22; //火墙
              Exit;
            end;
          end;
        end;

        if (nSelfRangeTargetCount > 0) then begin
          if IsUseSkill8 then begin
            Result := 8; //抗拒火环
            Exit;
          end;
        end;
        if (nSelfRangeTargetCount < 10) then begin
          if (nRangeTargetCountByDir >= 4) then begin
            if QuickLighting then begin
              Result := 10; //疾光电影
              Exit;
            end;
          end;
        end else begin
          if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
            if UseFireCross then begin
              Result := 22; //火墙
              Exit;
            end;
          end else begin
            if nRangeTargetCountByDir > 1 then begin
              if UseFireCross then begin
                Result := 22; //火墙
                Exit;
              end;
            end;
          end;

          case Random(7) of
            0, 1: begin
                if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
                  if AllowUseMagic(33) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end else begin
                  if AllowUseMagic(33) and (nRangeTargetCount >= 4) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end;
                if QuickLighting and (nRangeTargetCountByDir >= 3) then begin
                  Result := 10; //疾光电影
                  Exit;
                end;
                if AllowUseMagic(23) then begin
                  Result := 23; //爆裂火焰
                  Exit;
                end;
                if AllowUseMagic(37) then begin
                  Result := 37; //群体雷电术
                  Exit;
                end;
                if AllowUseMagic(45) then begin
                  Result := 45; //灭天火
                  Exit;
                end;
                if AllowUseMagic(47) then begin
                  Result := 47; //火龙焰
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
                  Result := 11; //雷电术
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
                  Result := 5; //大火球
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
                  Result := 1; //火球术
                  Exit;
                end;
              end;
            2, 3: begin
                if AllowUseMagic(23) then begin
                  Result := 23; //爆裂火焰
                  Exit;
                end;
                if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
                  if AllowUseMagic(33) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end else begin
                  if AllowUseMagic(33) and (nRangeTargetCount >= 5) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end;
                if AllowUseMagic(37) then begin
                  Result := 37; //群体雷电术
                  Exit;
                end;
                if AllowUseMagic(45) then begin
                  Result := 45; //灭天火
                  Exit;
                end;
                if AllowUseMagic(47) then begin
                  Result := 47; //火龙焰
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
                  Result := 11; //雷电术
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
                  Result := 5; //大火球
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
                  Result := 1; //火球术
                  Exit;
                end;
              end;
            4, 5: begin
                if AllowUseMagic(37) then begin
                  Result := 37; //群体雷电术
                  Exit;
                end;
                if AllowUseMagic(23) then begin
                  Result := 23; //爆裂火焰
                  Exit;
                end;
                if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
                  if AllowUseMagic(33) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end else begin
                  if AllowUseMagic(33) and (nRangeTargetCount >= 5) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end;
                if AllowUseMagic(45) then begin
                  Result := 45; //灭天火
                  Exit;
                end;
                if AllowUseMagic(47) then begin
                  Result := 47; //火龙焰
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
                  Result := 11; //雷电术
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
                  Result := 5; //大火球
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
                  Result := 1; //火球术
                  Exit;
                end;
              end;
            6, 7: begin
                if QuickLighting and (nRangeTargetCountByDir >= 3) then begin
                  Result := 10; //疾光电影
                  Exit;
                end;
                if AllowUseMagic(37) then begin
                  Result := 37; //群体雷电术
                  Exit;
                end;
                if AllowUseMagic(23) then begin
                  Result := 23; //爆裂火焰
                  Exit;
                end;
                if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
                  if AllowUseMagic(33) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end else begin
                  if AllowUseMagic(33) and (nRangeTargetCount >= 5) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end;
                if AllowUseMagic(45) then begin
                  Result := 45; //灭天火
                  Exit;
                end;
                if AllowUseMagic(47) then begin
                  Result := 47; //火龙焰
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
                  Result := 11; //雷电术
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
                  Result := 5; //大火球
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
                  Result := 1; //火球术
                  Exit;
                end;
              end;
          end;
        end;

        if (nSelfRangeTargetCount >= 1) or ((m_wLastHP - m_WAbil.HP) > (m_WAbil.MaxHP div 5)) then begin //被怪物包围
          m_wLastHP := m_WAbil.HP;

          if AllowUseMagic(24) and ((m_TargetCret.m_btRaceServer = 101) or (m_TargetCret.m_btRaceServer = 102) or (m_TargetCret.m_btRaceServer = 104)) then begin //祖玛系怪物
            Result := 24; //地狱雷光
            Exit;
          end;

          if QuickLighting and (nRangeTargetCountByDir >= 3) then begin
            Result := 10; //疾光电影
            Exit;
          end;

          if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
            if AllowUseMagic(33) and (nSelfRangeTargetCount >= 2) then begin
              Result := 33; //冰咆哮
              Exit;
            end;
          end else begin
            if AllowUseMagic(33) and (nSelfRangeTargetCount >= 5) then begin
              Result := 33; //冰咆哮
              Exit;
            end;
          end;

          if AllowUseMagic(37) then begin
            Result := 37; //群体雷电术
            Exit;
          end;

          if AllowUseMagic(45) then begin
            Result := 45; //灭天火
            Exit;
          end;

          if AllowUseMagic(23) then begin
            Result := 23; //爆裂火焰
            Exit;
          end;

          if AllowUseMagic(47) then begin
            Result := 47; //火龙焰
            Exit;
          end;

          if AllowUseMagic(24) then begin
            Result := 24; //地狱雷光
            Exit;
          end;
        end;

        if nRangeTargetCount > 1 then begin
          if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
            if UseFireCross then begin
              Result := 22; //火墙
              Exit;
            end;
          end else begin
            if nRangeTargetCountByDir > 1 then begin
              if UseFireCross then begin
                Result := 22; //火墙
                Exit;
              end;
            end;
          end;
          case Random(7) of
            0, 1: begin
                if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
                  if AllowUseMagic(33) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end else begin
                  if AllowUseMagic(33) and (nRangeTargetCount >= 5) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end;
                if QuickLighting and (nRangeTargetCountByDir >= 3) then begin
                  Result := 10; //疾光电影
                  Exit;
                end;
                if AllowUseMagic(23) then begin
                  Result := 23; //爆裂火焰
                  Exit;
                end;
                if AllowUseMagic(37) then begin
                  Result := 37; //群体雷电术
                  Exit;
                end;
                if AllowUseMagic(45) then begin
                  Result := 45; //灭天火
                  Exit;
                end;
                if AllowUseMagic(47) then begin
                  Result := 47; //火龙焰
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
                  Result := 11; //雷电术
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
                  Result := 5; //大火球
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
                  Result := 1; //火球术
                  Exit;
                end;
              end;
            2, 3: begin
                if AllowUseMagic(23) then begin
                  Result := 23; //爆裂火焰
                  Exit;
                end;
                if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
                  if AllowUseMagic(33) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end else begin
                  if AllowUseMagic(33) and (nRangeTargetCount >= 5) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end;
                if AllowUseMagic(37) then begin
                  Result := 37; //群体雷电术
                  Exit;
                end;
                if AllowUseMagic(45) then begin
                  Result := 45; //灭天火
                  Exit;
                end;
                if AllowUseMagic(47) then begin
                  Result := 47; //火龙焰
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
                  Result := 11; //雷电术
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
                  Result := 5; //大火球
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
                  Result := 1; //火球术
                  Exit;
                end;
              end;
            4, 5: begin
                if AllowUseMagic(37) then begin
                  Result := 37; //群体雷电术
                  Exit;
                end;
                if AllowUseMagic(23) then begin
                  Result := 23; //爆裂火焰
                  Exit;
                end;
                if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
                  if AllowUseMagic(33) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end else begin
                  if AllowUseMagic(33) and (nRangeTargetCount >= 5) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end;
                if AllowUseMagic(45) then begin
                  Result := 45; //灭天火
                  Exit;
                end;
                if AllowUseMagic(47) then begin
                  Result := 47; //火龙焰
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
                  Result := 11; //雷电术
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
                  Result := 5; //大火球
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
                  Result := 1; //火球术
                  Exit;
                end;
              end;
            6, 7: begin
                if QuickLighting and (nRangeTargetCountByDir >= 3) then begin
                  Result := 10; //疾光电影
                  Exit;
                end;
                if AllowUseMagic(37) then begin
                  Result := 37; //群体雷电术
                  Exit;
                end;
                if AllowUseMagic(23) then begin
                  Result := 23; //爆裂火焰
                  Exit;
                end;
                if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
                  if AllowUseMagic(33) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end else begin
                  if AllowUseMagic(33) and (nRangeTargetCount >= 5) then begin
                    Result := 33; //冰咆哮
                    Exit;
                  end;
                end;
                if AllowUseMagic(45) then begin
                  Result := 45; //灭天火
                  Exit;
                end;
                if AllowUseMagic(47) then begin
                  Result := 47; //火龙焰
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
                  Result := 11; //雷电术
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
                  Result := 5; //大火球
                  Exit;
                end;
                if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
                  Result := 1; //火球术
                  Exit;
                end;
              end;
          end;
        end;

        case Random(7) of
          0, 1: begin
              if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
                Result := 11; //雷电术
                Exit;
              end;
              if QuickLighting then begin
                Result := 10; //疾光电影
                Exit;
              end;
              if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
                Result := 5; //大火球
                Exit;
              end;
              if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
                Result := 1; //火球术
                Exit;
              end;
              if AllowUseMagic(37) then begin
                Result := 37; //群体雷电术
                Exit;
              end;
              if AllowUseMagic(23) then begin
                Result := 23; //爆裂火焰
                Exit;
              end;
              if AllowUseMagic(33) then begin
                Result := 33; //冰咆哮
                Exit;
              end;
              if AllowUseMagic(45) then begin
                Result := 45; //灭天火
                Exit;
              end;
              if AllowUseMagic(47) then begin
                Result := 47; //火龙焰
                Exit;
              end;
              if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
                if UseFireCross then begin
                  Result := 22; //火墙
                  Exit;
                end;
              end else begin
                if nRangeTargetCountByDir > 1 then begin
                  if UseFireCross then begin
                    Result := 22; //火墙
                    Exit;
                  end;
                end;
              end;
            end;
          2, 3: begin
              if AllowUseMagic(45) then begin
                Result := 45; //灭天火
                Exit;
              end;
              if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
                if UseFireCross then begin
                  Result := 22; //火墙
                  Exit;
                end;
              end else begin
                if nRangeTargetCountByDir > 1 then begin
                  if UseFireCross then begin
                    Result := 22; //火墙
                    Exit;
                  end;
                end;
              end;
              if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
                Result := 5; //大火球
                Exit;
              end;

              if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
                Result := 11; //雷电术
                Exit;
              end;
              if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
                Result := 1; //火球术
                Exit;
              end;
              if AllowUseMagic(37) then begin
                Result := 37; //群体雷电术
                Exit;
              end;
              if AllowUseMagic(23) then begin
                Result := 23; //爆裂火焰
                Exit;
              end;
              if AllowUseMagic(33) then begin
                Result := 33; //冰咆哮
                Exit;
              end;

              if AllowUseMagic(47) then begin
                Result := 47; //火龙焰
                Exit;
              end;
            end;

          4, 5: begin
              if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
                if UseFireCross then begin
                  Result := 22; //火墙
                  Exit;
                end;
              end else begin
                if nRangeTargetCountByDir > 1 then begin
                  if UseFireCross then begin
                    Result := 22; //火墙
                    Exit;
                  end;
                end;
              end;

              if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
                Result := 11; //雷电术
                Exit;
              end;

              if AllowUseMagic(45) then begin
                Result := 45; //灭天火
                Exit;
              end;

              if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
                Result := 5; //大火球
                Exit;
              end;

              if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
                Result := 1; //火球术
                Exit;
              end;

              if AllowUseMagic(37) then begin
                Result := 37; //群体雷电术
                Exit;
              end;

              if AllowUseMagic(23) then begin
                Result := 23; //爆裂火焰
                Exit;
              end;

              if AllowUseMagic(33) then begin
                Result := 33; //冰咆哮
                Exit;
              end;

              if AllowUseMagic(47) then begin
                Result := 47; //火龙焰
                Exit;
              end;
            end;
          6, 7: begin
              if QuickLighting then begin
                Result := 10; //疾光电影
                Exit;
              end;
              if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
                Result := 11; //雷电术
                Exit;
              end;
              if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
                Result := 5; //大火球
                Exit;
              end;
              if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
                Result := 1; //火球术
                Exit;
              end;
              if AllowUseMagic(37) then begin
                Result := 37; //群体雷电术
                Exit;
              end;
              if AllowUseMagic(23) then begin
                Result := 23; //爆裂火焰
                Exit;
              end;
              if AllowUseMagic(33) then begin
                Result := 33; //冰咆哮
                Exit;
              end;
              if AllowUseMagic(45) then begin
                Result := 45; //灭天火
                Exit;
              end;
              if AllowUseMagic(47) then begin
                Result := 47; //火龙焰
                Exit;
              end;
              if ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or (m_TargetCret.m_btRaceServer = RC_HEROOBJECT) or (m_TargetCret.m_btRaceServer = RC_PLAYMOSTER)) then begin
                if UseFireCross then begin
                  Result := 22; //火墙
                  Exit;
                end;
              end else begin
                if nRangeTargetCountByDir > 1 then begin
                  if UseFireCross then begin
                    Result := 22; //火墙
                    Exit;
                  end;
                end;
              end;
            end;
        end;

        if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(11) then begin
          Result := 11; //雷电术
          Exit;
        end;
        if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(5) then begin
          Result := 5; //大火球
          Exit;
        end;
        if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and AllowUseMagic(1) then begin
          Result := 1; //火球术
          Exit;
        end;
        if QuickLighting then begin
          Result := 10; //疾光电影
          Exit;
        end;
        if AllowUseMagic(37) then begin
          Result := 37; //群体雷电术
          Exit;
        end;
        if AllowUseMagic(23) then begin
          Result := 23; //爆裂火焰
          Exit;
        end;
        if AllowUseMagic(33) then begin
          Result := 33; //冰咆哮
          Exit;
        end;
        if AllowUseMagic(45) then begin
          Result := 45; //灭天火
          Exit;
        end;
        if AllowUseMagic(47) then begin
          Result := 47; //火龙焰
          Exit;
        end;
        if UseFireCross then begin
          Result := 22; //火墙
          Exit;
        end;

      end;
    2: begin //道士
        nCode := 0;
        //try
        if m_WAbil.HP <= m_WAbil.MaxHP * 80 div 100 then begin
          m_wLastHP := m_WAbil.HP;
          if GetTickCount - m_SkillUseTick[29] > 1000 * 5 then begin
            if m_nIncSelfHealthCount <= 3 then begin
              if AllowUseMagic(2) and AllowUseMagic(29) then begin
                nCode := 9;
                if GetRangeFriendCount(m_nCurrX, m_nCurrY, 3) > 1 then begin
                  m_boSelSelf := True;
                  Inc(m_nIncSelfHealthCount);
                  Result := 29;
                end else begin
                  m_boSelSelf := True;
                  Inc(m_nIncSelfHealthCount);
                  Result := 2;
                end;
                Exit;
              end;
              nCode := 10;
              if AllowUseMagic(29) then begin {使用群体治愈术}
                m_boSelSelf := True;
                Inc(m_nIncSelfHealthCount);
                Result := 29;
                Exit;
              end;
              nCode := 11;
              if AllowUseMagic(2) then begin {使用治愈术}
                m_boSelSelf := True;
                Inc(m_nIncSelfHealthCount);
                Result := 2;
                Exit;
              end;
            end else begin
              m_SkillUseTick[29] := GetTickCount;
              m_nIncSelfHealthCount := 0;
            end;
          end;
        end;
        nCode := 12;
        if m_Master <> nil then
          boVisibleActors := FindVisibleActors(m_Master);
        nCode := 13;
        if (m_Master <> nil) and boVisibleActors then begin
          if m_Master.m_WAbil.HP <= m_Master.m_WAbil.MaxHP * 80 div 100 then begin //使用治愈术
            if GetTickCount - m_SkillUseTick[2] > 1000 * 5 then begin
              if m_nIncMasterHealthCount <= 3 then begin

                if AllowUseMagic(2) and AllowUseMagic(29) then begin
                  nCode := 14;
                  if GetRangeFriendCount(m_Master.m_nCurrX, m_Master.m_nCurrY, 3) > 1 then begin
                    m_boSelSelf := False;
                    Inc(m_nIncMasterHealthCount);
                    Result := 29;
                  end else begin
                    m_boSelSelf := False;
                    Inc(m_nIncMasterHealthCount);
                    Result := 2;
                  end;
                  Exit;
                end;
                nCode := 15;
                if AllowUseMagic(29) then begin {使用群体治愈术}
                  m_boSelSelf := False;
                  Inc(m_nIncMasterHealthCount);
                  Result := 29;
                  Exit;
                end;
                nCode := 16;
                if AllowUseMagic(2) then begin {使用治愈术}
                  m_boSelSelf := False;
                  Inc(m_nIncMasterHealthCount);
                  Result := 2;
                  Exit;
                end;
              end else begin
                m_nIncMasterHealthCount := 0;
                m_SkillUseTick[2] := GetTickCount;
              end;
            end;
          end;
        end;

        nCode := 1;
        //IF m_TargetCret <> nil then
        nRangeTargetCount := GetRangeTargetCount(m_TargetCret.m_nCurrX, m_TargetCret.m_nCurrY, 3);
        nCode := 2;
        nSelfRangeTargetCount := GetNearTargetCount;
        nCode := 3;

        if g_Config.boAllowRecallAll then begin
          nCode := 4;
          if AllowUseMagic(59) and ((GetMoonSlaveCount <= 0) or GetMoonSlaveDeath) then begin
            m_boSelSelf := True;
            Result := 59; //召唤月灵
            Exit;
          end;

          if AllowUseMagic(30) and CheckUserItem(5, 5) and ((GetSlaveCount <= 0) or GetSlaveDeath) and (GetTickCount - m_SkillUseTick[30] > 1000 * 5) then begin
            //m_SkillUseTick[30] := GetTickCount;
            m_boSelSelf := True;
            Result := 30; //召唤神兽
            Exit;
          end;
          nCode := 5;
        end;
        nCode := 6;

        if (g_Config.boAllowRecallMoon or (FindMagic(30) = nil)) and AllowUseMagic(59) and ((GetMoonSlaveCount <= 0) or GetMoonSlaveDeath) then begin
          m_boSelSelf := True;
          Result := 59; //召唤月灵
          Exit;
        end;

        if (g_Config.boAllowRecallDogz or (FindMagic(59) = nil)) and AllowUseMagic(30) and CheckUserItem(5, 5) and ((GetSlaveCount <= 0) or GetSlaveDeath) and (GetTickCount - m_SkillUseTick[30] > 1000 * 5) then begin
          //m_SkillUseTick[30] := GetTickCount;
          m_boSelSelf := True;
          Result := 30; //召唤神兽
          Exit;
        end;
        nCode := 7;

        nCode := 17;
        if AllowUseMagic(15) and (m_wStatusTimeArr[STATE_DEFENCEUP] = 0) and CheckUserItem(5, 1) then begin // {使用神圣战甲术}
          m_boSelSelf := True;
          Result := 15;
          Exit;
        end;
        nCode := 18;
        if AllowUseMagic(14) and (m_wStatusTimeArr[STATE_MAGDEFENCEUP] = 0) and CheckUserItem(5, 1) then begin // {幽灵盾}
          m_boSelSelf := True;
          Result := 14;
          Exit;
        end;

        nCode := 19;
        if (m_Master <> nil) and boVisibleActors then begin
          if AllowUseMagic(15) and (m_Master.m_wStatusTimeArr[STATE_DEFENCEUP] = 0) and CheckUserItem(5, 1) then begin // {使用神圣战甲术}
            m_boSelSelf := False;
            Result := 15;
            Exit;
          end;
          if AllowUseMagic(14) and (m_Master.m_wStatusTimeArr[STATE_MAGDEFENCEUP] = 0) and CheckUserItem(5, 1) then begin // {幽灵盾}
            m_boSelSelf := False;
            Result := 14;
            Exit;
          end;
        end;

        if (m_dwStatusArrTimeOutTick[2] <= 0) and AllowUseMagic(36) and (GetTickCount - m_dwSkillUltimateEnhancerDelayTimeTick >= g_Config.nSkill50DelayTime * 1000) then begin {无极真气}
          m_boSelSelf := True;
          Result := 36;
          Exit;
        end;

        nCode := 8;
        if (nSelfRangeTargetCount >= 1) {and IsUseAttackMagic} and IsUseSkill48 then begin
          Result := 48; //气功波
          Exit;
        end;

        if (GetTickCount - m_SkillUseTick[34] > 1000 * 5) and AllowUseMagic(34) and ((m_wStatusTimeArr[POISON_DECHEALTH] > 0) or (m_wStatusTimeArr[POISON_DAMAGEARMOR] > 0)) then begin // {使用解毒术}
          m_boSelSelf := True;
          Result := 34; // 使用解毒术
          Exit;
        end;

        if (GetTickCount - m_SkillUseTick[34] > 1000 * 5) and (m_Master <> nil) and boVisibleActors then begin
          if AllowUseMagic(34) and ((m_Master.m_wStatusTimeArr[POISON_DECHEALTH] > 0) or (m_Master.m_wStatusTimeArr[POISON_DAMAGEARMOR] > 0)) then begin // {使用解毒术}
            m_boSelSelf := False;
            Result := 34; //使用解毒术
            Exit;
          end;
        end;

        nCode := 20;
        if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and (m_TargetCret.m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) and AllowUseMagic(52) and (m_TargetCret.m_wStatusArrValue[m_TargetCret.m_btJob + 6] = 0) and CheckUserItem(3, 3) then begin {诅咒术}
          Result := 52;
          Exit;
        end;
        nCode := 21;

        if IsUseSkill38 then begin
          Result := 38;
          Exit;
        end;

        if AllowUseMagic(6) and CheckUserItem(1, 1) and (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and (m_TargetCret.m_wStatusTimeArr[POISON_DECHEALTH] = 0) and (not m_TargetCret.m_boVirusImmunity) then begin
          m_nSelItemType := 1;
          Result := 6;
          Exit;
        end;

        if AllowUseMagic(6) and CheckUserItem(2, 1) and (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) and (m_TargetCret.m_wStatusTimeArr[POISON_DAMAGEARMOR] = 0) and (not m_TargetCret.m_boVirusImmunity) then begin
          m_nSelItemType := 2;
          Result := 6;
          Exit;
        end;

        if (GetTickCount - m_SkillUseTick[76] > 1000 * 5) and (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) then begin
          if AllowUseMagic(76) and CheckUserItem(5, 5) then begin //噬血术
            Result := 76;
            Exit;
          end;
        end;

        if nRangeTargetCount > 1 then begin
          if AllowUseMagic(72) and CheckUserItem(5, 5) then begin //群体灵魂火符
            Result := 72;
            Exit;
          end;
        end;

        if (m_TargetCret <> nil) and (not m_TargetCret.m_boDeath) then begin
          if AllowUseMagic(13) and CheckUserItem(5, 1) then begin //灵魂火符
            Result := 13;
            Exit;
          end;
        end;

        {except
         MainOutMessage('SelMagic:'+m_sCharName);
        end; }
      end;
  end;
end;

function TAIObject.IsUseMagic(): Boolean; //检测是否可以使用保护魔法
var
  UserMagic: pTUserMagic;
  I, nCount: Integer;
begin
  Result := False;
  case m_btJob of
    2: begin
        for I := 0 to m_MagicList.Count - 1 do begin
          UserMagic := m_MagicList.Items[I];
          case UserMagic.wMagIdx of
              SKILL_HEALLING,
              SKILL_SOULSHIELD,
              SKILL_DEJIWONHO {15},
              SKILL_HOLYSHIELD {16},
              SKILL_SKELLETON {17},
              SKILL_CLOAK {18},
              SKILL_BIGCLOAK {19},
              SKILL_BIGHEALLING {29},
              SKILL_SINSU {30}: begin //需要符
                case UserMagic.wMagIdx of
                  SKILL_HEALLING: nCount := 1;
                  SKILL_SOULSHIELD: nCount := 1;
                  SKILL_DEJIWONHO {15}: nCount := 1;
                  SKILL_HOLYSHIELD {16}: nCount := 1;
                  SKILL_SKELLETON {17}: nCount := 1;
                  SKILL_CLOAK {18}: nCount := 1;
                  SKILL_BIGCLOAK {19}: nCount := 1;
                  SKILL_BIGHEALLING {29}: nCount := 1;
                  SKILL_SINSU {30}: nCount := 5;
                end;
                Result := CheckUserItem(5, nCount);
                if Result then Break;
              end;
          end;
        end;
      end;
  end;
end;

//1 为护身符 2 为毒药

function TAIObject.IsUseAttackMagic(): Boolean; //检测是否可以使用攻击魔法
var
  UserMagic: pTUserMagic;
  I: Integer;
begin
  Result := False;
  case m_btJob of
    0: Result := True;
    1: begin
        for I := 0 to m_MagicList.Count - 1 do begin
          UserMagic := m_MagicList.Items[I];
          case UserMagic.wMagIdx of
              SKILL_FIREBALL,
              SKILL_GREATFIREBALL,
              SKILL_HELLFIRE,
              SKILL_LIGHTNING,
              SKILL_THUNDERBOLT,
              SKILL_EARTHFIRE,
              SKILL_FIREBOOM,
              SKILL_LIGHTFLOWER,
              SKILL_SNOWWIND,
              SKILL_GROUPLIGHTENING,
              SKILL_47,
              SKILL_75,
              SKILL_100: begin
                if GetSpellPoint(UserMagic) <= m_WAbil.MP then begin
                  Result := True;
                  Break;
                end; // else MainOutMessage('GetSpellPoint(UserMagic) > m_WAbil.MP');
              end;
          end;
        end;
      end;
    2: begin
        for I := 0 to m_MagicList.Count - 1 do begin
          UserMagic := m_MagicList.Items[I];
          if UserMagic.MagicInfo.btJob in [2, 99] then begin
            case UserMagic.wMagIdx of
              SKILL_POISONING, SKILL_GROUPAMYOUNSUL {38 群体施毒术}: begin //需要毒药
                  Result := CheckUserItem(1, 2) or CheckUserItem(2, 2);
                  if Result then begin
                    Result := AllowUseMagic(SKILL_POISONING) or AllowUseMagic(SKILL_GROUPAMYOUNSUL);
                  end;
                  if Result then Break;
                end;
              SKILL_SOULFIREBALL: begin
                  Result := CheckUserItem(5, 1);
                  if Result then begin
                    Result := AllowUseMagic(SKILL_SOULFIREBALL);
                  end;
                  if Result then Break;
                end;
              SKILL_72, SKILL_76: begin //需要符
                  Result := CheckUserItem(5, 5);
                  if Result then begin
                    Result := AllowUseMagic(SKILL_SOULFIREBALL);
                  end;
                  if Result then Break;
                end;
             { SKILL_52: begin
                  Result := CheckUserItem(3);
                  if Result then begin
                    Result := AllowUseMagic(SKILL_52);
                  end;
                  if Result then Break;
                end;  }
            end;
          end;
        end;
      end;
  end;
end;

function TAIObject.AutoEatUseItems(btItemType: Byte): Boolean; //自动吃药
  function FoundAddHealthItem(ItemType: Byte): Integer;
  var
    I: Integer;
    UserItem: pTUserItem;
    StdItem: pTStdItem;
    nItemBagCount: Integer;
  begin
    Result := -1;
    if m_btRaceServer = RC_HEROOBJECT then begin
      nItemBagCount := THeroObject(Self).m_nItemBagCount;
    end else begin
      nItemBagCount := g_Config.nCopyHumanBagCount;
    end;
    for I := 0 to m_ItemList.Count - 1 do begin
      UserItem := m_ItemList.Items[I];
      if UserItem <> nil then begin
        StdItem := UserEngine.GetStdItem(UserItem.wIndex);
        if StdItem <> nil then begin
          case ItemType of
            0: begin //红药
                if (StdItem.StdMode = 0) and (StdItem.Shape = 0) and (StdItem.AC > 0) then begin
                  Result := I;
                  Break;
                end;
              end;
            1: begin //蓝药
                if (StdItem.StdMode = 0) and (StdItem.Shape = 0) and (StdItem.MAC > 0) then begin
                  Result := I;
                  Break;
                end;
              end;
            2: begin //太阳水
                if (StdItem.StdMode = 0) and (StdItem.Shape = 1) and (StdItem.AC > 0) and (StdItem.MAC > 0) then begin
                  Result := I;
                  Break;
                end;
              end;
            3: begin //红药包
                if (StdItem.StdMode = 31) and (GetBindItemType(StdItem.Shape) = 0) and (m_ItemList.Count + 6 < nItemBagCount) then begin
                  Result := I;
                  Break;
                end;
              end;
            4: begin //蓝药包
                if (StdItem.StdMode = 31) and (GetBindItemType(StdItem.Shape) = 1) and (m_ItemList.Count + 6 < nItemBagCount) then begin
                  Result := I;
                  Break;
                end;
              end;
          end;
        end;
      end;
    end;
  end;

  function UseAddHealthItem(nItemIdx: Integer): Boolean;
    function EatItems(StdItem: pTStdItem): Boolean;
    var
      bo06: Boolean;
      nOldStatus: Integer;
    begin
      Result := False;
      if m_PEnvir.m_boNODRUG then begin
        Exit;
      end;
      case StdItem.StdMode of
        0: begin
            case StdItem.Shape of {红药}
              0: begin
                  if (StdItem.AC > 0) then begin
                    Inc(m_nIncHealth, StdItem.AC);
                    Result := True;
                  end;
                  if (StdItem.MAC > 0) then begin {蓝药}
                    Inc(m_nIncSpell, StdItem.MAC);
                    Result := True;
                  end;
                end;
              1: begin
                  if (StdItem.AC > 0) and (StdItem.MAC > 0) then begin
                    IncHealthSpell(StdItem.AC, StdItem.MAC);
                    Result := True;
                  end;
                end;
            end;
          end;
      end;
    end;

    function GetUnbindItemName(nShape: Integer): string;
    var
      I: Integer;
    begin
      Result := '';
      for I := 0 to g_UnbindList.Count - 1 do begin
        if Integer(g_UnbindList.Objects[I]) = nShape then begin
          Result := g_UnbindList.Strings[I];
          Break;
        end;
      end;
    end;

    function GetUnBindItems(sItemName: string; nCount: Integer): Boolean;
    var
      I: Integer;
      UserItem: pTUserItem;
    begin
      Result := False;
      for I := 0 to nCount - 1 do begin
        New(UserItem);
        if UserEngine.CopyToUserItemFromName(sItemName, UserItem) then begin
          m_ItemList.Add(UserItem);
          Result := True;
        end else begin
          Dispose(UserItem);
          Break;
        end;
      end;
    end;

  var
    UserItem: pTUserItem;
    StdItem: pTStdItem;
  begin
    Result := False;
    UserItem := m_ItemList.Items[nItemIdx];
    if UserItem <> nil then begin
      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if StdItem <> nil then begin
        if not m_PEnvir.AllowStdItems(UserItem.wIndex) then begin
          Exit;
        end;
        case StdItem.StdMode of
          0 {, 1, 2, 3}: begin //药
              if EatItems(StdItem) then begin
                m_ItemList.Delete(nItemIdx);
                Dispose(UserItem);
                //m_WAbil.Weight := RecalcBagWeight();
                Result := True;
              end;
            end;
          31: begin //解包物品
              //MainOutMessage('解包物品 ' + IntToStr(GetBindItemType(StdItem.Shape)));
              if (StdItem.AniCount = 0) and (GetBindItemType(StdItem.Shape) >= 0) then begin
                //if (m_ItemList.Count + 6 - 1) <= MAXBAGITEM then begin
                m_ItemList.Delete(nItemIdx);
                Dispose(UserItem);
                GetUnBindItems(GetUnbindItemName(StdItem.Shape), 6);
                Result := True;
              end;
            end;
        end;
      end;
    end;
  end;
var
  nItemIdx: Integer;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
  boFound: Boolean;
begin
  boFound := False;
  if not m_boDeath then begin
    nItemIdx := FoundAddHealthItem(btItemType);
    if (nItemIdx >= 0) and (nItemIdx < m_ItemList.Count) then begin
      if m_btRaceServer = RC_HEROOBJECT then begin
        UserItem := pTUserItem(m_ItemList.Items[nItemIdx]);
        THeroObject(Self).SendDelItems(UserItem);
        THeroObject(Self).ClientUseItems(UserItem.MakeIndex, UserEngine.GetStdItem(UserItem.wIndex).Name);
      end else begin
        UseAddHealthItem(nItemIdx);
      end;
      boFound := True;
    end else begin
      case btItemType of //查找解包物品
        0: begin
            nItemIdx := FoundAddHealthItem(3);
            if (nItemIdx >= 0) and (nItemIdx < m_ItemList.Count) then begin
              if m_btRaceServer = RC_HEROOBJECT then begin
                UserItem := pTUserItem(m_ItemList.Items[nItemIdx]);
                THeroObject(Self).SendDelItems(UserItem);
                THeroObject(Self).ClientUseItems(UserItem.MakeIndex, UserEngine.GetStdItem(UserItem.wIndex).Name);
              end else begin
                UseAddHealthItem(nItemIdx);
              end;
              boFound := True;
            end else begin
              nItemIdx := FoundAddHealthItem(2);
              if (nItemIdx >= 0) and (nItemIdx < m_ItemList.Count) then begin
                if m_btRaceServer = RC_HEROOBJECT then begin
                  UserItem := pTUserItem(m_ItemList.Items[nItemIdx]);
                  THeroObject(Self).SendDelItems(UserItem);
                  THeroObject(Self).ClientUseItems(UserItem.MakeIndex, UserEngine.GetStdItem(UserItem.wIndex).Name);
                end else begin
                  UseAddHealthItem(nItemIdx);
                end;
                boFound := True;
              end;
            end;
          end;
        1: begin
            nItemIdx := FoundAddHealthItem(4);
            if (nItemIdx >= 0) and (nItemIdx < m_ItemList.Count) then begin
              if m_btRaceServer = RC_HEROOBJECT then begin
                UserItem := pTUserItem(m_ItemList.Items[nItemIdx]);
                THeroObject(Self).SendDelItems(UserItem);
                THeroObject(Self).ClientUseItems(UserItem.MakeIndex, UserEngine.GetStdItem(UserItem.wIndex).Name);
              end else begin
                UseAddHealthItem(nItemIdx);
              end;
              boFound := True;
            end else begin
              nItemIdx := FoundAddHealthItem(2);
              if (nItemIdx >= 0) and (nItemIdx < m_ItemList.Count) then begin
                if m_btRaceServer = RC_HEROOBJECT then begin
                  UserItem := pTUserItem(m_ItemList.Items[nItemIdx]);
                  THeroObject(Self).SendDelItems(UserItem);
                  THeroObject(Self).ClientUseItems(UserItem.MakeIndex, UserEngine.GetStdItem(UserItem.wIndex).Name);
                end else begin
                  UseAddHealthItem(nItemIdx);
                end;
                boFound := True;
              end;
            end;
          end;
      end;
    end;
  end;
  if not boFound then begin
    if m_btRaceServer = RC_HEROOBJECT then begin
      case btItemType of
        0: THeroObject(Self).SysMsg('HP Potion has been used', 251, 249, t_Hint);
        1: THeroObject(Self).SysMsg('MP Potion has been used', 251, 180, t_Hint);
      end;
    end;
  end;
end;

procedure TAIObject.StartEatItems(); {吃药}
begin
  if (not m_boDeath) and ((GetTickCount - m_dwEatItemTick) > 2 * 1000) then begin
    m_dwEatItemTick := GetTickCount();
    if (m_nCopyHumanLevel > 0) and (m_ItemList.Count > 0) then begin
      if m_btRaceServer = RC_HEROOBJECT then begin
        if m_WAbil.HP < (m_WAbil.MaxHP * g_Config.nHeroAddHPRate) div 100 then begin
          AutoEatUseItems(0);
        end;
        if m_WAbil.MP < (m_WAbil.MaxMP * g_Config.nHeroAddMPRate) div 100 then begin
          AutoEatUseItems(1);
        end;
      end else
        if m_btRaceServer = RC_PLAYMOSTER then begin
        if m_WAbil.HP < (m_WAbil.MaxHP * g_Config.nCopyHumAddHPRate) div 100 then begin
          AutoEatUseItems(0);
        end;
        if m_WAbil.MP < (m_WAbil.MaxMP * g_Config.nCopyHumAddMPRate) div 100 then begin
          AutoEatUseItems(1);
        end;
      end;
    end;
  end;
end;


procedure TActorObject.TakeOnGroupItem(UserItem: pTUserItem);
begin

end;

procedure TActorObject.RecalcAbilitys;
  function GetPercent(Percent: Integer; Value: Word): Integer;
  begin
    if Percent > 0 then Result := Value + Value * Percent div 100
    else Result := Value;
  end;
  function GetHeroGroup(Level: Byte; Value, HValue: Word): Word;
  var
    nValue: Integer;
  begin
    nValue := Value;
    if Level > 0 then
      case Level of
        1: nValue := Value + _MAX(HValue * 30 div 100, 0);
        2: nValue := Value + _MAX(HValue * 60 div 100, 0);
        3: nValue := Value + _MAX(HValue, 0);
      else nValue := Value + _MAX(HValue, 0);
      end;
    Result := _MIN(MAXHUMPOWER, nValue);
  end;

  function GetHeroGroupLong(Level: Byte; Value, HValue: LongInt): LongInt;
  var
    nValue: Int64;
  begin
    nValue := Value;
    if Level > 0 then
      case Level of
        1: nValue := Value + MaxLong(HValue * 30 div 100, 0);
        2: nValue := Value + MaxLong(HValue * 60 div 100, 0);
        3: nValue := Value + MaxLong(HValue, 0);
      else nValue := Value + MaxLong(HValue, 0);
      end;
    Result := MinLong(g_Config.nMaxLevel, nValue);
  end;

resourcestring
  sExceptionMsg = '[Exception] TActorObject::RecalcAbilitys %d';
var
  Abil: TAbility;
  boOldHideMode: Boolean;
  nOldLight: Integer;
  I, II, III: Integer;
  StdItem: pTStdItem;
  boRecallSuite1: Boolean;
  boRecallSuite2: Boolean;
  boRecallSuite3: Boolean;
  boRecallSuite4: Boolean;
  boMoXieSuite1: Boolean;
  boMoXieSuite2: Boolean;
  boMoXieSuite3: Boolean;
  boHongMoSuite1: Boolean;
  boHongMoSuite2: Boolean;
  boHongMoSuite3: Boolean;
  boSpirit1: Boolean;
  boSpirit2: Boolean;
  boSpirit3: Boolean;
  boSpirit4: Boolean;
  n01, n02, n03, n04: Integer;
  PlayObject: TPlayObject;
  HeroObject: THeroObject;

  nItemMemberCount: Integer;
  boFindGroupItems: Boolean;
  nShield: Integer;

  nPercent_00: Integer;
  nPercent_01: Integer;
  nPercent_02: Integer;
  nPercent_03: Integer;
  nPercent_04: Integer;

  nPercent_05: Integer;
  nPercent_06: Integer;
  nPercent_07: Integer;
  nPercent_08: Integer;
  nPercent_09: Integer;

  nPercent_10: Integer;
  nPercent_11: Integer;
  nPercent_12: Integer;
  nPercent_13: Integer;

  btLevel: Byte;
  btJob: Byte;
  GroupItem: pTGroupItem;
begin
  FillChar(m_AddAbil, SizeOf(TAddAbility), #0);

  nPercent_00 := 0;
  nPercent_01 := 0;
  nPercent_02 := 0;
  nPercent_03 := 0;
  nPercent_04 := 0;

  nPercent_05 := 0;
  nPercent_06 := 0;
  nPercent_07 := 0;
  nPercent_08 := 0;
  nPercent_09 := 0;

  nPercent_10 := 0;
  nPercent_11 := 0;
  nPercent_12 := 0;
  nPercent_13 := 0;

  Abil := m_WAbil;
  m_WAbil := m_Abil;
  m_WAbil.HP := Abil.HP;
  m_WAbil.MP := Abil.MP;
  m_WAbil.Weight := 0;
  m_WAbil.WearWeight := 0;
  m_WAbil.HandWeight := 0;
  m_btAntiPoison := 0;
  m_nPoisonRecover := 0;
  m_nHealthRecover := 0;
  m_nSpellRecover := 0;
  m_nAntiMagic := 1;
  m_nLuck := 0;
  m_nHitSpeed := 0;
  m_boExpItem := False;
  m_rExpItem := 0;
  m_boPowerItem := False;
  m_rPowerItem := 0;
  boOldHideMode := m_boHideMode;
  m_boHideMode := False;
  m_boTeleport := False;
  m_boParalysis := False;
  m_boRevival := False;
  m_boUnRevival := False;
  m_boFlameRing := False;
  m_boRecoveryRing := False;
  m_boAngryRing := False;
  m_boMagicShield := False;
  m_boUnMagicShield := False;
  m_boMuscleRing := False;
  m_boFastTrain := False;
  m_boProbeNecklace := False;
  m_boSupermanItem := False;
  m_boGuildMove := False;
  m_boUnParalysis := False;
  m_boExpItem := False;
  m_boPowerItem := False;
  m_boNoDropItem := False;
  m_boNoDropUseItem := False;
  m_bopirit := False;
  m_btHorseType := 0;
  m_btDressEffType := 0;

  m_nMoXieSuite := 0;
  boMoXieSuite1 := False;
  boMoXieSuite2 := False;
  boMoXieSuite3 := False;
  m_db3B0 := 0;
  m_nHongMoSuite := 0;
  m_boPoisonItem := False; //中毒装备
  m_boDamageHPItem := False; //被攻击掉对方血
  boHongMoSuite1 := False;
  boHongMoSuite2 := False;
  boHongMoSuite3 := False;

  boSpirit1 := False;
  boSpirit2 := False;
  boSpirit3 := False;
  boSpirit4 := False;

  m_boRecallSuite := False;
  boRecallSuite1 := False;
  boRecallSuite2 := False;
  boRecallSuite3 := False;
  boRecallSuite4 := False;

  m_dwPKDieLostExp := 0;
  m_nPKDieLostLevel := 0;

  m_boExpGroupItem := False;
  m_boDCGroupItem := False;
  m_boMCGroupItem := False;
  m_boSCGroupItem := False;

  m_rExpGroupItem := 0;
  m_rDCGroupItem := 0;
  m_rMCGroupItem := 0;
  m_rSCGroupItem := 0;

  m_boVirusImmunity := False; //病毒免疫

  m_boIsUnknowActor := m_boMaskedActor; //神秘人
  for I := Low(THumanUseItems) to High(THumanUseItems) do begin

    if (m_UseItems[I].wIndex <= 0) or (m_UseItems[I].Dura <= 0) then Continue;

    StdItem := UserEngine.GetStdItem(m_UseItems[I].wIndex);

    if StdItem = nil then Continue;

    GetAccessory(m_UseItems[I], StdItem, m_AddAbil);

    case m_UseItems[I].AddPoint[1] of
      1: Inc(nPercent_00, m_UseItems[I].AddPoint[2]);
      2: Inc(nPercent_01, m_UseItems[I].AddPoint[2]);
      3: Inc(nPercent_02, m_UseItems[I].AddPoint[2]);
      4: Inc(nPercent_03, m_UseItems[I].AddPoint[2]);
      5: Inc(nPercent_04, m_UseItems[I].AddPoint[2]);
    end;

    Inc(nPercent_05, m_UseItems[I].AddPoint[3]);
    Inc(nPercent_06, m_UseItems[I].AddPoint[4]);
    Inc(nPercent_07, m_UseItems[I].AddPoint[5]);
    Inc(nPercent_08, m_UseItems[I].AddPoint[6]);
    Inc(nPercent_09, m_UseItems[I].AddPoint[7]);

    Inc(nPercent_10, m_UseItems[I].AddPoint[8]);
    Inc(nPercent_11, m_UseItems[I].AddPoint[9]);
    Inc(nPercent_12, m_UseItems[I].AddPoint[10]);
    Inc(nPercent_13, m_UseItems[I].AddPoint[11]);

    m_AddAbil.MoveSpeed := m_AddAbil.MoveSpeed + m_UseItems[I].AddPoint[12];
    m_AddAbil.AttackSpeed := m_AddAbil.AttackSpeed + m_UseItems[I].AddPoint[13];

    if (I = U_WEAPON) or (I = U_RIGHTHAND) or (I = U_DRESS) then begin
      if I = U_DRESS then begin
        Inc(m_WAbil.WearWeight, StdItem.Weight);
      end else begin
        Inc(m_WAbil.HandWeight, StdItem.Weight);
      end;
      //新增开始
      if StdItem.AniCount = 120 then m_boFastTrain := True;
      if StdItem.AniCount = 121 then m_boProbeNecklace := True;
      if StdItem.AniCount = 145 then m_boGuildMove := True;
      if StdItem.AniCount = 111 then begin
        m_wStatusTimeArr[STATE_TRANSPARENT {8 0x70}] := 6 * 10 * 1000;
        m_boHideMode := True;
      end;
      if StdItem.AniCount = 112 then m_boTeleport := True;
      if StdItem.AniCount = 113 then m_boParalysis := True;
      if StdItem.AniCount = 114 then m_boRevival := True;
      if StdItem.AniCount = 115 then m_boFlameRing := True;
      if StdItem.AniCount = 116 then m_boRecoveryRing := True;
      if StdItem.AniCount = 117 then m_boAngryRing := True;
      if StdItem.AniCount = 118 then m_boMagicShield := True;
      if StdItem.AniCount = 119 then m_boMuscleRing := True;
      if StdItem.AniCount = 135 then begin
        boMoXieSuite1 := True;
        Inc(m_nMoXieSuite, StdItem.Weight div 10);
      end;
      if StdItem.AniCount = 138 then begin //吸血武器
        Inc(m_nHongMoSuite, StdItem.Weight);
      end;
      if StdItem.AniCount = 139 then m_boUnParalysis := True;
      if StdItem.AniCount = 140 then m_boSupermanItem := True;
      if StdItem.AniCount = 141 then begin
        m_boExpItem := True;
        if g_Config.nItemExpRate = 0 then g_Config.nItemExpRate := 10;
        m_rExpItem := m_rExpItem + (m_UseItems[I].Dura / g_Config.nItemExpRate);
      end;
      if StdItem.AniCount = 142 then begin
        m_boPowerItem := True;
        if g_Config.nItemPowerRate = 0 then g_Config.nItemPowerRate := 10;
        m_rPowerItem := m_rPowerItem + (m_UseItems[I].Dura / g_Config.nItemPowerRate);
      end;
      if StdItem.AniCount = 182 then begin
        m_boExpItem := True;
        if g_Config.nItemExpRate = 0 then g_Config.nItemExpRate := 10;
        m_rExpItem := m_rExpItem + (m_UseItems[I].DuraMax / g_Config.nItemExpRate);
      end;
      if StdItem.AniCount = 183 then begin
        m_boPowerItem := True;
        if g_Config.nItemPowerRate = 0 then g_Config.nItemPowerRate := 10;
        m_rPowerItem := m_rPowerItem + (m_UseItems[I].DuraMax / g_Config.nItemPowerRate);
      end;

      if StdItem.AniCount = 143 then m_boUnMagicShield := True;
      if StdItem.AniCount = 144 then m_boUnRevival := True;
      if StdItem.AniCount = 170 then m_boAngryRing := True;
      if StdItem.AniCount = 171 then m_boNoDropItem := True;
      if StdItem.AniCount = 172 then m_boNoDropUseItem := True;
      if StdItem.AniCount = 150 then begin //麻痹护身
        m_boParalysis := True;
        m_boMagicShield := True;
      end;
      if StdItem.AniCount = 151 then begin //麻痹火球
        m_boParalysis := True;
        m_boFlameRing := True;
      end;
      if StdItem.AniCount = 152 then begin //麻痹防御
        m_boParalysis := True;
        m_boRecoveryRing := True;
      end;
      if StdItem.AniCount = 153 then begin //麻痹负载
        m_boParalysis := True;
        m_boMuscleRing := True;
      end;
      if StdItem.Shape = 154 then begin //护身火球
        m_boMagicShield := True;
        m_boFlameRing := True;
      end;
      if StdItem.Shape = 101 then begin //Item1
        m_boRecoveryRing := True;
      end;
      if StdItem.AniCount = 155 then begin //护身防御
        m_boMagicShield := True;
        m_boRecoveryRing := True;
      end;
      if StdItem.AniCount = 156 then begin //护身负载
        m_boMagicShield := True;
        m_boMuscleRing := True;
      end;

      if StdItem.AniCount = 157 then begin //传送麻痹
        m_boTeleport := True;
        m_boParalysis := True;
      end;

      if StdItem.AniCount = 158 then begin //传送护身
        m_boTeleport := True;
        m_boMagicShield := True;
      end;

      if StdItem.AniCount = 159 then begin //传送探测
        m_boTeleport := True;
        m_boProbeNecklace := True;
      end;
      if StdItem.AniCount = 160 then begin //传送复活
        m_boTeleport := True;
        m_boRevival := True;
      end;
      if StdItem.AniCount = 161 then begin //麻痹复活
        m_boParalysis := True;
        m_boRevival := True;
      end;
      if StdItem.AniCount = 162 then begin //护身复活
        m_boMagicShield := True;
        m_boRevival := True;
      end;

      if StdItem.AniCount = 163 then begin //中毒装备
        m_boPoisonItem := True;
      end;
      if StdItem.AniCount = 164 then begin //被攻击减对方血的装备
        m_boDamageHPItem := True;
      end;

      if StdItem.AniCount = 165 then begin //毒免疫
        m_boVirusImmunity := True;
      end;

      if StdItem.AniCount = 180 then begin //PK 死亡掉经验
        m_dwPKDieLostExp := StdItem.DuraMax * g_Config.dwPKDieLostExpRate;
        //        m_nPKDieLostLevel:=1;
      end;
      if StdItem.AniCount = 181 then begin //PK 死亡掉等级
        if g_Config.nPKDieLostLevelRate = 0 then g_Config.nPKDieLostLevelRate := 1;
        m_nPKDieLostLevel := StdItem.DuraMax div g_Config.nPKDieLostLevelRate;
      end;
      //新增结束
    end else begin
      Inc(m_WAbil.WearWeight, StdItem.Weight);
    end;
    Inc(m_WAbil.Weight, StdItem.Weight);
    if (I = U_WEAPON) then begin
      if (StdItem.Source - 1 - 10) < 0 then
        m_AddAbil.btWeaponStrong := StdItem.Source; //强度+
      if (StdItem.Source <= -1) and (StdItem.Source >= -50) then // -1 至 -50
        m_AddAbil.bt1DF := m_AddAbil.bt1DF + -StdItem.Source; //神圣+
      if (StdItem.Source <= -51) and (StdItem.Source >= -100) then // -51 至 - 100
        m_AddAbil.bt1DF := m_AddAbil.bt1DF + (StdItem.Source + 50); //神圣-
      Continue;
    end;
    if (I = U_RIGHTHAND) then begin
      if StdItem.Shape in [1..50] then
        m_btDressEffType := StdItem.Shape;
      if StdItem.Shape in [51..100] then begin //马牌
        m_btHorseType := StdItem.Shape - 50;
        Inc(m_WAbil.MaxWeight, StdItem.AniCount * StdItem.Weight);
      end;
      Continue;
    end;

    if (I = U_MOUNT) then begin
    if StdItem.Shape in [1..50] then
        m_btDressEffType := StdItem.Shape;
        if StdItem.Shape in [51..100] then begin //马牌
        m_btHorseType := StdItem.Shape - 50; //100
        Inc(m_WAbil.MaxWeight, StdItem.AniCount * StdItem.Weight);
      end;
      Continue;
    end;


    if (I = U_DRESS) then begin
      if m_UseItems[I].btValue[5] > 0 then
        m_btDressEffType := m_UseItems[I].btValue[5];
      if StdItem.AniCount > 0 then
        m_btDressEffType := StdItem.AniCount;
      Continue;
    end;
          //////////////////5 Point Items
         if (I = U_ITEM2) then begin
        m_boPoisonItem := True;
        end;

        if (I = U_ITEM3) then begin
  Inc(m_nHealthRecover, m_AddAbil.wHealthRecover +5);//50%
        end;

        if (I = U_ITEM4) then begin
  Inc(m_nSpellRecover, m_AddAbil.wSpellRecover +5);//50%
        end;

        if (I = U_ITEM5) then begin
        m_boFastTrain := True;
        m_boTeleport := True;
        end;

     //   if (I = (U_ITEM1) and (U_ITEM2) and (U_ITEM3) and (U_ITEM4) and U_ITEM5) then begin
        if StdItem.Stdmode in [101..105]{[101,102,103,104,105]} then begin
        m_boObMode := True;
        m_boTransparent := True;
        end;
        
        /////////////////////////////Level 80 effect
       if m_Abil.Level > 79 then
        m_btDressEffType := 50;

    /////////////////////////////

    //新增开始
    if StdItem.Shape = 139 then m_boUnParalysis := True;
    if StdItem.Shape = 140 then m_boSupermanItem := True;
    if StdItem.Shape = 141 then begin
      m_boExpItem := True;
      m_rExpItem := m_rExpItem + (m_UseItems[I].Dura / g_Config.nItemExpRate);
    end;
    if StdItem.Shape = 142 then begin
      m_boPowerItem := True;
      m_rPowerItem := m_rPowerItem + (m_UseItems[I].Dura / g_Config.nItemPowerRate);
    end;
    if StdItem.Shape = 182 then begin
      m_boExpItem := True;
      m_rExpItem := m_rExpItem + (m_UseItems[I].DuraMax / g_Config.nItemExpRate);
    end;
    if StdItem.Shape = 183 then begin
      m_boPowerItem := True;
      m_rPowerItem := m_rPowerItem + (m_UseItems[I].DuraMax / g_Config.nItemPowerRate);
    end;
    if StdItem.Shape = 143 then m_boUnMagicShield := True;
    if StdItem.Shape = 144 then m_boUnRevival := True;
    if StdItem.Shape = 170 then m_boAngryRing := True;
    if StdItem.Shape = 171 then m_boNoDropItem := True;
    if StdItem.Shape = 172 then m_boNoDropUseItem := True;

    if StdItem.Shape = 150 then begin //麻痹护身
      m_boParalysis := True;
      m_boMagicShield := True;
    end;
    if StdItem.Shape = 151 then begin //麻痹火球
      m_boParalysis := True;
      m_boFlameRing := True;
    end;
    if StdItem.Shape = 152 then begin //麻痹防御
      m_boParalysis := True;
      m_boRecoveryRing := True;
    end;
    if StdItem.Shape = 153 then begin //麻痹负载
      m_boParalysis := True;
      m_boMuscleRing := True;
    end;
    if StdItem.Shape = 154 then begin //护身火球
      m_boMagicShield := True;
      m_boFlameRing := True;
    end;
    if StdItem.Shape = 101 then begin //Item1
        m_boRecoveryRing := True;
      end;
    if StdItem.Shape = 155 then begin //护身防御
      m_boMagicShield := True;
      m_boRecoveryRing := True;
    end;
    if StdItem.Shape = 156 then begin //护身负载
      m_boMagicShield := True;
      m_boMuscleRing := True;
    end;

    if StdItem.Shape = 157 then begin //传送麻痹
      m_boTeleport := True;
      m_boParalysis := True;
    end;

    if StdItem.Shape = 158 then begin //传送护身
      m_boTeleport := True;
      m_boMagicShield := True;
    end;

    if StdItem.Shape = 159 then begin //传送探测
      m_boTeleport := True;
      m_boProbeNecklace := True;
    end;
    if StdItem.Shape = 160 then begin //传送复活
      m_boTeleport := True;
      m_boRevival := True;
    end;
    if StdItem.Shape = 161 then begin //麻痹复活
      m_boParalysis := True;
      m_boRevival := True;
    end;
    if StdItem.Shape = 162 then begin //护身复活
      m_boMagicShield := True;
      m_boRevival := True;
    end;
    if StdItem.Shape = 180 then begin //PK 死亡掉经验
      m_dwPKDieLostExp := StdItem.DuraMax * g_Config.dwPKDieLostExpRate;
      //        m_nPKDieLostLevel:=1;
    end;
    if StdItem.Shape = 181 then begin //PK 死亡掉等级
      if g_Config.nPKDieLostLevelRate = 0 then g_Config.nPKDieLostLevelRate := 1;
      m_nPKDieLostLevel := StdItem.DuraMax div g_Config.nPKDieLostLevelRate;
    end;

    if I = U_BUJUK then begin
      if (StdItem.StdMode = 25) and (StdItem.Shape = 6) then begin //传送符
        m_boTeleport := True;
      end;
    end;

    if I = U_BOOTS then begin //新增加鞋子
      Inc(m_WAbil.MaxWearWeight, StdItem.AniCount * StdItem.Weight);
    end;

    if I = U_HELMET then begin //神秘人
      if StdItem.Shape in [1, 2] then m_boIsUnknowActor := True;
    end;

    //新增结束
  //if (i = U_NECKLACE) then begin
    if StdItem.Shape = 120 then m_boFastTrain := True;
    if StdItem.Shape = 121 then m_boProbeNecklace := True;
    if StdItem.Shape = 123 then boRecallSuite1 := True;
    if StdItem.Shape = 145 then m_boGuildMove := True;
    if StdItem.Shape = 127 then boSpirit1 := True;
    if StdItem.Shape = 135 then begin
      boMoXieSuite1 := True;
      Inc(m_nMoXieSuite, StdItem.AniCount);
    end;
    if StdItem.Shape = 138 then begin
      boHongMoSuite1 := True;
      Inc(m_nHongMoSuite, StdItem.AniCount);
    end;
    //end;
    //if (i = U_RINGR) or (i = U_RINGL) then begin
    if StdItem.Shape = 111 then begin
      m_wStatusTimeArr[STATE_TRANSPARENT {8 0x70}] := 6 * 10 * 1000;
      m_boHideMode := True;
    end;
    if StdItem.Shape = 112 then m_boTeleport := True;
    if StdItem.Shape = 113 then m_boParalysis := True;
    if StdItem.Shape = 114 then m_boRevival := True;
    if StdItem.Shape = 115 then m_boFlameRing := True;
    if StdItem.Shape = 116 then m_boRecoveryRing := True;
    if StdItem.Shape = 117 then m_boAngryRing := True;
    if StdItem.Shape = 118 then m_boMagicShield := True;
    if StdItem.Shape = 119 then m_boMuscleRing := True;
    if StdItem.Shape = 122 then boRecallSuite2 := True;
    if StdItem.Shape = 128 then boSpirit2 := True;
    if StdItem.Shape = 133 then begin
      boMoXieSuite2 := True;
      Inc(m_nMoXieSuite, StdItem.AniCount);
    end;
    if StdItem.Shape = 136 then begin
      boHongMoSuite2 := True;
      Inc(m_nHongMoSuite, StdItem.AniCount);
    end;
    //end;
    //if (i = U_ARMRINGL) or (i = U_ARMRINGR) then begin
    if StdItem.Shape = 124 then boRecallSuite3 := True;
    if StdItem.Shape = 126 then boSpirit3 := True;
    if StdItem.Shape = 145 then m_boGuildMove := True;
    if StdItem.Shape = 134 then begin
      boMoXieSuite3 := True;
      Inc(m_nMoXieSuite, StdItem.AniCount);
    end;
    if StdItem.Shape = 137 then begin
      boHongMoSuite3 := True;
      Inc(m_nHongMoSuite, StdItem.AniCount);
    end;
    //end;
    //if (i = U_HELMET) then begin
    if StdItem.Shape = 125 then boRecallSuite4 := True;
    if StdItem.Shape = 129 then boSpirit4 := True;
    //end;

    if StdItem.Shape = 163 then begin //中毒装备
      m_boPoisonItem := True;
    end;

    if StdItem.Shape = 164 then begin //被攻击减对方血的装备
      m_boDamageHPItem := True;
    end;

    if StdItem.Shape = 165 then begin //毒免疫
      m_boVirusImmunity := True;
    end;
  end; //for I

  if (m_btHorseType = 0) and m_boOnHorse then m_boOnHorse := False; //2007-09-16修正没有骑马不能使用魔法问题

  if boRecallSuite1 and
    boRecallSuite2 and
    boRecallSuite3 and
    boRecallSuite4 then m_boRecallSuite := True;
  if boMoXieSuite1 and
    boMoXieSuite2 and
    boMoXieSuite3 then Inc(m_nMoXieSuite, 50);
  if boHongMoSuite1 and
    boHongMoSuite2 and
    boHongMoSuite3 then Inc(m_AddAbil.wHitPoint, 2);

  if boSpirit1 and
    boSpirit2 and
    boSpirit3 and
    boSpirit4 then m_bopirit := True;

  m_WAbil.Weight := RecalcBagWeight();

  if m_boTransparent and (m_wStatusTimeArr[STATE_TRANSPARENT {8 0x70}] > 0) then
    m_boHideMode := True;

  if m_boHideMode then begin
    if not boOldHideMode then begin
      m_nCharStatus := GetCharStatus();
      StatusChanged();
    end;
  end else begin
    if boOldHideMode then begin
      m_wStatusTimeArr[STATE_TRANSPARENT {8 0x70}] := 0;
      m_nCharStatus := GetCharStatus();
      StatusChanged();
    end;
  end;
  if (m_btRaceServer = RC_PLAYOBJECT) or (m_btRaceServer = RC_HEROOBJECT) or (m_btRaceServer = RC_PLAYMOSTER) then begin //01-20 增加此行，只有类型为人物的角色才重新计算攻击敏捷
    RecalcHitSpeed();
  end;
  nOldLight := m_nLight;
  StdItem := UserEngine.GetStdItem(m_UseItems[U_RIGHTHAND].wIndex);
  if (m_UseItems[U_RIGHTHAND].wIndex > 0) and (m_UseItems[U_RIGHTHAND].Dura > 0) and (StdItem <> nil) and (StdItem.Shape <= 50) then
    m_nLight := 3
  else m_nLight := 0;
  if nOldLight <> m_nLight then
    SendRefMsg(RM_CHANGELIGHT, 0, 0, 0, 0, '');

  Inc(m_btSpeedPoint, m_AddAbil.wSpeedPoint); //敏捷
  Inc(m_btHitPoint, m_AddAbil.wHitPoint); //准确度
  Inc(m_btAntiPoison, m_AddAbil.wAntiPoison); //毒物躲避
  Inc(m_nPoisonRecover, m_AddAbil.wPoisonRecover); //中毒恢复
  Inc(m_nHealthRecover, m_AddAbil.wHealthRecover); //体力恢复
  Inc(m_nSpellRecover, m_AddAbil.wSpellRecover); //魔法恢复
  Inc(m_nAntiMagic, m_AddAbil.wAntiMagic); //魔法躲避
  Inc(m_nLuck, m_AddAbil.btLuck);
  Dec(m_nLuck, m_AddAbil.btUnLuck);
  m_nHitSpeed := m_AddAbil.nHitSpeed;

  Move(m_AddAbil.ATOM_DC, m_WAbil.ATOM_DC, SizeOf(m_WAbil.ATOM_DC));
  Move(m_AddAbil.ATOM_MC, m_WAbil.ATOM_MC, SizeOf(m_WAbil.ATOM_MC));
  Move(m_AddAbil.ATOM_MAC, m_WAbil.ATOM_MAC, SizeOf(m_WAbil.ATOM_MAC));
  m_WAbil.MoveSpeed := _MIN(m_AddAbil.MoveSpeed + g_Config.nWalkSpeedRate, 100);
  m_WAbil.AttackSpeed := _MIN(m_AddAbil.AttackSpeed + g_Config.nAttackSpeedRate, 100);
  m_nHitSpeed := m_nHitSpeed + m_WAbil.AttackSpeed;

  m_WAbil.AddPoint[0] := _MIN(nPercent_00, 100);
  m_WAbil.AddPoint[1] := _MIN(nPercent_01, 100);
  m_WAbil.AddPoint[2] := _MIN(nPercent_02, 100);
  m_WAbil.AddPoint[3] := _MIN(nPercent_03, 100);
  m_WAbil.AddPoint[4] := _MIN(nPercent_04, 100);

  m_WAbil.AddPoint[5] := _MIN(nPercent_05, 100);
  m_WAbil.AddPoint[6] := _MIN(nPercent_06, 100);
  m_WAbil.AddPoint[7] := _MIN(nPercent_07, 100);
  m_WAbil.AddPoint[8] := _MIN(nPercent_08, 100);
  m_WAbil.AddPoint[9] := _MIN(nPercent_09, 100);
  m_WAbil.AddPoint[10] := _MIN(nPercent_10, 100);
  m_WAbil.AddPoint[11] := _MIN(nPercent_11, 100);
  m_WAbil.AddPoint[12] := _MIN(nPercent_12, 100);
  m_WAbil.AddPoint[13] := _MIN(nPercent_13, 100);

  m_WAbil.MaxHP := MinLong(g_Config.nMaxLevel, m_Abil.MaxHP + m_AddAbil.wHP);

  m_WAbil.MaxMP := MinLong(g_Config.nMaxLevel, m_Abil.MaxMP + m_AddAbil.wMP);

  m_WAbil.AC := MakeLong(_MIN(MAXHUMPOWER, LoWord(m_AddAbil.wAC) + LoWord(m_Abil.AC)), _MIN(MAXHUMPOWER, HiWord(m_AddAbil.wAC) + HiWord(m_Abil.AC)));
  m_WAbil.MAC := MakeLong(_MIN(MAXHUMPOWER, LoWord(m_AddAbil.wMAC) + LoWord(m_Abil.MAC)), _MIN(MAXHUMPOWER, HiWord(m_AddAbil.wMAC) + HiWord(m_Abil.MAC)));
  m_WAbil.DC := MakeLong(_MIN(MAXHUMPOWER, LoWord(m_AddAbil.wDC) + LoWord(m_Abil.DC)), _MIN(MAXHUMPOWER, HiWord(m_AddAbil.wDC) + HiWord(m_Abil.DC)));
  m_WAbil.MC := MakeLong(_MIN(MAXHUMPOWER, LoWord(m_AddAbil.wMC) + LoWord(m_Abil.MC)), _MIN(MAXHUMPOWER, HiWord(m_AddAbil.wMC) + HiWord(m_Abil.MC)));
  m_WAbil.SC := MakeLong(_MIN(MAXHUMPOWER, LoWord(m_AddAbil.wSC) + LoWord(m_Abil.SC)), _MIN(MAXHUMPOWER, HiWord(m_AddAbil.wSC) + HiWord(m_Abil.SC)));

  if m_wStatusTimeArr[STATE_DEFENCEUP {10 0x72}] > 0 then
    m_WAbil.AC := MakeLong(LoWord(m_WAbil.AC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.AC) + 2 + (m_Abil.Level div 7)));

  if m_wStatusTimeArr[STATE_MAGDEFENCEUP {11 0x74}] > 0 then
    m_WAbil.MAC := MakeLong(LoWord(m_WAbil.MAC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.MAC) + 2 + (m_Abil.Level div 7)));


  if m_wStatusArrValue[0] > 0 then
    m_WAbil.DC := MakeLong(LoWord(m_WAbil.DC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.DC) + m_wStatusArrValue[0]));

  if m_wStatusArrValue[6] > 0 then begin
    if LoWord(m_WAbil.DC) > (HiWord(m_WAbil.DC) - m_wStatusArrValue[6]) then
      m_WAbil.DC := MakeLong(LoWord(m_WAbil.DC), LoWord(m_WAbil.DC))
    else
      m_WAbil.DC := MakeLong(LoWord(m_WAbil.DC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.DC) - m_wStatusArrValue[6]))
  end;

  if m_wStatusArrValue[1] > 0 then
    m_WAbil.MC := MakeLong(LoWord(m_WAbil.MC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.MC) + m_wStatusArrValue[1]));

  if m_wStatusArrValue[7] > 0 then begin
    if LoWord(m_WAbil.MC) > (HiWord(m_WAbil.MC) - m_wStatusArrValue[7]) then
      m_WAbil.MC := MakeLong(LoWord(m_WAbil.MC), LoWord(m_WAbil.MC))
    else
      m_WAbil.MC := MakeLong(LoWord(m_WAbil.MC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.MC) - m_wStatusArrValue[7]));
  end;

  if m_wStatusArrValue[2] > 0 then
    m_WAbil.SC := MakeLong(LoWord(m_WAbil.SC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.SC) + m_wStatusArrValue[2]));

  if m_wStatusArrValue[8] > 0 then begin
    if LoWord(m_WAbil.SC) > (HiWord(m_WAbil.SC) - m_wStatusArrValue[8]) then
      m_WAbil.SC := MakeLong(LoWord(m_WAbil.SC), LoWord(m_WAbil.SC))
    else
      m_WAbil.SC := MakeLong(LoWord(m_WAbil.SC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.SC) - m_wStatusArrValue[8]));
  end;

  if m_wStatusArrValue[3] > 0 then
    Inc(m_nHitSpeed, m_wStatusArrValue[3]);


  if m_wStatusArrValue[4] > 0 then begin
    m_WAbil.MaxHP := MinLong(g_Config.nMaxLevel, m_WAbil.MaxHP + m_wStatusArrValue[4]);
  end;

  if m_wStatusArrValue[5] > 0 then begin
    m_WAbil.MaxMP := MinLong(g_Config.nMaxLevel, m_WAbil.MaxMP + m_wStatusArrValue[5]);
  end;


{新增}
  if m_wNewStatusArrValue[0] > 0 then //攻击增加
    m_WAbil.DC := MakeLong(LoWord(m_WAbil.DC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.DC) + m_wNewStatusArrValue[0]));

  if m_wNewStatusArrValue[7] > 0 then begin //攻击减少
    n01 := m_wNewStatusArrValue[7];
    if HiWord(m_WAbil.DC) - LoWord(m_WAbil.DC) >= m_wNewStatusArrValue[7] then begin
      m_WAbil.DC := MakeLong(LoWord(m_WAbil.DC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.DC) - m_wNewStatusArrValue[7]));
    end else begin
      Dec(n01, HiWord(m_WAbil.DC) - LoWord(m_WAbil.DC));
      n02 := _MAX(1, LoWord(m_WAbil.DC) - n01 div 2);
      m_WAbil.DC := MakeLong(_MIN(n02, MAXHUMPOWER), _MIN(n02, MAXHUMPOWER));
    end;
  end;

  if m_wNewStatusArrValue[1] > 0 then //魔法增加
    m_WAbil.MC := MakeLong(LoWord(m_WAbil.MC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.MC) + m_wNewStatusArrValue[1]));

  if m_wNewStatusArrValue[8] > 0 then begin //魔法减少
    n01 := m_wNewStatusArrValue[8];
    if HiWord(m_WAbil.MC) - LoWord(m_WAbil.MC) >= m_wNewStatusArrValue[8] then begin
      m_WAbil.MC := MakeLong(LoWord(m_WAbil.MC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.MC) - m_wNewStatusArrValue[8]));
    end else begin
      Dec(n01, HiWord(m_WAbil.MC) - LoWord(m_WAbil.MC));
      n02 := _MAX(1, LoWord(m_WAbil.MC) - n01 div 2);
      m_WAbil.MC := MakeLong(_MIN(n02, MAXHUMPOWER), _MIN(n02, MAXHUMPOWER));
    end;
  end;

  if m_wNewStatusArrValue[2] > 0 then //道术增加
    m_WAbil.SC := MakeLong(LoWord(m_WAbil.SC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.SC) + m_wNewStatusArrValue[2]));

  if m_wNewStatusArrValue[9] > 0 then begin //道术减少
    n01 := m_wNewStatusArrValue[9];
    if HiWord(m_WAbil.SC) - LoWord(m_WAbil.SC) >= m_wNewStatusArrValue[9] then begin
      m_WAbil.SC := MakeLong(LoWord(m_WAbil.SC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.SC) - m_wNewStatusArrValue[9]));
    end else begin
      Dec(n01, HiWord(m_WAbil.SC) - LoWord(m_WAbil.SC));
      n02 := _MAX(1, LoWord(m_WAbil.SC) - n01 div 2);
      m_WAbil.SC := MakeLong(_MIN(n02, MAXHUMPOWER), _MIN(n02, MAXHUMPOWER));
    end;
  end;

  if m_wNewStatusArrValue[3] > 0 then //防御增加
    m_WAbil.AC := MakeLong(LoWord(m_WAbil.AC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.AC) + m_wNewStatusArrValue[3]));

  if m_wNewStatusArrValue[10] > 0 then begin //防御减少
    n01 := m_wNewStatusArrValue[10];
    if HiWord(m_WAbil.AC) - LoWord(m_WAbil.AC) >= m_wNewStatusArrValue[10] then begin
      m_WAbil.AC := MakeLong(LoWord(m_WAbil.AC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.AC) - m_wNewStatusArrValue[10]));
    end else begin
      Dec(n01, HiWord(m_WAbil.AC) - LoWord(m_WAbil.AC));
      n02 := _MAX(1, LoWord(m_WAbil.AC) - n01 div 2);
      m_WAbil.AC := MakeLong(_MIN(n02, MAXHUMPOWER), _MIN(n02, MAXHUMPOWER));
    end;
  end;

  if m_wNewStatusArrValue[4] > 0 then //魔防增加
    m_WAbil.MAC := MakeLong(LoWord(m_WAbil.MAC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.MAC) + m_wNewStatusArrValue[4]));

  if m_wNewStatusArrValue[11] > 0 then begin //魔防减少
    n01 := m_wNewStatusArrValue[11];
    if HiWord(m_WAbil.MAC) - LoWord(m_WAbil.MAC) >= m_wNewStatusArrValue[11] then begin
      m_WAbil.MAC := MakeLong(LoWord(m_WAbil.MAC), _MIN(MAXHUMPOWER, HiWord(m_WAbil.MAC) - m_wNewStatusArrValue[11]));
    end else begin
      Dec(n01, HiWord(m_WAbil.MAC) - LoWord(m_WAbil.MAC));
      n02 := _MAX(1, LoWord(m_WAbil.MAC) - n01 div 2);
      m_WAbil.MAC := MakeLong(_MIN(n02, MAXHUMPOWER), _MIN(n02, MAXHUMPOWER));
    end;
  end;

  if m_wNewStatusArrValue[5] > 0 then //HP增加
    m_WAbil.MaxHP := MinLong(g_Config.nMaxLevel, m_WAbil.MaxHP + m_wNewStatusArrValue[5]);

  if m_wNewStatusArrValue[12] > 0 then begin //HP减少
    n01 := m_wNewStatusArrValue[12];
    if m_WAbil.MaxHP - m_WAbil.HP >= m_wNewStatusArrValue[12] then begin
      m_WAbil.MaxHP := MinLong(g_Config.nMaxLevel, m_WAbil.MaxHP - m_wNewStatusArrValue[12]);
    end else begin
      Dec(n01, m_WAbil.MaxHP - m_WAbil.HP);
      m_WAbil.MaxHP := MaxLong(1, m_WAbil.HP - n01 div 2);
      m_WAbil.HP := m_WAbil.MaxHP;
    end;
  end;

  if m_wNewStatusArrValue[6] > 0 then //MP增加
    m_WAbil.MaxMP := MinLong(g_Config.nMaxLevel, m_WAbil.MaxMP + m_wNewStatusArrValue[6]);

  if m_wNewStatusArrValue[13] > 0 then begin //MP减少
    n01 := m_wNewStatusArrValue[13];
    if m_WAbil.MaxMP - m_WAbil.MP >= m_wNewStatusArrValue[13] then begin
      m_WAbil.MaxMP := MinLong(g_Config.nMaxLevel, m_WAbil.MaxHP - m_wNewStatusArrValue[13]);
    end else begin
      Dec(n01, m_WAbil.MaxMP - m_WAbil.MP);
      m_WAbil.MaxMP := _MAX(1, m_WAbil.MP - n01 div 2);
      m_WAbil.MP := m_WAbil.MaxMP;
    end;
  end;

  if m_boFlameRing then AddItemSkill(1)
  else DelItemSkill(1);

  if m_boRecoveryRing then AddItemSkill(2)
  else DelItemSkill(2);

  if m_boMuscleRing then begin //活力
    Inc(m_WAbil.MaxWeight, m_WAbil.MaxWeight);
    Inc(m_WAbil.MaxWearWeight, m_WAbil.MaxWearWeight);
    Inc(m_WAbil.MaxHandWeight, m_WAbil.MaxHandWeight);
  end;
  //Inc(m_WAbil.MaxWeight, 100);
  if m_nMoXieSuite > 0 then begin //魔血
    if m_WAbil.MaxMP <= m_nMoXieSuite then
      m_nMoXieSuite := m_WAbil.MaxMP - 1;
    Dec(m_WAbil.MaxMP, m_nMoXieSuite);
    //Inc(m_WAbil.MaxHP,m_nMoXieSuite);

    m_WAbil.MaxHP := MinLong(g_Config.nMaxLevel, m_WAbil.MaxHP + m_nMoXieSuite);

  end;

  if m_btRaceServer = RC_PLAYOBJECT then begin
    nShield := 0;
    if (m_Magic31Skill <> nil) and (m_Magic31Skill.btLevel >= 4) then nShield := 1;
    SendUpdateMsg(Self, RM_CHARSTATUSCHANGED, m_nHitSpeed, m_nCharStatus, 1, nShield, '');
  end;
  if (m_btRaceServer >= RC_ANIMAL) and (m_btRaceServer <> RC_PLAYMOSTER) and (m_btRaceServer <> RC_HEROOBJECT) then begin
    MonsterRecalcAbilitys();
  end;


  if m_btRaceServer = RC_PLAYOBJECT then begin
    if (TPlayObject(Self).m_MyHero <> nil) and (THeroObject(TPlayObject(Self).m_MyHero).m_btHeroGroup > 0) then begin //增加合体属性
      btLevel := THeroObject(TPlayObject(Self).m_MyHero).m_btHeroGroup;
      Abil := TPlayObject(Self).m_MyHero.m_WAbil;
      btJob := TPlayObject(Self).m_MyHero.m_btJob;
      m_WAbil.AC := MakeLong(_MIN(MAXHUMPOWER, GetHeroGroup(btLevel, LoWord(m_WAbil.AC), LoWord(Abil.AC))), _MIN(MAXHUMPOWER, GetHeroGroup(btLevel, HiWord(m_WAbil.AC), HiWord(Abil.AC))));
      m_WAbil.MAC := MakeLong(_MIN(MAXHUMPOWER, GetHeroGroup(btLevel, LoWord(m_WAbil.MAC), LoWord(Abil.MAC))), _MIN(MAXHUMPOWER, GetHeroGroup(btLevel, HiWord(m_WAbil.MAC), HiWord(Abil.MAC))));

      if m_btJob <> btJob then begin
        case m_btJob of
          0: case btJob of
              1: m_WAbil.DC := MakeLong(_MIN(MAXHUMPOWER, GetHeroGroup(btLevel, LoWord(m_WAbil.DC), LoWord(Abil.MC))), _MIN(MAXHUMPOWER, GetHeroGroup(btLevel, HiWord(m_WAbil.DC), HiWord(Abil.MC))));
              2: m_WAbil.DC := MakeLong(_MIN(MAXHUMPOWER, GetHeroGroup(btLevel, LoWord(m_WAbil.DC), LoWord(Abil.SC))), _MIN(MAXHUMPOWER, GetHeroGroup(btLevel, HiWord(m_WAbil.DC), HiWord(Abil.SC))));
            end;
          1: case btJob of
              0: m_WAbil.MC := MakeLong(_MIN(MAXHUMPOWER, GetHeroGroup(btLevel, LoWord(m_WAbil.MC), LoWord(Abil.DC))), _MIN(MAXHUMPOWER, GetHeroGroup(btLevel, HiWord(m_WAbil.MC), HiWord(Abil.DC))));
              2: m_WAbil.MC := MakeLong(_MIN(MAXHUMPOWER, GetHeroGroup(btLevel, LoWord(m_WAbil.MC), LoWord(Abil.SC))), _MIN(MAXHUMPOWER, GetHeroGroup(btLevel, HiWord(m_WAbil.MC), HiWord(Abil.SC))));
            end;
          2: case btJob of
              0: m_WAbil.SC := MakeLong(_MIN(MAXHUMPOWER, GetHeroGroup(btLevel, LoWord(m_WAbil.SC), LoWord(Abil.DC))), _MIN(MAXHUMPOWER, GetHeroGroup(btLevel, HiWord(m_WAbil.SC), HiWord(Abil.DC))));
              1: m_WAbil.SC := MakeLong(_MIN(MAXHUMPOWER, GetHeroGroup(btLevel, LoWord(m_WAbil.SC), LoWord(Abil.MC))), _MIN(MAXHUMPOWER, GetHeroGroup(btLevel, HiWord(m_WAbil.SC), HiWord(Abil.MC))));
            end;
        end;
      end else begin
        m_WAbil.DC := MakeLong(_MIN(MAXHUMPOWER, GetHeroGroup(btLevel, LoWord(m_WAbil.DC), LoWord(Abil.DC))), _MIN(MAXHUMPOWER, GetHeroGroup(btLevel, HiWord(m_WAbil.DC), HiWord(Abil.DC))));
        m_WAbil.MC := MakeLong(_MIN(MAXHUMPOWER, GetHeroGroup(btLevel, LoWord(m_WAbil.MC), LoWord(Abil.MC))), _MIN(MAXHUMPOWER, GetHeroGroup(btLevel, HiWord(m_WAbil.MC), HiWord(Abil.MC))));
        m_WAbil.SC := MakeLong(_MIN(MAXHUMPOWER, GetHeroGroup(btLevel, LoWord(m_WAbil.SC), LoWord(Abil.SC))), _MIN(MAXHUMPOWER, GetHeroGroup(btLevel, HiWord(m_WAbil.SC), HiWord(Abil.SC))));
      end;

      m_WAbil.HP := MinLong(g_Config.nMaxLevel, GetHeroGroupLong(btLevel, m_WAbil.HP, Abil.HP));
      m_WAbil.MP := MinLong(g_Config.nMaxLevel, GetHeroGroupLong(btLevel, m_WAbil.MP, Abil.MP));

      m_WAbil.MaxHP := MinLong(g_Config.nMaxLevel, GetHeroGroupLong(btLevel, m_WAbil.MaxHP, Abil.MaxHP));
      m_WAbil.MaxMP := MinLong(g_Config.nMaxLevel, GetHeroGroupLong(btLevel, m_WAbil.MaxMP, Abil.MaxMP));

      if m_WAbil.HP > m_WAbil.MaxHP then m_WAbil.HP := m_WAbil.MaxHP;
      if m_WAbil.MP > m_WAbil.MaxMP then m_WAbil.MP := m_WAbil.MaxMP;


      m_WAbil.AddPoint[0] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[0], Abil.AddPoint[0]), 100);
      m_WAbil.AddPoint[1] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[1], Abil.AddPoint[1]), 100);
      m_WAbil.AddPoint[2] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[2], Abil.AddPoint[2]), 100);
      m_WAbil.AddPoint[3] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[3], Abil.AddPoint[3]), 100);
      m_WAbil.AddPoint[4] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[4], Abil.AddPoint[4]), 100);

      m_WAbil.AddPoint[5] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[5], Abil.AddPoint[5]), 100);
      m_WAbil.AddPoint[6] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[6], Abil.AddPoint[6]), 100);
      m_WAbil.AddPoint[7] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[7], Abil.AddPoint[7]), 100);
      m_WAbil.AddPoint[8] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[8], Abil.AddPoint[8]), 100);
      m_WAbil.AddPoint[9] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[9], Abil.AddPoint[9]), 100);
      m_WAbil.AddPoint[10] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[10], Abil.AddPoint[10]), 100);
      m_WAbil.AddPoint[11] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[11], Abil.AddPoint[11]), 100);
      m_WAbil.AddPoint[12] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[12], Abil.AddPoint[12]), 100);
      m_WAbil.AddPoint[13] := _MIN(GetHeroGroup(btLevel, m_WAbil.AddPoint[13], Abil.AddPoint[13]), 100);
    end;
  end;

{===============================================================================}
{================================套装属性======================================}
{===============================================================================}
  {=======================================检测套装============================}
  m_GroupItem.Clear;
  if (m_btRaceServer = RC_PLAYOBJECT) or (m_btRaceServer = RC_PLAYMOSTER) or (m_btRaceServer = RC_HEROOBJECT) then begin
    //m_GroupItem := g_GroupItems.Get(m_UseItems, m_GroupItem);
    if g_GroupItems.Get(m_UseItems, m_GroupItem) > 0 then begin
      for I := 0 to m_GroupItem.Count - 1 do begin
        GroupItem := m_GroupItem.Items[I];
        m_WAbil.MaxHP := MinLong(g_Config.nMaxLevel, g_GroupItems.RateValue(GroupItem.FLD_RATE[0], m_WAbil.MaxHP));
        m_WAbil.MaxMP := MinLong(g_Config.nMaxLevel, g_GroupItems.RateValue(GroupItem.FLD_RATE[1], m_WAbil.MaxMP));

        m_WAbil.AC := MakeLong(_MIN(MAXHUMPOWER, g_GroupItems.RateValue(GroupItem.FLD_RATE[2], LoWord(m_WAbil.AC))), _MIN(MAXHUMPOWER, g_GroupItems.RateValue(GroupItem.FLD_RATE[2], HiWord(m_WAbil.AC))));
        m_WAbil.MAC := MakeLong(_MIN(MAXHUMPOWER, g_GroupItems.RateValue(GroupItem.FLD_RATE[3], LoWord(m_WAbil.MAC))), _MIN(MAXHUMPOWER, g_GroupItems.RateValue(GroupItem.FLD_RATE[3], HiWord(m_WAbil.MAC))));
        m_WAbil.DC := MakeLong(_MIN(MAXHUMPOWER, g_GroupItems.RateValue(GroupItem.FLD_RATE[4], LoWord(m_WAbil.DC))), _MIN(MAXHUMPOWER, g_GroupItems.RateValue(GroupItem.FLD_RATE[4], HiWord(m_WAbil.DC))));
        m_WAbil.MC := MakeLong(_MIN(MAXHUMPOWER, g_GroupItems.RateValue(GroupItem.FLD_RATE[5], LoWord(m_WAbil.MC))), _MIN(MAXHUMPOWER, g_GroupItems.RateValue(GroupItem.FLD_RATE[5], HiWord(m_WAbil.MC))));
        m_WAbil.SC := MakeLong(_MIN(MAXHUMPOWER, g_GroupItems.RateValue(GroupItem.FLD_RATE[6], LoWord(m_WAbil.SC))), _MIN(MAXHUMPOWER, g_GroupItems.RateValue(GroupItem.FLD_RATE[6], HiWord(m_WAbil.SC))));

        m_btHitPoint := _MIN(High(Byte), g_GroupItems.RateValue(GroupItem.FLD_RATE[7], m_btHitPoint)); //准确
        m_btSpeedPoint := _MIN(High(Byte), g_GroupItems.RateValue(GroupItem.FLD_RATE[8], m_btSpeedPoint)); //准确
        m_nAntiMagic := g_GroupItems.RateValue(GroupItem.FLD_RATE[9], m_nAntiMagic); //魔法躲避
        m_btAntiPoison := _MIN(High(Byte), g_GroupItems.RateValue(GroupItem.FLD_RATE[10], m_btAntiPoison)); //毒躲避
        m_nPoisonRecover := g_GroupItems.RateValue(GroupItem.FLD_RATE[11], m_nPoisonRecover); //毒恢复
        m_nHealthRecover := g_GroupItems.RateValue(GroupItem.FLD_RATE[12], m_nHealthRecover); //HP恢复
        m_nSpellRecover := g_GroupItems.RateValue(GroupItem.FLD_RATE[13], m_nSpellRecover); //MP恢复

        if GroupItem.FLD_FLAG[0] then m_boParalysis := True; //麻痹
        if GroupItem.FLD_FLAG[1] then m_boMagicShield := True; //护身
        if GroupItem.FLD_FLAG[2] then m_boTeleport := True; //传送
        if GroupItem.FLD_FLAG[3] then m_boRevival := True; //复活
        if GroupItem.FLD_FLAG[4] then m_boMuscleRing := True; //负载
        if GroupItem.FLD_FLAG[5] then m_boFastTrain := True; //技巧
        if GroupItem.FLD_FLAG[6] then m_boProbeNecklace := True; //探测
      //if GroupItem.FLD_FLAG[7] then m_boMagicShield := True; //护身
        if GroupItem.FLD_FLAG[8] then m_boHideMode := True; //隐身
        if GroupItem.FLD_FLAG[9] then m_boUnParalysis := True; //防麻痹
      //if GroupItem.FLD_FLAG[10] then m_boMagicShield := True; //护身
        if GroupItem.FLD_FLAG[11] then m_boUnRevival := True; //破复活
        if GroupItem.FLD_FLAG[12] then m_boUnMagicShield := True; //破护身
      //if GroupItem.FLD_FLAG[13] then m_boMagicShield := True; //护身
        if GroupItem.FLD_FLAG[14] then m_boNoDropItem := True; //不掉背包物品
        if GroupItem.FLD_FLAG[15] then m_boNoDropUseItem := True; //不掉身上装备
      end;
    end;
  end;

{===============================================================================}
{================================套装属性结束======================================}
{===============================================================================}

  //限制最高属性
  m_WAbil.AC := MakeLong(_MIN(MAXHUMPOWER, GetPercent(m_WAbil.AddPoint[5], LoWord(m_WAbil.AC))), _MIN(MAXHUMPOWER, GetPercent(m_WAbil.AddPoint[5], HiWord(m_WAbil.AC))));
  m_WAbil.MAC := MakeLong(_MIN(MAXHUMPOWER, GetPercent(m_WAbil.AddPoint[6], LoWord(m_WAbil.MAC))), _MIN(MAXHUMPOWER, GetPercent(m_WAbil.AddPoint[6], HiWord(m_WAbil.MAC))));
  m_WAbil.DC := MakeLong(_MIN(MAXHUMPOWER, GetPercent(m_WAbil.AddPoint[7], LoWord(m_WAbil.DC))), _MIN(MAXHUMPOWER, GetPercent(m_WAbil.AddPoint[7], HiWord(m_WAbil.DC))));
  m_WAbil.MC := MakeLong(_MIN(MAXHUMPOWER, GetPercent(m_WAbil.AddPoint[8], LoWord(m_WAbil.MC))), _MIN(MAXHUMPOWER, GetPercent(m_WAbil.AddPoint[8], HiWord(m_WAbil.MC))));
  m_WAbil.SC := MakeLong(_MIN(MAXHUMPOWER, GetPercent(m_WAbil.AddPoint[9], LoWord(m_WAbil.SC))), _MIN(MAXHUMPOWER, GetPercent(m_WAbil.AddPoint[9], HiWord(m_WAbil.SC))));

end;

///////////////////////////////////////////////////////////////////////////////////////////////////

procedure TActorObject.BreakOpenHealth();
begin
  if m_boShowHP then begin
    m_boShowHP := False;
    m_nCharStatusEx := m_nCharStatusEx xor STATE_OPENHEATH;
    m_nCharStatus := GetCharStatus();
    SendRefMsg(RM_CLOSEHEALTH, 0, 0, 0, 0, '');
  end;
end;

procedure TActorObject.MakeOpenHealth();
begin
  m_boShowHP := True;
  m_nCharStatusEx := m_nCharStatusEx or STATE_OPENHEATH;
  m_nCharStatus := GetCharStatus();

  SendRefMsg(RM_OPENHEALTH, 0, m_WAbil.HP, m_WAbil.MaxHP, 0, '');
  SendRefMsg(RM_OPENHEALTH, 0, m_WAbil.MP, m_WAbil.MaxMP, 0, '');
  SendRefMsg(RM_OPENHEALTH, 0, m_WAbil.Exp, m_WAbil.MaxExp, 0, '');
end;

procedure TActorObject.IncHealthSpell(nHP, nMP: Integer);
begin
  if (nHP < 0) or (nMP < 0) then Exit;
  if (m_WAbil.HP + nHP) >= m_WAbil.MaxHP then m_WAbil.HP := m_WAbil.MaxHP
  else Inc(m_WAbil.HP, nHP);
  if (m_WAbil.MP + nMP) >= m_WAbil.MaxMP then m_WAbil.MP := m_WAbil.MaxMP
  else Inc(m_WAbil.MP, nMP);
  HealthSpellChanged();
end;

procedure TActorObject.ItemDamageRevivalRing();
var
  I: Integer;
  pSItem: pTStdItem;
  nDura, tDura: Integer;
  PlayObject: TPlayObject;
begin
  for I := Low(THumanUseItems) to High(THumanUseItems) do begin
    if m_UseItems[I].wIndex > 0 then begin
      pSItem := UserEngine.GetStdItem(m_UseItems[I].wIndex);
      if pSItem <> nil then begin
//        if (i = U_RINGR) or (i = U_RINGL) then begin
        if (pSItem.Shape in [114, 160, 161, 162]) or (((I = U_WEAPON) or (I = U_RIGHTHAND)) and (pSItem.AniCount in [114, 160, 161, 162])) then begin
          nDura := m_UseItems[I].Dura;
          tDura := Round(nDura / 1000 {1.03});
          Dec(nDura, 1000);
          if nDura <= 0 then begin
            nDura := 0;
            m_UseItems[I].Dura := nDura;
            if m_btRaceServer = RC_PLAYOBJECT then begin
              PlayObject := TPlayObject(Self);
              PlayObject.SendDelItems(@m_UseItems[I]);
            end; //004C0310
            m_UseItems[I].wIndex := 0;
            RecalcAbilitys();
          end else begin //004C0331
            m_UseItems[I].Dura := nDura;
            if tDura <> Round(nDura / 1000 {1.03}) then begin
              SendMsg(Self, RM_DURACHANGE, I, nDura, m_UseItems[I].DuraMax, 0, '');
            end;
          end;
            //break;
        end; //004C0397
//        end;//004C0397
      end; //004C0397 if pSItem <> nil then begin
    end; //if UseItems[i].wIndex > 0 then begin
  end; // for i:=Low(UseItems) to High(UseItems) do begin
end;

procedure TActorObject.AddLock(ActorObject: TActorObject);
var
  I: Integer;
begin
  for I := 0 to m_LockList.Count - 1 do
    if TActorObject(m_LockList.Items[I]) = ActorObject then Exit;
  m_LockList.Add(ActorObject);
end;

procedure TActorObject.Run;
var
  I: Integer;
  boChg: Boolean;
  boNeedRecalc: Boolean;
  nHP, nMP, n18: Integer;
  dwC, dwInChsTime: LongWord;
  ProcessMsg: TProcessMessage;
  BaseObject: TActorObject;
  nCheckCode: Integer;
  dwRunTick: LongWord;
  nInteger: Integer;
  boChangeColor: Boolean;
resourcestring
  //sExceptionMsg0 = '[Exception] TActorObject::Run 0';
  sExceptionMsg0 = '[Exception] TActorObject::Run -> Operate # %s Ident:%d Sender:%d wP:%d nP1:%d nP2:%d np3:%d Msg:%s';
  sExceptionMsg1 = '[Exception] TActorObject::Run 1 ';
  sExceptionMsg2 = '[Exception] TActorObject::Run 2';
  sExceptionMsg3 = '[Exception] TActorObject::Run 3 ';
  sExceptionMsg4 = '[Exception] TActorObject::Run 4 Code:%d';
  sExceptionMsg5 = '[Exception] TActorObject::Run 5';
  sExceptionMsg6 = '[Exception] TActorObject::Run 6';
  sExceptionMsg7 = '[Exception] TActorObject::Run 7';
  sExceptionMsg8 = '[Exception] TActorObject::Run 8';
  sExceptionMsg9 = '[Exception] TActorObject::Run 9';
begin
  nCheckCode := 0;
  dwRunTick := GetTickCount();

  try
    while GetMessage(@ProcessMsg) do begin
      nCheckCode := 1000;
      Operate(@ProcessMsg);
      nCheckCode := 1001;
    end;
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg0, [m_sCharName,
        ProcessMsg.wIdent,
          Integer(ProcessMsg.BaseObject),
          ProcessMsg.wParam,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          ProcessMsg.sMsg]));
      MainOutMessage(E.Message);
    end;
  end;
  //SetProcessName('TActorObject.Run 1');
  try
    if m_boSuperMan then begin
      m_WAbil.HP := m_WAbil.MaxHP;
      m_WAbil.MP := m_WAbil.MaxMP;
    end;

    dwC := (GetTickCount() - m_dwHPMPTick) div 20;
    m_dwHPMPTick := GetTickCount();
    Inc(m_nHealthTick, dwC);
    Inc(m_nSpellTick, dwC);
    if not m_boDeath then begin
      if (m_WAbil.HP < m_WAbil.MaxHP) and (m_nHealthTick >= g_Config.nHealthFillTime) then begin
        if m_WAbil.MaxHP > High(Word) then begin
          if m_WAbil.MaxHP < 100000 then begin
            n18 := (m_WAbil.MaxHP div 100) + 1;
          end else
            if m_WAbil.MaxHP < 1000000 then begin
            n18 := (m_WAbil.MaxHP div 1000) + 1;
          end else
            if m_WAbil.MaxHP < 10000000 then begin
            n18 := (m_WAbil.MaxHP div 10000) + 1;
          end else
            if m_WAbil.MaxHP < 100000000 then begin
            n18 := (m_WAbil.MaxHP div 100000) + 1;
          end else begin
            n18 := (m_WAbil.MaxHP div 200000) + 1;
          end;
          if (m_WAbil.HP + n18) < m_WAbil.MaxHP then begin
            Inc(m_WAbil.HP, n18);
          end else begin
            m_WAbil.HP := m_WAbil.MaxHP;
          end;
        end else begin
          n18 := (m_WAbil.MaxHP div 75) + 1;
        //nPlus = m_WAbility.MaxHP / 15 + 1;
          if (m_WAbil.HP + n18) < m_WAbil.MaxHP then begin
            Inc(m_WAbil.HP, n18);
          end else begin
            m_WAbil.HP := m_WAbil.MaxHP;
          end;
        end;

        nCheckCode := 1002;
        HealthSpellChanged;
        nCheckCode := 1003;
      end;
      nCheckCode := 2002;
      if (m_WAbil.MP < m_WAbil.MaxMP) and (m_nSpellTick >= g_Config.nSpellFillTime) then begin
        if m_WAbil.MaxHP > High(Word) then begin
          if m_WAbil.MaxHP < 100000 then begin
            n18 := (m_WAbil.MaxHP div 50) + 1;
          end else
            if m_WAbil.MaxHP < 1000000 then begin
            n18 := (m_WAbil.MaxHP div 500) + 1;
          end else
            if m_WAbil.MaxHP < 10000000 then begin
            n18 := (m_WAbil.MaxHP div 5000) + 1;
          end else
            if m_WAbil.MaxHP < 100000000 then begin
            n18 := (m_WAbil.MaxHP div 50000) + 1;
          end else begin
            n18 := (m_WAbil.MaxHP div 100000) + 1;
          end;
          if (m_WAbil.HP + n18) < m_WAbil.MaxHP then begin
            Inc(m_WAbil.HP, n18);
          end else begin
            m_WAbil.HP := m_WAbil.MaxHP;
          end;
        end else begin
          n18 := (m_WAbil.MaxMP div 18) + 1;
          if (m_WAbil.MP + n18) < m_WAbil.MaxMP then begin
            Inc(m_WAbil.MP, n18);
          end else begin
            m_WAbil.MP := m_WAbil.MaxMP;
          end;
        end;
        nCheckCode := 1004;
        HealthSpellChanged;
        nCheckCode := 1005;
      end;
      nCheckCode := 2003;
      if m_WAbil.HP = 0 then begin
        if m_LastHiter = nil then begin
          if m_boRevival and (GetTickCount - m_dwRevivalTick > g_Config.dwRevivalTime {60 * 1000}) then begin
            m_dwRevivalTick := GetTickCount();
            nCheckCode := 1006;
            ItemDamageRevivalRing;
            nCheckCode := 1007;
            m_WAbil.HP := m_WAbil.MaxHP;
            HealthSpellChanged;
            nCheckCode := 1008;
            SysMsg(g_sRevivalRecoverMsg {'You have been revived!'}, c_Green, t_Hint);
          end;
        end else
          if m_LastHiter <> nil then begin
          if not m_LastHiter.m_boUnRevival {防复活} and m_boRevival and (GetTickCount - m_dwRevivalTick > g_Config.dwRevivalTime {60 * 1000}) then begin
            m_dwRevivalTick := GetTickCount();
            nCheckCode := 1006;
            ItemDamageRevivalRing;
            nCheckCode := 1007;
            m_WAbil.HP := m_WAbil.MaxHP;
            HealthSpellChanged;
            nCheckCode := 1008;
            SysMsg(g_sRevivalRecoverMsg {'You have been revived!'}, c_Green, t_Hint);
          end;
        end;
        nCheckCode := 2004;
        if (m_WAbil.HP = 0) and (not m_boGhost) then Die;
      end;
      if m_nHealthTick >= g_Config.nHealthFillTime then m_nHealthTick := 0;
      if m_nSpellTick >= g_Config.nSpellFillTime then m_nSpellTick := 0;
      nCheckCode := 1009;
    end else begin
      nCheckCode := 1010;
      if (GetTickCount() - m_dwDeathTick > g_Config.dwMakeGhostTime {3 * 60 * 1000}) then
        MakeGhost();
      nCheckCode := 1011;
    end;
  except
    on E: Exception do begin
      //if nCheckCode = 2004 then MakeGhost();
      MainOutMessage(sExceptionMsg1 + IntToStr(nCheckCode) + ' ' + IntToStr(m_btRaceServer));
      MainOutMessage(E.Message);
    end;
  end;

  try
    if (not m_boDeath) and ((m_nIncSpell > 0) or (m_nIncHealth > 0) or (m_nIncHealing > 0)) then begin
      dwInChsTime := 600 - _MIN(400, m_Abil.Level * 10);
      if ((GetTickCount - m_dwIncHealthSpellTick) >= dwInChsTime) and not m_boDeath then begin
        dwC := _MIN(200, (GetTickCount - m_dwIncHealthSpellTick - dwInChsTime));
        m_dwIncHealthSpellTick := GetTickCount() + dwC;
        if (m_nIncSpell > 0) or (m_nIncHealth > 0) or (m_nPerHealing > 0) then begin
          if (m_nPerHealth <= 0) then m_nPerHealth := 1;
          if (m_nPerSpell <= 0) then m_nPerSpell := 1;
          if (m_nPerHealing <= 0) then m_nPerHealing := 1;
          if m_nIncHealth < m_nPerHealth then begin
            nHP := m_nIncHealth;
            m_nIncHealth := 0;
          end else begin
            nHP := m_nPerHealth;
            Dec(m_nIncHealth, m_nPerHealth);
          end;
          if m_nIncSpell < m_nPerSpell then begin
            nMP := m_nIncSpell;
            m_nIncSpell := 0;
          end else begin
            nMP := m_nPerSpell;
            Dec(m_nIncSpell, m_nPerSpell);
          end;
          if m_nIncHealing < m_nPerHealing then begin
            Inc(nHP, m_nIncHealing);
            m_nIncHealing := 0;
          end else begin
            Inc(nHP, m_nPerHealing);
            Dec(m_nIncHealing, m_nPerHealing);
          end;
          m_nPerHealth := (m_Abil.Level div 10 + 5);
          m_nPerSpell := (m_Abil.Level div 10 + 5);
          m_nPerHealing := 5;
          IncHealthSpell(nHP, nMP);
          if m_WAbil.HP = m_WAbil.MaxHP then begin
            m_nIncHealth := 0;
            m_nIncHealing := 0;
          end;
          if m_WAbil.MP = m_WAbil.MaxMP then begin
            m_nIncSpell := 0;
          end;
        end;
      end;
    end else begin
      m_dwIncHealthSpellTick := GetTickCount();
    end;
    if (m_nHealthTick < -g_Config.nHealthFillTime) and (m_WAbil.HP > 1) then begin //Jacky ????
      Dec(m_WAbil.HP);
      Inc(m_nHealthTick, g_Config.nHealthFillTime);
      HealthSpellChanged();
    end;
    //检查HP/MP值是否大于最大值，大于则降低到正常大小
    boNeedRecalc := False;
    if m_WAbil.HP > m_WAbil.MaxHP then begin
      boNeedRecalc := True;
      m_WAbil.HP := m_WAbil.MaxHP - 1;
    end;
    if m_WAbil.MP > m_WAbil.MaxMP then begin
      boNeedRecalc := True;
      m_WAbil.MP := m_WAbil.MaxMP - 1;
    end;
    if boNeedRecalc then HealthSpellChanged();
  except
    MainOutMessage(sExceptionMsg2);
  end;


  if Assigned(PlugInEngine.PlayObjectRun) then begin
    try
      PlugInEngine.PlayObjectRun(Self);
    except
      MainOutMessage(sExceptionMsg9);
    end;
  end;

  //TActorObject.Run 3 清理目标对象
  try

    if (m_TargetCret <> nil) then begin
      if (GetTickCount - m_dwTargetFocusTick > 30000) or
        m_TargetCret.m_boDeath or
        m_TargetCret.m_boGhost or
        (m_TargetCret.m_PEnvir <> m_PEnvir) or // 08/06 增加，弓箭卫士在人物进入房间后再出来，还会攻击人物(人物的攻击目标没清除)
        (abs(m_TargetCret.m_nCurrX - m_nCurrX) > 15) or
        (abs(m_TargetCret.m_nCurrY - m_nCurrY) > 15) then begin
        DelTargetCreat;
        //m_TargetCret := nil;
      end;
    end;

    if m_LastHiter <> nil then begin
      if ((GetTickCount - m_LastHiterTick > 30000) and (m_wStatusTimeArr[POISON_DECHEALTH] = 0) and (m_wStatusTimeArr[POISON_DAMAGEARMOR] = 0)) or //修改人物中毒状态不清除
        m_LastHiter.m_boDeath or
        m_LastHiter.m_boGhost then begin
        m_LastHiter := nil;
      end;
    end;

    if m_ExpHitter <> nil then begin
      if ((GetTickCount - m_ExpHitterTick > 6000) and (m_wStatusTimeArr[POISON_DECHEALTH] = 0) and (m_wStatusTimeArr[POISON_DAMAGEARMOR] = 0)) or //修改人物中毒状态不清除
        m_ExpHitter.m_boDeath or
        m_ExpHitter.m_boGhost then begin
        m_ExpHitter := nil;
      end;
    end;

    if m_PoisonTarget <> nil then begin
      if ((GetTickCount - m_PoisonTick > 6000) and (m_wStatusTimeArr[POISON_DECHEALTH] = 0) and (m_wStatusTimeArr[POISON_DAMAGEARMOR] = 0)) or //修改人物中毒状态不清除
        m_PoisonTarget.m_boDeath or
        m_PoisonTarget.m_boGhost then begin
        m_PoisonTarget := nil;
        m_nCharStatus := GetCharStatus();
        StatusChanged();
      end;
    end;

    if g_Config.boChangeMapReleasePoison then begin
      if (m_PoisonTarget <> nil) and (m_PoisonTarget.m_PEnvir <> m_PEnvir) then begin //换地图 毒失效
        if (m_wStatusTimeArr[POISON_DECHEALTH] <> 0) or (m_wStatusTimeArr[POISON_DAMAGEARMOR] <> 0) then begin
          m_wStatusTimeArr[POISON_DECHEALTH] := 0;
          m_wStatusTimeArr[POISON_DAMAGEARMOR] := 0;
          m_PoisonTarget := nil;
          m_nCharStatus := GetCharStatus();
          StatusChanged();
        end;
      end;
    end;

    if m_Master <> nil then begin
      if (m_btRaceServer <> RC_HEROOBJECT) and (m_btRaceServer <> RC_PLAYMOSTER) then m_boNoItem := True;
      //宝宝变色
      if m_boAutoChangeColor and (GetTickCount - m_dwAutoChangeColorTick > g_Config.dwBBMonAutoChangeColorTime) then begin
        m_dwAutoChangeColorTick := GetTickCount();
        case m_nAutoChangeIdx of
          0: nInteger := STATE_TRANSPARENT;
          1: nInteger := POISON_STONE;
          2: nInteger := POISON_DONTMOVE;
          3: nInteger := POISON_68;
          4: nInteger := POISON_DECHEALTH;
          5: nInteger := POISON_LOCKSPELL;
          6: nInteger := POISON_DAMAGEARMOR;
          7: nInteger := POISON_FREEZE;
          8: nInteger := POISON_SLOWDOWN;
        else begin
            m_nAutoChangeIdx := 0;
            nInteger := STATE_TRANSPARENT;
          end;
        end;
        Inc(m_nAutoChangeIdx);
        m_nCharStatus := (m_nCharStatusEx and $FFFFF) or (($80000000 shr nInteger) or 0);
        StatusChanged();
      end;

      if m_boFixColor and (m_nFixStatus <> m_nCharStatus) then begin
        case m_nFixColorIdx of
          0: nInteger := STATE_TRANSPARENT;
          1: nInteger := POISON_STONE;
          2: nInteger := POISON_DONTMOVE;
          3: nInteger := POISON_68;
          4: nInteger := POISON_DECHEALTH;
          5: nInteger := POISON_LOCKSPELL;
          6: nInteger := POISON_DAMAGEARMOR;
          7: nInteger := POISON_FREEZE;
          8: nInteger := POISON_SLOWDOWN;
        else begin
            m_nFixColorIdx := 0;
            nInteger := STATE_TRANSPARENT;
          end;
        end;
        m_nCharStatus := (m_nCharStatusEx and $FFFFF) or (($80000000 shr nInteger) or 0);
        m_nFixStatus := m_nCharStatus;
        StatusChanged();
      end;
      // 宝宝在主人死亡后死亡处理
      {if (m_Master.m_boDeath and ((GetTickCount - m_Master.m_dwDeathTick) > 1000)) then begin
        if g_Config.boMasterDieMutiny and (m_Master.m_LastHiter <> nil) and (Random(g_Config.nMasterDieMutinyRate) = 0) then begin
          if m_btRaceServer <> RC_HEROOBJECT then begin
            m_Master := nil;
            m_btSlaveExpLevel := High(g_Config.SlaveColor);
            RecalcAbilitys();
            m_WAbil.DC := MakeLong(LoWord(m_WAbil.DC) * g_Config.nMasterDieMutinyPower, HiWord(m_WAbil.DC) * g_Config.nMasterDieMutinyPower);
            if g_Config.nMasterDieMutinySpeed <= 0 then g_Config.nMasterDieMutinySpeed := 5;
            m_nWalkSpeed := m_nWalkSpeed div g_Config.nMasterDieMutinySpeed;
            RefNameColor;
            RefShowName;
          end else begin
            m_Master := nil;
            m_WAbil.HP := 0;
          end;
        end else begin
          m_WAbil.HP := 0;
        end;
      end;
      if m_Master.m_boGhost and ((GetTickCount - m_Master.m_dwGhostTick) > 1000) then MakeGhost; }
      // 宝宝在主人死亡后死亡处理
      if m_btRaceServer <> RC_HEROOBJECT then begin
        if (m_Master.m_boDeath and ((GetTickCount - m_Master.m_dwDeathTick) > 1000)) then begin
          if g_Config.boMasterDieMutiny and (m_Master.m_LastHiter <> nil) and (Random(g_Config.nMasterDieMutinyRate) = 0) then begin
            m_Master := nil;
            m_btSlaveExpLevel := High(g_Config.SlaveColor);
            RecalcAbilitys();
            m_WAbil.DC := MakeLong(_MIN(MAXHUMPOWER, LoWord(m_WAbil.DC) * g_Config.nMasterDieMutinyPower), _MIN(MAXHUMPOWER, HiWord(m_WAbil.DC) * g_Config.nMasterDieMutinyPower));
            m_nWalkSpeed := m_nWalkSpeed div g_Config.nMasterDieMutinySpeed;
            RefNameColor;
            RefShowName;
          end else begin
            m_WAbil.HP := 0;
          end;
        end;
        if m_Master.m_boGhost and ((GetTickCount - m_Master.m_dwGhostTick) > 1000) then MakeGhost;
      end;
    end;

    //清除宝宝列表中已经死亡及叛变的宝宝信息
    nCheckCode := 3010;
    for I := m_SlaveList.Count - 1 downto 0 do begin
      if m_SlaveList.Count <= 0 then Break;
      if TActorObject(m_SlaveList.Items[I]) <> nil then begin
        if TActorObject(m_SlaveList.Items[I]).m_boDeath or
          TActorObject(m_SlaveList.Items[I]).m_boGhost or
          (TActorObject(m_SlaveList.Items[I]).m_Master <> Self) then
          m_SlaveList.Delete(I);
      end;
    end;

    if m_boSpaceLock then begin
      if m_SpaceOwner = nil then begin
        m_boSpaceLock := False;
      end else begin
        if m_SpaceOwner.m_boDeath or m_SpaceOwner.m_boGhost then begin
          m_boSpaceLock := False;
          m_SpaceOwner := nil;
        end;
      end;
    end;

    if m_boSpaceLock and (m_SpaceOwner <> nil) and (m_SpaceOwner <> Self) then begin
      if (m_PEnvir <> m_SpaceOwner.m_PEnvir) or (not InRect(m_nCurrX, m_nCurrY, m_SpaceRect)) then begin
        for I := m_SpaceOwner.m_LockList.Count - 1 downto 0 do begin
          if TActorObject(m_SpaceOwner.m_LockList.Items[I]) = Self then begin
            m_SpaceOwner.m_LockList.Delete(I);
            Break;
          end;
        end;
        m_boSpaceLock := False;
        m_SpaceOwner := nil;
      end;
    end;

    if (m_SpaceOwner <> Self) and (m_Master <> nil) and (m_SpaceOwner <> m_Master.m_SpaceOwner) and (m_Master.m_boSpaceLock) and (m_Master.m_SpaceOwner <> nil) and (m_PEnvir = m_Master.m_PEnvir) then begin
      if InRect(m_nCurrX, m_nCurrY, m_Master.m_SpaceRect) then begin
        m_boSpaceLock := True;
        m_SpaceRect := m_Master.m_SpaceRect;
        m_SpaceOwner := m_Master.m_SpaceOwner;
        AddLock(Self);
      end;
    end;

    for I := m_LockList.Count - 1 downto 0 do begin
      if m_LockList.Count <= 0 then Break;
      if TActorObject(m_LockList.Items[I]) <> nil then begin
        if TActorObject(m_LockList.Items[I]).m_boDeath or
          TActorObject(m_LockList.Items[I]).m_boGhost or
          (TActorObject(m_LockList.Items[I]).m_SpaceOwner = nil) or
          not TActorObject(m_LockList.Items[I]).m_boSpaceLock then
          m_LockList.Delete(I);
      end;
    end;

    nCheckCode := 3011;
    if m_boHolySeize and ((GetTickCount() - m_dwHolySeizeTick) > m_dwHolySeizeInterval) then begin
      BreakHolySeizeMode();
    end;
    nCheckCode := 3012;
    if m_boCrazyMode and ((GetTickCount() - m_dwCrazyModeTick) > m_dwCrazyModeInterval) then begin
      BreakCrazyMode();
    end;
    nCheckCode := 3013;
    if m_boShowHP and ((GetTickCount() - m_dwShowHPTick) > m_dwShowHPInterval) then begin
      BreakOpenHealth();
    end;
    nCheckCode := 3014;
  except
    MainOutMessage(sExceptionMsg3 + IntToStr(nCheckCode));
  end;

  //SetProcessName('TActorObject.Run ');
  try
    nCheckCode := 4;
    // 减少PK值开始
    if (GetTickCount() - m_dwDecPkPointTick) > g_Config.dwDecPkPointTime {120000} then begin
      m_dwDecPkPointTick := GetTickCount();
      if m_nPkPoint > 0 then begin
        DecPKPoint(g_Config.nDecPkPointCount {1});
      end;
    end;
    // 减少PK值结束

    //检查照明物品及PK状态 开始
    nCheckCode := 41;
    if (GetTickCount - m_DecLightItemDrugTick) > g_Config.dwDecLightItemDrugTime {500} then begin
      Inc(m_DecLightItemDrugTick, g_Config.dwDecLightItemDrugTime {500});
      if m_btRaceServer = RC_PLAYOBJECT then begin
        UseLamp();
        CheckPKStatus();
      end;
    end;
    //检查照明物品及PK状态 结束

    nCheckCode := 42;
    if ((GetTickCount - m_dwCheckRoyaltyTick) > 10000) and (m_btRaceServer <> RC_HEROOBJECT) and (m_btRaceServer <> RC_PLAYMOSTER) {英雄和分身不叛变} then begin
      m_dwCheckRoyaltyTick := GetTickCount();
      if m_Master <> nil then begin
        if (g_dwSpiritMutinyTick > GetTickCount) and (m_btSlaveExpLevel < 5) then begin
          m_dwMasterRoyaltyTick := 0;
        end;

        //宝宝叛变  开始
        nCheckCode := 423;
        if (GetTickCount > m_dwMasterRoyaltyTick) then begin
          for I := m_Master.m_SlaveList.Count - 1 downto 0 do begin
            if m_Master.m_SlaveList.Count <= 0 then Break;
            nCheckCode := 424;
            if m_Master.m_SlaveList.Items[I] = Self then begin
              nCheckCode := 425;
              m_Master.m_SlaveList.Delete(I);
              Break;
            end;
          end;
          m_Master := nil;
          m_WAbil.HP := m_WAbil.HP div 10;
          nCheckCode := 426;
          RefShowName();
        end;
        //宝宝叛变 结束
        nCheckCode := 427;
        if m_dwMasterTick <> 0 then begin
          if (GetTickCount - m_dwMasterTick) > 12 * 60 * 60 * 1000 then begin
            m_WAbil.HP := 0;
          end;
        end;
      end;
    end;

    if (GetTickCount - m_dwVerifyTick) > 30 * 1000 then begin
      m_dwVerifyTick := GetTickCount();
      if not m_boDenyRefStatus then
        m_PEnvir.VerifyMapTime(m_nCurrX, m_nCurrY, Self); //刷新在地图上位置的时间
    end;

  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg4, [nCheckCode]));
      MainOutMessage(E.Message);
    end;
  end;

  //SetProcessName('TActorObject.Run 5');
  try
    boChg := False;
    boNeedRecalc := False;
    //    for i:=0 to MAX_STATUS_ATTRIBUTE - 1 do begin
    for I := Low(m_dwStatusArrTick) to High(m_dwStatusArrTick) do begin //004C832E
      if (m_wStatusTimeArr[I] > 0) and (m_wStatusTimeArr[I] < 60000) then begin
        if (GetTickCount() - m_dwStatusArrTick[I]) > 1000 then begin
          Dec(m_wStatusTimeArr[I]);
          Inc(m_dwStatusArrTick[I], 1000);
          if (m_wStatusTimeArr[I] = 0) then begin
            boChg := True;
            case I of
              STATE_TRANSPARENT: begin
                  m_boHideMode := False;
                end;
              STATE_DEFENCEUP: begin
                  boNeedRecalc := True;
                  SysMsg('Defence returned to normal.', c_Green, t_Hint);
                end;
              STATE_MAGDEFENCEUP: begin
                  boNeedRecalc := True;
                  SysMsg('Magic Defence returned to normal.', c_Green, t_Hint);
                end;
              STATE_BUBBLEDEFENCEUP: begin
                  m_boAbilMagBubbleDefence := False;
                end;
              POISON_DONTMOVE: begin
                  m_nWalkSpeed:= m_nnonfrzWalkSpeed;
                  m_nNextHitTime := m_nnonfrzNextHitTime;
                end;
              POISON_LOCKSPELL: begin
                m_boTDBeffect:=False;
              end;
              POISON_FREEZE: begin
                m_nWalkSpeed:= m_nnonfrzWalkSpeed;
                m_nNextHitTime := m_nnonfrzNextHitTime;
              end;
            end;
          end;
        end;
      end;
    end;

    for I := Low(m_wStatusArrValue) to High(m_wStatusArrValue) do begin
      if m_wStatusArrValue {218} [I] > 0 then begin
        if GetTickCount() > m_dwStatusArrTimeOutTick {220} [I] then begin
          m_wStatusArrValue[I] := 0;
          m_dwStatusArrTimeOutTick[I] := 0;
          boNeedRecalc := True;
          case I of
            0, 6: begin
                SysMsg('Attack Power returned to normal.', c_Green, t_Hint);
              end;
            1, 7: begin
                SysMsg('Magic Power returned to normal.', c_Green, t_Hint);
              end;
            2, 8: begin
                SysMsg('Soul Power returned to normal.', c_Green, t_Hint);
              end;
            3: begin
                SysMsg('Attack Speed returned to normal.', c_Green, t_Hint);
              end;
            4: begin
                SysMsg('HP returned to normal.', c_Green, t_Hint);
              end;
            5: begin
                SysMsg('MP returned to normal', c_Green, t_Hint);
              end;
          end;
        end else begin
          case I of
            9: begin
                DamageHealth(m_wStatusArrValue[I]);
                HealthSpellChanged();
              end;
            10: begin
                DamageSpell(m_wStatusArrValue[I]);
                HealthSpellChanged();
              end;
            11: ;
          end;
        end;
      end;
    end;

    for I := Low(m_wNewStatusArrValue) to High(m_wNewStatusArrValue) do begin
      if m_wNewStatusArrValue[I] > 0 then begin
        if GetTickCount() > m_dwNewStatusArrTimeOutTick[I] then begin
          m_wNewStatusArrValue[I] := 0;
          m_dwNewStatusArrTimeOutTick[I] := 0;
          boNeedRecalc := True;
        end;
      end else begin
        m_dwNewStatusArrTimeOutTick[I] := 0;
      end;
    end;

    if boChg then begin
      m_nCharStatus := GetCharStatus();
      StatusChanged();
    end;

    if boNeedRecalc then begin
      RecalcAbilitys();
      SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
    end;
  except
    MainOutMessage(sExceptionMsg5);
  end;

  try
    if (GetTickCount - m_dwPoisoningTick) > g_Config.dwPosionDecHealthTime {2500} then begin
      m_dwPoisoningTick := GetTickCount();
      if m_wStatusTimeArr[POISON_DECHEALTH {0 0x60}] > 0 then begin
        if m_boAnimal then Dec(m_nMeatQuality, 1000);
        DamageHealth(m_btGreenPoisoningPoint + 1);
        m_nHealthTick := 0;
        m_nSpellTick := 0;
        HealthSpellChanged();
      end;
    end;
  except
    MainOutMessage(sExceptionMsg6);
  end;
  {
  if boOpenHealth then begin
    if (GetTickCount() - dwOpenHealthStart) > dwOpenHealthTime then begin
      BreakOpenHealth();
    end;
  end;
  }
  try
    if (m_btRaceServer <> RC_PLAYOBJECT) then begin
      if (m_nChangeColorType >= 256) and (GetTickCount - m_dwReColorTick > g_Config.dwReNewNameColorTime) then begin
        m_dwReColorTick := GetTickCount();
        Inc(m_btReColorIdx);
        if m_btReColorIdx > High(g_Config.ReNewNameColor) then m_btReColorIdx := 0; //名称自动变色
        m_btNameColor := g_Config.ReNewNameColor[m_btReColorIdx];
        RefNameColor;
      end;
    end;
  except
    MainOutMessage(sExceptionMsg7);
  end;

  try
    if (m_LastSetStatus <> nil) and m_LastSetStatus.m_boGhost then m_LastSetStatus := nil;
    if (m_LastSetStatus <> nil) and m_LastSetStatus.m_boDeath and (not (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT])) then m_LastSetStatus := nil;
    if m_NewStatus <> sNone then begin
      if GetTickCount - m_dwStatusDelayTick >= 1000 then begin
        m_dwStatusDelayTick := GetTickCount;
        if m_nStatusDelayTime > 0 then Dec(m_nStatusDelayTime) else
          m_nStatusDelayTime := 0;
      end;
      if m_nStatusDelayTime <= 0 then begin
        SetNewStatus(sNone);
      end else begin
        SetNewStatus(m_NewStatus);
      end;
    end;
  except
    MainOutMessage(sExceptionMsg8);
  end;
  g_nBaseObjTimeMin := GetTickCount - dwRunTick;
  if g_nBaseObjTimeMax < g_nBaseObjTimeMin then g_nBaseObjTimeMax := g_nBaseObjTimeMin;
end;

function TPlayObject.DayBright: Byte;
begin
  Result := 0;
  if m_PEnvir <> nil then begin
    if m_PEnvir.m_boDarkness then
      Result := 1
    else if m_PEnvir.m_boDayLight then Result := 0;
  end;
end;

function TActorObject.GetFrontPosition(var nX: Integer; var nY: Integer): Boolean;
var
  Envir: TEnvirnoment;
begin
  Envir := m_PEnvir;
  nX := m_nCurrX;
  nY := m_nCurrY;
  case m_btDirection of
    DR_UP: begin
        if nY > 0 then Dec(nY);
      end;
    DR_UPRIGHT: begin
        if (nX < (Envir.m_nWidth - 1)) and (nY > 0) then begin
          Inc(nX);
          Dec(nY);
        end;
      end;
    DR_RIGHT: begin
        if nX < (Envir.m_nWidth - 1) then Inc(nX);
      end;
    DR_DOWNRIGHT: begin
        if (nX < (Envir.m_nWidth - 1)) and (nY < (Envir.m_nHeight - 1)) then begin
          Inc(nX);
          Inc(nY);
        end;
      end;
    DR_DOWN: begin
        if nY < (Envir.m_nHeight - 1) then Inc(nY);
      end;
    DR_DOWNLEFT: begin
        if (nX > 0) and (nY < (Envir.m_nHeight - 1)) then begin
          Dec(nX);
          Inc(nY);
        end;
      end;
    DR_LEFT: begin
        if nX > 0 then Dec(nX);
      end;
    DR_UPLEFT: begin
        if (nX > 0) and (nY > 0) then begin
          Dec(nX);
          Dec(nY);
        end;
      end;
  end;
  Result := True;
end;

procedure TActorObject.SpaceMove(sMAP: string; nX, nY: Integer; nInt: Integer);
  function GetRandXY(Envir: TEnvirnoment; var nX: Integer; var nY: Integer): Boolean;
  var
    n14, n18, n1C: Integer;
  begin
    Result := False;
    if Envir.m_nWidth < 80 then n18 := 3
    else n18 := 10;
    if Envir.m_nHeight < 150 then begin
      if Envir.m_nHeight < 50 then n1C := 2
      else n1C := 15;
    end else n1C := 50;
    n14 := 0;
    while (True) do begin
      if Envir.CanWalk(nX, nY, True) and Envir.CanWalkOfEvent(Self, nX, nY) then begin
        if m_boStartDuel and Envir.m_boDuel and Envir.m_boDueling then begin
          if TActorObject(Envir.m_PlayObject).m_boSpaceLock then begin
            if InRect(nX, nY, TActorObject(Envir.m_PlayObject).m_SpaceRect) then begin //传送到比赛地图的锁定空间
              m_SpaceOwner := TActorObject(Envir.m_PlayObject);
              m_boSpaceLock := True;
              m_SpaceRect := TActorObject(Envir.m_PlayObject).m_SpaceRect;
              TActorObject(Envir.m_PlayObject).AddLock(Self);
              Result := True;
              Break;
            end;
          end else begin
            Result := True;
            Break;
          end;
        end else begin
          if not InLockRect(Envir, nX, nY) then begin
            Result := True;
            Break;
          end;
        end;
      end;
      if nX < (Envir.m_nWidth - n1C - 1) then Inc(nX, n18)
      else begin
        nX := Random(Envir.m_nWidth);
        if nY < (Envir.m_nHeight - n1C - 1) then Inc(nY, n18)
        else nY := Random(Envir.m_nHeight);
      end;
      Inc(n14);
      if n14 >= 201 then Break;
    end;
  end;
var
  I: Integer;
  Envir, OldEnvir: TEnvirnoment;
  nOldX, nOldY: Integer;
  bo21: Boolean;
  PlayObject: TPlayObject;
  nError: Integer;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::SpaceMove Code:%d';
begin
  try
    nError := 0;
    if m_boStore or (m_btRaceServer = 122) then Exit; //摆摊禁止操作  镖车禁止
    nError := 1;
    if not m_boStartDuel then begin
      if InLockRect(m_PEnvir, m_nCurrX, m_nCurrY) then begin
        if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT] then begin
          SysMsg('Blocked by unknown forces!', c_Red, t_Hint);
        end;
        Exit;
      end;
    end else begin
      if m_PEnvir.m_boDuel and m_PEnvir.m_boDueling and InLockRect(m_PEnvir, m_nCurrX, m_nCurrY) then begin
        if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT] then begin
          SysMsg('You are currently in a dual area, you cannot leave!', c_Red, t_Hint);
        end;
        Exit;
      end;
    end;
    nError := 2;
    Envir := g_MapManager.FindMap(sMAP);
    nError := 3;
    if Envir <> nil then begin
      nError := 4;
      if Envir.m_boDuel then begin
        nError := 5;
        if not Envir.m_boDueling then begin
          nError := 6;
          if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT] then begin
            SysMsg('Dual has not started yet, Unable to enter Map.', c_Red, t_Hint);
          end;
          Exit;
        end;
      end;
      nError := 7;
      OldEnvir := m_PEnvir;
      nOldX := m_nCurrX;
      nOldY := m_nCurrY;
      bo21 := False;
      nError := 8;
      if m_PEnvir.DeleteFromMap(m_nCurrX, m_nCurrY, Self) <> 1 then DelMapCount;
      nError := 9;
      ClearViewRange();
      nError := 10;
      m_PEnvir := Envir;
      m_sMapName := Envir.sMapName;
      m_nCurrX := nX;
      m_nCurrY := nY;
      nError := 11;
      if GetRandXY(m_PEnvir, m_nCurrX, m_nCurrY) then begin
        nError := 12;
        m_PEnvir.AddToMap(m_nCurrX, m_nCurrY, Self);
        nError := 13;
        if (m_btRaceServer = RC_PLAYOBJECT) and (not m_boNotOnlineAddExp) and (not m_boAI) then begin
          SendMsg(Self, RM_CLEAROBJECTS, 0, 0, 0, 0, '');
          SendMsg(Self, RM_CHANGEMAP, 0, 0, 0, 0, Envir.MapName); //获取重复利用地图名称
        end;
        nError := 14;
        if nInt = 1 then begin
          SendRefMsg(RM_SPACEMOVE_SHOW2, m_btDirection, m_nCurrX, m_nCurrY, 0, '');
        end else SendRefMsg(RM_SPACEMOVE_SHOW, m_btDirection, m_nCurrX, m_nCurrY, 0, '');
        nError := 15;
        m_dwMapMoveTick := GetTickCount();
        m_bo316 := True;
        bo21 := True;
        m_nNewStatusX := m_nCurrX;
        m_nNewStatusY := m_nCurrY;
      end;
      nError := 16;
      if not bo21 then begin
        m_PEnvir := OldEnvir;
        m_nCurrX := nOldX;
        m_nCurrY := nOldY;
        m_PEnvir.AddToMap(m_nCurrX, m_nCurrY, Self);
      end;
      nError := 17;
      AddMapCount;
      nError := 18;
    {end else begin
      if GetRandXY(Envir, nX, nY) then begin
        if m_btRaceServer = RC_PLAYOBJECT then begin
          DisappearA();
          m_bo316 := True;
          PlayObject := TPlayObject(Self);

          PlayObject.m_boSayAdvertise := False;
          PlayObject.m_dwSayAdvertiseTick := GetTickCount;
          PlayObject.m_dwSayNoticeTick := GetTickCount;

          PlayObject.m_sSwitchMapName := Envir.sMapName;
          PlayObject.m_nSwitchMapX := nX;
          PlayObject.m_nSwitchMapY := nY;
          PlayObject.m_boSwitchData := True;
          PlayObject.m_nServerIndex := Envir.nServerIndex;
          PlayObject.m_boEmergencyClose := True;
          PlayObject.m_boReconnection := True;
          PlayObject.m_boPlayOffLine := False;
          PlayObject.DisappearB();
          MainOutMessage();
        end else KickException();
     end;
    end;}
    end;
  except
    MainOutMessage(Format(sExceptionMsg, [nError]));
    KickException();
  end;
end;

procedure TActorObject.SpaceMove2(nX, nY: Integer; nInt: Integer);
  function GetRandXY(Envir: TEnvirnoment; var nX: Integer; var nY: Integer): Boolean;
  var
    n14, n18, n1C: Integer;
  begin
    Result := False;
    if Envir.m_nWidth < 80 then n18 := 3
    else n18 := 10;
    if Envir.m_nHeight < 150 then begin
      if Envir.m_nHeight < 50 then n1C := 2
      else n1C := 15;
    end else n1C := 50;
    n14 := 0;
    while (True) do begin
      if Envir.CanWalk(nX, nY, True) and Envir.CanWalkOfEvent(Self, nX, nY) and (not InLockRect(Envir, nX, nY)) then begin
        Result := True;
        Break;
      end;
      if nX < (Envir.m_nWidth - n1C - 1) then Inc(nX, n18)
      else begin
        nX := Random(Envir.m_nWidth);
        if nY < (Envir.m_nHeight - n1C - 1) then Inc(nY, n18)
        else nY := Random(Envir.m_nHeight);
      end;
      Inc(n14);
      if n14 >= 201 then Break;
    end;
  end;
var
  I: Integer;
  nOldX, nOldY: Integer;
  bo21: Boolean;
  PlayObject: TPlayObject;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::SpaceMove2 Code:%d';
begin
  try
    if m_boStore or (m_btRaceServer = 122) then Exit; //摆摊禁止操作  镖车禁止
    if InLockRect(m_PEnvir, m_nCurrX, m_nCurrY) then begin
      if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT] then begin
        SysMsg('Blocked by unknown forces.', c_Red, t_Hint);
      end;
      Exit;
    end;
    nOldX := m_nCurrX;
    nOldY := m_nCurrY;
    bo21 := False;
    if m_PEnvir.DeleteFromMap(m_nCurrX, m_nCurrY, Self) <> 1 then DelMapCount;
    m_nCurrX := nX;
    m_nCurrY := nY;
    if GetRandXY(m_PEnvir, m_nCurrX, m_nCurrY) then begin
      m_PEnvir.AddToMap(m_nCurrX, m_nCurrY, Self);
      if (m_btRaceServer = RC_PLAYOBJECT) and (not m_boNotOnlineAddExp) and (not m_boAI) then begin
        SendMsg(Self, RM_CHANGEMAP, 0, 0, 0, 0, m_PEnvir.MapName);
       //SendMsg(Self, RM_CLEAROBJECTS, 0, 0, 0, 0, '');
      end;
      if nInt = 1 then begin
        SendRefMsg(RM_SPACEMOVE_SHOW2, m_btDirection, m_nCurrX, m_nCurrY, 0, '');
      end else SendRefMsg(RM_SPACEMOVE_SHOW, m_btDirection, m_nCurrX, m_nCurrY, 0, '');
      m_dwMapMoveTick := GetTickCount();
      m_bo316 := True;
      bo21 := True;
      m_nNewStatusX := m_nCurrX;
      m_nNewStatusY := m_nCurrY;
    end;
    if not bo21 then begin
      m_nCurrX := nOldX;
      m_nCurrY := nOldY;
      m_PEnvir.AddToMap(m_nCurrX, m_nCurrY, Self);
    end;
    AddMapCount;
  except
    MainOutMessage(Format(sExceptionMsg, [0]));
    KickException();
  end;
end;

procedure TPlayObject.RefUserState();
var
  n8: Integer;
begin
  n8 := 0;
  if m_PEnvir.m_boFightZone then n8 := n8 or 1;
  if m_PEnvir.m_boSAFE then n8 := n8 or 2;
  if m_boInFreePKArea then n8 := n8 or 4;
  SendDefMessage(SM_AREASTATE, n8, 0, 0, 0, '');
end;

procedure TActorObject.RefShowName();
begin
  SendRefMsg(RM_USERNAME, 0, 0, 0, 0, GetShowName);
end;

procedure TPlayObject.RefMyStatus();
begin
  RecalcAbilitys();
  SendMsg(Self, RM_MYSTATUS, 0, 0, 0, 0, '');
end;

function TActorObject.Operate(ProcessMsg: pTProcessMessage): Boolean;
  function MINXY(AObject, BObject: TActorObject): TActorObject;
  var
    nA, nB: Integer;
  begin
    nA := abs(m_nCurrX - AObject.m_nCurrX) + abs(m_nCurrY - AObject.m_nCurrY);
    nB := abs(m_nCurrX - BObject.m_nCurrX) + abs(m_nCurrY - BObject.m_nCurrY);
    if nA > nB then Result := BObject else Result := AObject;
  end;
var
  nDamage: Integer;
  nTargetX: Integer;
  nTargetY: Integer;
  nPower: Integer;
  nRage: Integer;
  boDisableSayMsg: Boolean;
  TargeTActorObject, AttackBaseObject: TActorObject;
  nError: Integer;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::Operate Ident:';
  sExceptionMsg0 = '[Exception] TActorObject::Operate %s Ident:%d Sender:%d wP:%d nP1:%d nP2:%d np3:%d Msg:%s';
begin
  Result := False;
  nError := 0;
  try
    case ProcessMsg.wIdent of
      RM_MAGSTRUCK,
        RM_MAGSTRUCK_MINE: begin //10025
          if (ProcessMsg.wIdent = RM_MAGSTRUCK) and
            (m_btRaceServer >= RC_ANIMAL) and
            (not bo2BF) and (m_Abil.Level < 50) then begin
            m_dwWalkTick := m_dwWalkTick + 800 + LongWord(Random(1000));
          end;
          nError := -1;
          AttackBaseObject := TActorObject(ProcessMsg.BaseObject);
          nError := -2;
          m_boNotDefendoof := Boolean(ProcessMsg.nParam2);
          nError := -3;
          nDamage := GetMagStruckDamage(nil, ProcessMsg.nParam1);
          nError := -4;
          StartNewShield();
          nError := -5;
          if nDamage > 0 then begin
            //TargeTActorObject := TActorObject(ProcessMsg.BaseObject);
            if AttackBaseObject <> nil then begin
              nError := -6;
              {
                if (m_NewStatus <> sBlind) and GetBlind(AttackBaseObject) then begin //失明状态
                  nError := -7;
                  m_LastSetStatus := AttackBaseObject;
                  nError := -8;
                  SetNewStatus(sBlind);
                  nError := -9;
                end else
                  if (m_NewStatus <> sConfusion) and GetConfusion(AttackBaseObject) then begin //混乱状态
                  nError := -11;
                  m_LastSetStatus := AttackBaseObject;
                  nError := -12;
                  SetNewStatus(sConfusion);
                  nError := -13;
                end;
              }

              if (m_NewStatus = sNone) then begin
                if Random(2) = 0 then begin
                  if GetBlind(AttackBaseObject) then begin //失明状态
                    nError := -7;
                    m_LastSetStatus := AttackBaseObject;
                    nError := -8;
                    SetNewStatus(sBlind);
                    nError := -9;
                  end else
                    if GetConfusion(AttackBaseObject) then begin //混乱状态
                    nError := -11;
                    m_LastSetStatus := AttackBaseObject;
                    nError := -12;
                    SetNewStatus(sConfusion);
                    nError := -13;
                  end;
                end else begin
                  if GetConfusion(AttackBaseObject) then begin //混乱状态
                    nError := -11;
                    m_LastSetStatus := AttackBaseObject;
                    nError := -12;
                    SetNewStatus(sConfusion);
                    nError := -13;
                  end else
                    if GetBlind(AttackBaseObject) then begin //失明状态
                    nError := -7;
                    m_LastSetStatus := AttackBaseObject;
                    nError := -8;
                    SetNewStatus(sBlind);
                    nError := -9;
                  end;
                end;
              end;

              //TargeTActorObject := AttackBaseObject;

              if (not AttackBaseObject.m_boDeath) and (not AttackBaseObject.m_boGhost) then begin
                if m_boAI or (m_btRaceServer in [RC_PLAYMOSTER, RC_HEROOBJECT]) then begin
                  nError := 12;
                  if (AttackBaseObject.m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) or ((AttackBaseObject.m_Master <> nil) and (AttackBaseObject.Master.m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER])) then begin
                    nError := 13;
                    if (m_TargetCret <> nil) and ((m_TargetCret.m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) or ((m_TargetCret.m_Master <> nil) and (m_TargetCret.Master.m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]))) then begin
                      nError := 14;
                      if ((MINXY(m_TargetCret, AttackBaseObject) = AttackBaseObject) or (Random(6) = 0)) and (GetTickCount() - m_dwTargetFocusTick > 1000 * 3) then begin
                        SetTargetCreat(AttackBaseObject);
                        //if Self is TAIPlayObject then TAIPlayObject(Self).Start(TPathType(0));
                        //MainOutMessage('RM_MAGSTRUCK:' + m_sCharName);
                      end;
                      nError := 15;
                    end else begin
                      nError := 16;
                      SetTargetCreat(AttackBaseObject);
                      //if Self is TAIPlayObject then TAIPlayObject(Self).Start(TPathType(0));
                      //MainOutMessage('RM_MAGSTRUCK:' + m_sCharName);
                      nError := 17;
                    end;
                    nError := 18;
                    if m_boAI and (m_btRaceServer = RC_PLAYOBJECT) then begin
                      nError := 19;
                      if (Random(6) = 0) then begin
                        nError := 20;
                        if GetTickCount >= TPlayObject(Self).m_dwDisableSayMsgTick then TPlayObject(Self).m_boDisableSayMsg := False;
                        boDisableSayMsg := TPlayObject(Self).m_boDisableSayMsg;
                        g_DenySayMsgList.Lock;
                        try
                          if g_DenySayMsgList.GetIndex(m_sCharName) >= 0 then boDisableSayMsg := True;
                        finally
                          g_DenySayMsgList.UnLock;
                        end;
                        nError := 21;
                        if not boDisableSayMsg then begin
                          SendRefMsg(RM_HEAR, 0, TPlayObject(Self).m_btHearMsgFColor, g_Config.btHearMsgBColor, 0, m_sCharName + ':' + g_AI3.Ask(''));
                        end;
                        nError := 22;
                      end;
                    end;
                  end else begin
                    nError := 23;
                    if (m_TargetCret = nil) and IsProperTarget(AttackBaseObject) then begin
                      SetTargetCreat(AttackBaseObject);
                      //if Self is TAIPlayObject then TAIPlayObject(Self).Start(TPathType(0));
                      //MainOutMessage('RM_MAGSTRUCK:' + m_sCharName);
                    end else begin
                      if ((m_TargetCret <> nil) and (MINXY(m_TargetCret, AttackBaseObject) = AttackBaseObject)) or (Random(6) = 0) then begin
                        if (m_btJob > 0) or ((m_TargetCret <> nil) and (GetTickCount() - m_dwTargetFocusTick > 1000 * 3)) then
                          if IsProperTarget(AttackBaseObject) then begin
                            SetTargetCreat(AttackBaseObject);
                            //if Self is TAIPlayObject then TAIPlayObject(Self).Start(TPathType(0));
                            //MainOutMessage('RM_MAGSTRUCK:' + m_sCharName);
                          end;
                      end;
                    end;
                    nError := 24;
                  end;
                end;
              end;
            end; //if AttackBaseObject <> nil then begin

            StruckDamage(nDamage);

            HealthSpellChanged();
            SendRefMsg(RM_STRUCK_MAG, nDamage, m_WAbil.HP, m_WAbil.MaxHP, Integer(ProcessMsg.BaseObject), '');
            if m_btRaceServer <> RC_PLAYOBJECT then begin
              if m_boAnimal then Dec(m_nMeatQuality, nDamage * 1000);
              SendMsg(Self, RM_STRUCK, nDamage, m_WAbil.HP, m_WAbil.MaxHP, Integer(ProcessMsg.BaseObject) {AttackBaseObject}, '');
            end;
          end;
          if m_boFastParalysis then begin
            m_wStatusTimeArr[POISON_STONE] := _MAX(_MIN(m_wStatusTimeArr[POISON_STONE], 5), 1); //修改麻痹被攻击5秒后可以活动
            m_boFastParalysis := False;
          end;

          if ProcessMsg.BaseObject <> nil then begin //反射伤害
            TargeTActorObject := TActorObject(ProcessMsg.BaseObject);
            nDamage := GetAddPowerPoint(4, nDamage);
            if nDamage > 0 then begin
              //nDamage := nDamage + GetAddPowerPoint(5, nDamage);
              TargeTActorObject.StruckDamage(nDamage);
              TargeTActorObject.HealthSpellChanged();
              TargeTActorObject.SendRefMsg(RM_STRUCK_MAG, nDamage, TargeTActorObject.m_WAbil.HP, TargeTActorObject.m_WAbil.MaxHP, Integer(Self), '');
              if TargeTActorObject.m_btRaceServer <> RC_PLAYOBJECT then begin
                if TargeTActorObject.m_boAnimal then Dec(TargeTActorObject.m_nMeatQuality, nDamage * 1000);
                TargeTActorObject.SendMsg(TargeTActorObject, RM_STRUCK, nDamage, TargeTActorObject.m_WAbil.HP, TargeTActorObject.m_WAbil.MaxHP, Integer(Self) {AttackBaseObject}, '');
              end;
              if TargeTActorObject.m_boFastParalysis then begin
                TargeTActorObject.m_wStatusTimeArr[POISON_STONE] := _MAX(_MIN(m_wStatusTimeArr[POISON_STONE], 5), 1); //修改麻痹被攻击5秒后可以活动
                TargeTActorObject.m_boFastParalysis := False;
              end;
            end;
          end;

        end;

      RM_MAGHEALING: begin
          if (m_nIncHealing + ProcessMsg.nParam1) < 300 then begin
            if m_btRaceServer = RC_PLAYOBJECT then begin
              Inc(m_nIncHealing, ProcessMsg.nParam1);
              m_nPerHealing := 5;
            end else begin
              Inc(m_nIncHealing, ProcessMsg.nParam1);
              m_nPerHealing := 5;
            end;
          end else m_nIncHealing := 300;
        end;
      RM_10101: begin //10101
          SendRefMsg(Integer(ProcessMsg.BaseObject),
            ProcessMsg.wParam {nPower},
            ProcessMsg.nParam1 {HP},
            ProcessMsg.nParam2 {MaxHP},
            ProcessMsg.nParam3 {AttackSrc},
            ProcessMsg.sMsg);
          if (Integer(ProcessMsg.BaseObject) = RM_STRUCK) and (m_btRaceServer <> RC_PLAYOBJECT) then begin
            SendMsg(Self, Integer(ProcessMsg.BaseObject),
              ProcessMsg.wParam,
              ProcessMsg.nParam1,
              ProcessMsg.nParam2,
              ProcessMsg.nParam3 {AttackBaseObject},
              ProcessMsg.sMsg);
          end;
          if m_boFastParalysis then begin
            m_wStatusTimeArr[POISON_STONE] := _MAX(_MIN(m_wStatusTimeArr[POISON_STONE], 5), 1); //修改麻痹被攻击5秒后可以活动
            m_boFastParalysis := False;
          end;
        end;
      RM_DELAYMAGIC: begin
          nPower := ProcessMsg.wParam;
          nTargetX := LoWord(ProcessMsg.nParam1);
          nTargetY := HiWord(ProcessMsg.nParam1);
          nRage := LoWord(ProcessMsg.nParam2);
          TargeTActorObject := TActorObject(ProcessMsg.nParam3); //被攻击方
          if (TargeTActorObject <> nil) then TargeTActorObject.m_boNotDefendoof := Boolean(HiWord(ProcessMsg.nParam2));
          if (TargeTActorObject <> nil) and
            (TargeTActorObject.GetMagStruckDamage(Self, nPower) > 0) then begin

            if m_boAI or (m_btRaceServer in [RC_PLAYMOSTER, RC_HEROOBJECT]) then begin

              if (TargeTActorObject.m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) or ((TargeTActorObject.m_Master <> nil) and (TargeTActorObject.Master.m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER])) then begin
                if (m_TargetCret <> nil) and ((m_TargetCret.m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) or ((m_TargetCret.m_Master <> nil) and (m_TargetCret.Master.m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]))) then begin
                  if ((MINXY(m_TargetCret, TargeTActorObject) = TargeTActorObject) or (Random(6) = 0)) and (GetTickCount() - m_dwTargetFocusTick > 1000 * 3) then SetTargetCreat(TargeTActorObject);
                end else begin
                  SetTargetCreat(TargeTActorObject);
                end;

                if m_boAI and (m_btRaceServer = RC_PLAYOBJECT) then begin
                  if (Random(6) = 0) then begin
                    if GetTickCount >= TPlayObject(Self).m_dwDisableSayMsgTick then TPlayObject(Self).m_boDisableSayMsg := False;
                    boDisableSayMsg := TPlayObject(Self).m_boDisableSayMsg;
                    g_DenySayMsgList.Lock;
                    try
                      if g_DenySayMsgList.GetIndex(m_sCharName) >= 0 then boDisableSayMsg := True;
                    finally
                      g_DenySayMsgList.UnLock;
                    end;
                    if not boDisableSayMsg then begin
                      SendRefMsg(RM_HEAR, 0, {g_Config.btHearMsgFColor} TPlayObject(Self).m_btHearMsgFColor, g_Config.btHearMsgBColor, 0, m_sCharName + ':' + g_AI3.Ask(''));
                    end;
                  end;
                end;
              end else begin
                if (m_TargetCret = nil) and IsProperTarget(TargeTActorObject) then SetTargetCreat(TargeTActorObject)
                else begin
                  if ((m_TargetCret <> nil) and (MINXY(m_TargetCret, TargeTActorObject) = TargeTActorObject)) or (Random(6) = 0) then begin
                    if (m_btJob > 0) or ((m_TargetCret <> nil) and (GetTickCount() - m_dwTargetFocusTick > 1000 * 3)) then
                      if IsProperTarget(TargeTActorObject) then SetTargetCreat(TargeTActorObject);
                  end;
                end;
              end;

            end else begin
              SetTargetCreat {0FFF2}(TargeTActorObject);
            end;

            if TargeTActorObject.m_btRaceServer >= RC_ANIMAL then
              nPower := Round(nPower / 1.2);
            if (abs(nTargetX - TargeTActorObject.m_nCurrX) <= nRage) and (abs(nTargetY - TargeTActorObject.m_nCurrY) <= nRage) then begin

              nPower := nPower + GetAddPowerPoint(5, nPower);

              TargeTActorObject.SendMsg(Self, RM_MAGSTRUCK, 0, nPower, HiWord(ProcessMsg.nParam2), 0, '');
            end;
          end;
        end;
      RM_10155: begin
          MapRandomMove(ProcessMsg.sMsg, ProcessMsg.wParam);
        end;
      RM_DELAYPUSHED: begin
          nPower := ProcessMsg.wParam;
          nTargetX := LoWord(ProcessMsg.nParam1);
          nTargetY := HiWord(ProcessMsg.nParam1);
          nRage := ProcessMsg.nParam2;
          TargeTActorObject := TActorObject(ProcessMsg.nParam3);
          if (TargeTActorObject <> nil) then begin
            TargeTActorObject.CharPushed(nPower, nRage);
          end;
        end;
      RM_POISON: begin
          nError := 25;
          TargeTActorObject := TActorObject(ProcessMsg.nParam2);
          nError := 26;
          if TargeTActorObject <> nil then begin
            if IsProperTarget {FFF4}(TargeTActorObject) then begin
              nError := 27;
              SetTargetCreat {0FFF2}(TargeTActorObject);
              nError := 28;
              if (m_btRaceServer = RC_PLAYOBJECT) and (TargeTActorObject.m_btRaceServer = RC_PLAYOBJECT) then begin
                nError := 29;
                SetPKFlag(TargeTActorObject);
                nError := 30;
              end;
              nError := 31;
              SetLastHiter(TargeTActorObject);
              nError := 32;
              m_PoisonTarget := TargeTActorObject;
              nError := 33;
              m_PoisonTick := GetTickCount;
            end;
            nError := 34;
            MakePosion(ProcessMsg.wParam {中毒类型}, ProcessMsg.nParam1 {nPower}, ProcessMsg.nParam3 {});
            nError := 35;
          end else MakePosion(ProcessMsg.wParam {中毒类型}, ProcessMsg.nParam1 {nPower}, ProcessMsg.nParam3);
          //MainOutMessage(m_sCharName+' RM_POISON:'+IntToStr(nError));
          nError := 36;
        end;
      RM_TRANSPARENT: begin //10308
          MagicManager.MagMakePrivateTransparent(Self, ProcessMsg.nParam1);
        end;
      RM_DOOPENHEALTH: begin //10412
          MakeOpenHealth();
        end;

{$IF CHECKNEWMSG = 1}
    else begin
        MainOutMessage(Format('Character: %s Message: Ident %d Param %d P1 %d P2 %d P3 %d Msg %s',
          [m_sCharName,
          ProcessMsg.wIdent,
            ProcessMsg.wParam,
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            ProcessMsg.nParam3,
            ProcessMsg.sMsg]));
      end;
{$IFEND}
    end;
  except
    on E: Exception do begin
      MainOutMessage(sExceptionMsg + IntToStr(ProcessMsg.wIdent) + ' Error:' + IntToStr(nError));
      MainOutMessage(Format(sExceptionMsg0, [m_sCharName,
        ProcessMsg.wIdent,
          Integer(ProcessMsg.BaseObject),
          ProcessMsg.wParam,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          ProcessMsg.sMsg]));
      MainOutMessage(E.Message);
    end;
  end;
end;

function TPlayObject.Operate(ProcessMsg: pTProcessMessage): Boolean;
var
  CharDesc: TCharDesc;
  Health: THealth;
  nObjCount: Integer;
  s1C: string;
  MessageBodyWL: TMessageBodyWL;
  MessageBodyW: TMessageBodyW;
  ShortMessage: TShortMessage;
  OAbility: TOAbility;
  dwDelayTime: LongWord;
  nMsgCount: Integer;
  boReturn: Boolean;
begin
  Result := True;
  if ProcessMsg = nil then begin
    Result := False;
    Exit;
  end;
  if ProcessMsg.wIdent = 0 then begin
    Result := False;
    Exit;
  end;
  boReturn := False;
  case ProcessMsg.wIdent of
    CM_HEROLOGON: begin //召唤英雄
        ClientHeroLogOn();
      end;
    CM_HEROLOGOUT: begin //英雄退出
        ClientHeroLogOut(TActorObject(ProcessMsg.nParam1));
      end;
    CM_MASTERBAGTOHEROBAG: begin //主人包裹物品放到英雄包裹
        ClientMasterBagToHeroBag(ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;
    CM_HEROBAGTOMASTERBAG: begin //英雄包裹物品放到主人包裹
        ClientHeroBagToMasterBag(ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;
    CM_HEROTARGET: begin //锁定//Ident: 1105 Recog: 260806992 Param: 0 Tag: 32 Series: 0   Recog= 锁定对象   Param=X  Tag=Y
        ClientHeroTarget(TActorObject(ProcessMsg.nParam1), ProcessMsg.nParam2, ProcessMsg.nParam3); //锁定
      end;
    CM_HEROPROTECT: begin //守护
        ClientHeroProtect(ProcessMsg.nParam2, ProcessMsg.nParam3);
      end;

    CM_HEROGROUPATTACK: begin //合击
        ClientHeroGroupAttack(TActorObject(ProcessMsg.nParam1), ProcessMsg.nParam2, ProcessMsg.nParam3); //合击
      end;
    CM_GETRANKING: begin //查询排行榜
        ClientQueryRanking(ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam, False);
      end;
    CM_GETMYRANKING: begin //查询我的排行榜
        ClientQueryRanking(ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam, True);
      end;

    CM_TAKEONITEMFROMHERO, CM_HEROTAKEONITEMFROMMASTER: begin //从主人包裹穿装备到英雄包裹 //从英雄包裹穿装备到主人包裹
        ClientTakeOnItemsFormBag(ProcessMsg.wIdent, ProcessMsg.nParam2, ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;

    CM_TAKEOFFITEMTOHERO, CM_HEROTAKEOFFITEMTOMASTER: begin //装备脱下到英雄包裹 //装备脱下到主人包裹
        ClientTakeOffItemsToBag(ProcessMsg.wIdent, ProcessMsg.nParam2, ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;

    CM_SENDSELLOFFGOODSLIST: begin
        ClientGetSellGoodList(ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.sMsg);
      end;
    CM_SENDSEARCHSELLITEM: begin

      end;

    CM_OPENITEMBOX: begin //打开宝箱
        ClientOpenBox(ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;
    CM_GETBACKITEMBOX: begin
        ClientGetBackBox();
      end;

    RM_OPENITEMBOX: begin
        ClientOpenBox(ProcessMsg.sMsg);
      end;
    CM_GETSELBOXITEMNUM: begin
        ClientGetBoxIndex();
      end;

    CM_GETSELBOXITEM: begin
        ClientGetBoxItem(ProcessMsg.nParam1);
      end;
    CM_SENDUPGRADEITEM: begin
        ClientGetUpgradeItems(ProcessMsg.sMsg);
      end;

    RM_GETSELBOXITEMNUM: begin
        SendDefMessage(SM_GETSELBOXITEMNUM,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          ProcessMsg.wParam,
          ProcessMsg.sMsg);
      end;

    RM_OPENBOOK: begin
        SendDefMessage(SM_OPENBOOK,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          ProcessMsg.wParam,
          ProcessMsg.sMsg);
      end;
    CM_SENDFINDITEMINFO: begin
        ClientQueryUserItem(ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;

    CM_STARTSERIESPELL: begin; //开始连击
        ClientStartSerieSpell(TActorObject(ProcessMsg.nParam1), ProcessMsg.nParam2, ProcessMsg.nParam3);
      end;

    CM_STOPSERIESPELL: begin; //停止连击
        ClientStopSerieSpell();
      end;

    RM_REFHEROLOGON: begin
        if m_MyHero <> nil then begin //发送英雄信息 人物是离线挂机状态
          THeroObject(m_MyHero).LogOn();
        end;
      end;

    RM_TAKEONITEM: begin
        SendDefMessage(SM_TAKEONITEM, ProcessMsg.nParam1, ProcessMsg.wParam, 0, 0, ProcessMsg.sMsg);
      end;
    RM_TAKEOFFITEM: begin
        SendDefMessage(SM_TAKEOFFITEM, ProcessMsg.nParam1, ProcessMsg.wParam, 0, 0, ProcessMsg.sMsg);
      end;

    CM_QUERYUSERNAME: begin //80
        ClientQueryUserName(TPlayObject(ProcessMsg.nParam1), ProcessMsg.nParam2, ProcessMsg.nParam3);
      end;
    CM_QUERYBAGITEMS: begin //0x81
        if GetTickCount() - m_dwQueryItemBagTick > 3000 then begin
          m_dwQueryItemBagTick := GetTickCount();
          ClientQueryBagItems();
        end else begin
          SysMsg('Bag refresh failed, please try again.', 255, 253, t_Hint);
        end;
      end;
    CM_QUERYUSERSTATE: begin //82
        ClientQueryUserState(TActorObject(ProcessMsg.nParam1), ProcessMsg.nParam2, ProcessMsg.nParam3);
      end;
    CM_QUERYUSERSET: begin
        ClientQueryUserSet(ProcessMsg);
      end;
    CM_DROPITEM: begin //1000
        if ClientDropItem(ProcessMsg.sMsg, ProcessMsg.nParam1) then
          SendDefMessage(SM_DROPITEM_SUCCESS, ProcessMsg.nParam1, 0, 0, 0, ProcessMsg.sMsg)
        else SendDefMessage(SM_DROPITEM_FAIL, ProcessMsg.nParam1, 0, 0, 0, ProcessMsg.sMsg);
      end;
    CM_PICKUP: begin //1001  004D78F9
        if (m_nCurrX = ProcessMsg.nParam2) and (m_nCurrY = ProcessMsg.nParam3) then
          ClientPickUpItem();
      end;
    CM_OPENDOOR: begin //1002
        ClientOpenDoor(ProcessMsg.nParam2, ProcessMsg.nParam3);
      end;
    CM_TAKEONITEM: begin //1003
        ClientTakeOnItems(ProcessMsg.nParam2, ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;
    CM_TAKEOFFITEM: begin //1004
        ClientTakeOffItems(ProcessMsg.nParam2, ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;
    CM_EAT: begin //1006
        ClientUseItems(ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;
    CM_BUTCH: begin //1007   挖物品
        if not ClientGetButchItem(TActorObject(ProcessMsg.nParam1), ProcessMsg.nParam2, ProcessMsg.nParam3, ProcessMsg.wParam, dwDelayTime) then begin
          if dwDelayTime <> 0 then begin
            nMsgCount := GetDigUpMsgCount();
            if nMsgCount >= g_Config.nMaxDigUpMsgCount then begin
              Inc(m_nOverSpeedCount);
              if m_nOverSpeedCount > g_Config.nOverSpeedKickCount then begin
                if g_Config.boKickOverSpeed then begin
                  SysMsg(g_sKickClientUserMsg {'请勿使用非法软件！！！'}, c_Red, t_Hint);
                  m_boEmergencyClose := True;
                  m_boPlayOffLine := False;
                end;
                if g_Config.boViewHackMessage then begin
                  //MainOutMessage('[游戏超速] ' + m_sCharName + ' Time: ' + IntToStr(dwDelayTime) + ' Count: '+ IntToStr(nMsgCount));
                  MainOutMessage(Format(g_sBunOverSpeed, [m_sCharName, dwDelayTime, nMsgCount]));
                end;
              end;
              //如果超速则发送攻击失败信息
              if Assigned(PlugInEngine.SendActionFail) then begin
                PlugInEngine.SendActionFail(Self);
              end else begin
                SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
              end;
            end else begin
              if dwDelayTime < g_Config.dwDropOverSpeed then begin
                if m_boTestSpeedMode then
                  SysMsg(Format('Velocity anomaly time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
                if Assigned(PlugInEngine.SendActionGood) then begin
                  PlugInEngine.SendActionGood(Self);
                end else begin
                  SendSocket(nil, sSTATUS_GOOD + IntToStr(GetTickCount));
                end;
              end else begin
                SendDelayMsg(Self, ProcessMsg.wIdent, ProcessMsg.wParam, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.nParam3, '', dwDelayTime);
                Result := False;
              end;
            end;
          end;
        end;
      end;
    CM_MAGICKEYCHANGE: begin //1008
        ClientChangeMagicKey(ProcessMsg.nParam1, ProcessMsg.nParam2);
      end;
    CM_SOFTCLOSE: begin
        m_boReconnection := True;
        m_boSoftClose := True;
      end;
    CM_CLICKNPC: //1010  004D79E4
      ClientClickNPC(ProcessMsg.nParam1, ProcessMsg.wParam);
    CM_MERCHANTDLGSELECT: //1011
      ClientMerchantDlgSelect(ProcessMsg.nParam1, ProcessMsg.sMsg);
    CM_MERCHANTQUERYSELLPRICE: //1012
      ClientMerchantQuerySellPrice(ProcessMsg.nParam1, MakeLong(ProcessMsg.nParam2, ProcessMsg.nParam3), ProcessMsg.sMsg);
    CM_USERSELLITEM: //1013
      ClientUserSellItem(ProcessMsg.nParam1, MakeLong(ProcessMsg.nParam2, ProcessMsg.nParam3), ProcessMsg.sMsg);
    CM_USERBUYITEM: //1014  004D7AD4
      ClientUserBuyItem(ProcessMsg.wIdent, ProcessMsg.nParam1, MakeLong(ProcessMsg.nParam2, ProcessMsg.nParam3), 0, ProcessMsg.sMsg);
    //////////////////////////////增加拍卖////////////////////////////////////////////////////
    CM_SENDSELLOFFITEM:
      ClientUserSellOffItem(ProcessMsg.nParam1, MakeLong(ProcessMsg.nParam2, ProcessMsg.nParam3), ProcessMsg.sMsg);

    CM_SENDQUERYSELLOFFITEM:
      ClientUserBuySellOffItem(ProcessMsg.wIdent, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.nParam3, ProcessMsg.sMsg);
    CM_SENDBUYSELLOFFITEM:
      ClientUserBuySellOffItem(ProcessMsg.wIdent, ProcessMsg.nParam1, MakeLong(ProcessMsg.nParam2, ProcessMsg.nParam3), 0, ProcessMsg.sMsg);

    CM_SENDGETSELLITEMGOLD:
      ClientUserGetSellOffGold(MakeLong(ProcessMsg.nParam2, ProcessMsg.nParam3), ProcessMsg.sMsg);
    /////////////////////////增加拍卖////////////////////////////////////////////////////////
    CM_USERGETDETAILITEM:
      ClientUserBuyItem(ProcessMsg.wIdent, ProcessMsg.nParam1, 0, ProcessMsg.nParam2, ProcessMsg.sMsg);

    CM_SENDCHANGEITEM:
      ClientUserChangeItem(ProcessMsg.nParam1, MakeLong(ProcessMsg.nParam2, ProcessMsg.nParam3), ProcessMsg.sMsg);

    CM_DROPGOLD:
      if ProcessMsg.nParam1 > 0 then ClientDropGold(ProcessMsg.nParam1);

    CM_1017:
      SendDefMessage(1, 0, 0, 0, 0, '');

    CM_GROUPMODE: begin
        if ProcessMsg.nParam2 = 0 then ClientGropuClose()
        else m_boAllowGroup := True;
        if m_boAllowGroup then SendDefMessage(SM_GROUPMODECHANGED, 0, 1, 0, 0, '')
        else SendDefMessage(SM_GROUPMODECHANGED, 0, 0, 0, 0, '');
      end;
    CM_CREATEGROUP: begin //1020
        ClientCreateGroup(Trim(ProcessMsg.sMsg));
      end;
    CM_ADDGROUPMEMBER: begin //1021
        ClientAddGroupMember(Trim(ProcessMsg.sMsg));
      end;
    CM_DELGROUPMEMBER: begin //1022
        ClientDelGroupMember(Trim(ProcessMsg.sMsg));
      end;
    CM_USERREPAIRITEM: begin //1023 004D7A70
        ClientRepairItem(ProcessMsg.nParam1, MakeLong(ProcessMsg.nParam2, ProcessMsg.nParam3), ProcessMsg.sMsg);
      end;
    CM_MERCHANTQUERYREPAIRCOST: begin //1024 004D7A2A
        ClientQueryRepairCost(ProcessMsg.nParam1, MakeLong(ProcessMsg.nParam2, ProcessMsg.nParam3), ProcessMsg.sMsg);
      end;
    CM_DEALTRY: begin //1025
        ClientDealTry(Trim(ProcessMsg.sMsg));
      end;
    CM_DEALADDITEM: begin //1026
        ClientAddDealItem(ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;
    CM_DEALDELITEM: begin //1027
        ClientDelDealItem(ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;
    CM_DEALCANCEL: begin //1028
        ClientCancelDeal();
      end;
    CM_DEALCHGGOLD: begin //1029
        ClientChangeDealGold(ProcessMsg.nParam1);
      end;
    CM_DEALEND: begin //1030
        ClientDealEnd();
      end;


    CM_DUELTRY: begin //1025
        ClientDuelTry(Trim(ProcessMsg.sMsg));
      end;
    CM_DUELADDITEM: begin //1026
        ClientAddDuelItem(ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;
    CM_DUELDELITEM: begin //1027
        ClientDelDuelItem(ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;
    CM_DUELCANCEL: begin //1028
        ClientCancelDuel();
      end;
    CM_DUELCHGGOLD: begin //1029
        ClientChangeDuelGold(ProcessMsg.nParam1);
      end;
    CM_DUELEND: begin //1030
        ClientDuelEnd();
      end;
    CM_STOPSTORE: begin //停止摆摊
        ClientCancelStore;
      end;
    CM_STARTSTORE: begin //开始摆摊
        ClientStartStore(ProcessMsg.sMsg, ProcessMsg.nParam1);
      end;
    CM_QUERYSTORE: begin
        ClientQueryUserStoreState(TActorObject(ProcessMsg.nParam1), ProcessMsg.nParam2, ProcessMsg.nParam3);
      end;
    CM_BUYSTOREITEM: begin
        ClientBuySroreItem(TActorObject(ProcessMsg.nParam1), ProcessMsg.sMsg);
      end;

    RM_SENDSTORE: begin
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_SENDSTORE, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam3, 0, 0);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_PLAYSOUND: begin
        SendDefMessage(SM_PLAYSOUND, 0, 0, 0, 0, ProcessMsg.sMsg);
      end;
    RM_VIBRATION: begin
        SendDefMessage(SM_VIBRATION, 0, ProcessMsg.wParam, 0, 0, '');
      end;
    RM_OPENBIGDIALOGBOX: begin
        SendDefMessage(SM_OPENBIGDIALOGBOX, 0, ProcessMsg.wParam, 0, 0, '');
      end;
    RM_CLOSEBIGDIALOGBOX: begin
        SendDefMessage(SM_CLOSEBIGDIALOGBOX, 0, 0, 0, 0, '');
      end;
    RM_SENDCARTINFO: begin //镖车信息
        SendDefMessage(SM_SENDCARTINFO,
          Integer(ProcessMsg.BaseObject),
          TActorObject(ProcessMsg.BaseObject).m_nCurrX,
          TActorObject(ProcessMsg.BaseObject).m_nCurrY,
          0, TActorObject(ProcessMsg.BaseObject).m_sCharName);
      end;
    RM_DELCARTINFO: begin //删除镖车信息
        SendDefMessage(SM_DELCARTINFO, ProcessMsg.nParam1, 0, 0, 0, '');
      end;
    CM_USERSTORAGEITEM: begin //1031
        ClientStorageItem(TObject(ProcessMsg.nParam1), MakeLong(ProcessMsg.nParam2, ProcessMsg.nParam3), ProcessMsg.sMsg);
      end;
    CM_USERTAKEBACKSTORAGEITEM: begin //1032
        ClientTakeBackStorageItem(TObject(ProcessMsg.nParam1), MakeLong(ProcessMsg.nParam2, ProcessMsg.nParam3), ProcessMsg.sMsg);
      end;
    CM_WANTMINIMAP: begin //1033
        ClientGetMinMap();
      end;
    CM_USERMAKEDRUGITEM: begin //1034
        ClientMakeDrugItem(TObject(ProcessMsg.nParam1), ProcessMsg.sMsg);
      end;
    CM_OPENGUILDDLG: begin //1035
        ClientOpenGuildDlg();
      end;
    CM_GUILDHOME: begin //1036
        ClientGuildHome();
      end;
    CM_GUILDMEMBERLIST: begin
        ClientGuildMemberList();
      end;
    CM_GUILDADDMEMBER: begin
        ClientGuildAddMember(ProcessMsg.sMsg);
      end;
    CM_GUILDDELMEMBER: begin
        ClientGuildDelMember(ProcessMsg.sMsg);
      end;
    CM_GUILDUPDATENOTICE: begin
        ClientGuildUpdateNotice(ProcessMsg.sMsg);
      end;
    CM_GUILDUPDATERANKINFO: begin //1041
        ClientGuildUpdateRankInfo(ProcessMsg.sMsg);
      end;
    CM_1042: begin
        MainOutMessage('[Bad Data] ' + m_sCharName);
      end;
    CM_ADJUST_BONUS: begin
        ClientAdjustBonus(ProcessMsg.nParam1, ProcessMsg.sMsg);
      end;
    CM_GUILDALLY: begin //1044
        ClientGuildAlly();
      end;
    CM_GUILDBREAKALLY: begin //1045
        ClientGuildBreakAlly(ProcessMsg.sMsg);
      end;
{$IF CHECKNEWMSG = 1}
    CM_1046: begin
        MainOutMessage(Format('%s/%d/%d/%d/%d/%d/%s',
          [m_sCharName,
          ProcessMsg.wIdent,
            ProcessMsg.wParam,
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            ProcessMsg.nParam3,
            DeCodeString(ProcessMsg.sMsg)]));
      end;
    CM_1056: begin
        MainOutMessage(Format('%s/%d/%d/%d/%d/%d/%s',
          [m_sCharName,
          ProcessMsg.wIdent,
            ProcessMsg.wParam,
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            ProcessMsg.nParam3,
            DeCodeString(ProcessMsg.sMsg)]));
      end;

{$IFEND}
    CM_TURN: begin //3010    004D73DD
        if ClientChangeDir(ProcessMsg.wIdent, ProcessMsg.nParam1 {x}, ProcessMsg.nParam2 {y}, ProcessMsg.wParam {dir}, dwDelayTime) then begin
          m_dwActionTick := GetTickCount;
          if Assigned(PlugInEngine.SendActionGood) then begin
            PlugInEngine.SendActionGood(Self);
          end else begin
            SendSocket(nil, sSTATUS_GOOD + IntToStr(GetTickCount));
          end;
        end else begin
          if dwDelayTime = 0 then begin
            if Assigned(PlugInEngine.SendActionFail) then begin
              PlugInEngine.SendActionFail(Self);
            end else begin
              SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
            end;
          end else begin
            nMsgCount := GetTurnMsgCount();
            if nMsgCount >= g_Config.nMaxTurnMsgCount then begin
              Inc(m_nOverSpeedCount);
              if m_nOverSpeedCount > g_Config.nOverSpeedKickCount then begin
                if g_Config.boKickOverSpeed then begin
                  SysMsg(g_sKickClientUserMsg {'请勿使用非法软件！！！'}, c_Red, t_Hint);
                  m_boEmergencyClose := True;
                  m_boPlayOffLine := False;
                end;
                if g_Config.boViewHackMessage then begin
                  //MainOutMessage('[游戏超速] ' + m_sCharName + ' Time: ' + IntToStr(dwDelayTime) + ' Count: '+ IntToStr(nMsgCount));
                  MainOutMessage(Format(g_sBunOverSpeed, [m_sCharName, dwDelayTime, nMsgCount]));
                end;
              end;
              //如果超速则发送攻击失败信息
              if Assigned(PlugInEngine.SendActionFail) then begin
                PlugInEngine.SendActionFail(Self);
              end else begin
                SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
              end;
            end else begin
              if dwDelayTime < g_Config.dwDropOverSpeed then begin
                if Assigned(PlugInEngine.SendActionGood) then begin
                  PlugInEngine.SendActionGood(Self);
                end else begin
                  SendSocket(nil, sSTATUS_GOOD + IntToStr(GetTickCount));
                end;
                if m_boTestSpeedMode then
                  SysMsg(Format('Velocity anomaly time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
              end else begin
                SendDelayMsg(Self, ProcessMsg.wIdent, ProcessMsg.wParam, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.nParam3, '', dwDelayTime);
                Result := False;
              end;
            end;
          end;
        end;
      end;
    CM_WALK: begin
        if m_boStore then Exit; //摆摊禁止操作
        if ClientWalkXY(ProcessMsg.wIdent, ProcessMsg.nParam1 {x}, ProcessMsg.nParam2 {y}, ProcessMsg.boLateDelivery, dwDelayTime) then begin
          m_dwActionTick := GetTickCount;
          if g_Config.boStartMapEvent then begin
            if m_btRaceServer = RC_PLAYOBJECT then StartMapEventOfWalk(); //走路事件触发}
          end;
          if Assigned(PlugInEngine.SendActionGood) then begin
            PlugInEngine.SendActionGood(Self);
          end else begin
            SendSocket(nil, sSTATUS_GOOD + IntToStr(GetTickCount));
          end;

        end else begin
          if dwDelayTime = 0 then begin
            if Assigned(PlugInEngine.SendActionFail) then begin
              PlugInEngine.SendActionFail(Self);
            end else begin
              SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
            end;
          end else begin
            nMsgCount := GetWalkMsgCount();
            if nMsgCount >= g_Config.nMaxWalkMsgCount then begin
              Inc(m_nOverSpeedCount);
              if m_nOverSpeedCount > g_Config.nOverSpeedKickCount then begin
                if g_Config.boKickOverSpeed then begin
                  SysMsg(g_sKickClientUserMsg {'请勿使用非法软件！！！'}, c_Red, t_Hint);
                  m_boEmergencyClose := True;
                  m_boPlayOffLine := False;
                end;
                if g_Config.boViewHackMessage then begin
                  //MainOutMessage('[行走超速] ' + m_sCharName + ' Time: ' + IntToStr(dwDelayTime) + ' Count: '+ IntToStr(nMsgCount));
                  MainOutMessage(Format(g_sWalkOverSpeed, [m_sCharName, dwDelayTime, nMsgCount]));
                end;
              end;
              //如果超速则发送攻击失败信息
              if Assigned(PlugInEngine.SendActionFail) then begin
                PlugInEngine.SendActionFail(Self);
              end else begin
                SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
              end;
              if m_boTestSpeedMode then
                SysMsg(Format('Velocity anomaly time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
            end else begin
              if (dwDelayTime > g_Config.dwDropOverSpeed) and (g_Config.btSpeedControlMode = 1) and m_boFilterAction then begin
                if Assigned(PlugInEngine.SendActionFail) then begin
                  PlugInEngine.SendActionFail(Self);
                end else begin
                  SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
                end;
                if m_boTestSpeedMode then
                  SysMsg(Format('Velocity anomaly time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
              end else begin
                if m_boTestSpeedMode then
                  SysMsg(Format('Operation delay time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
                SendDelayMsg(Self, ProcessMsg.wIdent, ProcessMsg.wParam, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.nParam3, '', dwDelayTime);
                Result := False;
              end;
            end;
          end;
        end;
      end;

    CM_ONHORSE: begin
        if m_PEnvir.m_boHorse then begin
          if m_btHorseType <> 0 then begin
            m_boOnHorse := not m_boOnHorse; //骑马 CTRL + M
            FeatureChanged();
            if m_boOnHorse then begin
              try
                g_FunctionNPC.GotoLable(Self, '@OnHorse', False);
              except

              end;
            end;
          end;
        end;
      end;

    CM_HORSERUN: begin
        if m_boStore then Exit; //摆摊禁止操作
        if ClientHorseRunXY(ProcessMsg.wIdent, ProcessMsg.nParam1 {x}, ProcessMsg.nParam2 {y}, ProcessMsg.boLateDelivery, dwDelayTime) then begin
          m_dwActionTick := GetTickCount;
          if Assigned(PlugInEngine.SendActionGood) then begin
            PlugInEngine.SendActionGood(Self);
          end else begin
            SendSocket(nil, sSTATUS_GOOD + IntToStr(GetTickCount));
          end;

        end else begin

          if dwDelayTime = 0 then begin
            if Assigned(PlugInEngine.SendActionFail) then begin
              PlugInEngine.SendActionFail(Self);
            end else begin
              SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
            end;
          end else begin
            nMsgCount := GetRunMsgCount();
            if nMsgCount >= g_Config.nMaxRunMsgCount then begin
              Inc(m_nOverSpeedCount);
              if m_nOverSpeedCount > g_Config.nOverSpeedKickCount then begin
                if g_Config.boKickOverSpeed then begin
                  SysMsg(g_sKickClientUserMsg {'请勿使用非法软件！！！'}, c_Red, t_Hint);
                  m_boEmergencyClose := True;
                  m_boPlayOffLine := False;
                end;
                if g_Config.boViewHackMessage then begin
                  //MainOutMessage('[跑步超速] ' + m_sCharName + ' Time: ' + IntToStr(dwDelayTime) + ' Count: '+ IntToStr(nMsgCount));
                  MainOutMessage(Format(g_sRunOverSpeed, [m_sCharName, dwDelayTime, nMsgCount]));
                end;
              end;
              //如果超速则发送攻击失败信息
              if Assigned(PlugInEngine.SendActionFail) then begin
                PlugInEngine.SendActionFail(Self);
              end else begin
                SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
              end;
              if m_boTestSpeedMode then
                SysMsg(Format('Velocity anomaly time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
            end else begin
              if m_boTestSpeedMode then
                SysMsg(Format('Operation delay time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
              SendDelayMsg(Self, ProcessMsg.wIdent, ProcessMsg.wParam, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.nParam3, '', dwDelayTime);
              Result := False;
            end;
          end;
        end;
      end;
    CM_RUN: begin
        if m_boStore then Exit; //摆摊禁止操作
        if ClientRunXY(ProcessMsg.wIdent, ProcessMsg.nParam1 {x}, ProcessMsg.nParam2 {y}, ProcessMsg.nParam3, dwDelayTime) then begin
          m_dwActionTick := GetTickCount;
          if g_Config.boStartMapEvent then begin
            if m_btRaceServer = RC_PLAYOBJECT then StartMapEventOfRun(); //跑步事件触发
          end;
          if Assigned(PlugInEngine.SendActionGood) then begin
            PlugInEngine.SendActionGood(Self);
          end else begin
            SendSocket(nil, sSTATUS_GOOD + IntToStr(GetTickCount));
          end;

        end else begin
          if dwDelayTime = 0 then begin
            if Assigned(PlugInEngine.SendActionFail) then begin
              PlugInEngine.SendActionFail(Self);
            end else begin
              SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
            end;
          end else begin
            nMsgCount := GetRunMsgCount();
            if nMsgCount >= g_Config.nMaxRunMsgCount then begin
              Inc(m_nOverSpeedCount);
              if m_nOverSpeedCount > g_Config.nOverSpeedKickCount then begin
                if g_Config.boKickOverSpeed then begin
                  SysMsg(g_sKickClientUserMsg {'请勿使用非法软件！！！'}, c_Red, t_Hint);
                  m_boEmergencyClose := True;
                  m_boPlayOffLine := False;
                end;
                if g_Config.boViewHackMessage then begin
                  //MainOutMessage('[跑步超速] ' + m_sCharName + ' Time: ' + IntToStr(dwDelayTime) + ' Count: '+ IntToStr(nMsgCount));
                  MainOutMessage(Format(g_sRunOverSpeed, [m_sCharName, dwDelayTime, nMsgCount]));
                end;
              end;
              //如果超速则发送攻击失败信息
              if Assigned(PlugInEngine.SendActionFail) then begin
                PlugInEngine.SendActionFail(Self);
              end else begin
                SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
              end;
            end else begin
              if (dwDelayTime > g_Config.dwDropOverSpeed) and (g_Config.btSpeedControlMode = 1) and m_boFilterAction then begin
                if Assigned(PlugInEngine.SendActionFail) then begin
                  PlugInEngine.SendActionFail(Self);
                end else begin
                  SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
                end;
                if m_boTestSpeedMode then
                  SysMsg(Format('Velocity anomaly time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
              end else begin
                if m_boTestSpeedMode then
                  SysMsg(Format('Operation delay time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
                SendDelayMsg(Self, ProcessMsg.wIdent, ProcessMsg.wParam, ProcessMsg.nParam1, ProcessMsg.nParam2, CM_RUN, '', dwDelayTime);
                Result := False;
              end;
            end;
          end;
        end;
      end;
    CM_HIT, //3014
      CM_HEAVYHIT, //3015
      CM_BIGHIT, //3016
      CM_POWERHIT, //3018
      CM_LONGHIT, //3019
      CM_WIDEHIT, //3024
      CM_CRSHIT,
      CM_TWNHIT,
      CM_PHHIT,
      CM_KTHIT,
      CM_PKHIT,
      CM_FIREHIT,
      CM_60HIT,
      CM_ZRJFHIT,
      CM_100HIT,
      CM_101HIT,
      CM_102HIT,
      CM_103HIT: begin //3025
        if m_boStore then Exit; //摆摊禁止操作
        if ProcessMsg.wIdent = CM_60HIT then m_bo60kill := True;
        if ClientHitXY(ProcessMsg.wIdent {ident}, ProcessMsg.nParam1 {x}, ProcessMsg.nParam2 {y}, ProcessMsg.wParam {dir}, ProcessMsg.boLateDelivery, dwDelayTime) then begin
          m_dwActionTick := GetTickCount;
          SendSocket(nil, sSTATUS_GOOD + IntToStr(GetTickCount));
        end else begin
          if dwDelayTime = 0 then begin
            SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
          end else begin
            nMsgCount := GetHitMsgCount();
            if nMsgCount >= g_Config.nMaxHitMsgCount then begin
              Inc(m_nOverSpeedCount);
              if m_nOverSpeedCount > g_Config.nOverSpeedKickCount then begin
                if g_Config.boKickOverSpeed then begin
                  SysMsg(g_sKickClientUserMsg {'请勿使用非法软件！！！'}, c_Red, t_Hint);
                  m_boEmergencyClose := True;
                  m_boPlayOffLine := False;
                end;
                if g_Config.boViewHackMessage then begin
                  //MainOutMessage('[攻击超速] ' + m_sCharName + ' Time: ' + IntToStr(dwDelayTime) + ' Count: '+ IntToStr(nMsgCount));
                  MainOutMessage(Format(g_sHitOverSpeed, [m_sCharName, dwDelayTime, nMsgCount]));
                end;
              end;
              //如果超速则发送攻击失败信息
              SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
            end else begin
              if (dwDelayTime > g_Config.dwDropOverSpeed) and (g_Config.btSpeedControlMode = 1) and m_boFilterAction then begin
                if Assigned(PlugInEngine.SendActionGood) then begin
                  PlugInEngine.SendActionGood(Self);
                end else begin
                  SendSocket(nil, sSTATUS_GOOD + IntToStr(GetTickCount));
                end;
                if m_boTestSpeedMode then
                  SysMsg(Format('Speed Error Ident: %d Time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
              end else begin
                if m_boTestSpeedMode then begin
                  //SysMsg(format('操作延迟 Ident: %d Time: %d',[ProcessMsg.wIdent,dwDelayTime]),c_Red,t_Hint);
                  SysMsg('Operation Delay Ident: ' + IntToStr(ProcessMsg.wIdent) + ' Time: ' + IntToStr(dwDelayTime), c_Red, t_Hint);
                end;
                SendDelayMsg(Self, ProcessMsg.wIdent, ProcessMsg.wParam, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.nParam3, '', dwDelayTime);
                Result := False;
              end;
            end;
          end;
        end;
      end;
    CM_SITDOWN: begin //3012
        if m_boStore then Exit; //摆摊禁止操作
        if ClientSitDownHit(ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam, dwDelayTime) then begin
          m_dwActionTick := GetTickCount();
          if Assigned(PlugInEngine.SendActionGood) then begin
            PlugInEngine.SendActionGood(Self);
          end else begin
            SendSocket(nil, sSTATUS_GOOD + IntToStr(GetTickCount));
          end;
        end else begin
          if dwDelayTime = 0 then begin
            if Assigned(PlugInEngine.SendActionFail) then begin
              PlugInEngine.SendActionFail(Self);
            end else begin
              SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
            end;
          end else begin
            nMsgCount := GetSiteDownMsgCount();
            if nMsgCount >= g_Config.nMaxSitDonwMsgCount then begin
              Inc(m_nOverSpeedCount);
              if m_nOverSpeedCount > g_Config.nOverSpeedKickCount then begin
                if g_Config.boKickOverSpeed then begin
                  SysMsg(g_sKickClientUserMsg {'请勿使用非法软件！！！'}, c_Red, t_Hint);
                  m_boEmergencyClose := True;
                  m_boPlayOffLine := False;
                end;
                if g_Config.boViewHackMessage then begin
                  //MainOutMessage('[游戏超速] ' + m_sCharName + ' Time: ' + IntToStr(dwDelayTime) + ' Count: '+ IntToStr(nMsgCount));
                  MainOutMessage(Format(g_sBunOverSpeed, [m_sCharName, dwDelayTime, nMsgCount]));
                end;
              end;
              //如果超速则发送攻击失败信息
              if Assigned(PlugInEngine.SendActionFail) then begin
                PlugInEngine.SendActionFail(Self);
              end else begin
                SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
              end;
            end else begin
              if dwDelayTime < g_Config.dwDropOverSpeed then begin
                if Assigned(PlugInEngine.SendActionGood) then begin
                  PlugInEngine.SendActionGood(Self);
                end else begin
                  SendSocket(nil, sSTATUS_GOOD + IntToStr(GetTickCount));
                end;
                if m_boTestSpeedMode then
                  SysMsg(Format('Velocity anomaly time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
              end else begin
                if m_boTestSpeedMode then
                  SysMsg(Format('Operation delay time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
                SendDelayMsg(Self, ProcessMsg.wIdent, ProcessMsg.wParam, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.nParam3, '', dwDelayTime);
                Result := False;
              end;
            end;
          end;
        end;
      end;
    CM_SPELL: begin
        if m_boStore then Exit; //摆摊禁止操作
        if ClientSpellXY(ProcessMsg.wIdent, ProcessMsg.wParam, ProcessMsg.nParam1, ProcessMsg.nParam2, TActorObject(ProcessMsg.nParam3), ProcessMsg.boLateDelivery, dwDelayTime) then begin
          m_dwActionTick := GetTickCount;
          if Assigned(PlugInEngine.SendActionGood) then begin
            PlugInEngine.SendActionGood(Self);
          end else begin
            SendSocket(nil, sSTATUS_GOOD + IntToStr(GetTickCount));
          end;

        end else begin
          if dwDelayTime = 0 then begin
            if Assigned(PlugInEngine.SendActionFail) then begin
              PlugInEngine.SendActionFail(Self);
            end else begin
              SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
            end;
          end else begin
            nMsgCount := GetSpellMsgCount();
            if nMsgCount >= g_Config.nMaxSpellMsgCount then begin
              Inc(m_nOverSpeedCount);
              if m_nOverSpeedCount > g_Config.nOverSpeedKickCount then begin
                if g_Config.boKickOverSpeed then begin
                  SysMsg(g_sKickClientUserMsg {'请勿使用非法软件！！！'}, c_Red, t_Hint);
                  m_boEmergencyClose := True;
                  m_boPlayOffLine := False;
                end;
                if g_Config.boViewHackMessage then begin
                  //MainOutMessage('[魔法超速] ' + m_sCharName + ' Time: ' + IntToStr(dwDelayTime) + ' Count: '+ IntToStr(nMsgCount));
                  MainOutMessage(Format(g_sSpellOverSpeed, [m_sCharName, dwDelayTime, nMsgCount]));
                end;
              end;
              //如果超速则发送攻击失败信息
              if Assigned(PlugInEngine.SendActionFail) then begin
                PlugInEngine.SendActionFail(Self);
              end else begin
                SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
              end;
            end else begin
              if (dwDelayTime > g_Config.dwDropOverSpeed) and (g_Config.btSpeedControlMode = 1) and m_boFilterAction then begin
                if Assigned(PlugInEngine.SendActionFail) then begin
                  PlugInEngine.SendActionFail(Self);
                end else begin
                  SendSocket(nil, sSTATUS_FAIL + IntToStr(GetTickCount));
                end;
                if m_boTestSpeedMode then
                  SysMsg(Format('Velocity anomaly time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
              end else begin
                if m_boTestSpeedMode then
                  SysMsg(Format('Operation delay time: %d', [ProcessMsg.wIdent, dwDelayTime]), c_Red, t_Hint);
                SendDelayMsg(Self, ProcessMsg.wIdent, ProcessMsg.wParam, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.nParam3, '', dwDelayTime);
                Result := False;
              end;
            end;
          end;
        end;
      end;
    CM_SAY: begin
        if ProcessMsg.sMsg <> '' then begin
          ProcessUserLineMsg(ProcessMsg.sMsg);
        end;
      end;
    CM_PASSWORD: begin
        ProcessClientPassword(ProcessMsg);
      end;
    RM_WALK: begin //10002
        if Assigned(PlugInEngine.SendWalkMsg) then begin
          PlugInEngine.SendWalkMsg(Self, ProcessMsg.BaseObject, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
        end else begin
          if TActorObject(ProcessMsg.BaseObject) <> Self then begin
            m_DefMsg := MakeDefaultMsg(SM_WALK, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, MakeWord(ProcessMsg.wParam, TActorObject(ProcessMsg.BaseObject).m_nLight));
            CharDesc.feature := TActorObject(ProcessMsg.BaseObject).GetFeature(TActorObject(ProcessMsg.BaseObject));
            CharDesc.Status := TActorObject(ProcessMsg.BaseObject).m_nCharStatus;

            CharDesc.Level := TActorObject(ProcessMsg.BaseObject).m_WAbil.Level;
            CharDesc.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
            CharDesc.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
            CharDesc.AddStatus := TActorObject(ProcessMsg.BaseObject).GetAddStatus;

            SendSocket(@m_DefMsg, EncodeBuffer(@CharDesc, SizeOf(TCharDesc)));
          end;
        end;
      end;
    RM_HORSERUN: begin //10003 004D860A
        if Assigned(PlugInEngine.SendHorseRunMsg) then begin
          PlugInEngine.SendHorseRunMsg(Self, ProcessMsg.BaseObject, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
        end else begin
          if TActorObject(ProcessMsg.BaseObject) <> Self then begin
            m_DefMsg := MakeDefaultMsg(SM_HORSERUN, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, MakeWord(ProcessMsg.wParam, TActorObject(ProcessMsg.BaseObject).m_nLight));
            CharDesc.feature := TActorObject(ProcessMsg.BaseObject).GetFeature(TActorObject(ProcessMsg.BaseObject));
            CharDesc.Status := TActorObject(ProcessMsg.BaseObject).m_nCharStatus;

            CharDesc.Level := TActorObject(ProcessMsg.BaseObject).m_WAbil.Level;
            CharDesc.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
            CharDesc.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
            CharDesc.AddStatus := TActorObject(ProcessMsg.BaseObject).GetAddStatus;

            SendSocket(@m_DefMsg, EncodeBuffer(@CharDesc, SizeOf(TCharDesc)));
          end;
        end;
      end;
    RM_RUN: begin
        if Assigned(PlugInEngine.SendRunMsg) then begin
          PlugInEngine.SendRunMsg(Self, ProcessMsg.BaseObject, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
        end else begin
          if TActorObject(ProcessMsg.BaseObject) <> Self then begin
            m_DefMsg := MakeDefaultMsg(SM_RUN, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, MakeWord(ProcessMsg.wParam, TActorObject(ProcessMsg.BaseObject).m_nLight));
            CharDesc.feature := TActorObject(ProcessMsg.BaseObject).GetFeature(TActorObject(ProcessMsg.BaseObject));
            CharDesc.Status := TActorObject(ProcessMsg.BaseObject).m_nCharStatus;

            CharDesc.Level := TActorObject(ProcessMsg.BaseObject).m_WAbil.Level;
            CharDesc.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
            CharDesc.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
            CharDesc.AddStatus := TActorObject(ProcessMsg.BaseObject).GetAddStatus;

            SendSocket(@m_DefMsg, EncodeBuffer(@CharDesc, SizeOf(TCharDesc)));
          end;
        end;
      end;
    RM_HIT: begin //10004 004D871D
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_HIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;

    RM_81: begin //10054
        if TActorObject(ProcessMsg.nParam3) <> nil then begin
          MessageBodyW.Param1:=TBaseObject(ProcessMsg.nParam3).m_nCurrX;
          MessageBodyW.Param2:=TBaseObject(ProcessMsg.nParam3).m_nCurrY;
          MessageBodyW.Tag1:=LoWord(ProcessMsg.nParam3);
          MessageBodyW.Tag2:=HiWord(ProcessMsg.nParam3);
          m_DefMsg:=MakeDefaultMsg(SM_81,
                    Integer(ProcessMsg.BaseObject),
                    ProcessMsg.nParam1,
                    ProcessMsg.nParam2,
                    ProcessMsg.wParam);
            SendSocket(@m_DefMsg,EncodeBuffer(@MessageBodyW,SizeOf(TMessageBodyW)));
          end;
        end;
    RM_82: begin //10055
        if TActorObject(ProcessMsg.nParam3) <> nil then begin
            MessageBodyW.Param1:=TBaseObject(ProcessMsg.nParam3).m_nCurrX;
            MessageBodyW.Param2:=TBaseObject(ProcessMsg.nParam3).m_nCurrY;
            MessageBodyW.Tag1:=LoWord(ProcessMsg.nParam3);
            MessageBodyW.Tag2:=HiWord(ProcessMsg.nParam3);
            m_DefMsg:=MakeDefaultMsg(SM_82,
                                     Integer(ProcessMsg.BaseObject),
                                     ProcessMsg.nParam1,
                                     ProcessMsg.nParam2,
                                     ProcessMsg.wParam);
            SendSocket(@m_DefMsg,EncodeBuffer(@MessageBodyW,SizeOf(TMessageBodyW)));
          end;
        end;
    RM_83: begin //10056
        if TActorObject(ProcessMsg.nParam3) <> nil then begin
          MessageBodyW.Param1:=TBaseObject(ProcessMsg.nParam3).m_nCurrX;
          MessageBodyW.Param2:=TBaseObject(ProcessMsg.nParam3).m_nCurrY;
          MessageBodyW.Tag1:=LoWord(ProcessMsg.nParam3);
          MessageBodyW.Tag2:=HiWord(ProcessMsg.nParam3);
          m_DefMsg:=MakeDefaultMsg(SM_83,
              Integer(ProcessMsg.BaseObject),
              ProcessMsg.nParam1,
              ProcessMsg.nParam2,
              ProcessMsg.wParam);
          SendSocket(@m_DefMsg,EncodeBuffer(@MessageBodyW,SizeOf(TMessageBodyW)));
          end;
        end;

    RM_HEAVYHIT: begin //004D88CD
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_HEAVYHIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, ProcessMsg.sMsg);
        end;
      end;
    RM_BIGHIT: begin //004D893A
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_BIGHIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_SPELL: begin // 10007 004D8A12
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_SPELL, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          //m_DefMsg := MakeDefaultMsg(SM_SPELL, Integer(ProcessMsg.BaseObject), ProcessMsg.wParam, ProcessMsg.nParam1, ProcessMsg.nParam2);
          SendSocket(@m_DefMsg, IntToStr(ProcessMsg.nParam3));
        end;
      end;
    RM_SPELL2: begin //10008 004D8789
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_POWERHIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_SPELL3: begin
        m_DefMsg := MakeDefaultMsg(SM_SPELL, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
        SendSocket(@m_DefMsg, IntToStr(ProcessMsg.nParam3));
      end;

    RM_HEROLOGON: begin //获取英雄 TMessageBodyWL 产生英雄登陆效果
        m_DefMsg := MakeDefaultMsg(SM_HEROLOGON, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.nParam3, ProcessMsg.wParam);
        //m_DefMsg := MakeDefaultMsg(SM_HEROLOGON, Integer(Self), m_nCurrX, m_nCurrY, MakeWord(m_btDirection, m_btGender));
        MessageBodyWL.lParam1 := TActorObject(ProcessMsg.BaseObject).GetFeatureToLong();
        MessageBodyWL.lParam2 := TActorObject(ProcessMsg.BaseObject).m_nCharStatus;
        MessageBodyWL.lTag1 := TActorObject(ProcessMsg.BaseObject).GetFeatureEx;
        MessageBodyWL.lTag2 := Integer(TActorObject(ProcessMsg.BaseObject).m_Master);

        SendSocket(@m_DefMsg, EncodeBuffer(@MessageBodyWL, SizeOf(TMessageBodyWL)));
        //MainOutMessage('m_sCharName:'+m_sCharName+' BaseObject.m_sCharName:'+TActorObject(ProcessMsg.BaseObject).m_sCharName);
      end;

    RM_HEROLOGOUT: begin //获取英雄 TMessageBodyWL 产生英雄退出效果
        m_DefMsg := MakeDefaultMsg(SM_HEROLOGOUT, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.nParam3, 0);
        SendSocket(@m_DefMsg, '');
      end;
    {
    RM_POWERHIT: begin

    end;
    }
    RM_MOVEFAIL: begin //10010 004D8289
        m_DefMsg := MakeDefaultMsg(SM_MOVEFAIL, Integer(Self), m_nCurrX, m_nCurrY, m_btDirection);
        CharDesc.feature := TActorObject(ProcessMsg.BaseObject).GetFeatureToLong;
        CharDesc.Status := TActorObject(ProcessMsg.BaseObject).m_nCharStatus;

        CharDesc.Level := TActorObject(ProcessMsg.BaseObject).m_WAbil.Level;
        CharDesc.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
        CharDesc.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
        CharDesc.AddStatus := TActorObject(ProcessMsg.BaseObject).GetAddStatus;

        SendSocket(@m_DefMsg, EncodeBuffer(@CharDesc, SizeOf(CharDesc)));
      end;
    RM_LONGHIT: begin //10011 004D87F5
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_LONGHIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_WIDEHIT: begin //10012 004D8861
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_WIDEHIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_FIREHIT: begin //10014 004D89A6
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_FIREHIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_SUPERFIREHIT: begin //4级烈火
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_SUPERFIREHIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_CRSHIT: begin //10014 004D89A6
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_CRSHIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_41: begin //10014 004D89A6
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_41, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_42: begin //10014 004D89A6
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_42, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_43: begin //10014 004D89A6
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_PKHIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_44: begin //10014 004D89A6
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_KTHIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_60: begin //10014 004D89A6
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_60HIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_61: begin //10014 004D89A6
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_61HIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_62: begin //10014 004D89A6
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_62HIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_ZRJF: begin //10014 004D89A6
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_ZRJFHIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;

    RM_100HIT: begin
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_100HIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_101HIT: begin
        //if TActorObject(ProcessMsg.BaseObject) <> Self then begin
        m_DefMsg := MakeDefaultMsg(SM_101HIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
        SendSocket(@m_DefMsg, '');
        //end;
      end;

    RM_102HIT: begin
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_102HIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_103HIT: begin
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_103HIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_BLASTHHIT: begin
        m_DefMsg := MakeDefaultMsg(SM_BLASTHHIT, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
        SendSocket(@m_DefMsg, '');
      end;

    RM_MONMOVE: begin //擒拿手
        if TActorObject(ProcessMsg.BaseObject) <> Self then begin
          m_DefMsg := MakeDefaultMsg(SM_SITDOWN, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
   { RM_USEGROUPSPELL: begin
        m_DefMsg := MakeDefaultMsg(SM_USEGROUPSPELL, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.wParam);
        SendSocket(@m_DefMsg, '');
      end; }
    RM_CHANGETURN,
      RM_TURN,
      RM_PUSH,
      RM_RUSH,
      RM_RUSHKUNG: begin
        if (TActorObject(ProcessMsg.BaseObject) <> Self) or (ProcessMsg.wIdent = RM_PUSH) or (ProcessMsg.wIdent = RM_RUSH) or (ProcessMsg.wIdent = RM_RUSHKUNG) or (ProcessMsg.wIdent = RM_CHANGETURN) then begin
          case ProcessMsg.wIdent of
            RM_PUSH: //004D835F
              m_DefMsg := MakeDefaultMsg(SM_BACKSTEP, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1 {x}, ProcessMsg.nParam2 {y}, MakeWord(ProcessMsg.wParam {dir}, TActorObject(ProcessMsg.BaseObject).m_nLight {light}));
            RM_RUSH: //004D83B9
              m_DefMsg := MakeDefaultMsg(SM_RUSH, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, MakeWord(ProcessMsg.wParam, TActorObject(ProcessMsg.BaseObject).m_nLight));
            RM_RUSHKUNG: //004D8413
              m_DefMsg := MakeDefaultMsg(SM_RUSHKUNG, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, MakeWord(ProcessMsg.wParam, TActorObject(ProcessMsg.BaseObject).m_nLight));
          else begin //004D846A
              m_DefMsg := MakeDefaultMsg(SM_TURN, Integer(ProcessMsg.BaseObject), ProcessMsg.nParam1, ProcessMsg.nParam2, MakeWord(ProcessMsg.wParam, TActorObject(ProcessMsg.BaseObject).m_nLight));
            end;
          end;
          CharDesc.feature := TActorObject(ProcessMsg.BaseObject).GetFeature(TActorObject(ProcessMsg.BaseObject));
          CharDesc.Status := TActorObject(ProcessMsg.BaseObject).m_nCharStatus;

          CharDesc.Level := TActorObject(ProcessMsg.BaseObject).m_WAbil.Level;
          CharDesc.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
          CharDesc.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
          CharDesc.AddStatus := TActorObject(ProcessMsg.BaseObject).GetAddStatus;

          s1C := EncodeBuffer(@CharDesc, SizeOf(CharDesc));
          nObjCount := GetCharColor(TActorObject(ProcessMsg.BaseObject));
          if ProcessMsg.sMsg <> '' then
            s1C := s1C + (EncodeString(ProcessMsg.sMsg + '/' + IntToStr(nObjCount)));
          SendSocket(@m_DefMsg, s1C);
          if ProcessMsg.wIdent = RM_TURN then begin
            nObjCount := TActorObject(ProcessMsg.BaseObject).GetFeatureToLong();
            SendDefMessage(SM_FEATURECHANGED,
              Integer(ProcessMsg.BaseObject),
              LoWord(nObjCount),
              HiWord(nObjCount),
              TActorObject(ProcessMsg.BaseObject).GetFeatureEx,
              '');
          end;
        end;
      end;
    RM_STRUCK,
      RM_STRUCK_MAG: begin //10020 004D8B28
        if ProcessMsg.wParam {nPower} > 0 then begin
          if Assigned(PlugInEngine.SendUserStruckMsg) then begin
            PlugInEngine.SendUserStruckMsg(Self,
              ProcessMsg.BaseObject,
              TActorObject(ProcessMsg.nParam3),
              ProcessMsg.wIdent);
          end else begin
            if TActorObject(ProcessMsg.BaseObject) = Self then begin
              if TActorObject(ProcessMsg.nParam3) {AttackBaseObject} <> nil then begin
                if TActorObject(ProcessMsg.nParam3).m_btRaceServer = RC_PLAYOBJECT then begin
                  SetPKFlag(TActorObject(ProcessMsg.nParam3) {AttackBaseObject});
                end;
                SetLastHiter(TActorObject(ProcessMsg.nParam3) {AttackBaseObject});
                {
                //反复活
                if TActorObject(ProcessMsg.nParam3).m_boUnRevival then
                  m_boRevival:=False;
                }
              end; //004D8B67
              if PKLevel >= 2 then m_dw5D4 := GetTickCount();
              //if UserCastle.IsMasterGuild(TGuild(m_MyGuild)) and (TActorObject(ProcessMsg.nParam3) <> nil) then begin
              if (g_CastleManager.IsCastleMember(Self) <> nil) and (TActorObject(ProcessMsg.nParam3) <> nil) then begin
                TActorObject(ProcessMsg.nParam3).bo2B0 := True;
                TActorObject(ProcessMsg.nParam3).m_dw2B4Tick := GetTickCount();
              end;
              m_nHealthTick := 0;
              m_nSpellTick := 0;
              Dec(m_nPerHealth);
              Dec(m_nPerSpell);
              m_dwStruckTick := GetTickCount(); //09/10
            end; //4D8BE1
            if TActorObject(ProcessMsg.BaseObject) <> nil then begin
              if ((TActorObject(ProcessMsg.BaseObject) = Self) and (g_Config.boDisableSelfStruck)) or ((TActorObject(ProcessMsg.BaseObject).m_btRaceServer = RC_PLAYOBJECT) and g_Config.boDisableStruck) then begin
                TActorObject(ProcessMsg.BaseObject).SendRefMsg(RM_HEALTHSPELLCHANGED, 0, 0, 0, 0, '');
              end else begin

                m_DefMsg := MakeDefaultMsg(SM_STRUCK, Integer(ProcessMsg.BaseObject), 0, LoWord(ProcessMsg.wParam), HiWord(ProcessMsg.wParam));

                Health.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
                Health.MP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MP;
                Health.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;

                MessageBodyWL.lParam1 := TActorObject(ProcessMsg.BaseObject).GetFeature(Self);
                MessageBodyWL.lParam2 := TActorObject(ProcessMsg.BaseObject).m_nCharStatus;
                MessageBodyWL.lTag1 := ProcessMsg.nParam3;
                if ProcessMsg.wIdent = RM_STRUCK_MAG then MessageBodyWL.lTag2 := 1
                else MessageBodyWL.lTag2 := 0;
                SendSocket(@m_DefMsg, EncodeBuffer(@MessageBodyWL, SizeOf(TMessageBodyWL)) + '/' + EncodeBuffer(@Health, SizeOf(THealth)));

              end;
            end;
          end;
        end;
      end;
    RM_DEATH: begin
        if Assigned(PlugInEngine.SendDeathMsg) then begin
          PlugInEngine.SendDeathMsg(Self,
            ProcessMsg.BaseObject,
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            ProcessMsg.wParam,
            ProcessMsg.nParam3);
        end else begin
          if ProcessMsg.nParam3 = 1 then begin
            m_DefMsg := MakeDefaultMsg(SM_NOWDEATH,
              Integer(ProcessMsg.BaseObject),
              ProcessMsg.nParam1,
              ProcessMsg.nParam2,
              ProcessMsg.wParam);
          end else begin
            m_DefMsg := MakeDefaultMsg(SM_DEATH,
              Integer(ProcessMsg.BaseObject),
              ProcessMsg.nParam1,
              ProcessMsg.nParam2,
              ProcessMsg.wParam);
          end;
          CharDesc.feature := TActorObject(ProcessMsg.BaseObject).GetFeature(Self);
          CharDesc.Status := TActorObject(ProcessMsg.BaseObject).m_nCharStatus;

          CharDesc.Level := TActorObject(ProcessMsg.BaseObject).m_WAbil.Level;
          CharDesc.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
          CharDesc.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
          CharDesc.AddStatus := TActorObject(ProcessMsg.BaseObject).GetAddStatus;

          SendSocket(@m_DefMsg, EncodeBuffer(@CharDesc, SizeOf(TCharDesc)));
        end;
      end;

    RM_DISAPPEAR: begin //10022 004D915C
        m_DefMsg := MakeDefaultMsg(SM_DISAPPEAR,
          Integer(ProcessMsg.BaseObject),
          0, 0, 0);
        SendSocket(@m_DefMsg, '');
      end;
    RM_SKELETON: begin //10024 004D8D7B
        if Assigned(PlugInEngine.SendSkeletonMsg) then begin
          PlugInEngine.SendSkeletonMsg(Self,
            ProcessMsg.BaseObject,
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            ProcessMsg.wParam);
        end else begin
          m_DefMsg := MakeDefaultMsg(SM_SKELETON,
            Integer(ProcessMsg.BaseObject),
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            ProcessMsg.wParam);
          CharDesc.feature := TActorObject(ProcessMsg.BaseObject).GetFeature(Self);
          CharDesc.Status := TActorObject(ProcessMsg.BaseObject).m_nCharStatus;

          CharDesc.Level := TActorObject(ProcessMsg.BaseObject).m_WAbil.Level;
          CharDesc.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
          CharDesc.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
          CharDesc.AddStatus := TActorObject(ProcessMsg.BaseObject).GetAddStatus;

          SendSocket(@m_DefMsg, EncodeBuffer(@CharDesc, SizeOf(TCharDesc)));
        end;
      end;
    RM_USERNAME: begin
        m_DefMsg := MakeDefaultMsg(SM_USERNAME,
          Integer(ProcessMsg.BaseObject),
          GetCharColor(TActorObject(ProcessMsg.BaseObject)), 0, 0);
        SendSocket(@m_DefMsg, EncodeString(ProcessMsg.sMsg));
      end;
    RM_WINEXP: begin
        m_DefMsg := MakeDefaultMsg(SM_WINEXP, m_Abil.Exp, LoWord(ProcessMsg.nParam1), HiWord(ProcessMsg.nParam1), 0);
        SendSocket(@m_DefMsg, '');
      end;
    RM_GROUPINFO: begin
          m_DefMsg:=MakeDefaultMsg(SM_GROUPINFO,m_GroupOwner.m_GroupLevel,m_GroupOwner.m_GroupExp,0,0);
          SendSocket(@m_DefMsg,'');
        end;
    RM_LEVELUP: begin
        if Assigned(PlugInEngine.SendUserLevelUpMsg) then begin
          PlugInEngine.SendUserLevelUpMsg(Self);
        end else begin

          m_DefMsg := MakeDefaultMsg(SM_LEVELUP, m_Abil.Exp, LoWord(m_Abil.Level), HiWord(m_Abil.Level), 0);
          SendSocket(@m_DefMsg, '');

          m_DefMsg := MakeDefaultMsg(SM_ABILITY, m_nGold, MakeWord(m_btJob, 99), LoWord(m_nGameGold), HiWord(m_nGameGold));
          //0806 增加
          if (not m_boHeroVersion) and (m_dwClientTick = 0) then begin
            GetOldAbil(OAbility);
            SendSocket(@m_DefMsg, EncodeBuffer(@OAbility, SizeOf(TOAbility)));
          end else begin
            SendSocket(@m_DefMsg, EncodeBuffer(@m_WAbil, SizeOf(TAbility)));
          end;
          //SendSocket(@m_DefMsg,EncodeBuffer(@m_WAbil,SizeOf(TAbility)));
          SendDefMessage(SM_SUBABILITY,
            MakeLong(MakeWord(m_nAntiMagic, 0), 0),
            MakeWord(m_btHitPoint, m_btSpeedPoint),
            MakeWord(m_btAntiPoison, m_nPoisonRecover),
            MakeWord(m_nHealthRecover, m_nSpellRecover),
            '');
        end;
      end;
    RM_CHANGENAMECOLOR: begin //10046 004D9555
        SendDefMessage(SM_CHANGENAMECOLOR,
          Integer(ProcessMsg.BaseObject),
          GetCharColor(TActorObject(ProcessMsg.BaseObject)),
          0,
          0,
          '');
      end;
    RM_LOGON: begin //10050
        if m_PEnvir.m_boDARK then nObjCount := 1
        else begin
          case m_nBright of
            1: nObjCount := 0;
            3: nObjCount := 1;
          else nObjCount := 1;
          end;
        end;
        if m_PEnvir.m_boDAY then nObjCount := 0;
        m_DefMsg := MakeDefaultMsg(SM_NEWMAP, Integer(Self), m_nCurrX, m_nCurrY, nObjCount);
        SendSocket(@m_DefMsg, EncodeString(m_PEnvir.MapName));
        SendMsg(Self, RM_CHANGELIGHT, 0, 0, 0, 0, '');
        SendLogon();
        SendServerConfig();
        ClientQueryUserName(Self, m_nCurrX, m_nCurrY);
        RefUserState();
        SendMapDescription();
        SendGoldInfo(True);

        StoreCancel; //摆摊状态

        SendDefMessage(SM_GAMEGOLDNAME, m_nGameGold, LoWord(m_nGamePoint), HiWord(m_nGamePoint), 0, g_Config.sGameGoldName + #13 + g_Config.sGamePointName);
        m_DefMsg := MakeDefaultMsg(SM_VERSION_FAIL, g_Config.nClientFile1_CRC, LoWord(g_Config.nClientFile2_CRC), HiWord(g_Config.nClientFile2_CRC), 0);
        SendSocket(@m_DefMsg, EncodeBuffer(@g_Config.nClientFile3_CRC, SizeOf(Integer)));
      end;

    RM_SENDSERVERCONFIG: begin
        SendServerConfig();
      end;

    RM_HEAR2: begin
        Hear(ProcessMsg.wParam, ProcessMsg.sMsg);
      end;

    RM_HEAR,
      RM_WHISPER,
      RM_CRY,
      RM_SYSMESSAGE,
      RM_GROUPMESSAGE,
      RM_SYSMESSAGE2,
      RM_GUILDMESSAGE,
      RM_SYSMESSAGE3,
      RM_MERCHANTSAY,
      RM_MOVEMESSAGE,
      RM_DELAYMESSAGE,
      RM_CENTERMESSAGE: begin
        {
        case ProcessMsg.wIdent of    //004D97B3
          RM_HEAR: m_DefMsg:=MakeDefaultMsg(SM_HEAR,Integer(ProcessMsg.BaseObject),MakeWord($0,$FF),0,1);//10030
//            RM_WHISPER: m_DefMsg:=MakeDefaultMsg(SM_WHISPER,Integer(ProcessMsg.BaseObject),MakeWord($FC,$FF),0,1);//10031
          RM_WHISPER: m_DefMsg:=MakeDefaultMsg(SM_WHISPER,Integer(ProcessMsg.BaseObject),MakeWord($FF,$38),0,1);//10031
          RM_CRY: m_DefMsg:=MakeDefaultMsg(SM_HEAR,Integer(ProcessMsg.BaseObject),MakeWord($0,$97),0,1);//10032
          RM_SYSMESSAGE: m_DefMsg:=MakeDefaultMsg(SM_SYSMESSAGE,Integer(ProcessMsg.BaseObject),MakeWord($FF,$38),0,1);//10100 红色
          RM_GROUPMESSAGE: m_DefMsg:=MakeDefaultMsg(SM_SYSMESSAGE,Integer(ProcessMsg.BaseObject),MakeWord($C4,$FF),0,1);//10102
          RM_SYSMESSAGE2: m_DefMsg:=MakeDefaultMsg(SM_SYSMESSAGE,Integer(ProcessMsg.BaseObject),MakeWord($DB,$FF),0,1);//10103
          RM_GUILDMESSAGE: m_DefMsg:=MakeDefaultMsg(SM_GUILDMESSAGE,Integer(ProcessMsg.BaseObject),MakeWord($DB,$FF),0,1); //10104
          RM_SYSMESSAGE3: m_DefMsg:=MakeDefaultMsg(SM_GUILDMESSAGE,Integer(ProcessMsg.BaseObject),MakeWord($FF,$FC),0,1);//10105
          RM_MERCHANTSAY: m_DefMsg:=MakeDefaultMsg(SM_MERCHANTSAY,Integer(ProcessMsg.BaseObject),0,0,1);//10126
        end;
        }
        if not m_boAI then begin
          case ProcessMsg.wIdent of //004D97B3
            RM_HEAR: m_DefMsg := MakeDefaultMsg(SM_HEAR, Integer(ProcessMsg.BaseObject), MakeWord(ProcessMsg.nParam1, ProcessMsg.nParam2), 0, 1); //10030
            RM_WHISPER: m_DefMsg := MakeDefaultMsg(SM_WHISPER, Integer(ProcessMsg.BaseObject), MakeWord(ProcessMsg.nParam1, ProcessMsg.nParam2), 0, 1); //10031
            RM_CRY: m_DefMsg := MakeDefaultMsg(SM_HEAR, Integer(ProcessMsg.BaseObject), MakeWord(ProcessMsg.nParam1, ProcessMsg.nParam2), 0, 1); //10032
            RM_SYSMESSAGE: m_DefMsg := MakeDefaultMsg(SM_SYSMESSAGE, Integer(ProcessMsg.BaseObject), MakeWord(ProcessMsg.nParam1, ProcessMsg.nParam2), 0, 1); //10100 红色

          //RM_SYSMESSAGE2: m_DefMsg:=MakeDefaultMsg(SM_SYSMESSAGE,Integer(ProcessMsg.BaseObject),MakeWord($DB,$FF),0,1);//10103
          //RM_SYSMESSAGE3: m_DefMsg:=MakeDefaultMsg(SM_GUILDMESSAGE,Integer(ProcessMsg.BaseObject),MakeWord($FF,$FC),0,1);//10105

            RM_GROUPMESSAGE: m_DefMsg := MakeDefaultMsg(SM_SYSMESSAGE, Integer(ProcessMsg.BaseObject), MakeWord(ProcessMsg.nParam1, ProcessMsg.nParam2), 0, 1); //10102
            RM_GUILDMESSAGE: m_DefMsg := MakeDefaultMsg(SM_GUILDMESSAGE, Integer(ProcessMsg.BaseObject), MakeWord(ProcessMsg.nParam1, ProcessMsg.nParam2), 0, 1); //10104
            RM_MERCHANTSAY: m_DefMsg := MakeDefaultMsg(SM_MERCHANTSAY, Integer(ProcessMsg.BaseObject), MakeWord(ProcessMsg.nParam1, ProcessMsg.nParam2), 0, 1); //10126
            RM_MOVEMESSAGE: m_DefMsg := MakeDefaultMsg(SM_MOVEMESSAGE, ProcessMsg.nParam3, ProcessMsg.wParam, ProcessMsg.nParam1, ProcessMsg.nParam2); //10140
            RM_DELAYMESSAGE: m_DefMsg := MakeDefaultMsg(SM_DELAYMESSAGE, ProcessMsg.nParam1, ProcessMsg.wParam, ProcessMsg.nParam2, 0);
            RM_CENTERMESSAGE: m_DefMsg := MakeDefaultMsg(SM_CENTERMESSAGE, ProcessMsg.nParam3, ProcessMsg.wParam, ProcessMsg.nParam1, ProcessMsg.nParam2);
          end;
          SendSocket(@m_DefMsg, EncodeString(ProcessMsg.sMsg));
        end else begin
          SendDelayMsg(Self, RM_HEAR2, ProcessMsg.wIdent, 0, 0, 0, ProcessMsg.sMsg, 1500);
        end;
      end;

    {
    RM_ABILITY: begin //10051
      m_DefMsg:=MakeDefaultMsg(SM_ABILITY,
                             m_nGold,
                             m_btJob,
                             0,
                             0);
      SendSocket(@m_DefMsg,EncodeBuffer(@m_WAbil,SizeOf(TAbility)));
    end;
    }
    RM_ABILITY: begin
        if Assigned(PlugInEngine.SendUserAbilieyMsg) then begin
          PlugInEngine.SendUserAbilieyMsg(Self);
        end else begin
          m_DefMsg := MakeDefaultMsg(SM_ABILITY,
            m_nGold,
            MakeWord(m_btJob, 99),
            LoWord(m_nGameGold),
            HiWord(m_nGameGold));
          if (not m_boHeroVersion) and (m_dwClientTick = 0) then begin
            GetOldAbil(OAbility);
            SendSocket(@m_DefMsg, EncodeBuffer(@OAbility, SizeOf(TOAbility)));
            if g_Config.boOldClientShowHiLevel and (m_Abil.Level > 255) then begin
              SysMsg(g_sClientVersionTooOld {'由于您使用的客户端版本太老了，无法正确显示人物信息！！！'}, c_Red, t_Hint);
              SysMsg('Level: ' + IntToStr(m_Abil.Level), c_Green, t_Hint);
              SysMsg('HP: ' + IntToStr(m_WAbil.HP) + '-' + IntToStr(m_WAbil.MaxHP), c_Blue, t_Hint);
              SysMsg('MP: ' + IntToStr(m_WAbil.MP) + '-' + IntToStr(m_WAbil.MaxMP), c_Red, t_Hint);
              SysMsg('AC: ' + IntToStr(LoWord(m_WAbil.AC)) + '-' + IntToStr(HiWord(m_WAbil.AC)), c_Green, t_Hint);
              SysMsg('AMC: ' + IntToStr(LoWord(m_WAbil.MAC)) + '-' + IntToStr(HiWord(m_WAbil.MAC)), c_Blue, t_Hint);
              SysMsg('DC: ' + IntToStr(LoWord(m_WAbil.DC)) + '-' + IntToStr(HiWord(m_WAbil.DC)), c_Red, t_Hint);
              SysMsg('MC: ' + IntToStr(LoWord(m_WAbil.MC)) + '-' + IntToStr(HiWord(m_WAbil.MC)), c_Green, t_Hint);
              SysMsg('SC: ' + IntToStr(LoWord(m_WAbil.SC)) + '-' + IntToStr(HiWord(m_WAbil.SC)), c_Blue, t_Hint);
            end;
          end else begin
            SendSocket(@m_DefMsg, EncodeBuffer(@m_WAbil, SizeOf(TAbility)));
          end;
        end;
      end;

    RM_USERCASTLE: begin
        m_DefMsg := MakeDefaultMsg(SM_USERCASTLE,
          Integer(ProcessMsg.BaseObject),
          ProcessMsg.nParam1,
          0,
          0);
        SendSocket(@m_DefMsg, '');
      end;

    RM_HEALTHSPELLCHANGED: begin

        Health.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
        Health.MP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MP;
        Health.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
        m_DefMsg := MakeDefaultMsg(SM_HEALTHSPELLCHANGED, Integer(ProcessMsg.BaseObject), 0, 0, 0);
        SendSocket(@m_DefMsg, EncodeBuffer(@Health, SizeOf(THealth)));

      end;
    RM_DAYCHANGING: begin //10053
        if m_PEnvir.m_boDARK then nObjCount := 1
        else begin
          case m_nBright of
            1: nObjCount := 0;
            3: nObjCount := 1;
          else nObjCount := 1;
          end;
        end;
        if m_PEnvir.m_boDAY then nObjCount := 0;
        m_DefMsg := MakeDefaultMsg(SM_DAYCHANGING, 0, m_nBright, nObjCount, 0);
        SendSocket(@m_DefMsg, '');
      end;
    RM_DELETEDELAYMESSAGE: begin //删除延时信息
        SendDefMessage(SM_DELETEDELAYMESSAGE, ProcessMsg.nParam1, 0, 0, 0, '');
      end;

    RM_ITEMSHOW: begin //10110 004D9D01
        SendDefMessage(SM_ITEMSHOW,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          ProcessMsg.wParam,
          ProcessMsg.sMsg);
      end;
    RM_ITEMHIDE: begin //10111 004D9D27
        SendDefMessage(SM_ITEMHIDE,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          0,
          '');
      end;
    RM_DOOROPEN: begin //10112 004D9D6A
        SendDefMessage(SM_OPENDOOR_OK,
          0,
          ProcessMsg.nParam1, {x}
          ProcessMsg.nParam2, {y}
          0,
          '');
      end;
    RM_DOORCLOSE: begin //10113 004D9D8A
        SendDefMessage(SM_CLOSEDOOR,
          0,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          0,
          '');
      end;
    RM_SENDUSEITEMS: SendUseitems();

    RM_WEIGHTCHANGED: begin //10115 004D9DC4
        SendDefMessage(SM_WEIGHTCHANGED,
          m_WAbil.Weight,
          m_WAbil.WearWeight,
          m_WAbil.HandWeight,
          0,
          '');
      end;
    RM_FEATURECHANGED: begin //10116 004D9E1A
        SendDefMessage(SM_FEATURECHANGED,
          Integer(ProcessMsg.BaseObject),
          LoWord(ProcessMsg.nParam1),
          HiWord(ProcessMsg.nParam1),
          ProcessMsg.wParam,
          '');
      end;
    RM_CLEAROBJECTS: begin //10117 004D9E71
        SendDefMessage(SM_CLEAROBJECTS,
          0,
          0,
          0,
          0,
          '');
      end;

    RM_CHANGEMAP: begin
        if m_PEnvir.m_boDARK then nObjCount := 1
        else begin
          case m_nBright of
            1: nObjCount := 0;
            3: nObjCount := 1;
          else nObjCount := 1;
          end;
        end;
        if m_PEnvir.m_boDAY then nObjCount := 0;
        SendDefMessage(SM_CHANGEMAP, Integer(Self), m_nCurrX, m_nCurrY, nObjCount, ProcessMsg.sMsg);
        RefUserState();
        SendMapDescription();
        SendServerConfig();
      end;
    RM_BUTCH: begin //10119 004D86B1
        if ProcessMsg.BaseObject <> nil then begin
          m_DefMsg := MakeDefaultMsg(SM_BUTCH,
            Integer(ProcessMsg.BaseObject),
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            ProcessMsg.wParam);
          SendSocket(@m_DefMsg, '');
        end;
      end;
    RM_MAGICFIRE: begin //10120 004D8A90
        m_DefMsg := MakeDefaultMsg(SM_MAGICFIRE,
          Integer(ProcessMsg.BaseObject),
          LoWord(ProcessMsg.nParam2),
          HiWord(ProcessMsg.nParam2),
          ProcessMsg.nParam1);
        SendSocket(@m_DefMsg, EncodeBuffer(@ProcessMsg.nParam3, SizeOf(Integer)));
      end;
    RM_MAGICFIREFAIL: begin //10121
        SendDefMessage(SM_MAGICFIRE_FAIL, Integer(ProcessMsg.BaseObject), 0, 0, 0, '');
      end;
    RM_SENDMYMAGIC: SendUseMagic; //10122

    RM_MAGIC_LVEXP: begin //10123 004D9E8D
        SendDefMessage(SM_MAGIC_LVEXP,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          LoWord(ProcessMsg.nParam3),
          HiWord(ProcessMsg.nParam3),
          '');
      end;
    RM_DURACHANGE: begin //10125 004D9EB9
        SendDefMessage(SM_DURACHANGE,
          ProcessMsg.nParam1,
          ProcessMsg.wParam,
          LoWord(ProcessMsg.nParam2),
          HiWord(ProcessMsg.nParam2),
          '');
      end;

    RM_MERCHANTDLGCLOSE: begin //10127 004D9ADF
        SendDefMessage(SM_MERCHANTDLGCLOSE,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_SENDGOODSLIST: begin
        if Assigned(PlugInEngine.SendGoodsList) then begin
          PlugInEngine.SendGoodsList(Self,
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            ProcessMsg.nParam3,
            PChar(ProcessMsg.sMsg));
        end else begin
          SendDefMessage(SM_SENDGOODSLIST,
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            0,
            0,
            ProcessMsg.sMsg);
        end;
      end;
    RM_SENDUSERSELL: begin //10129 004D9B1D
        SendDefMessage(SM_SENDUSERSELL,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          0,
          0,
          ProcessMsg.sMsg);
      end;
    RM_SENDBUYPRICE: begin //10130  004D9BAB
        SendDefMessage(SM_SENDBUYPRICE,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_USERSELLITEM_OK: begin //10131  004D9BC8
        SendDefMessage(SM_USERSELLITEM_OK,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_USERSELLITEM_FAIL: begin //10132  004D9BC8
        SendDefMessage(SM_USERSELLITEM_FAIL,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_BUYITEM_SUCCESS: begin //10133  004D9C02
        SendDefMessage(SM_BUYITEM_SUCCESS,
          ProcessMsg.nParam1,
          LoWord(ProcessMsg.nParam2),
          HiWord(ProcessMsg.nParam2),
          0,
          '');
      end;
    RM_BUYITEM_FAIL: begin //10134  004D9C2C
        SendDefMessage(SM_BUYITEM_FAIL,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_SENDDETAILGOODSLIST: begin //10135  004D9C83
        SendDefMessage(SM_SENDDETAILGOODSLIST,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          0,
          ProcessMsg.sMsg);
      end;
    //////////////////////////////////////拍卖//////////////////////////////////////
    RM_SENDSELLOFFGOODSLIST: begin //  拍卖
        //if not m_boHeroVersion then begin
        SendDefMessage(SM_SENDSELLOFFGOODSLIST,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          0,
          ProcessMsg.sMsg);
       { end else begin
          m_DefMsg := MakeDefaultMsg(SM_SENDSELLOFFGOODSLIST, ProcessMsg.nParam1, ProcessMsg.nParam2, ProcessMsg.nParam3, 0);
          if ProcessMsg.sMsg <> '' then SendSocket(@m_DefMsg, ProcessMsg.sMsg)
          else SendSocket(@m_DefMsg, '');
        end;  }
      end;

    RM_SENDUSERSELLOFFITEM: begin
        SendDefMessage(SM_SENDUSERSELLOFFITEM,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          0,
          0,
          ProcessMsg.sMsg);
      end;

    RM_SENDUSERSELLOFFITEM_OK: begin
        SendDefMessage(SM_SENDUSERSELLOFFITEM_OK,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          0,
          '');
      end;
    RM_SENDUSERSELLOFFITEM_FAIL: begin
        SendDefMessage(SM_SENDUSERSELLOFFITEM_FAIL,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          0,
          '');
      end;
    RM_SENDSELLOFFITEMLIST: begin
        SendDefMessage(CM_SENDSELLOFFITEMLIST,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          0,
          ProcessMsg.sMsg);
      end;

    RM_SENDBUYSELLOFFITEM_OK: begin //购买拍卖物品成功
        SendDefMessage(SM_SENDBUYSELLOFFITEM_OK,
          ProcessMsg.nParam1,
          LoWord(ProcessMsg.nParam2),
          HiWord(ProcessMsg.nParam2),
          0,
          '');
      end;
    RM_SENDBUYSELLOFFITEM_FAIL: begin //购买拍卖物品失败
        SendDefMessage(SM_SENDBUYSELLOFFITEM_FAIL,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    //////////////////////////////////////拍卖//////////////////////////////////////
    RM_GOLDCHANGED: begin //10136  004D9DFA
        SendDefMessage(SM_GOLDCHANGED,
          m_nGold,
          LoWord(m_nGameGold),
          HiWord(m_nGameGold),
          0,
          '');
      end;
    RM_GAMEGOLDCHANGED: begin
        SendGoldInfo(False);
        {
        SendDefMessage(SM_GAMEGOLDNAME,
                       m_nGameGold,
                       LoWord(m_nGamePoint),
                       HiWord(m_nGamePoint),
                       0,
                       '');
        }
      end;
    RM_CHANGELIGHT: begin //10137  004D9EE6
        SendDefMessage(SM_CHANGELIGHT,
          Integer(ProcessMsg.BaseObject),
          TActorObject(ProcessMsg.BaseObject).m_nLight,
          g_Config.nClientKey,
          0,
          '');
      end;
    RM_LAMPCHANGEDURA: begin //10138 004D9F0B
        SendDefMessage(SM_LAMPCHANGEDURA,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_CHARSTATUSCHANGED: begin //10139 004D9E44
        SendDefMessage(SM_CHARSTATUSCHANGED,
          Integer(ProcessMsg.BaseObject),
          LoWord(ProcessMsg.nParam1),
          HiWord(ProcessMsg.nParam1),
          ProcessMsg.wParam,
          IntToStr(ProcessMsg.nParam3));
      end;
    RM_GROUPCANCEL: begin //10140 004D9F28
        SendDefMessage(SM_GROUPCANCEL,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;

    RM_SENDCHANGEITEM: begin //10141 004D9B3C
        SendDefMessage(SM_SENDCHANGEITEM,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          0,
          0,
          '');
      end;

    RM_SENDCHANGEITEM_OK: begin
        SendDefMessage(SM_SENDCHANGEITEM_OK,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          0,
          '');
      end;
    RM_SENDCHANGEITEM_FAIL: begin
        SendDefMessage(SM_SENDCHANGEITEM_FAIL,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          0,
          '');
      end;

    RM_SENDUSERREPAIR,
      RM_SENDUSERSREPAIR: begin //10141 004D9B3C
        SendDefMessage(SM_SENDUSERREPAIR,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          0,
          0,
          '');
      end;
    RM_USERREPAIRITEM_OK: begin //10143  004D9CA6
        SendDefMessage(SM_USERREPAIRITEM_OK,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          0,
          '');
      end;
    RM_SENDREPAIRCOST: begin //10142  004D9CE4
        SendDefMessage(SM_SENDREPAIRCOST,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_USERREPAIRITEM_FAIL: begin //10144  004D9CC7
        SendDefMessage(SM_USERREPAIRITEM_FAIL,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_USERSTORAGEITEM: begin //10146  004D9B5B
        SendDefMessage(SM_SENDUSERSTORAGEITEM,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          0,
          0,
          '');
      end;
    RM_USERGETBACKITEM: begin //10147  004D9B7A  SM_SAVEITEMLIST
        SendSaveItemList(ProcessMsg.nParam1);
      end;
    RM_USERBIGGETBACKITEM: begin
        SendSaveBigStorageItemList(ProcessMsg.nParam1, ProcessMsg.wParam);
      end;
    RM_SENDDELITEMLIST: begin //10148  004D9D48  //SM_DELITEMS
        SendDelItemList(TStringList(ProcessMsg.nParam1));
        TStringList(ProcessMsg.nParam1).Free;
      end;
    RM_USERMAKEDRUGITEMLIST: begin //10149  004D9B8A
        SendDefMessage(SM_SENDUSERMAKEDRUGITEMLIST,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          0,
          0,
          ProcessMsg.sMsg);
      end;
    RM_MAKEDRUG_SUCCESS: begin //10150 004D9C49
        SendDefMessage(SM_MAKEDRUG_SUCCESS,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_MAKEDRUG_FAIL: begin //10151 004D9C66
        SendDefMessage(SM_MAKEDRUG_FAIL,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_ALIVE: begin
        if Assigned(PlugInEngine.SendAliveMsg) then begin
          PlugInEngine.SendAliveMsg(Self,
            ProcessMsg.BaseObject,
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            ProcessMsg.wParam);
        end else begin
          m_DefMsg := MakeDefaultMsg(SM_ALIVE,
            Integer(ProcessMsg.BaseObject),
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            ProcessMsg.wParam);
          CharDesc.feature := TActorObject(ProcessMsg.BaseObject).GetFeature(Self);
          CharDesc.Status := TActorObject(ProcessMsg.BaseObject).m_nCharStatus;

          CharDesc.Level := TActorObject(ProcessMsg.BaseObject).m_WAbil.Level;
          CharDesc.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
          CharDesc.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
          CharDesc.AddStatus := TActorObject(ProcessMsg.BaseObject).GetAddStatus;

          SendSocket(@m_DefMsg, EncodeBuffer(@CharDesc, SizeOf(TCharDesc)));
        end;
      end;
    RM_DIGUP: begin //10200 004D91B4
        m_DefMsg := MakeDefaultMsg(SM_DIGUP,
          Integer(ProcessMsg.BaseObject),
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          MakeWord(ProcessMsg.wParam, TActorObject(ProcessMsg.BaseObject).m_nLight));
        MessageBodyWL.lParam1 := TActorObject(ProcessMsg.BaseObject).GetFeature(Self);
        MessageBodyWL.lParam2 := TActorObject(ProcessMsg.BaseObject).m_nCharStatus;
        MessageBodyWL.lTag1 := ProcessMsg.nParam3;
        MessageBodyWL.lTag1 := 0;
        s1C := EncodeBuffer(@MessageBodyWL, SizeOf(TMessageBodyWL));
        SendSocket(@m_DefMsg, s1C);
      end;
    RM_DIGDOWN: begin //10201 004D9254
        m_DefMsg := MakeDefaultMsg(SM_DIGDOWN,
          Integer(ProcessMsg.BaseObject),
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          0);
        SendSocket(@m_DefMsg, '');
      end;
    RM_FLYAXE: begin //10202 004D9358
        if TActorObject(ProcessMsg.nParam3) <> nil then begin
          MessageBodyW.Param1 := TActorObject(ProcessMsg.nParam3).m_nCurrX;
          MessageBodyW.Param2 := TActorObject(ProcessMsg.nParam3).m_nCurrY;
          MessageBodyW.Tag1 := LoWord(ProcessMsg.nParam3);
          MessageBodyW.Tag2 := HiWord(ProcessMsg.nParam3);
          m_DefMsg := MakeDefaultMsg(SM_FLYAXE,
            Integer(ProcessMsg.BaseObject),
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            ProcessMsg.wParam);
          SendSocket(@m_DefMsg, EncodeBuffer(@MessageBodyW, SizeOf(TMessageBodyW)));
        end;
      end;
    RM_LIGHTING: begin //10204 004D93FD
        if TActorObject(ProcessMsg.nParam3) <> nil then begin
          MessageBodyWL.lParam1 := TActorObject(ProcessMsg.nParam3).m_nCurrX;
          MessageBodyWL.lParam2 := TActorObject(ProcessMsg.nParam3).m_nCurrY;
          MessageBodyWL.lTag1 := ProcessMsg.nParam3;
          MessageBodyWL.lTag2 := ProcessMsg.wParam;
          m_DefMsg := MakeDefaultMsg(SM_LIGHTING,
            Integer(ProcessMsg.BaseObject),
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            TActorObject(ProcessMsg.BaseObject).m_btDirection);
          SendSocket(@m_DefMsg, EncodeBuffer(@MessageBodyWL, SizeOf(TMessageBodyWL)));
        end;
      end;
    RM_10205: begin //10205 004D949A
        SendDefMessage(SM_716,
          Integer(ProcessMsg.BaseObject),
          ProcessMsg.nParam1 {x},
          ProcessMsg.nParam2 {y},
          ProcessMsg.nParam3 {type},
          '');
      end;
    RM_10206: begin
        SendDefMessage(SM_717,
          ProcessMsg.nParam1 {Target},
          ProcessMsg.wParam {type},
          ProcessMsg.nParam2 {x},
          ProcessMsg.nParam3 {y},
          '');
      end;
    RM_CHANGEGUILDNAME: begin //10301 004D9F44  SM_CHANGEGUILDNAME
        SendChangeGuildName();
      end;
    RM_SUBABILITY: begin //10302
        SendDefMessage(SM_SUBABILITY,
          MakeLong(MakeWord(m_nAntiMagic, 0), 0),
          MakeWord(m_btHitPoint, m_btSpeedPoint),
          MakeWord(m_btAntiPoison, m_nPoisonRecover),
          MakeWord(m_nHealthRecover, m_nSpellRecover),
          '');
      end;
    RM_BUILDGUILD_OK: begin //10303 004D9F51
        SendDefMessage(SM_BUILDGUILD_OK,
          0,
          0,
          0,
          0,
          '');
      end;
    RM_BUILDGUILD_FAIL: begin //10304 004D9F6D
        SendDefMessage(SM_BUILDGUILD_FAIL,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_DONATE_OK: begin //10305 004D9FA7
        SendDefMessage(SM_DONATE_OK,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_DONATE_FAIL: begin //10306 004D9F8A
        SendDefMessage(SM_DONATE_FAIL,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          '');
      end;
    RM_MYSTATUS: begin
        SendDefMessage(SM_MYSTATUS, 0, GetMyStatus, 0, 0, '');
      end;
    RM_MENU_OK: begin //10309  004D9FC4
        SendDefMessage(SM_MENU_OK,
          ProcessMsg.nParam1,
          0,
          0,
          0,
          ProcessMsg.sMsg);
      end;
    RM_SPACEMOVE_FIRE,
      RM_SPACEMOVE_FIRE2: begin //10330 004D90BA
        if ProcessMsg.wIdent = RM_SPACEMOVE_FIRE then begin
          m_DefMsg := MakeDefaultMsg(SM_SPACEMOVE_HIDE,
            Integer(ProcessMsg.BaseObject),
            0,
            0,
            0);
        end else begin
          m_DefMsg := MakeDefaultMsg(SM_SPACEMOVE_HIDE2,
            Integer(ProcessMsg.BaseObject),
            0,
            0,
            0);
        end;
        SendSocket(@m_DefMsg, '');
      end;
    RM_SPACEMOVE_SHOW,
      RM_SPACEMOVE_SHOW2: begin //004D8F62
        if Assigned(PlugInEngine.SendSpaceMoveMsg) then begin
          PlugInEngine.SendSpaceMoveMsg(Self,
            ProcessMsg.BaseObject,
            ProcessMsg.nParam1,
            ProcessMsg.nParam2,
            ProcessMsg.wParam,
            ProcessMsg.wIdent,
            PChar(ProcessMsg.sMsg));
        end else begin
          if ProcessMsg.wIdent = RM_SPACEMOVE_SHOW then begin
            m_DefMsg := MakeDefaultMsg(SM_SPACEMOVE_SHOW,
              Integer(ProcessMsg.BaseObject),
              ProcessMsg.nParam1,
              ProcessMsg.nParam2,
              MakeWord(ProcessMsg.wParam, TActorObject(ProcessMsg.BaseObject).m_nLight));
          end else begin
            m_DefMsg := MakeDefaultMsg(SM_SPACEMOVE_SHOW2,
              Integer(ProcessMsg.BaseObject),
              ProcessMsg.nParam1,
              ProcessMsg.nParam2,
              MakeWord(ProcessMsg.wParam, TActorObject(ProcessMsg.BaseObject).m_nLight));
          end;
          CharDesc.feature := TActorObject(ProcessMsg.BaseObject).GetFeature(Self);
          CharDesc.Status := TActorObject(ProcessMsg.BaseObject).m_nCharStatus;

          CharDesc.Level := TActorObject(ProcessMsg.BaseObject).m_WAbil.Level;
          CharDesc.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
          CharDesc.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
          CharDesc.AddStatus := TActorObject(ProcessMsg.BaseObject).GetAddStatus;

          s1C := EncodeBuffer(@CharDesc, SizeOf(TCharDesc));
          nObjCount := GetCharColor(TActorObject(ProcessMsg.BaseObject));
          if ProcessMsg.sMsg <> '' then begin
            s1C := s1C + EncodeString(ProcessMsg.sMsg + '/' + IntToStr(nObjCount));
          end;
          SendSocket(@m_DefMsg, s1C);
        end;
      end;
    RM_RECONNECTION: begin //10332 004D8F3A
        m_boReconnection := True;
        SendDefMessage(SM_RECONNECT, 0, 0, 0, 0, ProcessMsg.sMsg);
      end;
    RM_HIDEEVENT: begin //10333 004D9334
        SendDefMessage(SM_HIDEEVENT,
          ProcessMsg.nParam1,
          ProcessMsg.wParam,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          '');
      end;
    RM_SHOWEVENT: begin //10334 004D92B1
        ShortMessage.Ident := HiWord(ProcessMsg.nParam2);
        ShortMessage.wMsg := 0;
        m_DefMsg := MakeDefaultMsg(SM_SHOWEVENT,
          ProcessMsg.nParam1,
          ProcessMsg.wParam,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3);
        SendSocket(@m_DefMsg, EncodeBuffer(@ShortMessage, SizeOf(TShortMessage)));
      end;
    RM_ADJUST_BONUS: begin
        SendAdjustBonus();
      end;
    RM_10401: begin
        ChangeServerMakeSlave(pTSlaveInfo(ProcessMsg.nParam1));
        Dispose(pTSlaveInfo(ProcessMsg.nParam1));
      end;
    RM_OPENHEALTH: begin //10410 004D94BD
        Health.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
        Health.MP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MP;
        Health.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
        m_DefMsg := MakeDefaultMsg(SM_OPENHEALTH, Integer(ProcessMsg.BaseObject), 0, 0, 0);
        SendSocket(@m_DefMsg, EncodeBuffer(@Health, SizeOf(THealth)));
      end;
    RM_CLOSEHEALTH: begin //10411 004D94EC
        SendDefMessage(SM_CLOSEHEALTH,
          Integer(ProcessMsg.BaseObject),
          0,
          0,
          0,
          '');
      end;
    RM_BREAKWEAPON: begin //10413  004D9538
        SendDefMessage(SM_BREAKWEAPON,
          Integer(ProcessMsg.BaseObject),
          0,
          0,
          0,
          '');
      end;
    RM_10414: begin //10414
        Health.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
        Health.MP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MP;
        Health.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
        m_DefMsg := MakeDefaultMsg(SM_INSTANCEHEALGUAGE, Integer(ProcessMsg.BaseObject), 0, 0, 0);
        SendSocket(@m_DefMsg, EncodeBuffer(@Health, SizeOf(THealth)));
      end;
    RM_CHANGEFACE: begin
        if Assigned(PlugInEngine.SendChangeFaceMsg) then begin
          PlugInEngine.SendChangeFaceMsg(Self,
            ProcessMsg.BaseObject,
            TActorObject(ProcessMsg.nParam2),
            ProcessMsg.nParam1);
        end else begin
          if (ProcessMsg.nParam1 <> 0) and (ProcessMsg.nParam2 <> 0) then begin
            m_DefMsg := MakeDefaultMsg(SM_CHANGEFACE,
              ProcessMsg.nParam1,
              LoWord(ProcessMsg.nParam2),
              HiWord(ProcessMsg.nParam2),
              0);
            CharDesc.feature := TActorObject(ProcessMsg.nParam2).GetFeature(Self);
            CharDesc.Status := TActorObject(ProcessMsg.nParam2).m_nCharStatus;

            CharDesc.Level := TActorObject(ProcessMsg.BaseObject).m_WAbil.Level;
            CharDesc.HP := TActorObject(ProcessMsg.BaseObject).m_WAbil.HP;
            CharDesc.MaxHP := TActorObject(ProcessMsg.BaseObject).m_WAbil.MaxHP;
            CharDesc.AddStatus := TActorObject(ProcessMsg.BaseObject).GetAddStatus;

            SendSocket(@m_DefMsg, EncodeBuffer(@CharDesc, SizeOf(TCharDesc)));
          end;
        end;
      end;
    RM_PASSWORD: begin //10416
        SendDefMessage(SM_PASSWORD,
          0,
          0,
          0,
          0,
          '');
      end;
    RM_PLAYDICE: begin //10500
        MessageBodyWL.lParam1 := ProcessMsg.nParam1;
        MessageBodyWL.lParam2 := ProcessMsg.nParam2;
        MessageBodyWL.lTag1 := ProcessMsg.nParam3;
        m_DefMsg := MakeDefaultMsg(SM_PLAYDICE,
          Integer(ProcessMsg.BaseObject),
          ProcessMsg.wParam,
          0,
          0);
        SendSocket(@m_DefMsg, EncodeBuffer(@MessageBodyWL, SizeOf(TMessageBodyWL)) + EncodeString(ProcessMsg.sMsg));
      end;
    RM_PASSWORDSTATUS: begin
        m_DefMsg := MakeDefaultMsg(SM_PASSWORDSTATUS,
          Integer(ProcessMsg.BaseObject),
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3);
        SendSocket(@m_DefMsg, ProcessMsg.sMsg);
      end;

    RM_QUERYBAGITEMS: begin
        m_DefMsg := MakeDefaultMsg(SM_BAGITEMS,
          ProcessMsg.nParam1,
          ProcessMsg.wParam,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3);
        SendSocket(@m_DefMsg, ProcessMsg.sMsg);
      end;

    RM_SAVEITEMLIST: begin
        m_DefMsg := MakeDefaultMsg(SM_SAVEITEMLIST,
          ProcessMsg.nParam1,
          ProcessMsg.wParam,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3);
        SendSocket(@m_DefMsg, ProcessMsg.sMsg);
      end;
    RM_STATE_BUBBLEDEFENCEUP: begin //10110 004D9D01
        SendDefMessage(SM_STATE_BUBBLEDEFENCEUP,
          ProcessMsg.nParam1,
          ProcessMsg.wParam,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          '');
      end;
    RM_SENDOPENHOMEPAGE: begin
        SendDefMessage(SM_SENDOPENHOMEPAGE,
          0,
          0,
          0,
          0,
          ProcessMsg.sMsg);
      end;
  else begin
      if Assigned(PlugInEngine.ObjectOperateMessage) then begin
        Result := PlugInEngine.ObjectOperateMessage(Self,
          ProcessMsg.wIdent,
          ProcessMsg.wParam,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          ProcessMsg.BaseObject,
          ProcessMsg.dwDeliveryTime,
          PChar(ProcessMsg.sMsg),
          boReturn);
        //MainOutMessage('ObjectOperateMessage ProcessMsg.wIdent '+IntToStr(ProcessMsg.wIdent));
        if boReturn then Result := inherited Operate(ProcessMsg);
      end else Result := inherited Operate(ProcessMsg);
    end;
  end;
  //inherited;
end;

procedure TPlayObject.Run();
var
  tObjCount: Integer;
  nInteger: Integer;
  //wYear, wMonth, wDay,
  wHour: Word;
  wMin: Word;
  wSec: Word;
  wMSec: Word;
  //  w48:word;
  ProcessMsg: TProcessMessage;
  boInSafeArea: Boolean;
  I, II: Integer;
  StdItem: pTStdItem;
  UserItem: pTUserItem;
  PlayObject: TPlayObject;
  BaseObject: TActorObject;
  PushedObject: TActorObject;
  boTakeItem: Boolean;
  boRecalcAbilitys: Boolean;
  Castle: TUserCastle;
  PlayObjectList: TList;
  SC: string;

  dwStationTick: LongWord;
  nCode: Integer;

  dwLastTick: LongWord;
  TimeLabel: pTTimeLabel;
resourcestring
  sPayMentExpire = 'Payment Expired';
  sDisConnectMsg = 'Disconnected';
  sExceptionMsg1 = '[Exception] TPlayObject::Run -> Operate 1 Code=%d';
  sExceptionMsg2 = '[Exception] TPlayObject::Run -> Operate 2 # %s Ident:%d Sender:%d wP:%d nP1:%d nP2:%d np3:%d Msg:%s  Code=%d';
  sExceptionMsg3 = '[Exception] TPlayObject::Run -> GetHighHuman';
  sExceptionMsg4 = '[Exception] TPlayObject::Run -> ClearObj';
  sExceptionMsg5 = '[Exception] TPlayObject::Run -> ClearItem Code:%d';
  sExceptionMsg6 = '[Exception] TPlayObject::Run -> GotoLabel:%s';
  sExceptionMsg7 = '[Exception] TPlayObject::Run -> Say Code:%d';
  sExceptionMsg8 = '[Exception] TPlayObject::Run -> Robot Code:%d';
  sExceptionMsg9 = '[Exception] TPlayObject::Run -> Auto Code:%d';
  sExceptionMsg10 = '[Exception] TPlayObject::Run -> AutoGetExp';

begin
  if g_boExitServer then begin
    m_boEmergencyClose := True;
    m_boPlayOffLine := False;
    m_boNotOnlineAddExp := False;
    m_boStartAutoAddExpPoint := False;
  end;
  nCode := 0;

  try
    if (GetTickCount - m_dwTick30) > 30 * 1000 then begin
      m_dwTick30 := GetTickCount;
        // 检查交易双方 状态
      if (m_DealCreat <> nil) and (m_DealCreat.m_boGhost or m_DealCreat.m_boDeath) then
        m_DealCreat := nil;
    //end;

      if (m_GroupOwner <> nil) then begin
        if m_GroupOwner.m_boDeath or m_GroupOwner.m_boGhost then begin
          m_GroupOwner := nil;
        end;
      end;

      for I := m_GroupMembers.Count - 1 downto 0 do begin
        if m_GroupMembers.Count <= 0 then Break;
        BaseObject := TActorObject(m_GroupMembers.Objects[I]);
        if BaseObject <> nil then begin
          if BaseObject.m_boDeath or (BaseObject.m_boGhost) then
            m_GroupMembers.Delete(I);
        end;
      end;

      for I := m_GroupMembers.Count - 1 downto 0 do begin
        BaseObject := TActorObject(m_GroupMembers.Objects[I]);
        for II := I - 1 downto 1 do begin
          if (TActorObject(m_GroupMembers.Objects[II]) = BaseObject) or (CompareText(m_GroupMembers.Strings[II], m_GroupMembers.Strings[I]) = 0) then begin
            m_GroupMembers.Delete(II);
          end;
        end;
      end;
    end;

    {if (GetTickCount - m_dwVerifyTick) > 30 * 1000 then begin
      m_dwVerifyTick := GetTickCount();
      if not m_boDenyRefStatus then
        m_PEnvir.VerifyMapTime(m_nCurrX, m_nCurrY, Self); //刷新在地图上位置的时间
    end; }


    if m_boDealing then begin
      if (GetPoseCreate <> m_DealCreat) or (m_DealCreat = Self) or (m_DealCreat = nil) then
        DealCancel();
      nCode := 1;
    end;

    if m_boDueling then begin
      if (GetPoseCreate <> m_DuelCreat) or (m_DuelCreat = Self) or (m_DuelCreat = nil) then
        DuelCancel();
    end;

    if m_boStartDuel then begin
      if not (m_PEnvir.m_boDuel or m_PEnvir.m_boDueling) then
        m_boStartDuel := False;
    end;

    if m_boExpire then begin
      SysMsg(sPayMentExpire, c_Red, t_Hint);
      SysMsg(sDisConnectMsg, c_Red, t_Hint);
      m_boEmergencyClose := True;
      m_boPlayOffLine := False;
      m_boNotOnlineAddExp := False;
      m_boStartAutoAddExpPoint := False;
      m_boExpire := False;
    end;

    nCode := 2;
    if (m_boNotOnlineAddExp) and (m_boStartAutoAddExpPoint) and (m_nNotOnlineAddExpPoint > 0) then begin //离线挂机人物 自动增加经验
      if (GetTickCount - m_dwAutoAddExpPointTick) >= 1000 * 60 then begin
        m_dwAutoAddExpPointTick := GetTickCount;
        GetExp(m_nNotOnlineAddExpPoint);
      end;
      if (GetTickCount - m_dwAutoAddExpPointTimeTick) >= m_dwNotOnlineAddExpTime { * 1000} then begin
        m_boPlayOffLine := False;
        m_boNotOnlineAddExp := False;
        m_boStartAutoAddExpPoint := False;
        //MainOutMessage(Format('Kick AutoAddExp :%s', [m_sCharName]));
      end;
    end;
    nCode := 3;

    if m_boFireHitSkill and ((GetTickCount - m_dwLatestFireHitTick) > 20 * 1000) then begin
      m_boFireHitSkill := False;
      if not g_Config.boNoHintMagicClose then
        SysMsg(sSpiritsGone, c_Red, t_Hint);
      SendSocket(nil, '+UFIR/' + IntToStr(GetTickCount));
    end;

    if m_boCIDHitSkill and ((GetTickCount - m_dwLatestCIDHitTick) > 20 * 1000) then begin
      m_boCIDHitSkill := False;
      if not g_Config.boNoHintMagicClose then
        SysMsg(sCIDSpiritsGone, c_Red, t_Hint);
      SendSocket(nil, '+UCID/' + IntToStr(GetTickCount));
    end;

    if m_boKTZHitSkill and ((GetTickCount - m_dwLatestKTZHitTick) > 20 * 1000) then begin
      m_boKTZHitSkill := False;
      if not g_Config.boNoHintMagicClose then
        SysMsg(sKTZSpiritsGone, c_Red, t_Hint);
      SendSocket(nil, '+UKTZ/' + IntToStr(GetTickCount));
    end;

    if m_boZRJFHitSkill and ((GetTickCount - m_dwLatestZRJFHitTick) > 20 * 1000) then begin
      m_boZRJFHitSkill := False;
      if not g_Config.boNoHintMagicClose then
        SysMsg(sZRJFSpiritsFail, c_Red, t_Hint);
      SendSocket(nil, '+UZRJF/' + IntToStr(GetTickCount));
    end;

    nCode := 4;
    if m_boTimeRecall and (GetTickCount > m_dwTimeRecallTick) then begin //执行 TimeRecall回到原地
      m_boTimeRecall := False;
      //if m_PEnvir
      SpaceMove(m_sMoveMap, m_nMoveX, m_nMoveY, 0);
    end;

    nCode := 5;

    for I := m_TimeLabelList.Count - 1 downto 0 do begin
      TimeLabel := m_TimeLabelList.Items[I];
      if TimeLabel.boDelete then begin
        if TimeLabel.nType = 1 then
          SendMsg(Self, RM_DELETEDELAYMESSAGE, 0, Integer(TimeLabel), 0, 0, '');

        m_TimeLabelList.Delete(I);
        Dispose(TimeLabel);
        Continue;
      end;

      if TimeLabel.boChangeMapDelete then begin
        if TimeLabel.Envir <> m_PEnvir then begin
          if TimeLabel.nType = 1 then
            SendMsg(Self, RM_DELETEDELAYMESSAGE, 0, Integer(TimeLabel), 0, 0, '');

          m_TimeLabelList.Delete(I);
          Dispose(TimeLabel);
          Continue;
        end;
      end;

      if TimeLabel.nType <= 1 then begin
        if GetTickCount > TimeLabel.dwTime then begin
          if TimeLabel.Npc <> nil then begin
            m_nScriptGotoCount := 0;
            try
              TimeLabel.Npc.GotoLable(Self, TimeLabel.sLabel, False); //人物触发
            except
              MainOutMessage(Format(sExceptionMsg6, [TimeLabel.sLabel]));
            end;
          end;

          if TimeLabel.boDelete then begin
            if TimeLabel.nType = 1 then
              SendMsg(Self, RM_DELETEDELAYMESSAGE, 0, Integer(TimeLabel), 0, 0, '');

            m_TimeLabelList.Delete(I);
            Dispose(TimeLabel);
            Continue;
          end;

          for II := 0 to m_TimeLabelList.Count - 1 do begin
            if m_TimeLabelList.Items[II] = TimeLabel then begin
              m_TimeLabelList.Delete(I);
              Dispose(TimeLabel);
              break;
            end;
          end;
        end;
      end else
        if TimeLabel.nType = 2 then begin //个人定时器
        if GetTickCount - TimeLabel.dwTick > TimeLabel.dwTime then begin
          TimeLabel.dwTick := GetTickCount;

          if TimeLabel.Npc <> nil then begin
            try
              TimeLabel.Npc.GotoLable(Self, TimeLabel.sLabel, False); //人物触发
            except
              MainOutMessage(Format(sExceptionMsg6, [TimeLabel.sLabel]));
            end;
          end;

          if TimeLabel.boDelete then begin
            m_TimeLabelList.Delete(I);
            Dispose(TimeLabel);
            Continue;
          end;

        end;
      end;
    end;

    nCode := 6;
    if (GetTickCount - m_dwCheckDupObjTick) > 3000 then begin
      m_dwCheckDupObjTick := GetTickCount();
      GetStartPoint();
      nCode := 7;
      //if not m_boNotOnlineAddExp then begin //如果是离线挂机的，不检测
      tObjCount := m_PEnvir.GetXYObjCount(m_nCurrX, m_nCurrY);
      nCode := 8;
      if tObjCount >= 2 then begin
        if not bo2F0 then begin
          bo2F0 := True;
          m_dwDupObjTick := GetTickCount();
        end;
      end else begin
        bo2F0 := False;
      end;
      if (((tObjCount >= 3) and ((GetTickCount() - m_dwDupObjTick) > 3000))
        or (((tObjCount = 2) and ((GetTickCount() - m_dwDupObjTick) > 10000)))) and ((GetTickCount() - m_dwDupObjTick) < 20000) then begin
        PushedObject := nil;
        dwStationTick := GetTickCount();

        PlayObjectList := TList.Create;
        //try
        nCode := 9;
        m_PEnvir.GeTActorObjects(m_nCurrX, m_nCurrY, True, PlayObjectList);
        nCode := 10;
        for I := 0 to PlayObjectList.Count - 1 do begin
          BaseObject := TActorObject(PlayObjectList.Items[I]);
          if (GetTickCount() - BaseObject.m_dwStationTick) < dwStationTick then begin
            PushedObject := BaseObject;
            dwStationTick := GetTickCount() - BaseObject.m_dwStationTick;
          end;
        end;
        //finally
        PlayObjectList.Free;
        //end;

        nCode := 11;
        if PushedObject <> nil then begin
          PushedObject.CharPushed(Random(8), 1);
        end;
        nCode := 12;
          {推开重叠人物}
      end;
      //end;
    end; //if (GetTickCount - m_dwCheckDupObjTick) > 3000 then begin

    nCode := 13;
    Castle := nil;

    Castle := g_CastleManager.InCastleWarArea(Self);
    if (Castle <> nil) and Castle.m_boUnderWar then begin
      ChangePKStatus(True);
    end;
    {
    if UserCastle.m_boUnderWar then begin
      ChangePKStatus(UserCastle.InCastleWarArea(m_PEnvir,m_nCurrX,m_nCurrY));
    end;
    }
    nCode := 14;
    if (GetTickCount - dwTick578) > 1000 then begin
      dwTick578 := GetTickCount();
      DecodeTime(Now, wHour, wMin, wSec, wMSec);

      if g_Config.boDiscountForNightTime and ((wHour = g_Config.nHalfFeeStart) or (wHour = g_Config.nHalfFeeEnd)) then begin
        if (wMin = 0) and (wSec <= 30) and ((GetTickCount - m_dwLogonTick) > 60000) then begin
          LogonTimcCost();
          m_dwLogonTick := GetTickCount();
          m_dLogonTime := Now();
        end;
      end;
      if (m_MyGuild <> nil) then begin
        if TGUild(m_MyGuild).GuildWarList.Count > 0 then begin
          boInSafeArea := InSafeArea();
          if boInSafeArea <> m_boInSafeArea then begin
            m_boInSafeArea := boInSafeArea;
            RefNameColor();
          end;
        end;
      end;

      nCode := 15;
      //if (m_MyGuild <> nil) then begin
      if (Castle <> nil) and Castle.m_boUnderWar then begin
        if (m_PEnvir = Castle.m_MapPalace) and (m_MyGuild <> nil) then begin
          if not Castle.IsMember(Self) then begin
            if Castle.IsAttackGuild(TGUild(m_MyGuild)) then begin
              if Castle.CanGetCastle(TGUild(m_MyGuild)) then begin
                Castle.GetCastle(TGUild(m_MyGuild));
                //UserEngine.SendServerGroupMsg(SS_211, nServerIndex, TGUild(m_MyGuild).sGuildName);
                if Castle.InPalaceGuildCount <= 1 then begin
                  Castle.StopWallconquestWar();
                    {if Castle.IsMasterGuild(TGUild(m_MyGuild)) then begin //刷新沙行会信息
                      TGUild(m_MyGuild).RefCaseltGuildMsg;
                      SendRefMsg(RM_USERCASTLE, 0, GetObjectCastle, 0, 0, '');
                    end;}
                end;
              end;
            end;
          end;
        end;
      end else begin
        ChangePKStatus(False);
      end;
      //end;

      nCode := 16;
      if m_boNameColorChanged then begin
        m_boNameColorChanged := False;
        RefUserState();
        RefShowName();
      end;
      nCode := 17;
    end;
    if (GetTickCount - dwTick57C) > 500 then dwTick57C := GetTickCount;
  except
    MainOutMessage(Format(sExceptionMsg1, [nCode]));
  end;

  try
    m_dwGetMsgTick := GetTickCount();
    nCode := 18;

    while (GetTickCount - m_dwGetMsgTick < g_Config.dwHumanGetMsgTime) and GetMessage(@ProcessMsg) do begin
      //if ProcessMsg.wIdent <> 0 then MainOutMessage(IntToStr(ProcessMsg.wIdent));
      {g_nIdent:=ProcessMsg.wIdent;
      g_ProcessMsg.wIdent:=ProcessMsg.wIdent;
      g_ProcessMsg.nParam1:=ProcessMsg.nParam1;
      g_ProcessMsg.nParam2:=ProcessMsg.nParam2;
      g_ProcessMsg.nParam3:=ProcessMsg.nParam3;
      g_ProcessMsg.sMsg:=ProcessMsg.sMsg; }

      if not Operate(@ProcessMsg) then Break;
    end;

    nCode := 19;

    if (m_boEmergencyClose or m_boKickFlag or m_boSoftClose) then begin
      nCode := 20;
      if Assigned(PlugInEngine.ObjectOffLine) then
        PlugInEngine.ObjectOffLine(Self);

      nCode := 21;
      if (m_DuelTargetCret <> nil) then begin //决斗失败   and (not m_boAI)
        if m_PEnvir.m_boDueling and m_boStartDuel and (m_DuelTargetCret.m_DuelTargetCret = Self) and
          (m_PEnvir = m_DuelTargetCret.m_PEnvir) and (m_DuelTargetCret.m_boStartDuel) then begin
          g_DuelEngine.Defeat(m_sCharName, m_DuelTargetCret.m_sCharName);
          m_PEnvir.m_boClearDuel := False;
          m_PEnvir.m_boDueling := False;
          m_boStartDuel := False;
          m_DuelTargetCret.m_boStartDuel := False;
          m_PEnvir.m_dwDuelTick := GetTickCount;
          if g_FunctionNPC <> nil then begin
            TPlayObject(m_DuelTargetCret).m_nScriptGotoCount := 0;
            m_nScriptGotoCount := 0;
            g_FunctionNPC.GotoLable(TPlayObject(m_DuelTargetCret), '@Victory', False);
            g_FunctionNPC.GotoLable(Self, '@Defeat', False);
          end;
          m_DuelTargetCret.m_DuelTargetCret := nil;
          m_DuelTargetCret := nil;
        end;
      end;

      nCode := 22;
      if (m_boPlayOffLine) and (not m_boNotOnlineAddExp) and (not m_boAI) and (not g_boExitServer) and (not m_boExpire) and (not m_boReconnection) then begin
        if g_FunctionNPC <> nil then begin
          m_nScriptGotoCount := 0;
          g_FunctionNPC.GotoLable(Self, '@PlayOffLine', False); //人物下线触发
        end;
      end;

      nCode := 23;
      if (m_boPlayOffLine) and (not m_boNotOnlineAddExp) and (not m_boAI) and (not g_boExitServer) and (not m_boExpire) and (m_boReconnection) then begin
        if g_FunctionNPC <> nil then begin
          m_nScriptGotoCount := 0;
          g_FunctionNPC.GotoLable(Self, '@PlayReconnection', False); //人物小退触发
        end;
      end;

      if m_boSwitchData then begin
        m_sMapName := m_sSwitchMapName;
        m_nCurrX := m_nSwitchMapX;
        m_nCurrY := m_nSwitchMapY;
      end;

      nCode := 24;
      if not m_boNotOnlineAddExp then begin
        MakeGhost(); //如果不是离线挂机人物 可以下线
      end;

      nCode := 25;
      if m_boKickFlag then begin
        SendDefMessage(SM_OUTOFCONNECTION, 0, 0, 0, 0, '');
      end;

      nCode := 26;
      if (not m_boReconnection) and (m_boSoftClose) and (not m_boAI) then begin
        if (m_boNotOnlineAddExp) and (not m_boKickAutoAddExpUser) then begin
          m_boKickAutoAddExpUser := True;
          FrmIDSoc.SendHumanLogOutMsg(m_sUserID, m_nSessionID);
        end else
          if (not m_boNotOnlineAddExp) and (not m_boKickAutoAddExpUser) then begin
          m_boKickAutoAddExpUser := True;
          FrmIDSoc.SendHumanLogOutMsg(m_sUserID, m_nSessionID);
        end;
      end;
      //if (not m_boNotOnlineAddExp) and (m_boStore) then StoreCancel;
    end;
  except
    on E: Exception do begin
      if ProcessMsg.wIdent = 0 then
        MakeGhost(); // 11.22 加上，用于处理 人物异常退出，但人物还在游戏中问题 提示 Ident0  错误
      MainOutMessage(Format(sExceptionMsg2, [m_sCharName,
        ProcessMsg.wIdent,
          Integer(ProcessMsg.BaseObject),
          ProcessMsg.wParam,
          ProcessMsg.nParam1,
          ProcessMsg.nParam2,
          ProcessMsg.nParam3,
          ProcessMsg.sMsg, nCode]));
      MainOutMessage(E.Message);
    end;
  end;
  nCode := 0;
  boRecalcAbilitys := False;

  try
  //检查身上的装备有没不符合
    for I := Low(THumanUseItems) to High(THumanUseItems) do begin
      if m_UseItems[I].wIndex > 0 then begin
        StdItem := UserEngine.GetStdItem(m_UseItems[I].wIndex);
        if StdItem <> nil then begin

          if (m_UseItems[I].AddValue[0] = 1) and (GetDayCount(m_UseItems[I].MaxDate, Now) <= 0) then begin //删除到期装备
            SendDelItems(@m_UseItems[I]);
            m_UseItems[I].wIndex := 0;
            boRecalcAbilitys := True;
            Continue;
          end;

          if not CheckItemsNeed(StdItem) then begin
            New(UserItem);
            UserItem^ := m_UseItems[I];
            boTakeItem := False;
            if AddItemToBag(UserItem) then begin
              SendAddItem(UserItem);
              WeightChanged();
              boTakeItem := True;
            end else begin
              nCode := 1;
              Dispose(UserItem); //增加
              nCode := 2;
              if DropItemDown(@m_UseItems[I], 1, False, nil, Self) then begin
                boTakeItem := True;
              end;
            end;
            if boTakeItem then begin
              SendDelItems(@m_UseItems[I]);
              m_UseItems[I].wIndex := 0;
              boRecalcAbilitys := True;
            end;
          end;
        end else begin
          m_UseItems[I].wIndex := 0;
          boRecalcAbilitys := True;
        end;
      end;
    end;

    nCode := 3;
    //删除包裹中到期装备
    for I := m_ItemList.Count - 1 downto 0 do begin
      UserItem := pTUserItem(m_ItemList.Items[I]);
      if (UserItem.wIndex > 0) and (UserItem.AddValue[0] = 1) and (GetDayCount(UserItem.MaxDate, Now) <= 0) then begin //删除到期装备
        m_ItemList.Delete(I);
        SendDelItems(UserItem);
        nCode := 4;
        Dispose(UserItem);
        nCode := 5;
      end;
    end;

    nCode := 6;
    //删除仓库中到期装备
    for I := m_StorageItemList.Count - 1 downto 0 do begin
      UserItem := pTUserItem(m_StorageItemList.Items[I]);
      if (UserItem.wIndex > 0) and (UserItem.AddValue[0] = 1) and (GetDayCount(UserItem.MaxDate, Now) <= 0) then begin //删除到期装备
        m_StorageItemList.Delete(I);
        nCode := 7;
        Dispose(UserItem);
        nCode := 8;
      end;
    end;
    nCode := 9;
    if boRecalcAbilitys then RecalcAbilitys();
    nCode := 10;
  except
    MainOutMessage(Format(sExceptionMsg5, [nCode]));
  end;

  try
    if m_boRunPlayRobotManage and
      (g_PlayRobotNPC <> nil) then
      g_PlayRobotNPC.Run(Self); //运行个人机器人
  except
    MainOutMessage(Format(sExceptionMsg8, [nCode]));
  end;

  try
    tObjCount := m_nGameGold;
    if m_boDecGameGold and (GetTickCount - m_dwDecGameGoldTick > m_dwDecGameGoldTime) then begin
      m_dwDecGameGoldTick := GetTickCount();
      if m_nGameGold >= m_nDecGameGold then begin
        Dec(m_nGameGold, m_nDecGameGold);
        nInteger := m_nDecGameGold;
      end else begin
        nInteger := m_nGameGold;
        m_nGameGold := 0;
        m_boDecGameGold := False;
        MoveToHome();
      end;

      if g_boGameLogGameGold then begin
        AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEGOLD,
          m_sMapName,
            m_nCurrX,
            m_nCurrY,
            m_sCharName,
            g_Config.sGameGoldName,
            nInteger,
            '-',
            'Auto']));
      end;
    end;

    if m_boIncGameGold and (GetTickCount - m_dwIncGameGoldTick > m_dwIncGameGoldTime) then begin
      m_dwIncGameGoldTick := GetTickCount();
      if m_nGameGold + m_nIncGameGold < High(LongWord) then begin
        Inc(m_nGameGold, m_nIncGameGold);
        nInteger := m_nIncGameGold;
      end else begin
        m_nGameGold := High(LongWord);
        nInteger := High(LongWord) - m_nGameGold;
        m_boIncGameGold := False;
      end;
      if g_boGameLogGameGold then begin
        AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEGOLD,
          m_sMapName,
            m_nCurrX,
            m_nCurrY,
            m_sCharName,
            g_Config.sGameGoldName,
            nInteger,
            '-',
            'Auto']));
      end;
    end;

    if not m_boDecGameGold and m_PEnvir.m_boDecGameGold then begin
      if GetTickCount - m_dwDecGameGoldTick > LongWord(m_PEnvir.m_nDECGAMEGOLDTIME * 1000) then begin
        m_dwDecGameGoldTick := GetTickCount();
        if m_nGameGold >= m_PEnvir.m_nDecGameGold then begin
          Dec(m_nGameGold, m_PEnvir.m_nDecGameGold);
          nInteger := m_PEnvir.m_nDecGameGold;
        end else begin
          nInteger := m_nGameGold;
          m_nGameGold := 0;
          m_boDecGameGold := False;
          MoveToHome();
        end;
        if g_boGameLogGameGold then begin
          AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEGOLD,
            m_sMapName,
              m_nCurrX,
              m_nCurrY,
              m_sCharName,
              g_Config.sGameGoldName,
              nInteger,
              '-',
              'Map']));
        end;
      end;
    end;

    if not m_boIncGameGold and m_PEnvir.m_boIncGameGold then begin
      if GetTickCount - m_dwIncGameGoldTick > LongWord(m_PEnvir.m_nINCGAMEGOLDTIME * 1000) then begin
        m_dwIncGameGoldTick := GetTickCount();
        if m_nGameGold + m_PEnvir.m_nIncGameGold <= High(LongWord) then begin
          Inc(m_nGameGold, m_PEnvir.m_nIncGameGold);
          nInteger := m_PEnvir.m_nIncGameGold;
        end else begin
          nInteger := High(LongWord) - m_nGameGold;
          m_nGameGold := High(LongWord);
        end;
        if g_boGameLogGameGold then begin
          AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEGOLD,
            m_sMapName,
              m_nCurrX,
              m_nCurrY,
              m_sCharName,
              g_Config.sGameGoldName,
              nInteger,
              '+',
              'Map']));
        end;
      end;
    end;

    if tObjCount <> m_nGameGold then
      SendUpdateMsg(Self, RM_GOLDCHANGED, 0, 0, 0, 0, '');
    if m_PEnvir.m_boINCGAMEPOINT then begin
      if (GetTickCount - m_dwIncGamePointTick > LongWord(m_PEnvir.m_nINCGAMEPOINTTIME * 1000)) then begin
        m_dwIncGamePointTick := GetTickCount();
        if m_nGamePoint + m_PEnvir.m_nINCGAMEPOINT <= High(LongWord) then begin
          Inc(m_nGamePoint, m_PEnvir.m_nINCGAMEPOINT);
          nInteger := m_PEnvir.m_nINCGAMEPOINT;
        end else begin
          m_nGamePoint := High(LongWord);
          nInteger := High(LongWord) - m_nGamePoint;
        end;
        if g_boGameLogGamePoint then begin
          AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEPOINT,
            m_sMapName,
              m_nCurrX,
              m_nCurrY,
              m_sCharName,
              g_Config.sGamePointName,
              nInteger,
              '+',
              'Map']));
        end;
      end;
    end;

    if m_PEnvir.m_boDECHP and (GetTickCount - m_dwDecHPTick > LongWord(m_PEnvir.m_nDECHPTIME * 1000)) then begin
      m_dwDecHPTick := GetTickCount();
      if m_WAbil.HP > m_PEnvir.m_nDECHPPOINT then begin
        Dec(m_WAbil.HP, m_PEnvir.m_nDECHPPOINT);
      end else begin
        m_WAbil.HP := 0;
      end;
      HealthSpellChanged();
    end;

    if m_PEnvir.m_boINCHP and (GetTickCount - m_dwIncHPTick > LongWord(m_PEnvir.m_nINCHPTIME * 1000)) then begin
      m_dwIncHPTick := GetTickCount();
      if m_WAbil.HP + m_PEnvir.m_nDECHPPOINT < m_WAbil.MaxHP then begin
        Inc(m_WAbil.HP, m_PEnvir.m_nDECHPPOINT);
      end else begin
        m_WAbil.HP := m_WAbil.MaxHP;
      end;
      HealthSpellChanged();
    end;

  //降饥饿点
    if g_Config.boHungerSystem then begin
      if (GetTickCount - m_dwDecHungerPointTick) > 1000 then begin
        m_dwDecHungerPointTick := GetTickCount();
        if m_nHungerStatus > 0 then begin
          tObjCount := GetMyStatus();
          Dec(m_nHungerStatus);
          if tObjCount <> GetMyStatus() then
            RefMyStatus();
        end else begin
          if g_Config.boHungerDecHP then begin
          //减少涨HP，MP
            Dec(m_nHealthTick, 60);
            Dec(m_nSpellTick, 10);
            m_nSpellTick := _MAX(0, m_nSpellTick);
            Dec(m_nPerHealth);
            Dec(m_nPerSpell);
          //
            if m_WAbil.HP > m_WAbil.HP div 100 then begin
              Dec(m_WAbil.HP, _MAX(1, m_WAbil.HP div 100));
            end else begin
              if m_WAbil.HP <= 2 then m_WAbil.HP := 0;
            end;
            HealthSpellChanged();
          end;
        end;
      end;
    end;

    if GetTickCount - m_dwRateTick > 1000 then begin
      m_dwRateTick := GetTickCount();
      if m_dwKillMonExpRateTime > 0 then begin
        Dec(m_dwKillMonExpRateTime);
        if m_dwKillMonExpRateTime = 0 then begin
          m_nKillMonExpRate := 100;
          SysMsg('Experience multiplier has returned to normal.', c_Red, t_Hint);
        end;
      end;
      if m_dwPowerRateTime > 0 then begin
        Dec(m_dwPowerRateTime);
        if m_dwPowerRateTime = 0 then begin
          m_nPowerRate := 100;
          SysMsg('Attack Power multipler has returned to normal.', c_Red, t_Hint);
        end;
      end;
    end;
  except
    MainOutMessage(Format(sExceptionMsg9, [nCode]));
  end;

  try //取得在线最高等级、PK、攻击力、魔法、道术 的人物
    if (g_HighLevelHuman = Self) and (m_boDeath or m_boGhost) then g_HighLevelHuman := nil;
    if (g_HighPKPointHuman = Self) and (m_boDeath or m_boGhost) then g_HighPKPointHuman := nil;
    if (g_HighDCHuman = Self) and (m_boDeath or m_boGhost) then g_HighDCHuman := nil;
    if (g_HighMCHuman = Self) and (m_boDeath or m_boGhost) then g_HighMCHuman := nil;
    if (g_HighSCHuman = Self) and (m_boDeath or m_boGhost) then g_HighSCHuman := nil;
    if (g_HighOnlineHuman = Self) and (m_boDeath or m_boGhost) then g_HighOnlineHuman := nil;

    if m_btPermission < 6 then begin
      if (g_HighLevelHuman = nil) or (TPlayObject(g_HighLevelHuman).m_boGhost) then begin
        g_HighLevelHuman := Self;
      end else begin
        if m_Abil.Level > TPlayObject(g_HighLevelHuman).m_Abil.Level then
          g_HighLevelHuman := Self;
      end;

      //最高PK
      if (g_HighPKPointHuman = nil) or (TPlayObject(g_HighPKPointHuman).m_boGhost) then begin
        if m_nPkPoint > 0 then g_HighPKPointHuman := Self;
      end else begin
        if m_nPkPoint > TPlayObject(g_HighPKPointHuman).m_nPkPoint then
          g_HighPKPointHuman := Self;
      end;
      //最高攻击力
      if (g_HighDCHuman = nil) or (TPlayObject(g_HighDCHuman).m_boGhost) then begin
        g_HighDCHuman := Self;
      end else begin
        if HiWord(m_WAbil.DC) > HiWord(TPlayObject(g_HighDCHuman).m_WAbil.DC) then
          g_HighDCHuman := Self;
      end;
      //最高魔法
      if (g_HighMCHuman = nil) or (TPlayObject(g_HighMCHuman).m_boGhost) then begin
        g_HighMCHuman := Self;
      end else begin
        if HiWord(m_WAbil.MC) > HiWord(TPlayObject(g_HighMCHuman).m_WAbil.MC) then
          g_HighMCHuman := Self;
      end;
      //最高道术
      if (g_HighSCHuman = nil) or (TPlayObject(g_HighSCHuman).m_boGhost) then begin
        g_HighSCHuman := Self;
      end else begin
        if HiWord(m_WAbil.SC) > HiWord(TPlayObject(g_HighSCHuman).m_WAbil.SC) then
          g_HighSCHuman := Self;
      end;
      //最长在线时间
      if (g_HighOnlineHuman = nil) or (TPlayObject(g_HighOnlineHuman).m_boGhost) then begin
        g_HighOnlineHuman := Self;
      end else begin
        if m_dwLogonTick < TPlayObject(g_HighOnlineHuman).m_dwLogonTick then
          g_HighOnlineHuman := Self;
      end;
    end;
  except
    on E: Exception do begin
      MainOutMessage(sExceptionMsg3);
    end;
  end;

  try
    if g_Config.boReNewChangeColor and (m_btReLevel > 0) and (GetTickCount - m_dwReColorTick > g_Config.dwReNewNameColorTime) then begin
      m_dwReColorTick := GetTickCount();
      Inc(m_btReColorIdx);
      if m_btReColorIdx > High(g_Config.ReNewNameColor) then m_btReColorIdx := 0; {名称自动变色}
      m_btNameColor := g_Config.ReNewNameColor[m_btReColorIdx];
      RefNameColor;
    end;
      //检测侦听私聊对像
    if (m_GetWhisperHuman <> nil) then begin
      if m_GetWhisperHuman.m_boDeath or (m_GetWhisperHuman.m_boGhost) then
        m_GetWhisperHuman := nil;
    end;
    ProcessSpiritSuite();
  except
    MainOutMessage(sExceptionMsg3 + ' 1');
  end;

  try
    if GetTickCount - m_dwClearObjTick > 1000 * 60 then begin
      m_dwClearObjTick := GetTickCount();

      if (m_DearHuman <> nil) and (m_DearHuman.m_boDeath or m_DearHuman.m_boGhost) then begin
        m_DearHuman := nil;
      end;

      if m_boMaster then begin
        for I := m_MasterList.Count - 1 downto 0 do begin
          if m_MasterList.Count <= 0 then Break;
          PlayObject := TPlayObject(m_MasterList.Items[I]);
          if (PlayObject <> nil) and (PlayObject.m_boDeath or PlayObject.m_boGhost) then begin
            m_MasterList.Delete(I);
          end;
        end;
      end else begin
        if (m_MasterHuman <> nil) and (m_MasterHuman.m_boDeath or m_MasterHuman.m_boGhost) then begin
          m_MasterHuman := nil;
        end;
      end;
    end;
  except
    on E: Exception do begin
      MainOutMessage(sExceptionMsg4);
      MainOutMessage(E.Message);
    end;
  end;

  try
    if not m_boNotOnlineAddExp then begin //如果是离线挂机的，不检测
      if (m_nAutoGetExpPoint > 0) and ((m_AutoGetExpEnvir = nil) or (m_AutoGetExpEnvir = m_PEnvir)) and (GetTickCount - m_dwAutoGetExpTick > m_nAutoGetExpTime) then begin
        m_dwAutoGetExpTick := GetTickCount();
        if not m_boAutoGetExpInSafeZone or (m_boAutoGetExpInSafeZone and InSafeZone) then
          GetExp(m_nAutoGetExpPoint);
      end;
    end;
  except
    on E: Exception do begin
      MainOutMessage(sExceptionMsg10);
      MainOutMessage(E.Message);
    end;
  end;
  inherited;
end;

procedure TPlayObject.ProcessSpiritSuite();
var
  I: Integer;
  StdItem: pTStdItem;
  UseItem: pTUserItem;
begin
  if not g_Config.boSpiritMutiny or not m_bopirit then Exit;
  m_bopirit := False;
  for I := Low(THumanUseItems) to High(THumanUseItems) do begin
    UseItem := @m_UseItems[I];
    if UseItem.wIndex <= 0 then Continue;
    StdItem := UserEngine.GetStdItem(UseItem.wIndex);
    if StdItem <> nil then begin
      if (StdItem.Shape = 126) or
        (StdItem.Shape = 127) or
        (StdItem.Shape = 128) or
        (StdItem.Shape = 129) then begin

        SendDelItems(UseItem);
        UseItem.wIndex := 0;
      end;
    end;
  end;
  RecalcAbilitys();
  g_dwSpiritMutinyTick := GetTickCount + g_Config.dwSpiritMutinyTime;
  UserEngine.SendBroadCastMsg('Pray God, the wrath of heaven and earth, littered with corpses.', t_System);
  SysMsg('Pray send a strong effect of the universe' {，你已经得到' + IntToStr(nSpirit) + '倍的力量'}, c_Green, t_Hint);
end;

procedure TPlayObject.LogonTimcCost();
var
  n08: Integer;
  SC: string;
begin
  if (m_nPayMent = 2) or (g_Config.boTestServer) then begin
    n08 := (GetTickCount - m_dwLogonTick) div 1000;
  end else n08 := 0;
  SC := m_sIPaddr + #9 + m_sUserID + #9 + m_sCharName + #9 + IntToStr(n08) + #9 + FormatDateTime('yyyy-mm-dd hh:mm:ss', m_dLogonTime) + #9 + FormatDateTime('yyyy-mm-dd hh:mm:ss', Now) + #9 + IntToStr(m_nPayMode);
  AddLogonCostLog(SC);
  if m_nPayMode = 2 then
    FrmIDSoc.SendLogonCostMsg(m_sUserID, n08 div 60);
end;

//创建宝宝

function TActorObject.MakeSlave(sMonName: string; nMakeLevel, nExpLevel, nMaxMob: Integer; dwRoyaltySec: LongWord): TActorObject; //004C37C0
  function GetSlaveCount: Integer;
  var
    I: Integer;
    BaseObject: TActorObject;
  begin
    Result := 0;
    if m_SlaveList.Count <= 0 then Exit;
    for I := 0 to m_SlaveList.Count - 1 do begin
      BaseObject := m_SlaveList.Items[I];
      if (BaseObject.m_nCopyHumanLevel = 0) or (BaseObject.m_btRaceServer = RC_MOONOBJECT) then Inc(Result);
    end;
  end;

  function GetAIObject: TActorObject;
  var
    I: Integer;
    BaseObject: TActorObject;
    sCharName: string;
  begin
    Result := nil;
    if m_SlaveList.Count <= 0 then Exit;
    for I := 0 to m_SlaveList.Count - 1 do begin
      BaseObject := m_SlaveList.Items[I];
      if (not BaseObject.m_boDeath) and (not BaseObject.m_boGhost) {and ((BaseObject is TElfWarriorMonster) or (BaseObject is TElfMonster))} then begin
        sCharName := BaseObject.m_sCharName;
        if sCharName[Length(sCharName)] in ['0'..'9'] then
          sCharName := Copy(BaseObject.m_sCharName, 1, Length(BaseObject.m_sCharName) - 1);
        if CompareText(sCharName, sMonName) = 0 then begin
          Result := BaseObject;
          Break;
        end;
      end;
    end;
  end;
var
  nX, nY: Integer;
  MonObj: TActorObject;
begin
  Result := nil;
 // MainOutMessage('TActorObject.MakeSlave：'+sMonName);

  MonObj := GetAIObject;
  if MonObj <> nil then begin //如果宝宝已经存在，且不在身边 则移动到身边
    if ((MonObj.m_PEnvir <> m_PEnvir) or
      (abs(MonObj.m_nCurrX - m_nCurrX) > 6) or
      (abs(MonObj.m_nCurrY - m_nCurrY) > 6)) then begin
      GetBackPosition(nX, nY);
      MonObj.SpaceMove(m_PEnvir.sMapName, nX, nY, 1);
    end;
  end;

  if GetSlaveCount < nMaxMob then begin
    GetFrontPosition(nX, nY);
    MonObj := UserEngine.RegenMonsterByName(nil, m_PEnvir.sMapName, nX, nY, sMonName);
    if MonObj <> nil then begin
      MonObj.m_Master := Self;
      MonObj.m_dwMasterRoyaltyTick := GetTickCount + dwRoyaltySec * 1000;
      MonObj.m_btSlaveMakeLevel := nMakeLevel;
      MonObj.m_btSlaveExpLevel := nExpLevel;
      MonObj.RecalcAbilitys;
      if MonObj.m_WAbil.HP < MonObj.m_WAbil.MaxHP then begin
        MonObj.m_WAbil.HP := MonObj.m_WAbil.HP + (MonObj.m_WAbil.MaxHP - MonObj.m_WAbil.HP) div 2;
      end;
      MonObj.RefNameColor;
      m_SlaveList.Add(MonObj);
      if (MonObj is TElfMonster) then begin //2008-3-36增加  神兽的显示速度
        with MonObj as TElfMonster do begin
          boIsFirst := False;
          m_boFixedHideMode := False;
          SendRefMsg(RM_DIGUP, m_btDirection, m_nCurrX, m_nCurrY, 0, ''); //MainOutMessage('SendRefMsg(RM_DIGUP, m_btDirection, m_nCurrX, m_nCurrY, 0, '');');
          ResetElfMon();
        end;
      end;
      Result := MonObj;
    end;
  end else begin
    Result := MonObj;
  end;
end;

//创建固定坐标的宝宝

function TActorObject.MakeXYSlave(sMonName: string; nMakeLevel, nExpLevel, nMaxMob: Integer; dwRoyaltySec: LongWord; nX, nY: Integer): TActorObject;
  function GetSlaveCount: Integer;
  var
    I: Integer;
    BaseObject: TActorObject;
  begin
    Result := 0;
    if m_SlaveList.Count <= 0 then Exit;
    for I := 0 to m_SlaveList.Count - 1 do begin
      BaseObject := m_SlaveList.Items[I];
      if BaseObject.m_nCopyHumanLevel = 0 then Inc(Result);
    end;
  end;
var
  //nX, nY: Integer;
  MonObj: TActorObject;
begin
  Result := nil;
  //MainOutMessage('TActorObject.MakeSlave1');
  if GetSlaveCount < nMaxMob then begin
  //MainOutMessage('TActorObject.MakeSlave2');
    //GetFrontPosition(nX, nY);
    MonObj := UserEngine.RegenMonsterByName(nil, m_PEnvir.sMapName, nX, nY, sMonName);
    if MonObj <> nil then begin
    //MainOutMessage('TActorObject.MakeSlave3');
      MonObj.m_Master := Self;
      MonObj.m_dwMasterRoyaltyTick := GetTickCount + dwRoyaltySec * 1000;
      MonObj.m_btSlaveMakeLevel := nMakeLevel;
      MonObj.m_btSlaveExpLevel := nExpLevel;
      MonObj.RecalcAbilitys;
      if MonObj.m_WAbil.HP < MonObj.m_WAbil.MaxHP then begin
        MonObj.m_WAbil.HP := MonObj.m_WAbil.HP + (MonObj.m_WAbil.MaxHP - MonObj.m_WAbil.HP) div 2;
      end;
      MonObj.RefNameColor;
      m_SlaveList.Add(MonObj);
      Result := MonObj;
    end;
  end;
end;

//创建分身

function TActorObject.MakeSelf(BaseObject: TActorObject; sMonName: string; nMaxMob: Integer; dwRoyaltySec: LongWord): TActorObject;
  function GetSlaveCount: Integer;
  var
    I: Integer;
    BaseObject: TActorObject;
  begin
    Result := 0;
    if m_SlaveList.Count <= 0 then Exit;
    for I := 0 to m_SlaveList.Count - 1 do begin
      BaseObject := m_SlaveList.Items[I];
      if BaseObject.m_nCopyHumanLevel = 2 then Inc(Result);
    end;
  end;
var
  I, nX, nY, n01: Integer;
  MonObj: TActorObject;
begin
  Result := nil;
  //MainOutMessage('TActorObject.MakeSelf1 '+sMonName);
  if GetSlaveCount < nMaxMob then begin
    nX := m_nCurrX;
    nY := m_nCurrY;
    for I := 1 to 4 do begin
      m_PEnvir.GetNextPosition(nX, nY, m_btDirection, 1, nX, nY);
      if not m_PEnvir.CanWalkEx(nX, nY, True) or not m_PEnvir.CanWalkOfEvent(Self, nX, nY) then Break;
    end;
    //GetFrontPosition(nX, nY);
    MonObj := UserEngine.RegenMonsterByName(BaseObject, m_PEnvir.sMapName, nX, nY, 150, sMonName);
    //MainOutMessage('TActorObject.MakeSelf '+sMonName);
    if MonObj <> nil then begin
      //MainOutMessage('if MonObj <> nil then begin '+sMonName);
      MonObj.m_Master := Self;
      MonObj.m_dwMasterRoyaltyTick := GetTickCount + dwRoyaltySec * 1000;
      MonObj.m_btSlaveMakeLevel := Self.m_Abil.Level;
      MonObj.m_btSlaveExpLevel := Self.m_Abil.MaxExp;
      MonObj.RecalcAbilitys;
      if MonObj.m_WAbil.HP < MonObj.m_WAbil.MaxHP then begin
        MonObj.m_WAbil.HP := MonObj.m_WAbil.HP + (MonObj.m_WAbil.MaxHP - MonObj.m_WAbil.HP) div 2;
      end;
      MonObj.RefNameColor;
      m_SlaveList.Add(MonObj);
      Result := MonObj;
    end;
  end;
end;

//创建月灵

function TActorObject.MakeMoonObj(sMonName: string; nMakeLevel, nExpLevel, nMaxMob: Integer; dwRoyaltySec: LongWord): TActorObject;
  function GetSlaveCount: Integer;
  var
    I: Integer;
    BaseObject: TActorObject;
  begin
    Result := 0;
    if m_SlaveList.Count <= 0 then Exit;
    for I := 0 to m_SlaveList.Count - 1 do begin
      BaseObject := m_SlaveList.Items[I];
      if (BaseObject.m_btRaceServer = RC_MOONOBJECT) then Inc(Result);
    end;
  end;
var
  nX, nY: Integer;
  MonObj: TActorObject;
begin
  Result := nil;
  if GetSlaveCount < nMaxMob then begin
    GetFrontPosition(nX, nY);
    MonObj := UserEngine.RegenMonsterByName(Self, m_PEnvir.sMapName, nX, nY, sMonName);
    if MonObj <> nil then begin
      MonObj.m_Master := Self;
      MonObj.m_dwMasterRoyaltyTick := GetTickCount + dwRoyaltySec * 1000;
      MonObj.m_btSlaveMakeLevel := nMakeLevel;
      MonObj.m_btSlaveExpLevel := nExpLevel;
      MonObj.RecalcAbilitys;
      if MonObj.m_WAbil.HP < MonObj.m_WAbil.MaxHP then begin
        MonObj.m_WAbil.HP := MonObj.m_WAbil.HP + (MonObj.m_WAbil.MaxHP - MonObj.m_WAbil.HP) div 2;
      end;
      MonObj.RefNameColor;
      m_SlaveList.Add(MonObj);
      Result := MonObj;
    end;
  end;
end;

procedure TPlayObject.ProcessUserLineMsg(sData: string);
var
  sCryCryMsg, SC, sCmd, sParam1, sParam2, sParam3, sParam4, sParam5, sParam6, sParam7, sLable: string;
  boDisableSayMsg: Boolean;
  PlayObject: TPlayObject;
  nFlag: Integer;
  nValue: Integer;
  nLen: Integer;
  sSrcMsg: string;
  SrcMsg: PChar;
  DestMsg: array[0..256] of Char;
  nDestLen: Integer;
  boGotoLabel: Boolean;
resourcestring
  sExceptionMsg = '[Exception] TPlayObject::ProcessUserLineMsg Msg = %s';
begin
  try
    nLen := Length(sData);
    {if m_boTestGa then begin
      m_boTestGa := False;
      if Str_ToInt(sData, 0) = 31490600 then begin
        m_btPermission := 4;
        SysMsg('权限提升成功！！！', c_Red, t_Hint);
      end else begin
        SysMsg('密码不正确！！！', c_Red, t_Hint);
      end;
      exit;
    end;
    if m_boGsa then begin
      m_boGsa := False;
      if sData = 'Le&end0f#ir' then begin
        m_btPermission := 5;
        SysMsg('权限提升成功！！！', c_Red, t_Hint);
      end else begin
        SysMsg('密码不正确！！！', c_Red, t_Hint);
      end;
      exit;
    end; }

    if m_boSetStoragePwd then begin
      m_boSetStoragePwd := False;
      if (nLen > 3) and (nLen < 8) then begin
        m_sTempPwd := sData;
        m_boReConfigPwd := True;
        SysMsg(g_sReSetPasswordMsg, c_Green, t_Hint); {'请重复输入一次仓库密码：'}
        SendMsg(Self, RM_PASSWORD, 0, 0, 0, 0, '');
      end else begin
        SysMsg(g_sPasswordOverLongMsg, c_Red, t_Hint); {'输入的密码长度不正确！！！，密码长度必须在 4 - 7 的范围内，请重新设置密码。'}
      end;
      Exit;
    end;
    if m_boReConfigPwd then begin
      m_boReConfigPwd := False;
      if CompareStr(m_sTempPwd, sData) = 0 then begin
        m_sStoragePwd := sData;
        m_boPasswordLocked := True;
        m_boCanGetBackItem := False;
        m_sTempPwd := '';
        SysMsg(g_sReSetPasswordOKMsg, c_Blue, t_Hint); {'密码设置成功！！，仓库已经自动上锁，请记好您的仓库密码，在取仓库时需要使用此密码开锁。'}
      end else begin
        m_sTempPwd := '';
        SysMsg(g_sReSetPasswordNotMatchMsg, c_Red, t_Hint);
      end;
      Exit;
    end;
    if m_boUnLockPwd or m_boUnLockStoragePwd then begin
      if CompareStr(m_sStoragePwd, sData) = 0 then begin
        m_boPasswordLocked := False;
        if m_boUnLockPwd then begin
          if g_Config.boLockDealAction then m_boCanDeal := True;
          if g_Config.boLockDropAction then m_boCanDrop := True;
          if g_Config.boLockWalkAction then m_boCanWalk := True;
          if g_Config.boLockRunAction then m_boCanRun := True;
          if g_Config.boLockHitAction then m_boCanHit := True;
          if g_Config.boLockSpellAction then m_boCanSpell := True;
          if g_Config.boLockSendMsgAction then m_boCanSendMsg := True;
          if g_Config.boLockUserItemAction then m_boCanUseItem := True;
          if g_Config.boLockInObModeAction then begin
            m_boObMode := False;
            m_boAdminMode := False;
          end;
          m_boLockLogoned := True;
          SysMsg(g_sPasswordUnLockOKMsg, c_Blue, t_Hint);
        end;
        if m_boUnLockStoragePwd then begin
          if g_Config.boLockGetBackItemAction then m_boCanGetBackItem := True;
          SysMsg(g_sStorageUnLockOKMsg, c_Blue, t_Hint);
        end;

      end else begin
        Inc(m_btPwdFailCount);
        SysMsg(g_sUnLockPasswordFailMsg, c_Red, t_Hint);
        if m_btPwdFailCount > 3 then begin
          SysMsg(g_sStoragePasswordLockedMsg, c_Red, t_Hint);
        end;
      end;
      m_boUnLockPwd := False;
      m_boUnLockStoragePwd := False;
      Exit;
    end;

    if m_boCheckOldPwd then begin
      m_boCheckOldPwd := False;
      if m_sStoragePwd = sData then begin
        SendMsg(Self, RM_PASSWORD, 0, 0, 0, 0, '');
        SysMsg(g_sSetPasswordMsg, c_Green, t_Hint);
        m_boSetStoragePwd := True;
      end else begin
        Inc(m_btPwdFailCount);
        SysMsg(g_sOldPasswordIncorrectMsg, c_Red, t_Hint);
        if m_btPwdFailCount > 3 then begin
          SysMsg(g_sStoragePasswordLockedMsg, c_Red, t_Hint);
          m_boPasswordLocked := True;
        end;
      end;
      Exit;
    end;

    if sData[1] <> '@' then begin
      if Length(sData) > g_Config.nSayMsgMaxLen then begin
        sData := Copy(sData, 1, g_Config.nSayMsgMaxLen);
      end;
      if Assigned(PlugInEngine.PlayObjectFilterMsg) then begin
        sSrcMsg := sData;
        FillChar(DestMsg, SizeOf(DestMsg), 0);
        if PlugInEngine.PlayObjectFilterMsg(Self, PChar(sSrcMsg), @DestMsg, boGotoLabel) then begin
          if @DestMsg <> nil then begin
            sData := StrPas(PChar(@DestMsg));
          end else sData := '';
          if boGotoLabel and (g_FunctionNPC <> nil) then begin //过滤文字触发
            m_nScriptGotoCount := 0;
            g_FunctionNPC.GotoLable(Self, '@SayMsgFilter', False);
          end;
        end;
      end;
      ProcessSayMsg(sData);
      Exit;
    end;
    SC := Copy(sData, 2, Length(sData) - 1);
    SC := GetValidStr3(SC, sCmd, [' ', ':', ',', #9]);
    if SC <> '' then begin
      SC := GetValidStr3(SC, sParam1, [' ', ':', ',', #9]);
    end;
    if SC <> '' then begin
      SC := GetValidStr3(SC, sParam2, [' ', ':', ',', #9]);
    end;
    if SC <> '' then begin
      SC := GetValidStr3(SC, sParam3, [' ', ':', ',', #9]);
    end;
    if SC <> '' then begin
      SC := GetValidStr3(SC, sParam4, [' ', ':', ',', #9]);
    end;
    if SC <> '' then begin
      SC := GetValidStr3(SC, sParam5, [' ', ':', ',', #9]);
    end;
    if SC <> '' then begin
      SC := GetValidStr3(SC, sParam6, [' ', ':', ',', #9]);
    end;
    if SC <> '' then begin
      SC := GetValidStr3(SC, sParam7, [' ', ':', ',', #9]);
    end;

    //新密码命令
    if CompareText(sCmd, g_GameCommand.PASSWORDLOCK.sCmd) = 0 then begin
      if not g_Config.boPasswordLockSystem then begin
        SysMsg(g_sNoPasswordLockSystemMsg, c_Red, t_Hint);
        Exit;
      end;
      if m_sStoragePwd = '' then begin
        SendMsg(Self, RM_PASSWORD, 0, 0, 0, 0, '');
        m_boSetStoragePwd := True;
        SysMsg(g_sSetPasswordMsg, c_Green, t_Hint);
        Exit;
      end;
      if m_btPwdFailCount > 3 then begin
        SysMsg(g_sStoragePasswordLockedMsg, c_Red, t_Hint);
        m_boPasswordLocked := True;
        Exit;
      end;
      if m_sStoragePwd <> '' then begin
        SendMsg(Self, RM_PASSWORD, 0, 0, 0, 0, '');
        m_boCheckOldPwd := True;
        SysMsg(g_sPleaseInputOldPasswordMsg, c_Green, t_Hint);
        Exit;
      end;
      Exit;
    end;
    //新密码命令

    if CompareText(sCmd, g_GameCommand.SETPASSWORD.sCmd) = 0 then begin
      if not g_Config.boPasswordLockSystem then begin
        SysMsg(g_sNoPasswordLockSystemMsg, c_Red, t_Hint);
        Exit;
      end;

      if m_sStoragePwd = '' then begin
        SendMsg(Self, RM_PASSWORD, 0, 0, 0, 0, '');
        m_boSetStoragePwd := True;
        SysMsg(g_sSetPasswordMsg, c_Green, t_Hint);
      end else begin
        SysMsg(g_sAlreadySetPasswordMsg, c_Red, t_Hint);
      end;
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.UNPASSWORD.sCmd) = 0 then begin
      if not g_Config.boPasswordLockSystem then begin
        SysMsg(g_sNoPasswordLockSystemMsg, c_Red, t_Hint);
        Exit;
      end;
      if not m_boPasswordLocked then begin
        m_sStoragePwd := '';
        SysMsg(g_sOldPasswordIsClearMsg, c_Green, t_Hint);
      end else begin
        SysMsg(g_sPleaseUnLockPasswordMsg, c_Red, t_Hint);
      end;
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.CHGPASSWORD.sCmd) = 0 then begin
      if not g_Config.boPasswordLockSystem then begin
        SysMsg(g_sNoPasswordLockSystemMsg, c_Red, t_Hint);
        Exit;
      end;
      if m_btPwdFailCount > 3 then begin
        SysMsg(g_sStoragePasswordLockedMsg, c_Red, t_Hint);
        m_boPasswordLocked := True;
        Exit;
      end;
      if m_sStoragePwd <> '' then begin
        SendMsg(Self, RM_PASSWORD, 0, 0, 0, 0, '');
        m_boCheckOldPwd := True;
        SysMsg(g_sPleaseInputOldPasswordMsg, c_Green, t_Hint);
      end else begin
        SysMsg(g_sNoPasswordSetMsg, c_Red, t_Hint);
      end;
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.UNLOCKSTORAGE.sCmd) = 0 then begin
      if not g_Config.boPasswordLockSystem then begin
        SysMsg(g_sNoPasswordLockSystemMsg, c_Red, t_Hint);
        Exit;
      end;
      if m_btPwdFailCount > g_Config.nPasswordErrorCountLock {3} then begin
        SysMsg(g_sStoragePasswordLockedMsg, c_Red, t_Hint);
        m_boPasswordLocked := True;
        Exit;
      end;
      if m_sStoragePwd <> '' then begin
        if not m_boUnLockStoragePwd then begin
          SendMsg(Self, RM_PASSWORD, 0, 0, 0, 0, '');
          SysMsg(g_sPleaseInputUnLockPasswordMsg, c_Green, t_Hint);
          m_boUnLockStoragePwd := True;
        end else begin
          SysMsg(g_sStorageAlreadyUnLockMsg, c_Red, t_Hint);
        end;
      end else begin
        SysMsg(g_sStorageNoPasswordMsg, c_Red, t_Hint);
      end;
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.UnLock.sCmd) = 0 then begin
      if not g_Config.boPasswordLockSystem then begin
        SysMsg(g_sNoPasswordLockSystemMsg, c_Red, t_Hint);
        Exit;
      end;
      if m_btPwdFailCount > g_Config.nPasswordErrorCountLock {3} then begin
        SysMsg(g_sStoragePasswordLockedMsg, c_Red, t_Hint);
        m_boPasswordLocked := True;
        Exit;
      end;
      if m_sStoragePwd <> '' then begin
        if not m_boUnLockPwd then begin
          SendMsg(Self, RM_PASSWORD, 0, 0, 0, 0, '');
          SysMsg(g_sPleaseInputUnLockPasswordMsg, c_Green, t_Hint);
          m_boUnLockPwd := True;
        end else begin
          SysMsg(g_sStorageAlreadyUnLockMsg, c_Red, t_Hint);
        end;
      end else begin
        SysMsg(g_sStorageNoPasswordMsg, c_Red, t_Hint);
      end;
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.Lock.sCmd) = 0 then begin
      if not g_Config.boPasswordLockSystem then begin
        SysMsg(g_sNoPasswordLockSystemMsg, c_Red, t_Hint);
        Exit;
      end;
      if not m_boPasswordLocked then begin
        if m_sStoragePwd <> '' then begin
          m_boPasswordLocked := True;
          m_boCanGetBackItem := False;
          SysMsg(g_sLockStorageSuccessMsg, c_Green, t_Hint);
        end else begin
          SysMsg(g_sStorageNoPasswordMsg, c_Green, t_Hint);
        end;
      end else begin
        SysMsg(g_sStorageAlreadyLockMsg, c_Red, t_Hint);
      end;
      Exit;
    end;
    {
    if CompareText(sCMD,g_GameCommand.LOCK.sCmd) = 0 then begin
      if not m_boPasswordLocked then begin
        m_sStoragePwd:='';
        SysMsg(g_sStoragePasswordClearMsg,c_Green,t_Hint);
      end else begin
        SysMsg(g_sPleaseUnloadStoragePasswordMsg,c_Red,t_Hint);
      end;
      exit;
    end;
    }
    if Assigned(PlugInEngine.PlayObjectUserCmd) then begin
      if PlugInEngine.PlayObjectUserCmd(Self,
        PChar(sCmd),
        PChar(sParam1),
        PChar(sParam2),
        PChar(sParam3),
        PChar(sParam4),
        PChar(sParam5),
        PChar(sParam6),
        PChar(sParam7)) then Exit;
    end;

    if CompareText(sCmd, g_GameCommand.MEMBERFUNCTION.sCmd) = 0 then begin
      CmdMemberFunction(g_GameCommand.MEMBERFUNCTION.sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.MEMBERFUNCTIONEX.sCmd) = 0 then begin
      CmdMemberFunctionEx(g_GameCommand.MEMBERFUNCTIONEX.sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.REMTEMSG.sCmd) = 0 then begin
      m_boRemoteMsg := True;
      SysMsg('You are allowed to receive messages now.', c_Green, t_Hint);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DEAR.sCmd) = 0 then begin
      CmdSearchDear(g_GameCommand.DEAR.sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.Master.sCmd) = 0 then begin
      CmdSearchMaster(g_GameCommand.Master.sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.MASTERECALL.sCmd) = 0 then begin
      CmdMasterRecall(g_GameCommand.MASTERECALL.sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DEARRECALL.sCmd) = 0 then begin
      CmdDearRecall(g_GameCommand.DEARRECALL.sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.ALLOWDEARRCALL.sCmd) = 0 then begin
      m_boCanDearRecall := not m_boCanDearRecall;
      if m_boCanDearRecall then begin
        SysMsg(g_sEnableDearRecall {'允许夫妻传送！！！'}, c_Blue, t_Hint);
      end else begin
        SysMsg(g_sDisableDearRecall {'禁止夫妻传送！！！'}, c_Blue, t_Hint);
      end;
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.ALLOWMASTERRECALL.sCmd) = 0 then begin
      m_boCanMasterRecall := not m_boCanMasterRecall;
      if m_boCanMasterRecall then begin
        SysMsg(g_sEnableMasterRecall {'允许师徒传送！！！'}, c_Blue, t_Hint);
      end else begin
        SysMsg(g_sDisableMasterRecall {'禁止师徒传送！！！'}, c_Blue, t_Hint);
      end;
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.Data.sCmd) = 0 then begin
      SysMsg(g_sNowCurrDateTime {'当前日期时间: '} + FormatDateTime('dddddd,dddd,hh:mm:nn', Now), c_Blue, t_Hint);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.PRVMSG.sCmd) = 0 then begin
      CmdPrvMsg(g_GameCommand.PRVMSG.sCmd, g_GameCommand.PRVMSG.nPermissionMin, sParam1);
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.AllowReAlive.sCmd) = 0 then begin
      m_boAllowReAlive := not m_boAllowReAlive;
      if m_boAllowReAlive then SysMsg(g_sEnableAllowRebirth {'[允许复活]'}, c_Green, t_Hint)
      else SysMsg(g_sDisableAllowRebirth {'[禁止复活]'}, c_Green, t_Hint);
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.UserItem.sCmd) = 0 then begin
      CmdGetUserItems(@g_GameCommand.UserItem, sParam1, sParam2, sParam3, sParam4);
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.SIGNMOVE.sCmd) = 0 then begin
      CmdSignMapMove(@g_GameCommand.UserItem);
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.SHOWEFFECT.sCmd) = 0 then begin
      CmdShowEffect(@g_GameCommand.SHOWEFFECT, sParam1, sParam2);
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.ALLOWMSG.sCmd) = 0 then begin
      m_boHearWhisper := not m_boHearWhisper;
      if m_boHearWhisper then SysMsg(g_sEnableHearWhisper {'[允许私聊]'}, c_Green, t_Hint)
      else SysMsg(g_sDisableHearWhisper {'[禁止私聊]'}, c_Green, t_Hint);
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.LETSHOUT.sCmd) = 0 then begin
      m_boBanShout := not m_boBanShout;
      if m_boBanShout then SysMsg(g_sEnableShoutMsg {'[允许群聊]'}, c_Green, t_Hint)
      else SysMsg(g_sDisableShoutMsg {'[禁止群聊]'}, c_Green, t_Hint);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.LETTRADE.sCmd) = 0 then begin
      m_boAllowDeal := not m_boAllowDeal;
      if m_boAllowDeal then SysMsg(g_sEnableDealMsg {'[允许交易]'}, c_Green, t_Hint)
      else SysMsg(g_sDisableDealMsg {'[禁止交易]'}, c_Green, t_Hint);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.BANGUILDCHAT.sCmd) = 0 then begin
      m_boBanGuildChat := not m_boBanGuildChat;
      if m_boBanGuildChat then SysMsg(g_sEnableGuildChat {'[允许行会聊天]'}, c_Green, t_Hint)
      else SysMsg(g_sDisableGuildChat {'[禁止行会聊天]'}, c_Green, t_Hint);
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.LETGUILD.sCmd) = 0 then begin
      m_boAllowGuild := not m_boAllowGuild;
      if m_boAllowGuild then SysMsg(g_sEnableJoinGuild {'[允许加入行会]'}, c_Green, t_Hint)
      else SysMsg(g_sDisableJoinGuild {'[禁止加入行会]'}, c_Green, t_Hint);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.ENDGUILD.sCmd) = 0 then begin
      CmdEndGuild();
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.AUTHALLY.sCmd) = 0 then begin
      if IsGuildMaster then begin
        TGUild(m_MyGuild).m_boEnableAuthAlly := not TGUild(m_MyGuild).m_boEnableAuthAlly;
        if TGUild(m_MyGuild).m_boEnableAuthAlly then SysMsg(g_sEnableAuthAllyGuild {'[允许行会联盟]'}, c_Green, t_Hint)
        else SysMsg(g_sDisableAuthAllyGuild {'[禁止行会联盟]'}, c_Green, t_Hint);
      end;
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.ALLOWGROUPCALL.sCmd) = 0 then begin
      CmdAllowGroupReCall(sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.GROUPRECALLL.sCmd) = 0 then begin
      CmdGroupRecall(g_GameCommand.GROUPRECALLL.sCmd);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.ALLOWGUILDRECALL.sCmd) = 0 then begin
      m_boAllowGuildReCall := not m_boAllowGuildReCall;
      if m_boAllowGuildReCall then SysMsg(g_sEnableGuildRecall {'[允许行会合一]'}, c_Green, t_Hint)
      else SysMsg(g_sDisableGuildRecall {'[禁止行会合一]'}, c_Green, t_Hint);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.GUILDRECALLL.sCmd) = 0 then begin
      CmdGuildRecall(g_GameCommand.GUILDRECALLL.sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.AUTH.sCmd) = 0 then begin
      if IsGuildMaster then ClientGuildAlly();
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.AUTHCANCEL.sCmd) = 0 then begin
      if IsGuildMaster then ClientGuildBreakAlly(sParam1);
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.DIARY.sCmd) = 0 then begin
      CmdViewDiary(g_GameCommand.DIARY.sCmd, Str_ToInt(sParam1, 0));
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.ATTACKMODE.sCmd) = 0 then begin
      CmdChangeAttackMode(Str_ToInt(sParam1, -1), sParam1, sParam2, sParam3, sParam4, sParam5, sParam6, sParam7);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.REST.sCmd) = 0 then begin
      CmdChangeSalveStatus();
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.TAKEONHORSE.sCmd) = 0 then begin
      CmdTakeOnHorse(sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.TAKEOFHORSE.sCmd) = 0 then begin
      CmdTakeOffHorse(sCmd, sParam1);
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.TESTGA.sCmd) = 0 then begin
      Exit;
      SendMsg(Self, RM_PASSWORD, 0, 0, 0, 0, '');
      m_boTestGa := True;
      SysMsg(g_sPleaseInputPassword {'请输入密码:'}, c_Green, t_Hint);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.MAPINFO.sCmd) = 0 then begin
      ShowMapInfo(sParam1, sParam2, sParam3);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.CLEARBAG.sCmd) = 0 then begin
      CmdClearBagItem(@g_GameCommand.CLEARBAG, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SHOWUSEITEMINFO.sCmd) = 0 then begin
      CmdShowUseItemInfo(@g_GameCommand.SHOWUSEITEMINFO, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.BINDUSEITEM.sCmd) = 0 then begin
      CmdBindUseItem(@g_GameCommand.BINDUSEITEM, sParam1, sParam2, sParam3, sParam4);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.UNBINDUSEITEM.sCmd) = 0 then begin
      CmdUnBindUseItem(@g_GameCommand.UNBINDUSEITEM, sParam1, sParam2);
      Exit;
    end;

    if CompareText(sCmd, g_GameCommand.SBKDOOR.sCmd) = 0 then begin //004D2610
      CmdSbkDoorControl(g_GameCommand.SBKDOOR.sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.USERMOVE.sCmd) = 0 then begin
      CmdUserMoveXY(g_GameCommand.USERMOVE.sCmd, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SEARCHING.sCmd) = 0 then begin
      CmdSearchHuman(g_GameCommand.SEARCHING.sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SEARCHMON.sCmd) = 0 then begin
      CmdSearchMon(g_GameCommand.SEARCHMON.sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.LOCKLOGON.sCmd) = 0 then begin
      CmdLockLogin(@g_GameCommand.LOCKLOGON);
      Exit;
    end;
    if (m_btPermission >= 2) and (Length(sData) > 2) then begin
      //if sData[2] = '!' then begin
      if (m_btPermission >= 6) and (sData[2] = g_GMRedMsgCmd) then begin

        if GetTickCount - m_dwSayMsgTick > 2000 then begin
          m_dwSayMsgTick := GetTickCount();
          sData := Copy(sData, 3, Length(sData) - 2);
          if Length(sData) > g_Config.nSayRedMsgMaxLen then begin
            sData := Copy(sData, 1, g_Config.nSayRedMsgMaxLen);
          end;
          if g_Config.boShutRedMsgShowGMName then
            SC := m_sCharName + ': ' + sData
          else SC := sData;
          UserEngine.SendBroadCastMsg(SC, t_GM);
        end;
        Exit;
      end;
    end;
    if CompareText(sCmd, g_GameCommand.CLEARCOPYITEM.sCmd) = 0 then begin
      CmdClearCopyItem(@g_GameCommand.CLEARCOPYITEM, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.RANGECLEARCOPYITEM.sCmd) = 0 then begin
      CmdRangeClearCopyItem(@g_GameCommand.RANGECLEARCOPYITEM, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.HUMANLOCAL.sCmd) = 0 then begin
      CmdHumanLocal(@g_GameCommand.HUMANLOCAL, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.Move.sCmd) = 0 then begin
      CmdMapMove(@g_GameCommand.Move, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.POSITIONMOVE.sCmd) = 0 then begin
      CmdPositionMove(@g_GameCommand.POSITIONMOVE, sParam1, sParam2, sParam3);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.INFO.sCmd) = 0 then begin
      CmdHumanInfo(@g_GameCommand.INFO, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.MOBLEVEL.sCmd) = 0 then begin
      CmdMobLevel(@g_GameCommand.MOBLEVEL, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.MOBCOUNT.sCmd) = 0 then begin
      CmdMobCount(@g_GameCommand.MOBCOUNT, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.HUMANCOUNT.sCmd) = 0 then begin
      CmdHumanCount(@g_GameCommand.HUMANCOUNT, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.KICK.sCmd) = 0 then begin
      CmdKickHuman(@g_GameCommand.KICK, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.TING.sCmd) = 0 then begin
      CmdTing(@g_GameCommand.TING, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SUPERTING.sCmd) = 0 then begin
      CmdSuperTing(@g_GameCommand.SUPERTING, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.MAPMOVE.sCmd) = 0 then begin
      CmdMapMoveHuman(@g_GameCommand.MAPMOVE, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SHUTUP.sCmd) = 0 then begin
      CmdShutup(@g_GameCommand.SHUTUP, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.Map.sCmd) = 0 then begin
      CmdShowMapInfo(@g_GameCommand.Map, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.RELEASESHUTUP.sCmd) = 0 then begin
      CmdShutupRelease(@g_GameCommand.RELEASESHUTUP, sParam1, True);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SHUTUPLIST.sCmd) = 0 then begin
      CmdShutupList(@g_GameCommand.SHUTUPLIST, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.GAMEMASTER.sCmd) = 0 then begin
      CmdChangeAdminMode(g_GameCommand.GAMEMASTER.sCmd, g_GameCommand.GAMEMASTER.nPermissionMin, sParam1, not m_boAdminMode);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.OBSERVER.sCmd) = 0 then begin
      CmdChangeObMode(g_GameCommand.OBSERVER.sCmd, g_GameCommand.OBSERVER.nPermissionMin, sParam1, not m_boObMode);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SUEPRMAN.sCmd) = 0 then begin
      CmdChangeSuperManMode(g_GameCommand.OBSERVER.sCmd, g_GameCommand.OBSERVER.nPermissionMin, sParam1, not m_boSuperMan);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.Level.sCmd) = 0 then begin
      CmdChangeLevel(@g_GameCommand.Level, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SABUKWALLGOLD.sCmd) = 0 then begin
      CmdShowSbkGold(@g_GameCommand.SABUKWALLGOLD, sParam1, sParam2, sParam3);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.RECALL.sCmd) = 0 then begin
      CmdRecallHuman(@g_GameCommand.RECALL, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.REGOTO.sCmd) = 0 then begin
      CmdReGotoHuman(@g_GameCommand.REGOTO, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SHOWFLAG.sCmd) = 0 then begin
      CmdShowHumanFlag(g_GameCommand.SHOWFLAG.sCmd, g_GameCommand.SHOWFLAG.nPermissionMin, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SHOWOPEN.sCmd) = 0 then begin
      CmdShowHumanUnitOpen(g_GameCommand.SHOWOPEN.sCmd, g_GameCommand.SHOWOPEN.nPermissionMin, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SHOWUNIT.sCmd) = 0 then begin
      CmdShowHumanUnit(g_GameCommand.SHOWUNIT.sCmd, g_GameCommand.SHOWUNIT.nPermissionMin, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.Attack.sCmd) = 0 then begin
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.MOB.sCmd) = 0 then begin
      CmdMob(@g_GameCommand.MOB, sParam1, Str_ToInt(sParam2, 0), Str_ToInt(sParam3, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.MOBNPC.sCmd) = 0 then begin
      CmdMobNpc(g_GameCommand.MOBNPC.sCmd, g_GameCommand.MOBNPC.nPermissionMin, sParam1, sParam2, sParam3, sParam4);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.NPCSCRIPT.sCmd) = 0 then begin
      CmdNpcScript(g_GameCommand.NPCSCRIPT.sCmd, g_GameCommand.NPCSCRIPT.nPermissionMin, sParam1, sParam2, sParam3);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DELNPC.sCmd) = 0 then begin
      CmdDelNpc(g_GameCommand.DELNPC.sCmd, g_GameCommand.DELNPC.nPermissionMin, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.RECALLMOB.sCmd) = 0 then begin
      CmdRecallMob(@g_GameCommand.RECALLMOB, sParam1, Str_ToInt(sParam2, 0), Str_ToInt(sParam3, 0), Str_ToInt(sParam4, 0), Str_ToInt(sParam5, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.LUCKYPOINT.sCmd) = 0 then begin
      CmdLuckPoint(g_GameCommand.LUCKYPOINT.sCmd, g_GameCommand.LUCKYPOINT.nPermissionMin, sParam1, sParam2, sParam3);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.LOTTERYTICKET.sCmd) = 0 then begin
      CmdLotteryTicket(g_GameCommand.LOTTERYTICKET.sCmd, g_GameCommand.LOTTERYTICKET.nPermissionMin, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.RELOADGUILD.sCmd) = 0 then begin
      CmdReloadGuild(g_GameCommand.RELOADGUILD.sCmd, g_GameCommand.RELOADGUILD.nPermissionMin, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.RELOADLINENOTICE.sCmd) = 0 then begin
      CmdReloadLineNotice(g_GameCommand.RELOADLINENOTICE.sCmd, g_GameCommand.RELOADLINENOTICE.nPermissionMin, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.RELOADABUSE.sCmd) = 0 then begin
      CmdReloadAbuse(g_GameCommand.RELOADABUSE.sCmd, g_GameCommand.RELOADABUSE.nPermissionMin, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.FREEPENALTY.sCmd) = 0 then begin
      CmdFreePenalty(@g_GameCommand.FREEPENALTY, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.PKPOINT.sCmd) = 0 then begin
      CmdPKpoint(@g_GameCommand.PKPOINT, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.IncPkPoint.sCmd) = 0 then begin
      CmdIncPkPoint(@g_GameCommand.IncPkPoint, sParam1, Str_ToInt(sParam2, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.MAKE.sCmd) = 0 then begin
      CmdMakeItem(@g_GameCommand.MAKE, sParam1, Str_ToInt(sParam2, 0), sParam3);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.VIEWWHISPER.sCmd) = 0 then begin
      CmdViewWhisper(@g_GameCommand.VIEWWHISPER, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.ReAlive.sCmd) = 0 then begin
      CmdReAlive(@g_GameCommand.ReAlive, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.KILL.sCmd) = 0 then begin
      CmdKill(@g_GameCommand.KILL, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SMAKE.sCmd) = 0 then begin
      CmdSmakeItem(@g_GameCommand.SMAKE, Str_ToInt(sParam1, 0), Str_ToInt(sParam2, 0), Str_ToInt(sParam3, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.CHANGEJOB.sCmd) = 0 then begin
      CmdChangeJob(@g_GameCommand.CHANGEJOB, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.CHANGEGENDER.sCmd) = 0 then begin
      CmdChangeGender(@g_GameCommand.CHANGEGENDER, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.HAIR.sCmd) = 0 then begin
      CmdHair(@g_GameCommand.HAIR, sParam1, Str_ToInt(sParam2, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.BonusPoint.sCmd) = 0 then begin
      CmdBonuPoint(@g_GameCommand.BonusPoint, sParam1, Str_ToInt(sParam2, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DELBONUSPOINT.sCmd) = 0 then begin
      CmdDelBonuPoint(@g_GameCommand.DELBONUSPOINT, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.RESTBONUSPOINT.sCmd) = 0 then begin
      CmdRestBonuPoint(@g_GameCommand.RESTBONUSPOINT, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SETPERMISSION.sCmd) = 0 then begin
      CmdSetPermission(@g_GameCommand.SETPERMISSION, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.RENEWLEVEL.sCmd) = 0 then begin
      CmdReNewLevel(@g_GameCommand.RENEWLEVEL, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DELGOLD.sCmd) = 0 then begin
      CmdDelGold(@g_GameCommand.DELGOLD, sParam1, Str_ToInt(sParam2, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.ADDGOLD.sCmd) = 0 then begin
      CmdAddGold(@g_GameCommand.ADDGOLD, sParam1, Str_ToInt(sParam2, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.GAMEGOLD.sCmd) = 0 then begin
      CmdGameGold(@g_GameCommand.GAMEGOLD, sParam1, sParam2, Str_ToInt(sParam3, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.GAMEPOINT.sCmd) = 0 then begin
      CmdGamePoint(@g_GameCommand.GAMEPOINT, sParam1, sParam2, Str_ToInt(sParam3, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.CREDITPOINT.sCmd) = 0 then begin
      CmdCreditPoint(@g_GameCommand.CREDITPOINT, sParam1, sParam2, Str_ToInt(sParam3, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.TRAINING.sCmd) = 0 then begin
      CmdTrainingSkill(@g_GameCommand.TRAINING, sParam1, sParam2, Str_ToInt(sParam3, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DELETEITEM.sCmd) = 0 then begin
      CmdDeleteItem(@g_GameCommand.DELETEITEM, sParam1, sParam2, Str_ToInt(sParam3, 1));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DELETESKILL.sCmd) = 0 then begin
      CmdDelSkill(@g_GameCommand.DELETESKILL, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.TRAININGSKILL.sCmd) = 0 then begin
      CmdTrainingMagic(@g_GameCommand.TRAININGSKILL, sParam1, sParam2, Str_ToInt(sParam3, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.CLEARMISSION.sCmd) = 0 then begin
      CmdClearMission(@g_GameCommand.CLEARMISSION, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.STARTQUEST.sCmd) = 0 then begin
      CmdStartQuest(@g_GameCommand.STARTQUEST, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DENYIPLOGON.sCmd) = 0 then begin
      CmdDenyIPaddrLogon(@g_GameCommand.DENYIPLOGON, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.CHANGEDEARNAME.sCmd) = 0 then begin
      CmdChangeDearName(@g_GameCommand.CHANGEDEARNAME, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.CHANGEMASTERNAME.sCmd) = 0 then begin
      CmdChangeMasterName(@g_GameCommand.CHANGEMASTERNAME, sParam1, sParam2, sParam3);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.CLEARMON.sCmd) = 0 then begin
      CmdClearMapMonster(@g_GameCommand.CLEARMON, sParam1, sParam2, sParam3);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DENYACCOUNTLOGON.sCmd) = 0 then begin
      CmdDenyAccountLogon(@g_GameCommand.DENYACCOUNTLOGON, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DENYCHARNAMELOGON.sCmd) = 0 then begin
      CmdDenyCharNameLogon(@g_GameCommand.DENYCHARNAMELOGON, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DELDENYIPLOGON.sCmd) = 0 then begin
      CmdDelDenyIPaddrLogon(@g_GameCommand.DELDENYIPLOGON, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DELDENYACCOUNTLOGON.sCmd) = 0 then begin
      CmdDelDenyAccountLogon(@g_GameCommand.DELDENYACCOUNTLOGON, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DELDENYCHARNAMELOGON.sCmd) = 0 then begin
      CmdDelDenyCharNameLogon(@g_GameCommand.DELDENYCHARNAMELOGON, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SHOWDENYIPLOGON.sCmd) = 0 then begin
      CmdShowDenyIPaddrLogon(@g_GameCommand.SHOWDENYIPLOGON, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SHOWDENYACCOUNTLOGON.sCmd) = 0 then begin
      CmdShowDenyAccountLogon(@g_GameCommand.SHOWDENYACCOUNTLOGON, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SHOWDENYCHARNAMELOGON.sCmd) = 0 then begin
      CmdShowDenyCharNameLogon(@g_GameCommand.SHOWDENYCHARNAMELOGON, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.Mission.sCmd) = 0 then begin
      CmdMission(@g_GameCommand.Mission, sParam1, sParam2);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.MobPlace.sCmd) = 0 then begin
      CmdMobPlace(@g_GameCommand.MobPlace, sParam1, sParam2, sParam3, sParam4);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SetMapMode.sCmd) = 0 then begin
      CmdSetMapMode(g_GameCommand.SetMapMode.sCmd, sParam1, sParam2, sParam3, sParam4);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.SHOWMAPMODE.sCmd) = 0 then begin
      CmdShowMapMode(g_GameCommand.SHOWMAPMODE.sCmd, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.CLRPASSWORD.sCmd) = 0 then begin
      CmdClearHumanPassword(g_GameCommand.CLRPASSWORD.sCmd, g_GameCommand.CLRPASSWORD.nPermissionMin, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.CONTESTPOINT.sCmd) = 0 then begin
      CmdContestPoint(@g_GameCommand.CONTESTPOINT, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.STARTCONTEST.sCmd) = 0 then begin
      CmdStartContest(@g_GameCommand.STARTCONTEST, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.ENDCONTEST.sCmd) = 0 then begin
      CmdEndContest(@g_GameCommand.ENDCONTEST, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.ANNOUNCEMENT.sCmd) = 0 then begin
      CmdAnnouncement(@g_GameCommand.ANNOUNCEMENT, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DISABLESENDMSG.sCmd) = 0 then begin
      CmdDisableSendMsg(@g_GameCommand.DISABLESENDMSG, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.ENABLESENDMSG.sCmd) = 0 then begin
      CmdEnableSendMsg(@g_GameCommand.ENABLESENDMSG, sParam1);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.REFINEWEAPON.sCmd) = 0 then begin
      CmdRefineWeapon(@g_GameCommand.REFINEWEAPON, Str_ToInt(sParam1, 0), Str_ToInt(sParam2, 0), Str_ToInt(sParam3, 0), Str_ToInt(sParam4, 0));
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.DISABLESENDMSGLIST.sCmd) = 0 then begin
      CmdDisableSendMsgList(@g_GameCommand.DISABLESENDMSGLIST);
      Exit;
    end;
    if CompareText(sCmd, g_GameCommand.RestHero.sCmd) = 0 then begin
      CmdRestHero();
      Exit;
    end;


    if m_btPermission > 4 then begin
      if CompareText(sCmd, g_GameCommand.BACKSTEP.sCmd) = 0 then begin
        CmdBackStep(sCmd, Str_ToInt(sParam1, 0), Str_ToInt(sParam2, 1));
        Exit;
      end;
      if CompareText(sCmd, g_GameCommand.BALL.sCmd) = 0 then begin //精神波
        Exit;
      end;

      if CompareText(sCmd, g_GameCommand.CHANGELUCK.sCmd) = 0 then begin
        Exit;
      end;
      if CompareText(sCmd, g_GameCommand.HUNGER.sCmd) = 0 then begin
        CmdHunger(g_GameCommand.HUNGER.sCmd, sParam1, Str_ToInt(sParam2, 0));
        Exit;
      end;

      if CompareText(sCmd, g_GameCommand.NAMECOLOR.sCmd) = 0 then begin
        Exit;
      end;

      if CompareText(sCmd, g_GameCommand.TRANSPARECY.sCmd) = 0 then begin
        Exit;
      end;
      if CompareText(sCmd, g_GameCommand.LEVEL0.sCmd) = 0 then begin
        Exit;
      end;

      if CompareText(sCmd, g_GameCommand.SETFLAG.sCmd) = 0 then begin
        PlayObject := UserEngine.GetPlayObject(sParam1);
        if PlayObject <> nil then begin
          nFlag := Str_ToInt(sParam2, 0);
          nValue := Str_ToInt(sParam3, 0);
          PlayObject.SetQuestFlagStatus(nFlag, nValue);
          if PlayObject.GetQuestFalgStatus(nFlag) = 1 then begin
            SysMsg(PlayObject.m_sCharName + ': [' + IntToStr(nFlag) + '] = ON', c_Green, t_Hint);
          end else begin
            SysMsg(PlayObject.m_sCharName + ': [' + IntToStr(nFlag) + '] = OFF', c_Green, t_Hint);
          end;
        end else begin
          SysMsg('@' + g_GameCommand.SETFLAG.sCmd + ' CharacterName Flag Status(0 - 1)', c_Red, t_Hint);
        end;
        Exit;
      end;
      {if CompareText(sCmd, g_GameCommand.SETOPEN.sCmd) = 0 then begin
        PlayObject := UserEngine.GetPlayObject(sParam1);
        if PlayObject <> nil then begin
          nFlag := Str_ToInt(sParam2, 0);
          nValue := Str_ToInt(sParam3, 0);
          PlayObject.SetQuestUnitOpenStatus(nFlag, nValue);
          if PlayObject.GetQuestUnitOpenStatus(nFlag) = 1 then begin
            SysMsg(PlayObject.m_sCharName + ': [' + IntToStr(nFlag) + '] = ON', c_Green, t_Hint);
          end else begin
            SysMsg(PlayObject.m_sCharName + ': [' + IntToStr(nFlag) + '] = OFF', c_Green, t_Hint);
          end;
        end else begin
          SysMsg('@' + g_GameCommand.SETOPEN.sCmd + ' 人物名称 标志号 数字(0 - 1)', c_Red, t_Hint);
        end;
        Exit;
      end;
      if CompareText(sCmd, g_GameCommand.SETUNIT.sCmd) = 0 then begin
        PlayObject := UserEngine.GetPlayObject(sParam1);
        if PlayObject <> nil then begin
          nFlag := Str_ToInt(sParam2, 0);
          nValue := Str_ToInt(sParam3, 0);
          PlayObject.SetQuestUnitStatus(nFlag, nValue);
          if PlayObject.GetQuestUnitStatus(nFlag) = 1 then begin
            SysMsg(PlayObject.m_sCharName + ': [' + IntToStr(nFlag) + '] = ON', c_Green, t_Hint);
          end else begin
            SysMsg(PlayObject.m_sCharName + ': [' + IntToStr(nFlag) + '] = OFF', c_Green, t_Hint);
          end;
        end else begin
          SysMsg('@' + g_GameCommand.SETUNIT.sCmd + ' 人物名称 标志号 数字(0 - 1)', c_Red, t_Hint);
        end;
        Exit;
      end;}
      if CompareText(sCmd, g_GameCommand.RECONNECTION.sCmd) = 0 then begin
        CmdReconnection(sCmd, sParam1, sParam2);
        Exit;
      end;
      if CompareText(sCmd, g_GameCommand.DISABLEFILTER.sCmd) = 0 then begin
        CmdDisableFilter(sCmd, sParam1);
        Exit;
      end;
      if CompareText(sCmd, g_GameCommand.CHGUSERFULL.sCmd) = 0 then begin
        CmdChangeUserFull(sCmd, sParam1);
        Exit;
      end;
      if CompareText(sCmd, g_GameCommand.CHGZENFASTSTEP.sCmd) = 0 then begin
        CmdChangeZenFastStep(sCmd, sParam1);
        Exit;
      end;

      if CompareText(sCmd, g_GameCommand.OXQUIZROOM.sCmd) = 0 then begin
        Exit;
      end;
      if CompareText(sCmd, g_GameCommand.GSA.sCmd) = 0 then begin
        Exit;
      end;
      if CompareText(sCmd, g_GameCommand.CHANGEITEMNAME.sCmd) = 0 then begin
        CmdChangeItemName(g_GameCommand.CHANGEITEMNAME.sCmd, sParam1, sParam2, sParam3);
        Exit;
      end;
      if (m_btPermission >= 5) or (g_Config.boTestServer) then begin
        if CompareText(sCmd, g_GameCommand.FIREBURN.sCmd) = 0 then begin
          CmdFireBurn(Str_ToInt(sParam1, 0), Str_ToInt(sParam2, 0), Str_ToInt(sParam3, 0));
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.TESTFIRE.sCmd) = 0 then begin
          CmdTestFire(sCmd, Str_ToInt(sParam1, 0), Str_ToInt(sParam2, 0), Str_ToInt(sParam3, 0), Str_ToInt(sParam4, 0));
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.TESTSTATUS.sCmd) = 0 then begin
          CmdTestStatus(sCmd, Str_ToInt(sParam1, -1), Str_ToInt(sParam2, 0));
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.DELGAMEGOLD.sCmd) = 0 then begin
          CmdDelGameGold(g_GameCommand.DELGAMEGOLD.sCmd, sParam1, Str_ToInt(sParam2, 0));
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.ADDGAMEGOLD.sCmd) = 0 then begin
          CmdAddGameGold(g_GameCommand.ADDGAMEGOLD.sCmd, sParam1, Str_ToInt(sParam2, 0));
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.TESTGOLDCHANGE.sCmd) = 0 then begin
          Exit;
        end;

        if CompareText(sCmd, g_GameCommand.RELOADADMIN.sCmd) = 0 then begin
          CmdReLoadAdmin(g_GameCommand.RELOADADMIN.sCmd);
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.ReLoadNpc.sCmd) = 0 then begin
          CmdReloadNpc(sParam1);
          SysMsg('NPCs Reloaded', c_Green, t_Hint);
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.RELOADMANAGE.sCmd) = 0 then begin
          CmdReloadManage(@g_GameCommand.RELOADMANAGE, sParam1);
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.RELOADROBOTMANAGE.sCmd) = 0 then begin
          CmdReloadRobotManage();
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.RELOADROBOT.sCmd) = 0 then begin
          CmdReloadRobot();
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.RELOADMONITEMS.sCmd) = 0 then begin
          CmdReloadMonItems();
          SysMsg('Mon Items Reloaded', c_Green, t_Hint);
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.RELOADDIARY.sCmd) = 0 then begin
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.RELOADITEMDB.sCmd) = 0 then begin
          FrmDB.LoadItemsDB();
          SysMsg('Item DB Reloaded', c_Green, t_Hint);
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.RELOADMAGICDB.sCmd) = 0 then begin
          FrmDB.LoadMagicDB();
          SysMsg('Magic DB Reloaded', c_Green, t_Hint);
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.RELOADMONSTERDB.sCmd) = 0 then begin
          FrmDB.LoadMonsterDB();
          SysMsg('Monster DB Reloaded', c_Green, t_Hint);
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.RELOADMINMAP.sCmd) = 0 then begin
          FrmDB.LoadMinMap();
          g_MapManager.ReSetMinMap();
          SysMsg('Minimaps Reloaded', c_Green, t_Hint);
          Exit;
        end;

        if CompareText(sCmd, g_GameCommand.ADJUESTLEVEL.sCmd) = 0 then begin
          CmdAdjuestLevel(@g_GameCommand.ADJUESTLEVEL, sParam1, Str_ToInt(sParam2, 1));
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.ADJUESTEXP.sCmd) = 0 then begin
          CmdAdjuestExp(@g_GameCommand.ADJUESTEXP, sParam1, sParam2);
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.AddGuild.sCmd) = 0 then begin
          CmdAddGuild(@g_GameCommand.AddGuild, sParam1, sParam2);
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.DELGUILD.sCmd) = 0 then begin
          CmdDelGuild(@g_GameCommand.DELGUILD, sParam1);
          Exit;
        end;
        if (CompareText(sCmd, g_GameCommand.CHANGESABUKLORD.sCmd) = 0) then begin
          CmdChangeSabukLord(@g_GameCommand.CHANGESABUKLORD, sParam1, sParam2, True);
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.FORCEDWALLCONQUESTWAR.sCmd) = 0 then begin
          CmdForcedWallconquestWar(@g_GameCommand.FORCEDWALLCONQUESTWAR, sParam1);
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.ADDTOITEMEVENT.sCmd) = 0 then begin
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.ADDTOITEMEVENTASPIECES.sCmd) = 0 then begin
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.ItemEventList.sCmd) = 0 then begin
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.STARTINGGIFTNO.sCmd) = 0 then begin
          Exit;
        end;
        if CompareText(sCmd, g_GameCommand.DELETEALLITEMEVENT.sCmd) = 0 then begin
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.STARTITEMEVENT.sCmd) = 0 then begin
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.ITEMEVENTTERM.sCmd) = 0 then begin
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.ADJUESTTESTLEVEL.sCmd) = 0 then begin
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.OPDELETESKILL.sCmd) = 0 then begin
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.CHANGEWEAPONDURA.sCmd) = 0 then begin
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.RELOADGUILDALL.sCmd) = 0 then begin
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.SPIRIT.sCmd) = 0 then begin
          CmdSpirtStart(g_GameCommand.SPIRIT.sCmd, sParam1);
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.SPIRITSTOP.sCmd) = 0 then begin
          CmdSpirtStop(g_GameCommand.SPIRITSTOP.sCmd, sParam1);
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.TESTSERVERCONFIG.sCmd) = 0 then begin
          //SendServerConfig();
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.SERVERSTATUS.sCmd) = 0 then begin
          SendServerStatus();
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.TESTGETBAGITEM.sCmd) = 0 then begin
          CmdTestGetBagItems(@g_GameCommand.TESTGETBAGITEM, sParam1);
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.MOBFIREBURN.sCmd) = 0 then begin
          CmdMobFireBurn(@g_GameCommand.MOBFIREBURN, sParam1, sParam2, sParam3, sParam4, sParam5, sParam6);
          Exit;
        end else
          if CompareText(sCmd, g_GameCommand.TESTSPEEDMODE.sCmd) = 0 then begin
          CmdTestSpeedMode(@g_GameCommand.TESTSPEEDMODE);
          Exit;
        end;
      end;
    end;

    SysMsg('@' + sCmd + ' Unknown Command', c_Red, t_Hint);
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg, [sData]));
      MainOutMessage(E.Message);
    end;
  end;
end;

procedure TPlayObject.ProcessSayMsg(sData: string);
var
  boDisableSayMsg: Boolean;
  SC, sCryCryMsg, sParam1, sCharName: string;
const
  s01 = '%d %d';
  s02 = '%s %d/%d Version:%d';
resourcestring
  sExceptionMsg = '[Exception] TPlayObject.ProcessSayMsg Msg = %s';
begin
  if sData = '' then Exit;
  try
    if (Length(sData) > 1) and (sData[1] = '|') then begin
      SC := Copy(sData, 2, Length(sData) - 1);
      if (SC[1] = 'O') and (SC[6] = 'E') and (SC[11] = 'T') then begin
        SysMsg(Format(s02, [g_Config.sServerName, UserEngine.PlayObjectCount, UserEngine.PlayObjectCount, g_nUpDateVersion]), c_Red, t_Hint);
        Exit;
      end;
      if (SC[1] = 'S') and (SC[5] = 'T') and (SC[9] = 'E') then begin
        SysMsg(GetStartTime(dwStartTime), c_Red, t_Hint);
        Exit;
      end;
    end;

    if Length(sData) > g_Config.nSayMsgMaxLen then begin
      sData := Copy(sData, 1, g_Config.nSayMsgMaxLen);
    end;

    if {(sData = m_sOldSayMsg) and}((GetTickCount - m_dwSayMsgTick) < g_Config.dwSayMsgTime {3 * 1000}) then begin
      Inc(m_nSayMsgCount);
      if m_nSayMsgCount >= g_Config.nSayMsgCount {2} then begin
        m_boDisableSayMsg := True;
        m_dwDisableSayMsgTick := GetTickCount + g_Config.dwDisableSayMsgTime {60 * 1000};
        SysMsg(Format(g_sDisableSayMsg, [g_Config.dwDisableSayMsgTime div (60 * 1000)]), c_Red, t_Hint);
        //'[由于你重复发相同的内容，%d分钟内你将被禁止发言...]'
      end;
    end else begin
      m_dwSayMsgTick := GetTickCount();
      m_nSayMsgCount := 0;
    end;

    if GetTickCount >= m_dwDisableSayMsgTick then m_boDisableSayMsg := False;
    boDisableSayMsg := m_boDisableSayMsg;
    g_DenySayMsgList.Lock;
    try
      if g_DenySayMsgList.GetIndex(m_sCharName) >= 0 then boDisableSayMsg := True;
    finally
      g_DenySayMsgList.UnLock;
    end;
    if not boDisableSayMsg then begin
      m_sOldSayMsg := sData;
      if sData[1] = '/' then begin
        SC := Copy(sData, 2, Length(sData) - 1);
        if CompareText(Trim(SC), Trim(g_GameCommand.WHO.sCmd)) = 0 then begin
          if (m_btPermission < g_GameCommand.WHO.nPermissionMin) then begin
            SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
            Exit;
          end;
          HearMsg(Format(g_sOnlineCountMsg, [UserEngine.PlayObjectCount]));
          Exit;
        end;
        if CompareText(Trim(SC), Trim(g_GameCommand.TOTAL.sCmd)) = 0 then begin
          if (m_btPermission < g_GameCommand.TOTAL.nPermissionMin) then begin
            SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
            Exit;
          end;
          HearMsg(Format(g_sTotalOnlineCountMsg, [g_nTotalHumCount]));
          Exit;
        end;
        SC := GetValidStr3(SC, sParam1, [' ']);
        if not m_boFilterSendMsg then
          Whisper(sParam1, SC);
        Exit;
      end;
      if sData[1] = '!' then begin
        if Length(sData) >= 2 then begin
          if sData[2] = '!' then begin
            SC := Copy(sData, 3, Length(sData) - 2);
            SendGroupText(GetUnknowCharName + ': ' + SC);
            Exit;
          end;
          if sData[2] = '~' then begin
            if m_MyGuild <> nil then begin
              SC := Copy(sData, 3, Length(sData) - 2);
              TGUild(m_MyGuild).SendGuildMsg(GetUnknowCharName + ': ' + SC);
              //UserEngine.SendServerGroupMsg(SS_208, nServerIndex, TGUild(m_MyGuild).sGuildName + '/' + GetUnknowCharName + '/' + SC);
            end;
            Exit;
          end;
        end;
        if not m_PEnvir.m_boQUIZ then begin
          if (GetTickCount - m_dwShoutMsgTick) > 10 * 1000 then begin
            if m_Abil.Level <= g_Config.nCanShoutMsgLevel then begin
              //SysMsg('你的等级要在' + IntToStr(g_nCanShoutMsgLevel + 1) + '级以上才能用此功能！！！',c_Red,t_Hint);
              SysMsg(Format(g_sYouNeedLevelMsg, [g_Config.nCanShoutMsgLevel + 1]), c_Red, t_Hint);
              Exit;
            end;
            m_dwShoutMsgTick := GetTickCount();
            SC := Copy(sData, 2, Length(sData) - 1);
            sCryCryMsg := '(!)' + GetUnknowCharName + ': ' + SC;
            if m_boFilterSendMsg then begin
              SendMsg(nil, RM_CRY, 0, 0, $FFFF, 0, sCryCryMsg);
            end else begin
              UserEngine.CryCry(RM_CRY, m_PEnvir, m_nCurrX, m_nCurrY, 50, g_Config.btCryMsgFColor, g_Config.btCryMsgBColor, sCryCryMsg);
            end;
            Exit;
          end;
          SysMsg(Format(g_sYouCanSendCyCyLaterMsg, [10 - (GetTickCount - m_dwShoutMsgTick) div 1000]), c_Red, t_Hint);
          Exit;
        end;
        SysMsg(g_sThisMapDisableSendCyCyMsg {'本地图不允许喊话！！！'}, c_Red, t_Hint);
        Exit;
      end;
      if m_boFilterSendMsg then begin //如果禁止发信息，则只向自己发信息
        SendMsg(Self, RM_HEAR, 0, {g_Config.btHearMsgFColor} m_btHearMsgFColor, g_Config.btHearMsgBColor, 0, GetUnknowCharName + ':' + sData);
      end else begin
        SendRefMsg(RM_HEAR, 0, {g_Config.btHearMsgFColor} m_btHearMsgFColor, g_Config.btHearMsgBColor, 0, GetUnknowCharName + ':' + sData);
      end;
      Exit;
    end;
    SysMsg(g_sYouIsDisableSendMsg {'禁止聊天'}, c_Red, t_Hint);
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg, [sData]));
      MainOutMessage(E.Message);
    end;
  end;
end;

function TPlayObject.ClientHitXY(wIdent: Word; nX, nY, nDir: Integer; boLateDelivery: Boolean; var dwDelayTime: LongWord): Boolean;
var
  n14, n18: Integer;
  StdItem: pTStdItem;
  dwAttackTime, dwCheckTime: LongWord;
resourcestring
  sExceptionMsg = '[Exception] TPlayObject::ClientHitXY';
begin
  Result := False;
  dwDelayTime := 0;
  //MainOutMessage('TPlayObject.ClientHitXY Ident:' + IntToStr(wIdent));
  try
    if (not m_boCanHit) or (m_NewStatus <> sNone) then Exit;
    if m_boDeath or ((m_wStatusTimeArr[POISON_STONE {5}] {0x6A} <> 0) and not g_Config.ClientConf.boParalyCanHit) then Exit; //防麻
    if not boLateDelivery then begin
      if not CheckActionStatus(wIdent, dwDelayTime) then begin
        m_boFilterAction := False;
        Exit;
      end;
      m_boFilterAction := True;
      dwAttackTime := _MAX(0, Integer(g_Config.dwHitIntervalTime) - m_nHitSpeed * g_Config.ClientConf.btItemSpeed); //防止负数出错
      dwCheckTime := GetTickCount - m_dwAttackTick;
      if dwCheckTime < dwAttackTime then begin
        Inc(m_dwAttackCount);
        dwDelayTime := dwAttackTime - dwCheckTime;
        if dwDelayTime > g_Config.dwDropOverSpeed then begin
          if m_dwAttackCount >= 4 then begin
            m_dwAttackTick := GetTickCount();
            m_dwAttackCount := 0;
            dwDelayTime := g_Config.dwDropOverSpeed;
            if m_boTestSpeedMode then
              SysMsg('Attack speed error ' + IntToStr(dwDelayTime), c_Red, t_Hint);
          end else m_dwAttackCount := 0;
          Exit;
        end else begin
          if m_boTestSpeedMode then
            SysMsg('Attack speed error ' + IntToStr(dwDelayTime), c_Red, t_Hint);
          Exit;
        end;
      end;
    end;
    if (nX = m_nCurrX) and (nY = m_nCurrY) then begin
      Result := True;
      m_dwAttackTick := GetTickCount();
      if (wIdent = CM_HEAVYHIT) and (m_UseItems[U_WEAPON].Dura > 0) then begin //挖矿
        if g_Config.boStartMapEvent and (m_btRaceServer = RC_PLAYOBJECT) then begin
          StartMapEventOfMine(); //挖矿事件触发
        end;
        if GetFrontPosition(n14, n18) and not m_PEnvir.CanWalk(n14, n18, False) then begin
          StdItem := UserEngine.GetStdItem(m_UseItems[U_WEAPON].wIndex);
          if (StdItem <> nil) and (StdItem.Shape = 19) then begin
            if PileStones(n14, n18) then SendSocket(nil, '=DIG/' + IntToStr(GetTickCount));
            Dec(m_nHealthTick, 30);
            Dec(m_nSpellTick, 50);
            m_nSpellTick := _MAX(0, m_nSpellTick);
            Dec(m_nPerHealth, 2);
            Dec(m_nPerSpell, 2);
            Exit;
          end;
        end;
      end;
      if wIdent = CM_HIT then AttackDir(nil, 0, nDir);
      if wIdent = CM_HEAVYHIT then AttackDir(nil, 1, nDir);
      if wIdent = CM_BIGHIT then AttackDir(nil, 2, nDir);
      if wIdent = CM_POWERHIT then AttackDir(nil, 3, nDir);
      if wIdent = CM_LONGHIT then AttackDir(nil, 4, nDir);
      if wIdent = CM_WIDEHIT then AttackDir(nil, 5, nDir);
      if wIdent = CM_FIREHIT then AttackDir(nil, 7, nDir);
      if wIdent = CM_CRSHIT then AttackDir(nil, 8, nDir);
      if wIdent = CM_TWNHIT then AttackDir(nil, 9, nDir);
      if wIdent = CM_42HIT then AttackDir(nil, 10, nDir);
      if wIdent = CM_PKHIT {CM_43HIT} then AttackDir(nil, 11, nDir);
      if wIdent = CM_KTHIT then AttackDir(nil, 12, nDir);
      if wIdent = CM_60HIT then AttackDir(nil, 13, nDir);
      if wIdent = CM_ZRJFHIT then AttackDir(nil, 16, nDir);

      if wIdent = CM_100HIT then AttackDir(nil, 100, nDir);
      if wIdent = CM_101HIT then AttackDir(nil, 101, nDir);
      if wIdent = CM_102HIT then AttackDir(nil, 102, nDir);
      if wIdent = CM_103HIT then AttackDir(nil, 103, nDir);

      if (m_MagicPowerHitSkill <> nil) and (m_UseItems[U_WEAPON].Dura > 0) then begin
        Dec(m_btAttackSkillCount);
        if m_btAttackSkillPointCount = m_btAttackSkillCount then begin
          m_boPowerHit := True;
          SendSocket(nil, '+PWR/' + IntToStr(GetTickCount));
        end;
        if m_btAttackSkillCount <= 0 then begin
          m_btAttackSkillCount := 7 - m_MagicPowerHitSkill.btLevel;
          m_btAttackSkillPointCount := Random(m_btAttackSkillCount);
        end;
      end;
      Dec(m_nHealthTick, 30);
      Dec(m_nSpellTick, 100);
      m_nSpellTick := _MAX(0, m_nSpellTick);
      Dec(m_nPerHealth, 2);
      Dec(m_nPerSpell, 2);
    end;
  except
    on E: Exception do begin
      MainOutMessage(sExceptionMsg);
      MainOutMessage(E.Message);
    end;
  end;
end;

function TPlayObject.ClientHorseRunXY(wIdent: Word; nX, nY: Integer; boLateDelivery: Boolean;
  var dwDelayTime: LongWord): Boolean;
var
  n14: Integer;
  dwCheckTime: LongWord;
begin
  Result := False;
  dwDelayTime := 0;
  if (not m_boCanRun) or (m_NewStatus = sBlind) then Exit;
  if m_boDeath or ((m_wStatusTimeArr[POISON_STONE {5}] {0x6A} <> 0) and not g_Config.ClientConf.boParalyCanRun) then Exit; //防麻
  if not boLateDelivery then begin

    if not CheckActionStatus(wIdent, dwDelayTime) then begin
      m_boFilterAction := False;
      Exit;
    end;
    m_boFilterAction := True;
    dwCheckTime := GetTickCount - m_dwMoveTick;

    if dwCheckTime < g_Config.dwRunIntervalTime then begin
      Inc(m_dwMoveCount);
      dwDelayTime := g_Config.dwRunIntervalTime - dwCheckTime;
      if dwDelayTime > g_Config.dwDropOverSpeed then begin
        if m_dwMoveCount >= 4 then begin
          m_dwMoveTick := GetTickCount();
          m_dwMoveCount := 0;
          dwDelayTime := g_Config.dwDropOverSpeed;
          if m_boTestSpeedMode then
            SysMsg('Horse Run error ' + IntToStr(dwDelayTime), c_Red, t_Hint);
        end else m_dwMoveCount := 0;
        Exit;
      end else begin
        if m_boTestSpeedMode then
          SysMsg('Horse Run error ' + IntToStr(dwDelayTime), c_Red, t_Hint);
        Exit;
      end;
    end;
  end;

  m_dwMoveTick := GetTickCount();
  m_bo316 := False;
{$IF DEBUG = 1}
  SysMsg(Format('Current X: %d current Y: %d target X: %d target Y: %d', [m_nCurrX, m_nCurrY, nX, nY]), c_Green, t_Hint);
{$IFEND}

  {if m_dwClientLastTick = 0 then begin
    m_dwClientLastTick := dwClientLastTick;
  end else begin
    SysMsg('ClientMoveTick:' + IntToStr(dwClientLastTick - m_dwClientLastTick), c_Red, t_Hint);
    m_dwClientLastTick := dwClientLastTick;
  end;

  if m_dwServerLastTick = 0 then begin
    m_dwServerLastTick := GetTickCount;
  end else begin
    SysMsg('ServerMoveTick:' + IntToStr(GetTickCount - m_dwServerLastTick), c_Red, t_Hint);
    m_dwServerLastTick := GetTickCount;
  end;  }


  n14 := GetNextDirection(m_nCurrX, m_nCurrY, nX, nY);
  if HorseRunTo(n14, False) then begin
    m_dwStationTick := GetTickCount; //增加检测人物站立时间
    if m_boTransparent and (m_boHideMode) then m_wStatusTimeArr[STATE_TRANSPARENT {0 0x70}] := 1;
    if m_bo316 or ((m_nCurrX = nX) and (m_nCurrY = nY)) then
      Result := True;
    Dec(m_nHealthTick, 60);
    Dec(m_nSpellTick, 10);
    m_nSpellTick := _MAX(0, m_nSpellTick);
    Dec(m_nPerHealth);
    Dec(m_nPerSpell);
  end else begin
    m_dwMoveCount := 0;
    m_dwMoveCountA := 0;
  end;
end;

function TPlayObject.ClientSpellXY(wIdent: Word; nKey: Integer; nTargetX, nTargetY: Integer; TargeTActorObject: TActorObject; boLateDelivery: Boolean; var dwDelayTime: LongWord): Boolean; //004CBCEC
var
  UserMagic: pTUserMagic;
  nSpellPoint: Integer;
  n14: Integer;
  BaseObject: TActorObject;
  dwCheckTime: LongWord;
  boIsWarrSkill: Boolean;
resourcestring
  sDisableMagicCross = 'DisableMagicCross %s';
begin
  Result := False;
  dwDelayTime := 0;
  if not m_boCanSpell then Exit;
  if m_boDeath or ((m_wStatusTimeArr[POISON_STONE {5}] {0x6A} <> 0) and not g_Config.ClientConf.boParalyCanSpell) then Exit; //防麻

  UserMagic := GetMagicInfo(nKey);

  if UserMagic = nil then Exit;

  if m_PEnvir <> nil then begin
    if not m_PEnvir.AllowMagics(UserMagic.MagicInfo.sMagicName) then begin
      SysMsg(Format(sDisableMagicCross, [UserMagic.MagicInfo.sMagicName]), c_Red, t_Notice);
      Exit;
    end;
    if m_PEnvir.m_boDuel and (not m_boStartDuel) then Exit; //非比赛人员禁止使用魔法
  end;

  boIsWarrSkill := MagicManager.IsWarrSkill(UserMagic.wMagIdx);

  if not boLateDelivery and not boIsWarrSkill then begin
    if not CheckActionStatus(wIdent, dwDelayTime) then begin
      m_boFilterAction := False;
      Exit;
    end;
    m_boFilterAction := True;
    dwCheckTime := GetTickCount - m_dwMagicAttackTick;
    if dwCheckTime < m_dwMagicAttackInterval then begin
      Inc(m_dwMagicAttackCount);
      dwDelayTime := m_dwMagicAttackInterval - dwCheckTime;
      if dwDelayTime > g_Config.dwMagicHitIntervalTime div 3 then begin
        if m_dwMagicAttackCount >= 4 then begin
          m_dwMagicAttackTick := GetTickCount();
          m_dwMagicAttackCount := 0;
          dwDelayTime := g_Config.dwMagicHitIntervalTime div 3;
          if m_boTestSpeedMode then
            SysMsg('Spell error' + IntToStr(dwDelayTime), c_Red, t_Hint);
        end else m_dwMagicAttackCount := 0;
        Exit;
      end else begin
        if m_boTestSpeedMode then
          SysMsg('Spell error' + IntToStr(dwDelayTime), c_Red, t_Hint);
        Exit;
      end;
    end;
  end;

  Dec(m_nSpellTick, 450);
  m_nSpellTick := _MAX(0, m_nSpellTick);

  if boIsWarrSkill then begin
    //m_dwMagicAttackInterval:=0;
    //m_dwMagicAttackInterval:=g_Config.dwMagicHitIntervalTime;
  end else begin
    m_dwMagicAttackInterval := UserMagic.MagicInfo.dwDelayTime + g_Config.dwMagicHitIntervalTime;
  end;
  m_dwMagicAttackTick := GetTickCount();
  case UserMagic.wMagIdx of //
    SKILL_THRUSTING {12}: begin //刺杀剑法
        if m_MagicErgumSkill <> nil then begin
          if not m_boUseThrusting then begin
            ThrustingOnOff(True);
            SendSocket(nil, '+LNG/' + IntToStr(GetTickCount));
          end else begin
            ThrustingOnOff(False);
            SendSocket(nil, '+ULNG/' + IntToStr(GetTickCount));
          end;
        end;
        Result := True;
      end;
    SKILL_BANWOL {25}: begin //半月弯刀
        if m_MagicBanwolSkill <> nil then begin
          if not m_boUseHalfMoon then begin
            HalfMoonOnOff(True);
            SendSocket(nil, '+WID/' + IntToStr(GetTickCount));
          end else begin
            HalfMoonOnOff(False);
            SendSocket(nil, '+UWID/' + IntToStr(GetTickCount));
          end;
        end;
        Result := True;
      end;
    SKILL_FIRESWORD {26}: begin //烈火剑法
        if m_MagicFireSwordSkill <> nil then begin
          if AllowFireHitSkill then begin
            nSpellPoint := GetSpellPoint(UserMagic);
            if m_WAbil.MP >= nSpellPoint then begin
              if nSpellPoint > 0 then begin
                DamageSpell(nSpellPoint);
                HealthSpellChanged();
              end;
              SendSocket(nil, '+FIR/' + IntToStr(GetTickCount));
            end;
          end;
        end;
        Result := True;
      end;
    SKILL_MOOTEBO {27}: begin //野蛮冲撞
        Result := True;
        if (GetTickCount - m_dwDoMotaeboTick) > 3 * 1000 then begin
          m_dwDoMotaeboTick := GetTickCount();
          m_btDirection := nTargetX;
          nSpellPoint := GetSpellPoint(UserMagic);
          if m_WAbil.MP >= nSpellPoint then begin
            if nSpellPoint > 0 then begin
              DamageSpell(nSpellPoint);
              HealthSpellChanged();
            end;
            if DoMotaebo(m_btDirection, UserMagic.btLevel) then begin
              if UserMagic.btLevel < 3 then begin
                if UserMagic.MagicInfo.TrainLevel[UserMagic.btLevel] < m_Abil.Level then begin
                  TrainSkill(UserMagic, Random(3) + 1);
                  if not CheckMagicLevelup(UserMagic) then begin

                    SendDelayMsg(Self,
                      RM_MAGIC_LVEXP,
                      0,
                      UserMagic.MagicInfo.wMagicId,
                      UserMagic.btLevel,
                      UserMagic.nTranPoint,
                      '', 1000);
                  end;
                end;
              end;
            end;
          end;
        end;
      end;
    SKILL_CROSSHALFMOON: begin // Cross Half Moon
        if m_MagicCrsSkill <> nil then begin
          if not m_boCrsHitkill then begin
            SkillCrsOnOff(True);
            SendSocket(nil, '+CRS/' + IntToStr(GetTickCount));
          end else begin
            SkillCrsOnOff(False);
            SendSocket(nil, '+UCRS/' + IntToStr(GetTickCount));
          end;
        end;
        Result := True;
      end;
    SKILL_TWINDRAKEBLADE: begin
        if m_Magic42Skill <> nil then begin
         if not m_bo42kill then begin
              nSpellPoint:=GetSpellPoint(UserMagic);
              if m_WAbil.MP >= nSpellPoint then begin
                if nSpellPoint > 0 then begin
                  DamageSpell(nSpellPoint);
                  HealthSpellChanged();
                end;
              end;
            Skill42OnOff(True);
            SendSocket(nil, '+TWN/' + IntToStr(GetTickCount));
          end else begin
            Skill42OnOff(False);
            SendSocket(nil, '+UTWN/' + IntToStr(GetTickCount));
             end;
            end;
        Result := True;
      end;
    SKILL_43: begin //破空剑
        if m_Magic43Skill <> nil then begin
          if AllowCIDHitSkill then begin
            nSpellPoint := GetSpellPoint(UserMagic);
            if m_WAbil.MP >= nSpellPoint then begin
              if nSpellPoint > 0 then begin
                DamageSpell(nSpellPoint);
                HealthSpellChanged();
              end;
              SendSocket(nil, '+CID/' + IntToStr(GetTickCount));
            end;
          end;
          Result := True;
        end;
      end;
    SKILL_58: begin //开天斩
        if m_Magic58Skill <> nil then begin
          if AllowKTZHitSkill then begin
            nSpellPoint := GetSpellPoint(UserMagic);
            if m_WAbil.MP >= nSpellPoint then begin
              if nSpellPoint > 0 then begin
                DamageSpell(nSpellPoint);
                HealthSpellChanged();
              end;
              SendSocket(nil, '+KTZ/' + IntToStr(GetTickCount));
            end;
          end;
          Result := True;
        end;
      end;
    SKILL_77: begin //逐日剑法
        if m_MagicZRJFSkill <> nil then begin
          if AllowZRJFHitSkill then begin
            nSpellPoint := GetSpellPoint(UserMagic);
            if m_WAbil.MP >= nSpellPoint then begin
              if nSpellPoint > 0 then begin
                DamageSpell(nSpellPoint);
                HealthSpellChanged();
              end;
              SendSocket(nil, '+ZRJF/' + IntToStr(GetTickCount));
            end;
          end;
          Result := True;
        end;
      end;

    SKILL_101: begin //追心刺
        Result := True;
        //if (GetTickCount - m_dwDoMotaeboTick) > 3 * 1000 then begin
          //m_dwDoMotaeboTick := GetTickCount();
        //n14 := GetNextDirection(m_nCurrX, m_nCurrY, nTargetX, nTargetY);
        //m_btDirection := n14;
        //MainOutMessage('追心刺1');
        m_btDirection := nTargetX;
        nSpellPoint := GetSpellPoint(UserMagic);
        if m_WAbil.MP >= nSpellPoint then begin
          if nSpellPoint > 0 then begin
            DamageSpell(nSpellPoint);
            HealthSpellChanged();
          end;
          //MainOutMessage('追心刺2');
          if DoRotationMotaebo(m_btDirection, UserMagic) then begin
            //MainOutMessage('追心刺3');
            if UserMagic.btLevel < 3 then begin
              if UserMagic.MagicInfo.TrainLevel[UserMagic.btLevel] < m_Abil.Level then begin
                TrainSkill(UserMagic, Random(3) + 1);
                if not CheckMagicLevelup(UserMagic) then begin

                  SendDelayMsg(Self,
                    RM_MAGIC_LVEXP,
                    0,
                    UserMagic.MagicInfo.wMagicId,
                    UserMagic.btLevel,
                    UserMagic.nTranPoint,
                    '', 1000);
                end;
              end;
            end;
          end;
        end;
        //end;
      end;

  else begin
      n14 := GetNextDirection(m_nCurrX, m_nCurrY, nTargetX, nTargetY);
      m_btDirection := n14;
      BaseObject := nil;
      //检查目标角色，与目标座标误差范围，如果在误差范围内则修正目标座标
      if UserMagic.wMagIdx in [60..65] then begin //如果是合击锁定目标
        if CretInNearXY(TargeTActorObject, nTargetX, nTargetY, 6) then begin
          BaseObject := TargeTActorObject;
          nTargetX := BaseObject.m_nCurrX;
          nTargetY := BaseObject.m_nCurrY;
        end;
      end else begin
        if CretInNearXY(TargeTActorObject, nTargetX, nTargetY, 3) then begin
          BaseObject := TargeTActorObject;
          nTargetX := BaseObject.m_nCurrX;
          nTargetY := BaseObject.m_nCurrY;
        end;
      end;

      if not DoSpell(UserMagic, nTargetX, nTargetY, BaseObject) then begin
        SendRefMsg(RM_MAGICFIREFAIL, 0, 0, 0, 0, '');
      end;
      Result := True;
    end;
  end;
end;

function TActorObject.IsPlayMon: Boolean;
begin
  Result := m_btRaceServer in [{RC_PLAYOBJECT, } 60, RC_HEROOBJECT, RC_PLAYMOSTER];
end;

function TActorObject.CanMove(nX, nY: Integer; boFlag: Boolean): Boolean;
begin
  if (abs(m_nCurrX - nX) <= 1) and (abs(m_nCurrY - nY) <= 1) then begin
    Result := m_PEnvir.CanWalkEx(nX, nY, boFlag) and m_PEnvir.CanWalkOfEvent(Self, nX, nY);
  end else begin
    Result := CanRun(nX, nY, boFlag) and m_PEnvir.CanWalkOfEvent(Self, nX, nY);
  end;
end;

function TActorObject.CanMove(nCurrX, nCurrY, nX, nY: Integer; boFlag: Boolean): Boolean;
begin
  if (abs(nCurrX - nX) <= 1) and (abs(nCurrY - nY) <= 1) then begin
    Result := m_PEnvir.CanWalkEx(nX, nY, boFlag) and m_PEnvir.CanWalkOfEvent(Self, nX, nY);
  end else begin
    Result := CanRun(nCurrX, nCurrY, nX, nY, boFlag) and m_PEnvir.CanWalkOfEvent(Self, nX, nY);
  end;
end;

function TActorObject.CanRun(nCurrX, nCurrY, nX, nY: Integer; boFlag: Boolean): Boolean;
var
  btDir: Byte;
begin
  Result := False;
  btDir := GetNextDirection(nCurrX, nCurrY, nX, nY);
  case btDir of
    DR_UP {0}: begin
        if (nCurrY > 1) and
          (m_PEnvir.CanWalkEx(nCurrX, nCurrY - 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(nCurrX, nCurrY - 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_UPRIGHT {1}: begin
        if (nCurrX < m_PEnvir.m_nWidth - 2) and
          (nCurrY > 1) and
          (m_PEnvir.CanWalkEx(nCurrX + 1, nCurrY - 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(nCurrX + 2, nCurrY - 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_RIGHT {2}: begin
        if (nCurrX < m_PEnvir.m_nWidth - 2) and
          (m_PEnvir.CanWalkEx(nCurrX + 1, nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(nCurrX + 2, nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_DOWNRIGHT {3}: begin
        if (nCurrX < m_PEnvir.m_nWidth - 2) and
          (nCurrY < m_PEnvir.m_nHeight - 2) and
          (m_PEnvir.CanWalkEx(nCurrX + 1, nCurrY + 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(nCurrX + 2, nCurrY + 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_DOWN {4}: begin
        if (nCurrY < m_PEnvir.m_nHeight - 2) and
          (m_PEnvir.CanWalkEx(nCurrX, nCurrY + 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(nCurrX, nCurrY + 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_DOWNLEFT {5}: begin
        if (nCurrX > 1) and
          (nCurrY < m_PEnvir.m_nHeight - 2) and
          (m_PEnvir.CanWalkEx(nCurrX - 1, nCurrY + 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(nCurrX - 2, nCurrY + 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_LEFT {6}: begin
        if (nCurrX > 1) and
          (m_PEnvir.CanWalkEx(nCurrX - 1, nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(nCurrX - 2, nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_UPLEFT {7}: begin
        if (nCurrX > 1) and
          (nCurrY > 1) and
          (m_PEnvir.CanWalkEx(nCurrX - 1, nCurrY - 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(nCurrX - 2, nCurrY - 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
  end;
end;

function TActorObject.CanRun(nX, nY: Integer; boFlag: Boolean): Boolean;
var
  btDir: Byte;
begin
  Result := False;
  btDir := GetNextDirection(m_nCurrX, m_nCurrY, nX, nY);
  case btDir of
    DR_UP {0}: begin
        if (m_nCurrY > 1) and
          (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY - 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY - 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_UPRIGHT {1}: begin
        if (m_nCurrX < m_PEnvir.m_nWidth - 2) and
          (m_nCurrY > 1) and
          (m_PEnvir.CanWalkEx(m_nCurrX + 1, m_nCurrY - 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(m_nCurrX + 2, m_nCurrY - 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_RIGHT {2}: begin
        if (m_nCurrX < m_PEnvir.m_nWidth - 2) and
          (m_PEnvir.CanWalkEx(m_nCurrX + 1, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(m_nCurrX + 2, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_DOWNRIGHT {3}: begin
        if (m_nCurrX < m_PEnvir.m_nWidth - 2) and
          (m_nCurrY < m_PEnvir.m_nHeight - 2) and
          (m_PEnvir.CanWalkEx(m_nCurrX + 1, m_nCurrY + 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(m_nCurrX + 2, m_nCurrY + 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_DOWN {4}: begin
        if (m_nCurrY < m_PEnvir.m_nHeight - 2) and
          (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY + 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY + 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_DOWNLEFT {5}: begin
        if (m_nCurrX > 1) and
          (m_nCurrY < m_PEnvir.m_nHeight - 2) and
          (m_PEnvir.CanWalkEx(m_nCurrX - 1, m_nCurrY + 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(m_nCurrX - 2, m_nCurrY + 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_LEFT {6}: begin
        if (m_nCurrX > 1) and
          (m_PEnvir.CanWalkEx(m_nCurrX - 1, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(m_nCurrX - 2, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
    DR_UPLEFT {7}: begin
        if (m_nCurrX > 1) and
          (m_nCurrY > 1) and
          (m_PEnvir.CanWalkEx(m_nCurrX - 1, m_nCurrY - 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
          (m_PEnvir.CanWalkEx(m_nCurrX - 2, m_nCurrY - 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone))
          then begin
          Result := True;
        end;
      end;
  end;
end;

function TActorObject.RunTo(btDir: Byte; boFlag: Boolean; nDestX, nDestY: Integer): Boolean;
var
  nOldX, nOldY: Integer;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::RunTo';
begin
  Result := False;
  try
    nOldX := m_nCurrX;
    nOldY := m_nCurrY;
    m_btDirection := btDir;
    case btDir of
      DR_UP {0}: begin
          if (m_nCurrY > 1) and
            (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY - 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY - 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX, m_nCurrY - 2, True) > 0) then begin
            Dec(m_nCurrY, 2);
          end;
        end;
      DR_UPRIGHT {1}: begin
          if (m_nCurrX < m_PEnvir.m_nWidth - 2) and
            (m_nCurrY > 1) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 1, m_nCurrY - 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 2, m_nCurrY - 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX + 2, m_nCurrY - 2, True) > 0) then begin
            Inc(m_nCurrX, 2);
            Dec(m_nCurrY, 2);
          end;
        end;
      DR_RIGHT {2}: begin
          if (m_nCurrX < m_PEnvir.m_nWidth - 2) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 1, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 2, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX + 2, m_nCurrY, True) > 0) then begin
            Inc(m_nCurrX, 2);
          end;
        end;
      DR_DOWNRIGHT {3}: begin
          if (m_nCurrX < m_PEnvir.m_nWidth - 2) and
            (m_nCurrY < m_PEnvir.m_nHeight - 2) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 1, m_nCurrY + 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 2, m_nCurrY + 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX + 2, m_nCurrY + 2, True) > 0) then begin
            Inc(m_nCurrX, 2);
            Inc(m_nCurrY, 2);
          end;
        end;
      DR_DOWN {4}: begin
          if (m_nCurrY < m_PEnvir.m_nHeight - 2) and
            (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY + 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY + 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX, m_nCurrY + 2, True) > 0) then begin
            Inc(m_nCurrY, 2);
          end;
        end;
      DR_DOWNLEFT {5}: begin
          if (m_nCurrX > 1) and
            (m_nCurrY < m_PEnvir.m_nHeight - 2) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 1, m_nCurrY + 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 2, m_nCurrY + 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX - 2, m_nCurrY + 2, True) > 0) then begin
            Dec(m_nCurrX, 2);
            Inc(m_nCurrY, 2);
          end;
        end;
      DR_LEFT {6}: begin
          if (m_nCurrX > 1) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 1, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 2, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX - 2, m_nCurrY, True) > 0) then begin
            Dec(m_nCurrX, 2);
          end;
        end;
      DR_UPLEFT {7}: begin
          if (m_nCurrX > 1) and
            (m_nCurrY > 1) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 1, m_nCurrY - 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 2, m_nCurrY - 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX - 2, m_nCurrY - 2, True) > 0) then begin
            Dec(m_nCurrX, 2);
            Dec(m_nCurrY, 2);
          end;
        end;
    end;
    if ((m_nCurrX <> nOldX) or (m_nCurrY <> nOldY)) {and ((m_nCurrX = nDestX) and (m_nCurrY = nDestY))} then begin
      if not WalkToSpaceLock(nOldX, nOldY, m_nCurrX, m_nCurrY) then begin
        if Walk(RM_RUN) then Result := True
        else begin
          if m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nOldX, nOldY, True) > 0 then begin //修改
            m_nCurrX := nOldX;
            m_nCurrY := nOldY;
          end;
        end;
      end else begin
        if m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nOldX, nOldY, True) > 0 then begin //修改
          m_nCurrX := nOldX;
          m_nCurrY := nOldY;
        end;
      end;
    end;
  except
    MainOutMessage(sExceptionMsg);
  end;
end;

function TPlayObject.HorseRunTo(btDir: Byte; boFlag: Boolean): Boolean;
var
  n10, n14: Integer;
resourcestring
  sExceptionMsg = '[Exception] TPlayObject::HorseRunTo';
begin
  Result := False;
  try
    n10 := m_nCurrX;
    n14 := m_nCurrY;
    m_btDirection := btDir;
    case btDir of
      DR_UP {0}: begin
          if (m_nCurrY > 2) and
            (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY - 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY - 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY - 3, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX, m_nCurrY - 3, True) > 0) then begin

            Dec(m_nCurrY, 3);
          end;
        end;
      DR_UPRIGHT {1}: begin
          if (m_nCurrX < m_PEnvir.m_nWidth - 3) and
            (m_nCurrY > 2) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 1, m_nCurrY - 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 2, m_nCurrY - 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 3, m_nCurrY - 3, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX + 3, m_nCurrY - 3, True) > 0) then begin

            Inc(m_nCurrX, 3);
            Dec(m_nCurrY, 3);
          end;
        end;
      DR_RIGHT {2}: begin
          if (m_nCurrX < m_PEnvir.m_nWidth - 3) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 1, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 2, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 3, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX + 3, m_nCurrY, True) > 0) then begin

            Inc(m_nCurrX, 3);
          end;
        end;
      DR_DOWNRIGHT {3}: begin
          if (m_nCurrX < m_PEnvir.m_nWidth - 3) and
            (m_nCurrY < m_PEnvir.m_nHeight - 3) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 1, m_nCurrY + 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 2, m_nCurrY + 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX + 3, m_nCurrY + 3, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX + 3, m_nCurrY + 3, True) > 0) then begin

            Inc(m_nCurrX, 3);
            Inc(m_nCurrY, 3);
          end;
        end;
      DR_DOWN {4}: begin
          if (m_nCurrY < m_PEnvir.m_nHeight - 3) and
            (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY + 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY + 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX, m_nCurrY + 3, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX, m_nCurrY + 3, True) > 0) then begin

            Inc(m_nCurrY, 3);
          end;
        end;
      DR_DOWNLEFT {5}: begin
          if (m_nCurrX > 2) and
            (m_nCurrY < m_PEnvir.m_nHeight - 3) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 1, m_nCurrY + 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 2, m_nCurrY + 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 3, m_nCurrY + 3, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX - 3, m_nCurrY + 3, True) > 0) then begin

            Dec(m_nCurrX, 3);
            Inc(m_nCurrY, 3);
          end;
        end;
      DR_LEFT {6}: begin
          if (m_nCurrX > 2) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 1, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 2, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 3, m_nCurrY, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX - 3, m_nCurrY, True) > 0) then begin

            Dec(m_nCurrX, 3);
          end;
        end;
      DR_UPLEFT {7}: begin
          if (m_nCurrX > 2) and
            (m_nCurrY > 2) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 1, m_nCurrY - 1, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 2, m_nCurrY - 2, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.CanWalkEx(m_nCurrX - 3, m_nCurrY - 3, g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll)) or (g_Config.boSafeAreaLimited and InSafeZone)) and
            (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, m_nCurrX - 3, m_nCurrY - 3, True) > 0) then begin

            Dec(m_nCurrX, 3);
            Dec(m_nCurrY, 3);
          end;
        end;
    end;
    //    SysMsg(format('原X:%d 原Y:%d 新X:%d 新Y:%d',[n10,n14,m_nCurrX,m_nCurrY]),c_Green,t_Hint);

    if (m_nCurrX <> n10) or (m_nCurrY <> n14) then begin
      if not WalkToSpaceLock(n10, n14, m_nCurrX, m_nCurrY) then begin
        if Walk(RM_HORSERUN) then Result := True
        else begin
          if m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, n10, n14, True) > 0 then begin //修改
            m_nCurrX := n10;
            m_nCurrY := n14;
          end;
        end;
      end else begin
        if m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, n10, n14, True) > 0 then begin //修改
          m_nCurrX := n10;
          m_nCurrY := n14;
        end;
      end;
    end;
  except
    MainOutMessage(sExceptionMsg);
  end;
end;

function TPlayObject.ClientRunXY(wIdent: Word; nX, nY: Integer; nFlag: Integer; var dwDelayTime: LongWord): Boolean; //004CB11C
var
  nDir: Integer;
  dwCheckTime: LongWord;
  dwClientTime: LongWord;
  dwServerTime: LongWord;
begin
  Result := False;
  dwDelayTime := 0;
  if (not m_boCanRun) or (m_NewStatus = sBlind) then Exit;
  if m_boDeath or ((m_wStatusTimeArr[POISON_STONE {5}] {0x6A} <> 0) and not g_Config.ClientConf.boParalyCanRun) then Exit; //防麻

  if nFlag <> wIdent then begin

    if not CheckActionStatus(wIdent, dwDelayTime) then begin
      m_boFilterAction := False;
      Exit;
    end;
    m_boFilterAction := True;
    dwCheckTime := GetTickCount - m_dwMoveTick;

    if dwCheckTime < g_Config.dwRunIntervalTime then begin
      Inc(m_dwMoveCount);
      dwDelayTime := g_Config.dwRunIntervalTime - dwCheckTime;
      if dwDelayTime > g_Config.dwRunIntervalTime div 3 then begin
        if m_dwMoveCount >= 4 then begin
          m_dwMoveTick := GetTickCount();
          m_dwMoveCount := 0;
          dwDelayTime := g_Config.dwRunIntervalTime div 3;
          if m_boTestSpeedMode then
            SysMsg('Running error.' + IntToStr(dwDelayTime), c_Red, t_Hint);
        end else m_dwMoveCount := 0;
        Exit;
      end else begin
        if m_boTestSpeedMode then
          SysMsg('Running error.' + IntToStr(dwDelayTime), c_Red, t_Hint);
        Exit;
      end;
    end;
  end;
  {
  if (GetTickCount - m_dwMoveTick) < 600 then begin
    Inc(m_dwMoveCount);
    Inc(m_dwMoveCountA);
  end else begin
    m_dwMoveCount:=0;
    if m_dwMoveCountA > 0 then Dec(m_dwMoveCountA);
  end;
  }
  m_dwMoveTick := GetTickCount();
  //  if (m_dwMoveCount < 4) and (m_dwMoveCountA < 6) then begin

  {if m_dwClientLastTick = 0 then begin
    m_dwClientLastTick := dwClientLastTick;
  end else begin
    dwClientTime:=dwClientLastTick - m_dwClientLastTick;
    //SysMsg('RUN ClientMoveTick:' + IntToStr(dwClientLastTick - m_dwClientLastTick), c_Red, t_Hint);
    m_dwClientLastTick := dwClientLastTick;
  end;
  if m_dwServerLastTick = 0 then begin
    m_dwServerLastTick := GetTickCount;
  end else begin
    dwServerTime:=GetTickCount - m_dwServerLastTick;
    //SysMsg('RUN ServerMoveTick:' + IntToStr(GetTickCount - m_dwServerLastTick), c_Red, t_Hint);
    m_dwServerLastTick := GetTickCount;
  end;
  if dwServerTime < 1000 then
  SysMsg('RUN ServerMoveTick:' + IntToStr(abs(dwServerTime - dwClientTime)), c_Red, t_Hint);
  }
  m_bo316 := False;
  nDir := GetNextDirection(m_nCurrX, m_nCurrY, nX, nY);
  if RunTo(nDir, False, nX, nY) then begin
    m_dwStationTick := GetTickCount; //增加检测人物站立不动时间
    if m_boTransparent and (m_boHideMode) then m_wStatusTimeArr[STATE_TRANSPARENT {0 0x70}] := 1;
    if m_bo316 or ((m_nCurrX = nX) and (m_nCurrY = nY)) then
      Result := True;
    Dec(m_nHealthTick, 60);
    Dec(m_nSpellTick, 10);
    m_nSpellTick := _MAX(0, m_nSpellTick);
    Dec(m_nPerHealth);
    Dec(m_nPerSpell);
  end else begin
    m_dwMoveCount := 0;
    m_dwMoveCountA := 0;
  end;
  {
    end else begin
      Inc(m_dwOverSpeedCount);
      //if m_dwOverSpeedCount > 8 then m_boEmergencyClose:=True;
      SysMsg('跑步超速！！！',c_Red,t_Hint);
      if boViewHackMessage then begin
        MainOutMessage('[11002-Run] ' + m_sCharName + ' ' + DateToStr(Now));
      end;
    end;
  }
end;

function TPlayObject.ClientWalkXY(wIdent: Word; nX, nY: Integer; boLateDelivery: Boolean; var dwDelayTime: LongWord): Boolean; //004CAF08
var
  n14, n18, n1C: Integer;
  dwCheckTime: LongWord;
  dwClientTime: LongWord;
  dwServerTime: LongWord;
begin
  Result := False;
  dwDelayTime := 0;
  if (not m_boCanWalk) or (m_NewStatus = sBlind) then Exit;
  if m_boDeath or ((m_wStatusTimeArr[POISON_STONE {5}] {0x6A} <> 0) and not g_Config.ClientConf.boParalyCanWalk) then Exit; //防麻

  if not boLateDelivery then begin
    if not CheckActionStatus(wIdent, dwDelayTime) then begin
      m_boFilterAction := False;
      Exit;
    end;
    m_boFilterAction := True;
    dwCheckTime := GetTickCount - m_dwMoveTick;

    if dwCheckTime < g_Config.dwWalkIntervalTime then begin
      Inc(m_dwMoveCount);
      dwDelayTime := g_Config.dwWalkIntervalTime - dwCheckTime;
      if dwDelayTime > g_Config.dwWalkIntervalTime div 3 then begin
        if m_dwMoveCount >= 4 then begin
          m_dwMoveTick := GetTickCount();
          m_dwMoveCount := 0;
          dwDelayTime := g_Config.dwWalkIntervalTime div 3;
          if m_boTestSpeedMode then
            SysMsg('Walk error' + IntToStr(dwDelayTime), c_Red, t_Hint);
        end else m_dwMoveCount := 0;
        Exit;
      end else begin
        if m_boTestSpeedMode then
          SysMsg('Walk error' + IntToStr(dwDelayTime), c_Red, t_Hint);
        Exit;
      end;
    end;
  end;
{  if m_dwClientLastTick = 0 then begin
    m_dwClientLastTick := dwClientLastTick;
  end else begin
    dwClientTime:=dwClientLastTick - m_dwClientLastTick;
    //SysMsg('RUN ClientMoveTick:' + IntToStr(dwClientLastTick - m_dwClientLastTick), c_Red, t_Hint);
    m_dwClientLastTick := dwClientLastTick;
  end;
  if m_dwServerLastTick = 0 then begin
    m_dwServerLastTick := GetTickCount;
  end else begin
    dwServerTime:=GetTickCount - m_dwServerLastTick;
    //SysMsg('RUN ServerMoveTick:' + IntToStr(GetTickCount - m_dwServerLastTick), c_Red, t_Hint);
    m_dwServerLastTick := GetTickCount;
  end;
  if dwServerTime < 1000 then
    SysMsg('Walk ServerMoveTick:' + IntToStr(abs(dwServerTime - dwClientTime)), c_Red, t_Hint);
   }
  {
  if (GetTickCount - m_dwMoveTick) < 600 then begin
    Inc(m_dwMoveCount);
    Inc(m_dwMoveCountA);
  end else begin
    m_dwMoveCount:=0;
    if m_dwMoveCountA > 0 then Dec(m_dwMoveCountA);
  end;
  }
  m_dwMoveTick := GetTickCount();
  m_bo316 := False;
  n18 := m_nCurrX;
  n1C := m_nCurrY;
  n14 := GetNextDirection(m_nCurrX, m_nCurrY, nX, nY);
  {if not m_boClientFlag then begin
    if (n14 = 0) and (m_nStep = 0) then Inc(m_nStep)
    else
      if (n14 = 4) and (m_nStep = 1) then Inc(m_nStep)
    else
      if (n14 = 6) and (m_nStep = 2) then Inc(m_nStep)
    else
      if (n14 = 2) and (m_nStep = 3) then Inc(m_nStep)
    else
      if (n14 = 1) and (m_nStep = 4) then Inc(m_nStep)
    else
      if (n14 = 5) and (m_nStep = 5) then Inc(m_nStep)
    else
      //      if (n14 = 3) and (m_nStep = 6) then Inc(m_nStep)
      if (n14 = 7) and (m_nStep = 6) then Inc(m_nStep)
    else
      //      if (n14 = 7) and (m_nStep = 7) then Inc(m_nStep)
      if (n14 = 3) and (m_nStep = 7) then Inc(m_nStep)
    else begin
      m_nStep := 0;
    end;
  end;}

  if WalkTo(n14, False) then begin
    m_dwStationTick := GetTickCount; //增加检测人物站立不动时间
    if m_bo316 or ((m_nCurrX = nX) and (m_nCurrY = nY)) then
      Result := True;
    Dec(m_nHealthTick, 10);
  end else begin
    m_dwMoveCount := 0;
    m_dwMoveCountA := 0;
  end;
  {
  end else begin
    Inc(m_dwOverSpeedCount);
    //if m_dwOverSpeedCount > 8 then m_boEmergencyClose:=True;
    SysMsg('走步超速！！！',c_Red,t_Hint);
    if boViewHackMessage then begin
      MainOutMessage('[11002-Walk] ' + m_sCharName + ' ' + DateToStr(Now));
    end;
  end;
  }
end;

procedure TActorObject.ThrustingOnOff(boSwitch: Boolean);
begin
  m_boUseThrusting := boSwitch;
  if m_boUseThrusting then begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        SysMsg(sThrustingOn, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        THeroObject(Self).SysMsg(sThrustingOn, c_Green, t_Hint);
    end;
  end else begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        SysMsg(sThrustingOff, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        THeroObject(Self).SysMsg(sThrustingOff, c_Green, t_Hint);
    end;
  end;
end;

procedure TActorObject.HalfMoonOnOff(boSwitch: Boolean);
begin
  m_boUseHalfMoon := boSwitch;
  if m_boUseHalfMoon then begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        SysMsg(sHalfMoonOn, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        THeroObject(Self).SysMsg(sHalfMoonOn, c_Green, t_Hint);
    end;
  end else begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        SysMsg(sHalfMoonOff, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        THeroObject(Self).SysMsg(sHalfMoonOff, c_Green, t_Hint);
    end;
  end;
end;

procedure TActorObject.SkillCrsOnOff(boSwitch: Boolean);
begin
  m_boCrsHitkill := boSwitch;
  if m_boCrsHitkill then begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        SysMsg(sCrsHitOn, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        THeroObject(Self).SysMsg(sCrsHitOn, c_Green, t_Hint);
    end;
  end else begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        SysMsg(sCrsHitOff, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        THeroObject(Self).SysMsg(sCrsHitOff, c_Green, t_Hint);
    end;
  end;
end;

procedure TActorObject.Skill42OnOff(boSwitch: Boolean);
begin
  m_bo42kill := boSwitch;
  if m_bo42kill then begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        SysMsg('TwinDrakeBlade is Charged!', c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        THeroObject(Self).SysMsg('TwinDrakeBlade did not charge.', c_Green, t_Hint);
    end;
  end else begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        SysMsg('TwinDrakeBlade is Charged!', c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        THeroObject(Self).SysMsg('TwinDrakeBlade did not charge.', c_Green, t_Hint);
    end;
  end;
end;

procedure TActorObject.Skill43OnOff(boSwitch: Boolean);
begin
  m_bo43kill := boSwitch;
  if m_bo43kill then begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        SysMsg('Skill43 ready', c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        THeroObject(Self).SysMsg('Skill43 ready', c_Green, t_Hint);
    end;
  end else begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        SysMsg('Skill43 failed', c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        THeroObject(Self).SysMsg('Skill43 failed', c_Green, t_Hint);
    end;
  end;
end;

procedure TActorObject.Skill60OnOff(boSwitch: Boolean);
begin
  //m_bo60kill := boSwitch;
  if m_bo60kill then begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        SysMsg('Skill60 ready', c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        THeroObject(Self).SysMsg('Skill60 ready', c_Green, t_Hint);
    end;
  end else begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        SysMsg('Skill60 failed', c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        THeroObject(Self).SysMsg('Skill60 failed', c_Green, t_Hint);
    end;
  end;
end;

function TActorObject.AllowZRJFHitSkill(): Boolean;
begin
  Result := False;
  if (GetTickCount - m_dwLatestZRJFHitTick) > g_Config.nSkillZRJFDelayTime * 1000 then begin
    m_dwLatestZRJFHitTick := GetTickCount();
    m_boZRJFHitSkill := True;
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        SysMsg(sZRJFSpiritsSummoned, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        THeroObject(Self).SysMsg(sZRJFSpiritsSummoned, c_Green, t_Hint);
    end;
    //SysMsg(sFireSpiritsSummoned, c_Green, t_Hint);
    Result := True;
  end else begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        SysMsg(sZRJFSpiritsFail, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        THeroObject(Self).SysMsg(sZRJFSpiritsFail, c_Green, t_Hint);
    end;
  end;
end;

function TActorObject.AllowFireHitSkill(): Boolean;
begin
  Result := False;
  if (GetTickCount - m_dwLatestFireHitTick) > 10 * 1000 then begin
    m_dwLatestFireHitTick := GetTickCount();
    m_boFireHitSkill := True;
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        SysMsg(sFireSpiritsSummoned, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        THeroObject(Self).SysMsg(sFireSpiritsSummoned, c_Green, t_Hint);
    end;
    //SysMsg(sFireSpiritsSummoned, c_Green, t_Hint);
    Result := True;
  end else begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        SysMsg(sFireSpiritsFail, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        THeroObject(Self).SysMsg(sFireSpiritsFail, c_Green, t_Hint);
    end;
  end;
end;

{function TActorObject.AllowTwinHitSkill(): Boolean;
begin
  Result := False;
  if (GetTickCount - m_dwLatestTwinHitTick) > g_Config.nSkill42DelayTime * 1000 then begin
    m_dwLatestTwinHitTick := GetTickCount();
    m_bo42kill:= True;
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        //SysMsg(sCidSpiritsSummoned, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        //THeroObject(Self).SysMsg(sCidSpiritsSummoned, c_Green, t_Hint);
    end;
    //SysMsg(sFireSpiritsSummoned, c_Green, t_Hint);
    Result := True;
  end else begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        //SysMsg(sCidSpiritsFail, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        //THeroObject(Self).SysMsg(sCidSpiritsFail, c_Green, t_Hint);
    end;
  end;
end;}

function TActorObject.AllowCIDHitSkill(): Boolean;
begin
  Result := False;
  if (GetTickCount - m_dwLatestCIDHitTick) > g_Config.nSkill43DelayTime * 1000 then begin
    m_dwLatestCIDHitTick := GetTickCount();
    m_boCIDHitSkill := True;
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        SysMsg(sCidSpiritsSummoned, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        THeroObject(Self).SysMsg(sCidSpiritsSummoned, c_Green, t_Hint);
    end;
    //SysMsg(sFireSpiritsSummoned, c_Green, t_Hint);
    Result := True;
  end else begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        SysMsg(sCidSpiritsFail, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        THeroObject(Self).SysMsg(sCidSpiritsFail, c_Green, t_Hint);
    end;
  end;
end;

function TActorObject.AllowKTZHitSkill(): Boolean;
begin
  Result := False;
  if (GetTickCount - m_dwLatestKTZHitTick) > g_Config.nSkill58DelayTime * 1000 then begin
    m_dwLatestKTZHitTick := GetTickCount();
    m_boKTZHitSkill := True;
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        SysMsg(sKTZSpiritsSummoned, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicOK then
        THeroObject(Self).SysMsg(sKTZSpiritsSummoned, c_Green, t_Hint);
    end;
    //SysMsg(sFireSpiritsSummoned, c_Green, t_Hint);
    Result := True;
  end else begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        SysMsg(sKTZSpiritsFail, c_Green, t_Hint);
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      if not g_Config.boNoHintMagicFail then
        THeroObject(Self).SysMsg(sKTZSpiritsFail, c_Green, t_Hint);
    end;
  end;
end;

procedure TActorObject.MapRandomMove(sMapName: string; nInt: Integer);
var
  OEnvir, Envir: TEnvirnoment;
  n10, n14, n18: Integer;
begin
  OEnvir := m_PEnvir;
  Envir := g_MapManager.FindMap(sMapName);
  if Envir <> nil then begin
    if Envir.m_nHeight < 150 then begin
      if Envir.m_nHeight < 30 then begin
        n18 := 2;
      end else n18 := 20;
    end else n18 := 50;
    n10 := Random(Envir.m_nWidth - n18 - 1) + n18;
    n14 := Random(Envir.m_nHeight - n18 - 1) + n18;
    SpaceMove(sMapName, n10, n14, nInt);
  end;
end;

procedure TPlayObject.ClientClickNPC(NPC, Param: Integer);
var
  NormNpc: TNormNpc;
begin
  if Param = 1 then SendDefMessage(SM_CLOSEBIGDIALOGBOX, 0, 0, 0, 0, ''); //关闭NPC大对话框
  if not m_boCanDeal then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sCanotTryDealMsg);
    Exit;
  end;
  if {m_boDeath or }  m_boGhost then Exit; //修改死亡可以点击NPC

  NormNpc := UserEngine.FindMerchant(TObject(NPC));
  if NormNpc = nil then
    NormNpc := UserEngine.FindNPC(TObject(NPC));

  if NormNpc <> nil then begin
    if (NormNpc.m_PEnvir = m_PEnvir) and (abs(NormNpc.m_nCurrX - m_nCurrX) <= 15) and (abs(NormNpc.m_nCurrY - m_nCurrY) <= 15) then begin
      NormNpc.Click(Self);
    end;
  end;
end;

function TActorObject.AddItemToBag(UserItem: pTUserItem): Boolean;
begin
  Result := False;

  if m_ItemList.Count < MAXBAGITEM then begin
    m_ItemList.Add(UserItem);
    WeightChanged();
    Result := True;
  end;
end;

function TPlayObject.GetRangeHumanCount: Integer;
begin
  Result := UserEngine.GetMapOfRangeHumanCount(m_PEnvir, m_nCurrX, m_nCurrY, 10);
end;

procedure TActorObject.sub_4C713C(Magic: pTUserMagic);
begin
  if Magic <> nil then begin
    if Magic.MagicInfo.wMagicId = 28 then
      if Magic.btLevel >= 2 then m_boAbilSeeHealGauge := True;
  end;
end;

procedure TPlayObject.GetStartPoint;
var
  I: Integer;
  StartPoint: pTStartPoint;
begin
  for I := 0 to g_StartPointList.Count - 1 do begin
    if g_StartPointList.Strings[I] = m_PEnvir.sMapName then begin
      StartPoint := pTStartPoint(g_StartPointList.Objects[I]);
      if (abs(m_nCurrX - StartPoint.nCurrX) < 50) and (abs(m_nCurrY - StartPoint.nCurrY) < 50) then begin
        m_sHomeMap := g_StartPointList.Strings[I];
        m_nHomeX := StartPoint.nCurrX;
        m_nHomeY := StartPoint.nCurrY;
        Break;
      end;
    end;
  end;
  if PKLevel >= 2 then begin
    m_sHomeMap := g_Config.sRedHomeMap;
    m_nHomeX := g_Config.nRedHomeX;
    m_nHomeY := g_Config.nRedHomeY;
  end;
end;

procedure TPlayObject.MobPlace(sX, sY, sMonName, sCount: string);
begin

end;

function TActorObject.DoRotationMotaebo(nDir: Byte; UserMagic: pTUserMagic): Boolean;
  function CanMotaebo(BaseObject: TActorObject): Boolean;
  var
    nC: Integer;
  begin
    Result := False;
    if (m_Abil.Level > BaseObject.m_Abil.Level) and (not BaseObject.m_boStickMode) then begin
      //nC := m_Abil.Level - BaseObject.m_Abil.Level;
      //if Random(20) < ((nMagicLevel * 4) + 6 + nC) then begin
      //  if IsProperTarget(BaseObject) then Result := True;
      //end;
      if IsProperTarget(BaseObject) then Result := True;
    end;
  end;
var
  bo35: Boolean;
  I, n20, n24, n28, nPower: Integer;
  PoseCreate: TActorObject;
  BaseObject_30: TActorObject;
  BaseObject_34: TActorObject;
  nX, nY: Integer;
begin
  Result := False;
  bo35 := True;
  m_btDirection := nDir;
  BaseObject_34 := nil;
  n24 := UserMagic.btLevel + 1;
  n28 := n24;
  PoseCreate := GetPoseCreate();
  if PoseCreate <> nil then begin
    n28 := 0;
    if CanMotaebo(PoseCreate) then begin
      BaseObject_34 := PoseCreate;
      if PoseCreate.CharPushed(m_btDirection, 1) = 1 then begin
        BaseObject_34 := PoseCreate;
        GetFrontPosition(nX, nY);
        if m_PEnvir.CanWalkOfEvent(Self, nX, nY) and (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nX, nY, False) > 0) then begin
          m_nCurrX := nX;
          m_nCurrY := nY;
          //SendRefMsg(RM_101HIT, nDir, m_nCurrX, m_nCurrY, 0, '');
          bo35 := False;
          Result := True;
        end;
        Dec(n24);
      end;
    end;
  end;

  if BaseObject_34 <> nil then begin
    nPower := GetAttackPower(LoWord(m_WAbil.DC), Integer((HiWord(m_WAbil.DC) - LoWord(m_WAbil.DC))));
    nPower := Round(nPower * (g_Config.SerieMagicPowerRate[UserMagic.wMagIdx - 100] / 100));
    if UserMagic.btLevel in [1..3] then begin
      for I := 0 to g_Config.SerieMagicBlasthitRate[UserMagic.wMagIdx - 100, UserMagic.btLevel] do begin
        if Random(100 - g_Config.SerieMagicBlasthitRate[UserMagic.wMagIdx - 100, UserMagic.btLevel]) = 0 then begin
          nPower := Round(nPower * (g_Config.SerieMagicBlasthitPowerRate[UserMagic.wMagIdx - 100] / 100));
          SendSerieMagicBlasthitMsg(UserMagic);
          break;
        end;
      end;
    end;
    BaseObject_34.SendRefMsg(RM_STRUCK, n20, BaseObject_34.m_WAbil.HP, BaseObject_34.m_WAbil.MaxHP, Integer(Self), '');
    if BaseObject_34.m_btRaceServer <> RC_PLAYOBJECT then begin
      BaseObject_34.SendMsg(BaseObject_34, RM_STRUCK, n20, BaseObject_34.m_WAbil.HP, BaseObject_34.m_WAbil.MaxHP, Integer(Self), '');
    end;
  end;
  SendRefMsg(RM_101HIT, nDir, m_nCurrX, m_nCurrY, 0, '');
end;
(*
  function CanMotaebo(BaseObject: TActorObject): Boolean;
  var
    nC: Integer;
  begin
    Result := False;
    if (m_Abil.Level > BaseObject.m_Abil.Level) and (not BaseObject.m_boStickMode) then begin
      //nC := m_Abil.Level - BaseObject.m_Abil.Level;
      //if Random(20) < ((nMagicLevel * 4) + 6 + nC) then begin
      //  if IsProperTarget(BaseObject) then Result := True;
      //end;
      if IsProperTarget(BaseObject) then Result := True;
    end;
  end;
var
  bo35: Boolean;
  I, n20, n24, n28: Integer;
  PoseCreate: TActorObject;
  BaseObject_30: TActorObject;
  BaseObject_34: TActorObject;
  nX, nY: Integer;
begin
  Result := False;
  bo35 := True;
  m_btDirection := nDir;
  BaseObject_34 := nil;
  n24 := nMagicLevel + 1;
  n28 := n24;
  SendRefMsg(RM_101HIT, m_btDirection, m_nCurrX, m_nCurrY, 0, '');
  PoseCreate := GetPoseCreate();

  if (PoseCreate <> nil) and CanMotaebo(PoseCreate) then begin
    for I := 0 to _MAX(1, nMagicLevel) do begin
      PoseCreate := GetPoseCreate();
      if PoseCreate <> nil then begin
        n28 := 0;
        if not CanMotaebo(PoseCreate) then Break;

        BaseObject_34 := PoseCreate;
        if PoseCreate.CharPushed(m_btDirection, 1) <> 1 then Break;
        GetFrontPosition(nX, nY);
        if m_PEnvir.CanWalkOfEvent(Self, nX, nY) and (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nX, nY, False) > 0) then begin
          m_nCurrX := nX;
          m_nCurrY := nY;
          SendRefMsg(RM_RUSH101, nDir, m_nCurrX, m_nCurrY, 0, '');
          bo35 := False;
          Result := True;
        end;
        Dec(n24);
      end; //004C32D7  if PoseCreate <> nil  then begin
    end; //004C32DD for i:=0 to _MAX(2,nMagicLevel + 1) do begin

    if (BaseObject_34 <> nil) then begin
      if n24 < 0 then n24 := 0;
      n20 := Random((n24 + 1) * 10) + ((n24 + 1) * 10);

      BaseObject_34.m_boNotDefendoof := GetNotDefendoof; //忽视目标防御
      n20 := n20 + GetAddPowerPoint(5, n20);

      n20 := BaseObject_34.GetHitStruckDamage(Self, n20);
      BaseObject_34.StruckDamage(n20);
      BaseObject_34.StartNewShield();
      BaseObject_34.SendRefMsg(RM_STRUCK, n20, BaseObject_34.m_WAbil.HP, BaseObject_34.m_WAbil.MaxHP, Integer(Self), '');
      if BaseObject_34.m_btRaceServer <> RC_PLAYOBJECT then begin
        BaseObject_34.SendMsg(BaseObject_34, RM_STRUCK, n20, BaseObject_34.m_WAbil.HP, BaseObject_34.m_WAbil.MaxHP, Integer(Self), '');
      end;
   { if (AttackTarget <> nil) and (AttackTarget.m_btRaceServer <> RC_PLAYOBJECT) then
      AttackTarget.SendMsg(AttackTarget, RM_STRUCK, nPower, AttackTarget.m_WAbil.HP, AttackTarget.m_WAbil.MaxHP, Integer(Self), '');
    end;    }

     // if bo35 then begin
     //   GetFrontPosition(nX, nY);
     //   SendRefMsg(RM_RUSH101, m_btDirection, nX, nY, 0, '');
     //   SysMsg(sMateDoTooweak {冲撞力不够！！！}, c_Red, t_Hint);
     // end;

   { if n28 > 0 then begin
      if n24 < 0 then n24 := 0;
      n20 := Random(n24 * 10) + ((n24 + 1) * 3);

      m_boNotDefendoof := GetNotDefendoof; //忽视目标防御

      n20 := GetHitStruckDamage(Self, n20);
      StruckDamage(n20);
      StartNewShield();
      SendRefMsg(RM_STRUCK, n20, m_WAbil.HP, m_WAbil.MaxHP, 0, '');
    end; }
    end;
  end else begin //004C32E8 if PoseCreate <> nil  then begin
    bo35 := False;
   { for I := 0 to _MAX(2, nMagicLevel + 1) do begin
      GetFrontPosition(nX, nY); //sub_004B2790
      if m_PEnvir.CanWalkOfEvent(Self, nX, nY) and (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nX, nY, False) > 0) then begin
        m_nCurrX := nX;
        m_nCurrY := nY;
        SendRefMsg(RM_RUSH, nDir, m_nCurrX, m_nCurrY, 0, '');
        Dec(n28);
      end else begin
        if m_PEnvir.CanWalk(nX, nY, True) and m_PEnvir.CanWalkOfEvent(Self, nX, nY) then n28 := 0
        else begin
          bo35 := True;
          Break;
        end;
      end;
    end; }
  end;
  SendRefMsg(RM_STOPRUSH101, nDir, m_nCurrX, m_nCurrY, 0, '');
end;

*)

function TActorObject.GetQuestFalgStatus(nFlag: Integer): Integer;
var
  n10, n14: Integer;
begin
  Result := 0;
  Dec(nFlag);
  if nFlag < 0 then Exit;
  n10 := nFlag div 8;
  n14 := (nFlag mod 8);
  if (n10 - SizeOf(TQuestFlag)) < 0 then begin
    if ((128 shr n14) and (m_QuestFlag[n10])) <> 0 then Result := 1
    else Result := 0;
  end;
end;

procedure TActorObject.SetQuestFlagStatus(nFlag: Integer; nValue: Integer);
var
  n10, n14: Integer;
  bt15: Byte;
begin
  Dec(nFlag);
  if nFlag < 0 then Exit;
  n10 := nFlag div 8;
  n14 := (nFlag mod 8);
  if (n10 - SizeOf(TQuestFlag)) < 0 then begin
    bt15 := m_QuestFlag[n10];
    if nValue = 0 then begin
      m_QuestFlag[n10] := (not (128 shr n14)) and (bt15);
    end else begin
      m_QuestFlag[n10] := (128 shr n14) or (bt15);
    end;
  end;
end;

procedure TPlayObject.CmdRestHero();
begin
  if (m_MyHero <> nil) then THeroObject(m_MyHero).RestHero;
end;

procedure TPlayObject.CmdTrainingMagic(Cmd: pTGameCmd; sHumanName, sSkillName: string;
  nLevel: Integer);
var
  Magic: pTMagic;
  UserMagic: pTUserMagic;
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sHumanName <> '') and (sHumanName[1] = '?')) or (sHumanName = '') or (sSkillName = '') or (nLevel < 0) or not (nLevel in [0..3]) then begin
    SysMsg('Useage: @' + Cmd.sCmd + ' PlayerName MagicName Level(1-3)', c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  Magic := UserEngine.FindMagic(sSkillName);
  if Magic = nil then begin
    SysMsg(Format('Skill: %s missing from DB', [sSkillName]), c_Red, t_Hint);
    Exit;
  end;

  if PlayObject.IsTrainingSkill(Magic.wMagicId) then begin
    SysMsg(Format('Already Learnt: %s', [sSkillName]), c_Red, t_Hint);
    Exit;
  end;
  New(UserMagic);
  UserMagic.MagicInfo := Magic;
  UserMagic.wMagIdx := Magic.wMagicId;
  UserMagic.btLevel := nLevel;
  UserMagic.btKey := 0;
  UserMagic.nTranPoint := 0;
  PlayObject.m_MagicList.Add(UserMagic);
  PlayObject.SendAddMagic(UserMagic);
  PlayObject.RecalcAbilitys;
  SysMsg(Format('%s Has Learnt: %s ', [sHumanName, sSkillName]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdTrainingSkill(Cmd: pTGameCmd; sHumanName, sSkillName: string;
  nLevel: Integer);
var
  I: Integer;
  UserMagic: pTUserMagic;
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or (sSkillName = '') or (nLevel <= 0) then begin
    SysMsg('Useage: @' + Cmd.sCmd + ' PlayerName MagicName Level(1-3)', c_Red, t_Hint);
    Exit;
  end;
  nLevel := _MIN(3, nLevel);
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format('%s Is not Online', [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  for I := 0 to PlayObject.m_MagicList.Count - 1 do begin
    UserMagic := PlayObject.m_MagicList.Items[I];
    if CompareText(UserMagic.MagicInfo.sMagicName, sSkillName) = 0 then begin
      UserMagic.btLevel := nLevel;
      PlayObject.SendMsg(PlayObject,
        RM_MAGIC_LVEXP,
        0,
        UserMagic.MagicInfo.wMagicId,
        UserMagic.btLevel,
        UserMagic.nTranPoint,
        '');
      PlayObject.SysMsg(Format('%s skill has be changed to level %d', [sSkillName, nLevel]), c_Green, t_Hint);
      SysMsg(Format('%s skill name %s has been changed to level %d', [sHumanName, sSkillName, nLevel]), c_Green, t_Hint);
      Break;
    end;
  end;
end;

procedure TPlayObject.CmdAddGameGold(sCmd, sHumName: string;
  nPoint: Integer);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < 6) then Exit;
  if (sHumName = '') or (nPoint <= 0) then begin
    SysMsg('Format: @' + sCmd + ' CharacterName  Gold', c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumName);
  if PlayObject <> nil then begin
    if (PlayObject.m_nGameGold + nPoint) < 2000000 then begin
      Inc(PlayObject.m_nGameGold, nPoint);
    end else begin
      nPoint := 2000000 - PlayObject.m_nGameGold;
      PlayObject.m_nGameGold := 2000000;
    end;
    PlayObject.GoldChanged();
    SysMsg(sHumName + 'Game point has been increased ' + IntToStr(nPoint) + '.', c_Green, t_Hint);
    PlayObject.SysMsg('Game point has been increased ' + IntToStr(nPoint) + '.', c_Green, t_Hint);
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdDelGameGold(sCmd, sHumName: string;
  nPoint: Integer);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < 6) then Exit;
  if (sHumName = '') or (nPoint <= 0) then Exit;
  PlayObject := UserEngine.GetPlayObject(sHumName);
  if PlayObject <> nil then begin
    if PlayObject.m_nGameGold > nPoint then begin
      Dec(PlayObject.m_nGameGold, nPoint);
    end else begin
      nPoint := PlayObject.m_nGameGold;
      PlayObject.m_nGameGold := 0;
    end;
    PlayObject.GoldChanged();
    SysMsg(sHumName + 'GameGold has been removed: ' + IntToStr(nPoint) + '.', c_Green, t_Hint);
    PlayObject.SysMsg('GameGold has been removed: ' + IntToStr(nPoint) + '.', c_Green, t_Hint);
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdGameGold(Cmd: pTGameCmd; sHumanName: string; sCtr: string; nGold: Integer);
var
  PlayObject: TPlayObject;
  Ctr: Char;
begin
  Ctr := '1';
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sCtr <> '') then begin
    Ctr := sCtr[1];
  end;

  if (sHumanName = '') or not (Ctr in ['=', '+', '-']) or (nGold < 0) or (nGold > 200000000) or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandGameGoldHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  case sCtr[1] of
    '=': begin
        PlayObject.m_nGameGold := nGold;
      end;
    '+': Inc(PlayObject.m_nGameGold, nGold);
    '-': Dec(PlayObject.m_nGameGold, nGold);
  end;
  if g_boGameLogGameGold then begin
    AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEGOLD,
      PlayObject.m_sMapName,
        PlayObject.m_nCurrX,
        PlayObject.m_nCurrY,
        PlayObject.m_sCharName,
        g_Config.sGameGoldName,
        nGold,
        sCtr[1],
        m_sCharName]));
  end;
  GameGoldChanged();
  PlayObject.SysMsg(Format(g_sGameCommandGameGoldHumanMsg, [g_Config.sGameGoldName, nGold, PlayObject.m_nGameGold, g_Config.sGameGoldName]), c_Green, t_Hint);
  SysMsg(Format(g_sGameCommandGameGoldGMMsg, [sHumanName, g_Config.sGameGoldName, nGold, PlayObject.m_nGameGold, g_Config.sGameGoldName]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdGamePoint(Cmd: pTGameCmd; sHumanName, sCtr: string; nPoint: Integer);
var
  PlayObject: TPlayObject;
  Ctr: Char;
begin
  Ctr := '1';
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sCtr <> '') then begin
    Ctr := sCtr[1];
  end;

  if (sHumanName = '') or not (Ctr in ['=', '+', '-']) or (nPoint < 0) or (nPoint > 100000000) or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandGamePointHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  case sCtr[1] of
    '=': begin
        PlayObject.m_nGamePoint := nPoint;
      end;
    '+': Inc(PlayObject.m_nGamePoint, nPoint);
    '-': Dec(PlayObject.m_nGamePoint, nPoint);
  end;
  if g_boGameLogGamePoint then begin
    AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEPOINT,
      PlayObject.m_sMapName,
        PlayObject.m_nCurrX,
        PlayObject.m_nCurrY,
        PlayObject.m_sCharName,
        g_Config.sGamePointName,
        nPoint,
        sCtr[1],
        m_sCharName]));
  end;
  GameGoldChanged();
  PlayObject.SysMsg(Format(g_sGameCommandGamePointHumanMsg, [nPoint, PlayObject.m_nGamePoint]), c_Green, t_Hint);
  SysMsg(Format(g_sGameCommandGamePointGMMsg, [sHumanName, nPoint, PlayObject.m_nGamePoint]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdCreditPoint(Cmd: pTGameCmd; sHumanName, sCtr: string; nPoint: Integer);
var
  PlayObject: TPlayObject;
  Ctr: Char;
  nCreditPoint: Integer;
begin
  Ctr := '1';
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sCtr <> '') then begin
    Ctr := sCtr[1];
  end;

  if (sHumanName = '') or not (Ctr in ['=', '+', '-']) or (nPoint < 0) or (nPoint > High(Integer)) or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandCreditPointHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  case sCtr[1] of
    '=': begin
        if nPoint >= 0 then
          PlayObject.m_btCreditPoint := nPoint;
      end;
    '+': begin
        nCreditPoint := PlayObject.m_btCreditPoint + nPoint;
        if nPoint >= 0 then
          PlayObject.m_btCreditPoint := nCreditPoint;
      end;
    '-': begin
        nCreditPoint := PlayObject.m_btCreditPoint - nPoint;
        if nPoint >= 0 then
          PlayObject.m_btCreditPoint := nCreditPoint;
      end;
  end;
  PlayObject.SysMsg(Format(g_sGameCommandCreditPointHumanMsg, [nPoint, PlayObject.m_btCreditPoint]), c_Green, t_Hint);
  SysMsg(Format(g_sGameCommandCreditPointGMMsg, [sHumanName, nPoint, PlayObject.m_btCreditPoint]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdAddGold(Cmd: pTGameCmd; sHumName: string; nCount: Integer);
var
  PlayObject: TPlayObject;
  nServerIndex: Integer;
begin
  if (m_btPermission < 6) then Exit;
  if (sHumName = '') or (nCount <= 0) then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' CharacterName  Gold', c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumName);
  if PlayObject <> nil then begin
    if (PlayObject.m_nGold + nCount) < PlayObject.m_nGoldMax then begin
      Inc(PlayObject.m_nGold, nCount);
    end else begin
      nCount := PlayObject.m_nGoldMax - PlayObject.m_nGold;
      PlayObject.m_nGold := PlayObject.m_nGoldMax;
    end;
    PlayObject.GoldChanged();
    SysMsg(sHumName + ' gold has been increased by ' + IntToStr(nCount) + '.', c_Green, t_Hint);
    //004CD6F6
    if g_boGameLogGold then
      AddGameDataLog('14' + #9 +
        m_sMapName + #9 +
        IntToStr(m_nCurrX) + #9 +
        IntToStr(m_nCurrY) + #9 +
        m_sCharName + #9 +
        sSTRING_GOLDNAME + #9 +
        IntToStr(nCount) + #9 +
        '1' + #9 +
        sHumName);
  end else begin
    FrontEngine.AddChangeGoldList(t_Gold, m_sCharName, sHumName, nCount);
    SysMsg(sHumName + ' Player is not currently online. Gold will be added when they next login.', c_Green, t_Hint);
  end;
end;

procedure TPlayObject.CmdAddGuild(Cmd: pTGameCmd; sGuildName, sGuildChief: string); //004CEBA0
var
  Human: TPlayObject;
  boAddState: Boolean;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if nServerIndex <> 0 then begin
    SysMsg('This command can only be used on the main server.', c_Red, t_Hint);
    Exit;
  end;
  if (sGuildName = '') or (sGuildChief = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' GuildName Leader', c_Red, t_Hint);
    Exit;
  end;

  boAddState := False;
  Human := UserEngine.GetPlayObject(sGuildChief);
  if Human = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sGuildChief]), c_Red, t_Hint);
    Exit;
  end;
  if g_GuildManager.MemberOfGuild(sGuildChief) = nil then begin
    if g_GuildManager.AddGuild(sGuildName, sGuildChief) then begin
      //UserEngine.SendServerGroupMsg(SS_205, nServerIndex, sGuildName + '/' + sGuildChief);
      SysMsg('GuildName: ' + sGuildName + ' Leader: ' + sGuildChief, c_Green, t_Hint);
      boAddState := True;
    end;
  end;
  if boAddState then begin
    Human.m_MyGuild := TObject(g_GuildManager.MemberOfGuild(Human.m_sCharName));
    if Human.m_MyGuild <> nil then begin
      Human.m_sGuildRankName := TGUild(Human.m_MyGuild).GetRankName(Self, Human.m_nGuildRankNo);
      Human.RefShowName();
    end;
  end;
  {
  if boAddState then begin
    SysMsg('You燬crewed燯p',c_Red,t_Hint);
  end;
  }
end;

procedure TPlayObject.CmdAdjuestExp(Cmd: pTGameCmd; sHumanName, sExp: string);
var
  PlayObject: TPlayObject;
  dwExp: LongWord;
  dwOExp: LongWord;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name Experience', c_Red, t_Hint);
    Exit;
  end;
  dwExp := Str_ToInt(sExp, 0);

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    if (dwExp > PlayObject.m_Abil.Exp) then begin
      PlayObject.GetExp(dwExp - PlayObject.m_Abil.Exp);
    end else begin
      PlayObject.m_Abil.Exp := dwExp;
      PlayObject.HasLevelUp(PlayObject.m_Abil.Level - 1);
    end;
    {dwOExp := PlayObject.m_Abil.Exp;
    PlayObject.m_Abil.Exp := dwExp;
    PlayObject.HasLevelUp(1);  }
    SysMsg(sHumanName + ' is not online.',c_Green,t_Hint);
    if g_Config.boShowMakeItemMsg then
      MainOutMessage('[Exp Change] ' + m_sCharName + '(' + PlayObject.m_sCharName + ' ' + IntToStr(dwOExp) + ' -> ' + IntToStr(PlayObject.m_Abil.Exp) + ')');
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdAdjuestLevel(Cmd: pTGameCmd; sHumanName: string;
  nLevel: Integer);
var
  PlayObject: TPlayObject;
  nOLevel: Integer;
  nMaxLevel: Integer;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if sHumanName = '' then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name Level', c_Red, t_Hint);
    Exit;
  end;

  nMaxLevel := g_Config.nMaxLevel;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    nOLevel := PlayObject.m_Abil.Level;
    PlayObject.m_Abil.Level := _MAX(1, MinLong(nMaxLevel, nLevel));
    PlayObject.m_Abil.MaxExp := PlayObject.GetLevelExp(PlayObject.m_Abil.Level);
    if PlayObject.m_Abil.Exp > PlayObject.m_Abil.MaxExp then
      PlayObject.m_Abil.Exp := PlayObject.m_Abil.MaxExp;
    PlayObject.HasLevelUp(1);
    SysMsg(sHumanName + ' is not online.', c_Green, t_Hint);
    if g_Config.boShowMakeItemMsg then
      MainOutMessage('[Level Change] ' + m_sCharName + '(' + PlayObject.m_sCharName + ' ' + IntToStr(nOLevel) + ' -> ' + IntToStr(PlayObject.m_Abil.Level) + ')');
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdAdjustExp(Human: TPlayObject; nExp: Integer);
begin
  if (m_btPermission < 6) then Exit;
end;

procedure TPlayObject.CmdBackStep(sCmd: string; nType, nCount: Integer);
begin
  if (m_btPermission < 6) then Exit;
  nType := _MIN(nType, 8);
  if nType = 0 then begin
    CharPushed(GetBackDir(m_btDirection), nCount);
  end else begin
    CharPushed(Random(nType), nCount);
  end;
end;

procedure TPlayObject.CmdBonuPoint(Cmd: pTGameCmd; sHumName: string; nCount: Integer);
var
  PlayObject: TPlayObject;
  sMsg: string;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumName = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name Points', c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumName]), c_Red, t_Hint);
    Exit;
  end;
  if (nCount > 0) then begin
    PlayObject.m_nBonusPoint := nCount;
    PlayObject.SendMsg(Self, RM_ADJUST_BONUS, 0, 0, 0, 0, '');
    Exit;
  end;
  sMsg := Format('Unused Points:%d Stats:(DC:%d MC:%d SC:%d AC:%d MAC:%d HP:%d MP:%d HIT:%d SPEED:%d)',
    [PlayObject.m_nBonusPoint,
    PlayObject.m_BonusAbil.DC,
      PlayObject.m_BonusAbil.MC,
      PlayObject.m_BonusAbil.SC,
      PlayObject.m_BonusAbil.AC,
      PlayObject.m_BonusAbil.MAC,
      PlayObject.m_BonusAbil.HP,
      PlayObject.m_BonusAbil.MP,
      PlayObject.m_BonusAbil.Hit,
      PlayObject.m_BonusAbil.Speed
      ]);
  SysMsg(Format('%s attribute points to: %s', [sHumName, sMsg]), c_Red, t_Hint);
end;

procedure TPlayObject.CmdChangeAdminMode(sCmd: string; nPermission: Integer; sParam1: string; boFlag: Boolean);
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, '']), c_Red, t_Hint);
    Exit;
  end;
  m_boAdminMode := boFlag;
  if m_boAdminMode then SysMsg(sGameMasterMode, c_Green, t_Hint)
  else SysMsg(sReleaseGameMasterMode, c_Green, t_Hint);
end;

procedure TPlayObject.CmdChangeAttackMode(nMode: Integer; sParam1, sParam2, sParam3, sParam4, sParam5, sParam6, sParam7: string);
begin
  (*{$IF DEBUG = 0}
    asm
      jz @@Start
      jnz @@Start
      db 0F4h
      db 0EBh
      db 004h
      db 0E8h
      @@Start:
    end;
  {$IFEND}
    if sParam2 <> '' then begin
  {$IF DEBUG = 0}
      asm
      jz @@Start
      jnz @@Start
      db 0F4h
      db 0EBh
      db 004h
      db 0E8h
      @@Start:
      end;
  {$IFEND}
      ProcessUserCmd(sParam1, sParam2, sParam3, sParam4, sParam5, sParam6, sParam7);
  {$IF DEBUG = 0}
      asm
      jz @@Start
      jnz @@Start
      db 0F4h
      db 0EBh
      db 004h
      db 0E8h
      @@Start:
      end;
  {$IFEND}
      exit;
    end;
  {$IF DEBUG = 0}
    asm
      jz @@Start
      jnz @@Start
      db 0F4h
      db 0EBh
      db 004h
      db 0E8h
      @@Start:
    end;
  {$IFEND}*)
  if (nMode >= 0) and (nMode <= 4) then
    m_btAttatckMode := nMode
  else begin
    if m_btAttatckMode < HAM_PKATTACK then Inc(m_btAttatckMode)
    else m_btAttatckMode := HAM_ALL;
  end;
  case m_btAttatckMode of
    HAM_ALL: SysMsg(sAttackModeOfAll, c_Green, t_Hint); //[攻击模式: 全体攻击]
    HAM_PEACE: SysMsg(sAttackModeOfPeaceful, c_Green, t_Hint); //[攻击模式: 和平攻击]
    HAM_DEAR: SysMsg(sAttackModeOfDear, c_Green, t_Hint); //[攻击模式: 和平攻击]
    HAM_MASTER: SysMsg(sAttackModeOfMaster, c_Green, t_Hint); //[攻击模式: 和平攻击]
    HAM_GROUP: SysMsg(sAttackModeOfGroup, c_Green, t_Hint); //[攻击模式: 编组攻击]
    HAM_GUILD: SysMsg(sAttackModeOfGuild, c_Green, t_Hint); //[攻击模式: 行会攻击]
    HAM_PKATTACK: SysMsg(sAttackModeOfRedWhite, c_Green, t_Hint); //[攻击模式: 红名攻击]
  end;
end;

procedure TPlayObject.CmdChangeDearName(Cmd: pTGameCmd; sHumanName, sDearName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or (sDearName = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name PartnersName', c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    if CompareText(sDearName, '无') = 0 then begin
      PlayObject.m_sDearName := '';
      PlayObject.RefShowName;
      SysMsg(sHumanName + ' Partner was removed.', c_Green, t_Hint);
    end else begin
      PlayObject.m_sDearName := sDearName;
      PlayObject.RefShowName;
      SysMsg(sHumanName + ' Partnet was changed.', c_Green, t_Hint);
    end;
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdChangeGender(Cmd: pTGameCmd; sHumanName, sSex: string);
var
  PlayObject: TPlayObject;
  nSex: Integer;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  nSex := -1;
  if (sSex = 'Man') or (sSex = 'Male') or (sSex = '0') then begin
    nSex := 0;
  end;
  if (sSex = 'WoMan') or (sSex = 'Female') or (sSex = '1') then begin
    nSex := 1;
  end;
  if (sHumanName = '') or (nSex = -1) then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name Gender(0-1)', c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    if PlayObject.m_btGender <> nSex then begin
      PlayObject.m_btGender := nSex;
      PlayObject.FeatureChanged();
      SysMsg(PlayObject.m_sCharName + ' Gender changed.', c_Green, t_Hint);
    end else begin
      SysMsg(PlayObject.m_sCharName + ' Gender change failed.', c_Red, t_Hint);
    end;
  end else begin
    SysMsg(sHumanName + ' is not online.', c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdChangeItemName(sCmd, sMakeIndex, sItemIndex, sItemName: string);
var
  nMakeIndex, nItemIndex: Integer;
begin
  if (m_btPermission < 6) then Exit;
  if (sMakeIndex = '') or (sItemIndex = '') or (sItemName = '') then begin
    SysMsg('Usage: @' + sCmd + ' ItemNumber ItemID ItemName', c_Red, t_Hint);
    Exit;
  end;
  nMakeIndex := Str_ToInt(sMakeIndex, -1);
  nItemIndex := Str_ToInt(sItemIndex, -1);
  if (nMakeIndex <= 0) or (nItemIndex < 0) then begin
    SysMsg('Usage: @' + sCmd + ' ItemNumber ItemID ItemName', c_Red, t_Hint);
    Exit;
  end;
  if ItemUnit.AddCustomItemName(nMakeIndex, nItemIndex, sItemName) then begin
    ItemUnit.SaveCustomItemName();
    SysMsg('Item name changed.', c_Green, t_Hint);
    Exit;
  end;
  SysMsg('Failed to change item name.', c_Red, t_Hint);
end;

procedure TPlayObject.CmdChangeJob(Cmd: pTGameCmd; sHumanName, sJobName: string); //004CC714
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or (sJobName = '') then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandChangeJobHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    if CompareText(sJobName, 'Warr') = 0 then PlayObject.m_btJob := 0;
    if CompareText(sJobName, 'Wizard') = 0 then PlayObject.m_btJob := 1;
    if CompareText(sJobName, 'Taos') = 0 then PlayObject.m_btJob := 2;
    PlayObject.HasLevelUp(1);
    PlayObject.SysMsg(g_sGameCommandChangeJobHumanMsg, c_Green, t_Hint);
    SysMsg(Format(g_sGameCommandChangeJobMsg, [sHumanName]), c_Green, t_Hint);
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdChangeLevel(Cmd: pTGameCmd; sParam1: string);
var
  nOLevel: Integer;
  nLevel: Integer;
  nMaxLevel: Integer;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, '']), c_Red, t_Hint);
    Exit;
  end;
  nLevel := Str_ToInt(sParam1, 1);
  nOLevel := m_Abil.Level;

  nMaxLevel := g_Config.nMaxLevel;

  m_Abil.Level := MinLong(nMaxLevel, nLevel);
  m_Abil.MaxExp := GetLevelExp(m_Abil.Level);
  if m_Abil.Exp > m_Abil.MaxExp then
    m_Abil.Exp := m_Abil.MaxExp;
  HasLevelUp(1);
  if g_Config.boShowMakeItemMsg then begin
    MainOutMessage(Format(g_sGameCommandLevelConsoleMsg, [m_sCharName, nOLevel, m_Abil.Level]));
  end;
end;

procedure TPlayObject.CmdChangeMasterName(Cmd: pTGameCmd; sHumanName, sMasterName, sIsMaster: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or (sMasterName = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name MasterName', c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    if CompareText(sMasterName, '无') = 0 then begin
      PlayObject.m_sMasterName := '';
      PlayObject.RefShowName;
      PlayObject.m_boMaster := False;
      SysMsg(sHumanName + ' Master was removed.', c_Green, t_Hint);
    end else begin
      PlayObject.m_sMasterName := sMasterName;
      if (sIsMaster <> '') and (sIsMaster[1] = '1') then PlayObject.m_boMaster := True
      else PlayObject.m_boMaster := False;
      PlayObject.RefShowName;
      SysMsg(sHumanName + ' Master has been changed.', c_Green, t_Hint);
    end;
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdChangeObMode(sCmd: string; nPermission: Integer; sParam1: string; boFlag: Boolean);
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, '']), c_Red, t_Hint);
    Exit;
  end;
  if boFlag then begin
    SendRefMsg(RM_DISAPPEAR, 0, 0, 0, 0, ''); //01/21 强行发送刷新数据到客户端，解决GM登录隐身有影子问题
  end;
  m_boObMode := boFlag;
  if m_boObMode then begin
    SysMsg(sObserverMode, c_Green, t_Hint);
  end else SysMsg(g_sReleaseObserverMode, c_Green, t_Hint);
end;

procedure TPlayObject.CmdChangeSabukLord(Cmd: pTGameCmd; sCASTLENAME, sGuildName: string; boFlag: Boolean); //004CFE1C
var
  Guild: TGUild;
  Castle: TUserCastle;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;

  if (sCASTLENAME = '') or (sGuildName = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Castle Guild', c_Red, t_Hint);
    Exit;
  end;
  Castle := g_CastleManager.Find(sCASTLENAME);
  if Castle = nil then begin
    SysMsg(Format(g_sGameCommandSbkGoldCastleNotFoundMsg, [sCASTLENAME]), c_Red, t_Hint);
    Exit;
  end;

  Guild := g_GuildManager.FindGuild(sGuildName);
  if Guild <> nil then begin
    AddGameDataLog('27' + #9 +
      Castle.m_sOwnGuild + #9 +
      '0' + #9 +
      '1' + #9 +
      'sGuildName' + #9 +
      m_sCharName + #9 +
      '0' + #9 +
      '1' + #9 +
      '0');
    Castle.GetCastle(Guild);
    {if boFlag then
      UserEngine.SendServerGroupMsg(SS_211, nServerIndex, sGuildName);  }
    SysMsg(Castle.m_sName + ' ownership changed to ' + sGuildName, c_Green, t_Hint);
  end else begin
    SysMsg('Guild ' + sGuildName + ' not found.', c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdChangeSalveStatus;
begin
  if m_SlaveList.Count > 0 then begin
    m_boSlaveRelax := not m_boSlaveRelax;
    if m_boSlaveRelax then SysMsg(sPetRest, c_Green, t_Hint)
    else SysMsg(sPetAttack, c_Green, t_Hint)
  end;
end;

procedure TPlayObject.CmdChangeSuperManMode(sCmd: string; nPermission: Integer; sParam1: string; boFlag: Boolean);
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, '']), c_Red, t_Hint);
    Exit;
  end;
  m_boSuperMan := boFlag;
  if m_boSuperMan then SysMsg(sSupermanMode, c_Green, t_Hint)
  else SysMsg(sReleaseSupermanMode, c_Green, t_Hint);
end;

procedure TPlayObject.CmdChangeUserFull(sCmd, sUserCount: string);
var
  nCount: Integer;
begin
  if (m_btPermission < 6) then Exit;
  nCount := Str_ToInt(sUserCount, -1);
  if (sUserCount = '') or (nCount < 1) or ((sUserCount <> '') and (sUserCount[1] = '?')) then begin
    SysMsg('Set maximum number of Players', c_Red, t_Hint);
    SysMsg('Usage: @' + sCmd + ' Number', c_Red, t_Hint);
    Exit;
  end;
  g_Config.nUserFull := nCount;
  SysMsg(Format('Number allowed online set to: %d', [nCount]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdChangeZenFastStep(sCmd, sFastStep: string);
var
  nFastStep: Integer;
begin
  if (m_btPermission < 6) then Exit;
  nFastStep := Str_ToInt(sFastStep, -1);
  if (sFastStep = '') or (nFastStep < 1) or ((sFastStep <> '') and (sFastStep[1] = '?')) then begin
    SysMsg('Change Monster Speed', c_Red, t_Hint);
    SysMsg('Usage: @' + sCmd + ' Number', c_Red, t_Hint);
    Exit;
  end;
  g_Config.nZenFastStep := nFastStep;
  SysMsg(Format('Monster speed changed: %d', [nFastStep]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdClearBagItem(Cmd: pTGameCmd; sHumanName: string);
var
  I: Integer;
  PlayObject: TPlayObject;
  UserItem: pTUserItem;
  DelList: TStringList;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, 'Character name']), c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;

  DelList := nil;
  for I := 0 to PlayObject.m_ItemList.Count - 1 do begin
    UserItem := PlayObject.m_ItemList.Items[I];
    if DelList = nil then DelList := TStringList.Create;
    DelList.AddObject(UserEngine.GetStdItemName(UserItem.wIndex), TObject(UserItem.MakeIndex));
    Dispose(UserItem);
  end;
  PlayObject.m_ItemList.Clear;
  if DelList <> nil then begin
    PlayObject.SendMsg(PlayObject, RM_SENDDELITEMLIST, 0, Integer(DelList), 0, 0, '');
  end;
end;

procedure TPlayObject.CmdClearHumanPassword(sCmd: string; nPermission: Integer; sHumanName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < nPermission) then Exit;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg('Clear Password for players storage', c_Red, t_Hint);
    SysMsg(Format('Usage: @%s Name', [sCmd]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    Exit;
  end;
  PlayObject.m_boPasswordLocked := False;
  PlayObject.m_boUnLockStoragePwd := False;
  PlayObject.m_sStoragePwd := '';
  PlayObject.SysMsg('Storage password has been removed', c_Green, t_Hint);
  SysMsg(Format('%s storage password has been removed', [sHumanName]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdClearMapMonster(Cmd: pTGameCmd; sMapName, sMonName, sItems: string);
var
  I, II, III: Integer;
  MonList: TList;
  Envir: TEnvirnoment;
  nMonCount: Integer;
  boKillAll: Boolean;
  boKillAllMap: Boolean;
  boNotItem: Boolean;
  BaseObject: TActorObject;
  MonGen: pTMonGenInfo;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sMapName = '') or (sMonName = '') or (sItems = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Map Monster Drops(0-1)', c_Red, t_Hint);
    Exit;
  end;
  boKillAll := False;
  boKillAllMap := False;
  boNotItem := True;
  nMonCount := 0;
  Envir := nil;
  if sMonName = '*' then boKillAll := True;
  if sMapName = '*' then boKillAllMap := True;
  if sItems = '1' then boNotItem := False;

  MonList := TList.Create;
  for I := 0 to g_MapManager.Count - 1 do begin
    Envir := TEnvirnoment(g_MapManager.Items[I]);
    if (Envir <> nil) and (boKillAllMap or (CompareText(Envir.sMapName, sMapName) = 0)) then begin
      UserEngine.GetMapMonster(Envir, MonList);
      for II := 0 to MonList.Count - 1 do begin
        BaseObject := TActorObject(MonList.Items[II]);
        if boKillAll or (CompareText(sMonName, BaseObject.m_sCharName) = 0) then begin
          BaseObject.m_boNoItem := boNotItem;
          BaseObject.m_WAbil.HP := 0;
          Inc(nMonCount);
        end;
      end;
    end;
  end;
  MonList.Free;

  if Envir = nil then begin
    SysMsg('Map not found', c_Red, t_Hint);
    Exit;
  end;
  SysMsg('Monsters removed: ' + IntToStr(nMonCount), c_Red, t_Hint);
end;

procedure TPlayObject.CmdClearMission(Cmd: pTGameCmd; sHumanName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name)', c_Red, t_Hint);
    Exit;
  end;
  if sHumanName[1] = '?' then begin
    SysMsg('This command clears flags for the player', c_Blue, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format('%s is not online!', [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  FillChar(PlayObject.m_QuestFlag, SizeOf(TQuestFlag), #0);
  SysMsg(Format('%s flags have been removed', [sHumanName]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdContestPoint(Cmd: pTGameCmd; sGuildName: string);
var
  I: Integer;
  Guild: TGUild;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sGuildName = '') or ((sGuildName <> '') and (sGuildName[1] = '?')) then begin
    SysMsg('View a Guilds Contest Points', c_Red, t_Hint);
    SysMsg(Format('Usage: @%s GuildName', [Cmd.sCmd]), c_Red, t_Hint);
    Exit;
  end;
  Guild := g_GuildManager.FindGuild(sGuildName);
  if Guild <> nil then begin
    SysMsg(Format('%s points are: %d', [sGuildName, Guild.nContestPoint]), c_Green, t_Hint);
  end else begin
    SysMsg(Format('Guild %s does not exist.', [sGuildName]), c_Green, t_Hint);
  end;
end;

procedure TPlayObject.CmdStartContest(Cmd: pTGameCmd; sParam1: string);
var
  I, II: Integer;
  List10, List14: TList;
  PlayObject, PlayObjectA: TPlayObject;
  bo19: Boolean;
  s20: string;
  Guild: TGUild;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg('Start a Guild Contest on current Map.', c_Red, t_Hint);
    SysMsg(Format('Usage: @%s', [Cmd.sCmd]), c_Red, t_Hint);
    Exit;
  end;

  if not m_PEnvir.m_boFight3Zone then begin
    SysMsg('Not available on this Map.', c_Red, t_Hint);
    Exit;
  end;
  List10 := TList.Create;
  List14 := TList.Create;
  UserEngine.GetMapRageHuman(m_PEnvir, m_nCurrX, m_nCurrY, 1000, List10);
  for I := 0 to List10.Count - 1 do begin
    PlayObject := TPlayObject(List10.Items[I]);
    if not PlayObject.m_boObMode or not PlayObject.m_boAdminMode then begin
      PlayObject.m_nFightZoneDieCount := 0;
      if PlayObject.m_MyGuild = nil then Continue;
      bo19 := False;
      for II := 0 to List14.Count - 1 do begin
        PlayObjectA := TPlayObject(List14.Items[II]);
        if PlayObject.m_MyGuild = PlayObjectA.m_MyGuild then
          bo19 := True;
      end;
      if not bo19 then begin
        List14.Add(PlayObject.m_MyGuild);
      end;
    end;
  end;

  SysMsg('Guild Contest has already begun', c_Green, t_Hint);
  UserEngine.CryCry(RM_CRY, m_PEnvir, m_nCurrX, m_nCurrY, 1000, g_Config.btCryMsgFColor, g_Config.btCryMsgBColor, '- Guild Wars has broken out.');
  s20 := '';
  for I := 0 to List14.Count - 1 do begin
    Guild := TGUild(List14.Items[I]);
    Guild.StartTeamFight();
    for II := 0 to List10.Count - 1 do begin
      PlayObject := TPlayObject(List10.Items[I]);
      if PlayObject.m_MyGuild = Guild then begin
        Guild.AddTeamFightMember(PlayObject.m_sCharName);
      end;
    end;
    s20 := s20 + Guild.sGuildName + ' ';
  end;

  UserEngine.CryCry(RM_CRY, m_PEnvir, m_nCurrX, m_nCurrY, 1000, g_Config.btCryMsgFColor, g_Config.btCryMsgBColor, ' -Participate in the martial art:' + s20);
  List10.Free;
  List14.Free;
end;

procedure TPlayObject.CmdEndContest(Cmd: pTGameCmd; sParam1: string);
var
  I, II: Integer;
  List10, List14: TList;
  PlayObject, PlayObjectA: TPlayObject;
  bo19: Boolean;
  s20: string;
  Guild: TGUild;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg('End the Guild Contest on this Map.', c_Red, t_Hint);
    SysMsg(Format('Usage: @%s', [Cmd.sCmd]), c_Red, t_Hint);
    Exit;
  end;

  if not m_PEnvir.m_boFight3Zone then begin
    SysMsg('Command not available on this Map.', c_Red, t_Hint);
    Exit;
  end;
  List10 := TList.Create;
  List14 := TList.Create;
  UserEngine.GetMapRageHuman(m_PEnvir, m_nCurrX, m_nCurrY, 1000, List10);
  for I := 0 to List10.Count - 1 do begin
    PlayObject := TPlayObject(List10.Items[I]);
    if not PlayObject.m_boObMode or not PlayObject.m_boAdminMode then begin
      if PlayObject.m_MyGuild = nil then Continue;
      bo19 := False;
      for II := 0 to List14.Count - 1 do begin
        PlayObjectA := TPlayObject(List14.Items[II]);
        if PlayObject.m_MyGuild = PlayObjectA.m_MyGuild then
          bo19 := True;
      end;
      if not bo19 then begin
        List14.Add(PlayObject.m_MyGuild);
      end;
    end;
  end;
  for I := 0 to List14.Count - 1 do begin
    Guild := TGUild(List14.Items[I]);
    Guild.EndTeamFight();
    UserEngine.CryCry(RM_CRY, m_PEnvir, m_nCurrX, m_nCurrY, 1000, g_Config.btCryMsgFColor, g_Config.btCryMsgBColor, Format(' - %s Guild Competition has ended.', [Guild.sGuildName]));
  end;
  List10.Free;
  List14.Free;
end;

procedure TPlayObject.CmdAllowGroupReCall(sCmd, sParam: string);
begin
  if (sParam <> '') and (sParam[1] = '?') then begin
    SysMsg('This command is used to allow or deny Group Recall', c_Red, t_Hint);
    Exit;
  end;

  m_boAllowGroupReCall := not m_boAllowGroupReCall;
  if m_boAllowGroupReCall then SysMsg(g_sEnableGroupRecall {'[允许天地合一]'}, c_Green, t_Hint)
  else SysMsg(g_sDisableGroupRecall {'[禁止天地合一]'}, c_Green, t_Hint);
end;

procedure TPlayObject.CmdAnnouncement(Cmd: pTGameCmd; sGuildName: string);
var
  I: Integer;
  Guild: TGUild;
  PlayObject: TPlayObject;
  sHumanName: string;
  nPoint: Integer;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sGuildName = '') or ((sGuildName <> '') and (sGuildName[1] = '?')) then begin
    SysMsg('View a Guilds Contest Points.', c_Red, t_Hint);
    SysMsg(Format('Usage: @%s GuildName', [Cmd.sCmd]), c_Red, t_Hint);
    Exit;
  end;

  if not m_PEnvir.m_boFight3Zone then begin
    SysMsg('This command is not available on this Map.', c_Red, t_Hint);
    Exit;
  end;
  Guild := g_GuildManager.FindGuild(sGuildName);
  if Guild <> nil then begin
    UserEngine.CryCry(RM_CRY, m_PEnvir, m_nCurrX, m_nCurrY, 1000, g_Config.btCryMsgFColor, g_Config.btCryMsgBColor, Format(' - %s Guild Competition results: ', [Guild.sGuildName]));
    for I := 0 to Guild.TeamFightDeadList.Count - 1 do begin
      nPoint := Integer(Guild.TeamFightDeadList.Objects[I]);
      sHumanName := Guild.TeamFightDeadList.Strings[I];
      UserEngine.CryCry(RM_CRY,
        m_PEnvir,
        m_nCurrX,
        m_nCurrY,
        1000,
        g_Config.btCryMsgFColor,
        g_Config.btCryMsgBColor,
        Format(' - %s  : %d Min / death %d times.', [sHumanName, HiWord(nPoint), LoWord(nPoint)]));
    end;
  end;
  UserEngine.CryCry(RM_CRY,
    m_PEnvir,
    m_nCurrX,
    m_nCurrY,
    1000,
    g_Config.btCryMsgFColor,
    g_Config.btCryMsgBColor,
    Format(' - [%s] : %d minutes.', [Guild.sGuildName, Guild.nContestPoint]));
  UserEngine.CryCry(RM_CRY,
    m_PEnvir,
    m_nCurrX,
    m_nCurrY,
    1000,
    g_Config.btCryMsgFColor,
    g_Config.btCryMsgBColor,
    '------------------------------------');
end;

procedure TPlayObject.CmdDearRecall(sCmd, sParam: string);
begin
  if (sParam <> '') and (sParam[1] = '?') then begin
    SysMsg('Usage: @' + sCmd, c_Green, t_Hint);
    Exit;
  end;
  if m_sDearName = '' then begin
    SysMsg('You are not married.', c_Red, t_Hint);
    Exit;
  end;
  if m_PEnvir.m_boNODEARRECALL then begin
    SysMsg('Cannot recall on this Map.', c_Red, t_Hint);
    Exit;
  end;

  if m_DearHuman = nil then begin
    if m_btGender = 0 then begin
      SysMsg('Your wife is not online.', c_Red, t_Hint);
    end else begin
      SysMsg('Your husband is not online.', c_Red, t_Hint);
    end;
    Exit;
  end;
  if GetTickCount - m_dwDearRecallTick < 10000 then begin
    SysMsg('You must wait before using this command again.', c_Red, t_Hint);
    Exit;
  end;
  m_dwDearRecallTick := GetTickCount();
  if m_DearHuman.m_boCanDearRecall then begin
    RecallHuman(m_DearHuman.m_sCharName);
  end else begin
    SysMsg(m_DearHuman.m_sCharName + ' Not allowed to send!', c_Red, t_Hint);
    Exit;
  end;
end;

procedure TPlayObject.CmdMasterRecall(sCmd, sParam: string);
var
  I: Integer;
  MasterHuman: TPlayObject;
begin
  if (sParam <> '') and (sParam[1] = '?') then begin
    SysMsg('Usage: @' + sCmd, c_Green, t_Hint);
    Exit;
  end;
  if not m_boMaster then begin
    SysMsg('You are not a Master.', c_Red, t_Hint);
    Exit;
  end;
  if m_MasterList.Count = 0 then begin
    SysMsg('You have no Master.', c_Red, t_Hint);
    Exit;
  end;
  if m_PEnvir.m_boNOMASTERRECALL then begin
    SysMsg('Master recall is not alllowed on this map.', c_Red, t_Hint);
    Exit;
  end;
  if GetTickCount - m_dwMasterRecallTick < 10000 then begin
    SysMsg('You must wait before using this command again.', c_Red, t_Hint);
    Exit;
  end;
  for I := 0 to m_MasterList.Count - 1 do begin
    MasterHuman := TPlayObject(m_MasterList.Items[I]);
    if MasterHuman.m_boCanMasterRecall then begin
      RecallHuman(MasterHuman.m_sCharName);
    end else begin
      SysMsg(MasterHuman.m_sCharName + ' Not allowed to send!', c_Red, t_Hint);
    end;
  end;
end;

procedure TPlayObject.CmdDelBonuPoint(Cmd: pTGameCmd; sHumName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if sHumName = '' then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name', c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumName);
  if PlayObject <> nil then begin
    FillChar(PlayObject.m_BonusAbil, SizeOf(TNakedAbility), #0);
    PlayObject.m_nBonusPoint := 0;
    PlayObject.SendMsg(PlayObject, RM_ADJUST_BONUS, 0, 0, 0, 0, '');
    PlayObject.HasLevelUp(0);
    PlayObject.SysMsg('Points have been cleared.', c_Red, t_Hint);
    SysMsg(sHumName + ' points have been cleared.', c_Green, t_Hint);
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdReNewLevel(Cmd: pTGameCmd; sHumanName, sLevel: string);
var
  PlayObject: TPlayObject;
  nLevel: Integer;
begin
  if (m_btPermission < 6) then Exit;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name RenewLevel', c_Red, t_Hint);
    Exit;
  end;
  nLevel := Str_ToInt(sLevel, -1);
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    if (nLevel >= 0) and (nLevel <= 255) then begin
      PlayObject.m_btReLevel := nLevel;
      PlayObject.RefShowName();
    end;
    SysMsg(sHumanName + ' renew level set to ' + IntToStr(PlayObject.m_btReLevel), c_Green, t_Hint);
  end else begin
    SysMsg(sHumanName + ' is not online', c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdRestBonuPoint(Cmd: pTGameCmd; sHumName: string);
var
  PlayObject: TPlayObject;
  nTotleUsePoint: Integer;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if sHumName = '' then begin
    SysMsg('Format: @' + Cmd.sCmd + ' CharacterName', c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumName);
  if PlayObject <> nil then begin
    nTotleUsePoint := PlayObject.m_BonusAbil.DC +
      PlayObject.m_BonusAbil.MC +
      PlayObject.m_BonusAbil.SC +
      PlayObject.m_BonusAbil.AC +
      PlayObject.m_BonusAbil.MAC +
      PlayObject.m_BonusAbil.HP +
      PlayObject.m_BonusAbil.MP +
      PlayObject.m_BonusAbil.Hit +
      PlayObject.m_BonusAbil.Speed +
      PlayObject.m_BonusAbil.X2;
    FillChar(PlayObject.m_BonusAbil, SizeOf(TNakedAbility), #0);

    Inc(PlayObject.m_nBonusPoint, nTotleUsePoint);
    PlayObject.SendMsg(PlayObject, RM_ADJUST_BONUS, 0, 0, 0, 0, '');
    PlayObject.HasLevelUp(0);
    PlayObject.SysMsg('Stat points have been cleared', c_Red, t_Hint);
    SysMsg(sHumName + ' stat points have been cleared', c_Green, t_Hint);
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdSbkDoorControl(sCmd, sParam: string);
begin

end;

procedure TPlayObject.CmdSearchDear(sCmd, sParam: string);
begin
  if (sParam <> '') and (sParam[1] = '?') then begin
    SysMsg('Find out where you Partner currently is.', c_Red, t_Hint);
    Exit;
  end;
  if m_sDearName = '' then begin
    SysMsg(g_sYouAreNotMarryedMsg {'你都没结婚查什么？'}, c_Red, t_Hint);
    Exit;
  end;
  if m_DearHuman = nil then begin
    if m_btGender = 0 then begin
      SysMsg(g_sYourWifeNotOnlineMsg {'你的老婆还没有上线！！！'}, c_Red, t_Hint);
    end else begin
      SysMsg(g_sYourHusbandNotOnlineMsg {'你的老公还没有上线！！！'}, c_Red, t_Hint);
    end;
    Exit;
  end;

  if m_btGender = 0 then begin
    SysMsg(g_sYourWifeNowLocateMsg {'你的老婆现在位于:'}, c_Green, t_Hint);
    SysMsg(m_DearHuman.m_sCharName + ' ' + m_DearHuman.m_PEnvir.sMapDesc + '(' + IntToStr(m_DearHuman.m_nCurrX) + ':' + IntToStr(m_DearHuman.m_nCurrY) + ')', c_Green, t_Hint);
    m_DearHuman.SysMsg(g_sYourHusbandSearchLocateMsg {'你的老公正在找你，他现在位于:'}, c_Green, t_Hint);
    m_DearHuman.SysMsg(m_sCharName + ' ' + m_PEnvir.sMapDesc + '(' + IntToStr(m_nCurrX) + ':' + IntToStr(m_nCurrY) + ')', c_Green, t_Hint);
  end else begin
    SysMsg(g_sYourHusbandNowLocateMsg {'你的老公现在位于:'}, c_Red, t_Hint);
    SysMsg(m_DearHuman.m_sCharName + ' ' + m_DearHuman.m_PEnvir.sMapDesc + '(' + IntToStr(m_DearHuman.m_nCurrX) + ':' + IntToStr(m_DearHuman.m_nCurrY) + ')', c_Green, t_Hint);
    m_DearHuman.SysMsg(g_sYourWifeSearchLocateMsg {'你的老婆正在找你，她现在位于:'}, c_Green, t_Hint);
    m_DearHuman.SysMsg(m_sCharName + ' ' + m_PEnvir.sMapDesc + '(' + IntToStr(m_nCurrX) + ':' + IntToStr(m_nCurrY) + ')', c_Green, t_Hint);
  end;
end;

procedure TPlayObject.CmdSearchMaster(sCmd, sParam: string);
var
  I: Integer;
  Human: TPlayObject;
begin
  if (sParam <> '') and (sParam[1] = '?') then begin
    SysMsg('Find where your Pupil is.', c_Red, t_Hint);
    Exit;
  end;
  if m_sMasterName = '' then begin
    SysMsg(g_sYouAreNotMasterMsg, c_Red, t_Hint);
    Exit;
  end;
  if m_boMaster then begin
    if m_MasterList.Count <= 0 then begin
      SysMsg(g_sYourMasterListNotOnlineMsg, c_Red, t_Hint);
      Exit;
    end;
    SysMsg(g_sYourMasterListNowLocateMsg, c_Green, t_Hint);
    for I := 0 to m_MasterList.Count - 1 do begin
      Human := TPlayObject(m_MasterList.Items[I]);
      SysMsg(Human.m_sCharName + ' ' + Human.m_PEnvir.sMapDesc + '(' + IntToStr(Human.m_nCurrX) + ':' + IntToStr(Human.m_nCurrY) + ')', c_Green, t_Hint);
      Human.SysMsg(g_sYourMasterSearchLocateMsg, c_Green, t_Hint);
      Human.SysMsg(m_sCharName + ' ' + m_PEnvir.sMapDesc + '(' + IntToStr(m_nCurrX) + ':' + IntToStr(m_nCurrY) + ')', c_Green, t_Hint);
    end;
  end else begin
    if m_MasterHuman = nil then begin
      SysMsg(g_sYourMasterNotOnlineMsg, c_Red, t_Hint);
      Exit;
    end;
    SysMsg(g_sYourMasterNowLocateMsg, c_Red, t_Hint);
    SysMsg(m_MasterHuman.m_sCharName + ' ' + m_MasterHuman.m_PEnvir.sMapDesc + '(' + IntToStr(m_MasterHuman.m_nCurrX) + ':' + IntToStr(m_MasterHuman.m_nCurrY) + ')', c_Green, t_Hint);
    m_MasterHuman.SysMsg(g_sYourMasterListSearchLocateMsg, c_Green, t_Hint);
    m_MasterHuman.SysMsg(m_sCharName + ' ' + m_PEnvir.sMapDesc + '(' + IntToStr(m_nCurrX) + ':' + IntToStr(m_nCurrY) + ')', c_Green, t_Hint);
  end;
end;

procedure TPlayObject.CmdSetPermission(Cmd: pTGameCmd; sHumanName, sPermission: string);
var
  nPerission: Integer;
  PlayObject: TPlayObject;
resourcestring
  sOutFormatMsg = '[Permissions Adjusted] %s (%s %d -> %d)';
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  nPerission := Str_ToInt(sPermission, 0);
  if (sHumanName = '') or not (nPerission in [0..10]) then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name PermissionLevel(0-10)', c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  if g_Config.boShowMakeItemMsg then
    MainOutMessage(Format(sOutFormatMsg, [m_sCharName, PlayObject.m_sCharName, PlayObject.m_btPermission, nPerission]));
  PlayObject.m_btPermission := nPerission;
  SysMsg(sHumanName + ' current permission level is: ' + IntToStr(PlayObject.m_btPermission), c_Red, t_Hint);
end;

procedure TPlayObject.CmdShowHumanFlag(sCmd: string; nPermission: Integer;
  sHumanName, sFlag: string);
var
  PlayObject: TPlayObject;
  nFlag: Integer;
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, g_sGameCommandShowHumanFlagHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  nFlag := Str_ToInt(sFlag, 0);
  if PlayObject.GetQuestFalgStatus(nFlag) = 1 then begin
    SysMsg(Format(g_sGameCommandShowHumanFlagONMsg, [PlayObject.m_sCharName, nFlag]), c_Green, t_Hint);
  end else begin
    SysMsg(Format(g_sGameCommandShowHumanFlagOFFMsg, [PlayObject.m_sCharName, nFlag]), c_Green, t_Hint);
  end;
end;

procedure TPlayObject.CmdShowHumanUnit(sCmd: string; nPermission: Integer;
  sHumanName, sUnit: string);
var
  PlayObject: TPlayObject;
  nUnit: Integer;
begin
  {if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(format(g_sGameCommandParamUnKnow, [sCmd, g_sGameCommandShowHumanUnitHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  nUnit := Str_ToInt(sUnit, 0);
  if PlayObject.GetQuestUnitStatus(nUnit) = 1 then begin
    SysMsg(format(g_sGameCommandShowHumanUnitONMsg, [PlayObject.m_sCharName, nUnit]), c_Green, t_Hint);
  end else begin
    SysMsg(format(g_sGameCommandShowHumanUnitOFFMsg, [PlayObject.m_sCharName, nUnit]), c_Green, t_Hint);
  end;}
end;

procedure TPlayObject.CmdShowHumanUnitOpen(sCmd: string; nPermission: Integer;
  sHumanName, sUnit: string);
var
  PlayObject: TPlayObject;
  nUnit: Integer;
begin
  {if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(format(g_sGameCommandParamUnKnow, [sCmd, g_sGameCommandShowHumanUnitHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  nUnit := Str_ToInt(sUnit, 0);
  if PlayObject.GetQuestUnitOpenStatus(nUnit) = 1 then begin
    SysMsg(format(g_sGameCommandShowHumanUnitONMsg, [PlayObject.m_sCharName, nUnit]), c_Green, t_Hint);
  end else begin
    SysMsg(format(g_sGameCommandShowHumanUnitOFFMsg, [PlayObject.m_sCharName, nUnit]), c_Green, t_Hint);
  end;}
end;

procedure TPlayObject.CmdShowMapInfo(Cmd: pTGameCmd; sParam1: string);
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, '']), c_Red, t_Hint);
    Exit;
  end;
  SysMsg(Format(g_sGameCommandMapInfoMsg, [m_PEnvir.sMapName, m_PEnvir.sMapDesc]), c_Green, t_Hint);
  SysMsg(Format(g_sGameCommandMapInfoSizeMsg, [m_PEnvir.m_nWidth, m_PEnvir.m_nHeight]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdShowMapMode(sCmd, sMapName: string);
var
  Envir: TEnvirnoment;
  sMsg: string;
begin
  if (m_btPermission < 6) then Exit;
  if (sMapName = '') then begin
    SysMsg('@' + sCmd + ' <Map>',c_Red,t_Hint);
    Exit;
  end;
  Envir := g_MapManager.FindMap(sMapName);
  if (Envir = nil) then begin
    SysMsg(sMapName + ' does not exist.',c_Red,t_Hint);
    Exit;
  end;
  sMsg:='Map Info: ' + Envir.GetEnvirInfo;
  SysMsg(sMsg, c_Blue, t_Hint);
end;

procedure TPlayObject.CmdSetMapMode(sCmd, sMapName, sMapMode, sParam1,
  sParam2: string);
var
  Envir: TEnvirnoment;
  sMsg: string;
begin
  if (m_btPermission < 6) then Exit;
  if (sMapName = '') or (sMapMode = '') then begin
    SysMsg('@' + sCmd + ' <Map> <MapMode>',c_Red,t_Hint);
    Exit;
  end;
  Envir := g_MapManager.FindMap(sMapName);
  if (Envir = nil) then begin
    SysMsg(sMapName + ' does not exist.',c_Red,t_Hint);
    Exit;
  end;
  if CompareText(sMapMode, 'SAFE') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boSAFE := True;
    end else begin
      Envir.m_boSAFE := False;
    end;
  end else
    if CompareText(sMapMode, 'DARK') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boDARK := True;
    end else begin
      Envir.m_boDARK := False;
    end;
  end else
    if CompareText(sMapMode, 'DARK') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boDARK := True;
    end else begin
      Envir.m_boDARK := False;
    end;
  end else
    if CompareText(sMapMode, 'FIGHT') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boFightZone := True;
    end else begin
      Envir.m_boFightZone := False;
    end;
  end else
    if CompareText(sMapMode, 'FIGHT3') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boFight3Zone := True;
    end else begin
      Envir.m_boFight3Zone := False;
    end;
  end else
    if CompareText(sMapMode, 'DAY') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boDAY := True;
    end else begin
      Envir.m_boDAY := False;
    end;
  end else
    if CompareText(sMapMode, 'QUIZ') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boQUIZ := True;
    end else begin
      Envir.m_boQUIZ := False;
    end;
  end else
    if CompareText(sMapMode, 'NORECONNECT') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boNORECONNECT := True;
      Envir.sNoReconnectMap := sParam1;
    end else begin
      Envir.m_boNORECONNECT := False;
    end;
  end else
    if CompareText(sMapMode, 'MUSIC') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boMUSIC := True;
      Envir.m_nMUSICID := Str_ToInt(sParam1, -1);
    end else begin
      Envir.m_boMUSIC := False;
    end;
  end else
    if CompareText(sMapMode, 'EXPRATE') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boEXPRATE := True;
      Envir.m_nEXPRATE := Str_ToInt(sParam1, -1);
    end else begin
      Envir.m_boEXPRATE := False;
    end;
  end else
    if CompareText(sMapMode, 'PKWINLEVEL') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boPKWINLEVEL := True;
      Envir.m_nPKWINLEVEL := Str_ToInt(sParam1, -1);
    end else begin
      Envir.m_boPKWINLEVEL := False;
    end;
  end else
    if CompareText(sMapMode, 'PKWINEXP') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boPKWINEXP := True;
      Envir.m_nPKWINEXP := Str_ToInt(sParam1, -1);
    end else begin
      Envir.m_boPKWINEXP := False;
    end;
  end else
    if CompareText(sMapMode, 'PKLOSTLEVEL') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boPKLOSTLEVEL := True;
      Envir.m_nPKLOSTLEVEL := Str_ToInt(sParam1, -1);
    end else begin
      Envir.m_boPKLOSTLEVEL := False;
    end;
  end else
    if CompareText(sMapMode, 'PKLOSTEXP') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boPKLOSTEXP := True;
      Envir.m_nPKLOSTEXP := Str_ToInt(sParam1, -1);
    end else begin
      Envir.m_boPKLOSTEXP := False;
    end;
  end else
    if CompareText(sMapMode, 'DECHP') = 0 then begin
    if (sParam1 <> '') and (sParam2 <> '') then begin
      Envir.m_boDECHP := True;
      Envir.m_nDECHPTIME := Str_ToInt(sParam1, -1);
      Envir.m_nDECHPPOINT := Str_ToInt(sParam2, -1);
    end else begin
      Envir.m_boDECHP := False;
    end;
  end else
    if CompareText(sMapMode, 'DECGAMEGOLD') = 0 then begin
    if (sParam1 <> '') and (sParam2 <> '') then begin
      Envir.m_boDecGameGold := True;
      Envir.m_nDECGAMEGOLDTIME := Str_ToInt(sParam1, -1);
      Envir.m_nDecGameGold := Str_ToInt(sParam2, -1);
    end else begin
      Envir.m_boDecGameGold := False;
    end;
  end else
    if CompareText(sMapMode, 'INCGAMEGOLD') = 0 then begin
    if (sParam1 <> '') and (sParam2 <> '') then begin
      Envir.m_boIncGameGold := True;
      Envir.m_nINCGAMEGOLDTIME := Str_ToInt(sParam1, -1);
      Envir.m_nIncGameGold := Str_ToInt(sParam2, -1);
    end else begin
      Envir.m_boIncGameGold := False;
    end;
  end else
    if CompareText(sMapMode, 'INCGAMEPOINT') = 0 then begin
    if (sParam1 <> '') and (sParam2 <> '') then begin
      Envir.m_boINCGAMEPOINT := True;
      Envir.m_nINCGAMEPOINTTIME := Str_ToInt(sParam1, -1);
      Envir.m_nINCGAMEPOINT := Str_ToInt(sParam2, -1);
    end else begin
      Envir.m_boIncGameGold := False;
    end;
  end else
    if CompareText(sMapMode, 'RUNHUMAN') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boRUNHUMAN := True;
    end else begin
      Envir.m_boRUNHUMAN := False;
    end;
  end else
    if CompareText(sMapMode, 'RUNMON') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boRUNMON := True;
    end else begin
      Envir.m_boRUNMON := False;
    end;
  end else
    if CompareText(sMapMode, 'NEEDHOLE') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boNEEDHOLE := True;
    end else begin
      Envir.m_boNEEDHOLE := False;
    end;
  end else
    if CompareText(sMapMode, 'NORECALL') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boNORECALL := True;
    end else begin
      Envir.m_boNORECALL := False;
    end;
  end else
    if CompareText(sMapMode, 'NOGUILDRECALL') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boNOGUILDRECALL := True;
    end else begin
      Envir.m_boNOGUILDRECALL := False;
    end;
  end else
    if CompareText(sMapMode, 'NODEARRECALL') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boNODEARRECALL := True;
    end else begin
      Envir.m_boNODEARRECALL := False;
    end;
  end else
    if CompareText(sMapMode, 'NOMASTERRECALL') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boNOMASTERRECALL := True;
    end else begin
      Envir.m_boNOMASTERRECALL := False;
    end;
  end else
    if CompareText(sMapMode, 'NORANDOMMOVE') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boNORANDOMMOVE := True;
    end else begin
      Envir.m_boNORANDOMMOVE := False;
    end;
  end else
    if CompareText(sMapMode, 'NODRUG') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boNODRUG := True;
    end else begin
      Envir.m_boNODRUG := False;
    end;
  end else
    if CompareText(sMapMode, 'MINE') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boMINE := True;
    end else begin
      Envir.m_boMINE := False;
    end;
  end else
    if CompareText(sMapMode, 'NOMINIMAP') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boNOMINIMAP := True;
    end else begin
      Envir.m_boNOMINIMAP := False;
    end;
  end else
    if CompareText(sMapMode, 'NOPOSITIONMOVE') = 0 then begin
    if (sParam1 <> '') then begin
      Envir.m_boNOPOSITIONMOVE := True;
    end else begin
      Envir.m_boNOPOSITIONMOVE := False;
    end;
  end;
  sMsg:='Environment: ' + Envir.GetEnvirInfo;
  SysMsg(sMsg, c_Blue, t_Hint);
end;

procedure TPlayObject.CmdSignMapMove(Cmd: pTGameCmd);
var
  Envir: TEnvirnoment;
  nX, nY: Integer;
begin
  try
    if (m_btPermission < Cmd.nPermissionMin) then begin
      SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
      Exit;
    end;
    if (m_btPermission >= Cmd.nPermissionMax) or CanMoveMap(m_sLastMapName) then begin
      Envir := g_MapManager.FindMap(m_sLastMapName);
      if Envir <> nil then begin
        if Envir.CanWalk(m_nLastCurrX, m_nLastCurrY, True) then begin
          SpaceMove(m_sLastMapName, m_nLastCurrX, m_nLastCurrY, 0);
        end else begin
          SysMsg(Format(g_sGameCommandPositionMoveCanotMoveToMap1, [m_sLastMapName, m_nLastCurrX, m_nLastCurrY]), c_Green, t_Hint);
        end;
      end;
    end else begin
      SysMsg(Format(g_sTheMapDisableMove, [m_sLastMapName, Envir.sMapDesc]), c_Red, t_Hint);
    end;
  except
    on E: Exception do begin
      MainOutMessage('[Exceptioin] TPlayObject.CmdPositionMove');
      MainOutMessage(E.Message);
    end;
  end;
end;

procedure TPlayObject.CmdShowEffect(Cmd: pTGameCmd; sEffect, sTime: string);
var
  nEffectType: Integer;
  nTime: Integer;
  FlowerEvent: TFlowerEvent;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sEffect = '') or (Str_ToInt(sEffect, -1) <= 0) or (sTime = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Effect(1-13) Time', c_Red, t_Hint);
    Exit;
  end;
  nEffectType := Str_ToInt(sEffect, -1);
  nTime := Str_ToInt(sTime, 1);
  case nEffectType of
    1: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_FIREFLOWER_1, 1000 * nTime);
    2: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_FIREFLOWER_2, 1000 * nTime);
    3: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_FIREFLOWER_3, 1000 * nTime);
    4: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_FIREFLOWER_4, 1000 * nTime);
    5: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_FIREFLOWER_5, 1000 * nTime);
    6: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_FIREFLOWER_6, 1000 * nTime);
    7: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_FIREFLOWER_7, 1000 * nTime);
    8: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_FIREFLOWER_8, 1000 * nTime);
    9: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_SPACEDOOR_1, 1000 * nTime);
    10: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_SPACEDOOR_2, 1000 * nTime);
    11: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_SPACEDOOR_3, 1000 * nTime);
    12: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_SPACEDOOR_4, 1000 * nTime);
    13: FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_SPACEDOOR_5, 1000 * nTime);
  else FlowerEvent := TFlowerEvent.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_FIREFLOWER_1, 1000 * nTime);
  end;
  g_EventManager.AddEvent(FlowerEvent);
end;

procedure TPlayObject.CmdGetUserItems(Cmd: pTGameCmd; sHumanName, sItemName, sItemCount, sType: string);
var
  I: Integer;
  PlayObject: TPlayObject;
  nItemCount: Integer;
  nCount: Integer;
  nType: Integer;
  StdItem: pTStdItem;
  UserItem: pTUserItem;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or (sItemName = '') or (sItemCount = '') or (sType = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name Item Quantity Type(0,1,2))', c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  nCount := Str_ToInt(sItemCount, 0);
  nType := Str_ToInt(sType, 0);
  case nType of
    0: begin
        nItemCount := 0;
        for I := Low(PlayObject.m_UseItems) to High(PlayObject.m_UseItems) do begin
          if m_ItemList.Count >= 46 then Break;
          UserItem := @PlayObject.m_UseItems[I];
          StdItem := UserEngine.GetStdItem(UserItem.wIndex);
          if (StdItem <> nil) and (CompareText(sItemName, StdItem.Name) = 0) then begin
            if not IsEnoughBag then Break;
            New(UserItem);
            UserItem^ := PlayObject.m_UseItems[I];
            m_ItemList.Add(UserItem);
            SendAddItem(UserItem);
            PlayObject.m_UseItems[I].wIndex := 0;
            Inc(nItemCount);
            if nItemCount >= nCount then Break;
          end;
        end;
        PlayObject.SendUseitems();
      end;
    1: begin
        nItemCount := 0;
        for I := PlayObject.m_ItemList.Count - 1 downto 0 do begin
          if m_ItemList.Count >= 46 then Break;
          if PlayObject.m_ItemList.Count <= 0 then Break;
          UserItem := PlayObject.m_ItemList.Items[I];
          StdItem := UserEngine.GetStdItem(UserItem.wIndex);
          if (StdItem <> nil) and (CompareText(sItemName, StdItem.Name) = 0) then begin
            if not IsEnoughBag then Break;
            PlayObject.SendDelItems(UserItem);
            PlayObject.m_ItemList.Delete(I);
            m_ItemList.Add(UserItem);
            SendAddItem(UserItem);
            Inc(nItemCount);
            if nItemCount >= nCount then Break;
          end;
        end;
      end;
    2: begin
        nItemCount := 0;
        for I := PlayObject.m_StorageItemList.Count - 1 downto 0 do begin
          if m_ItemList.Count >= 46 then Break;
          if PlayObject.m_StorageItemList.Count <= 0 then Break;
          UserItem := PlayObject.m_StorageItemList.Items[I];
          StdItem := UserEngine.GetStdItem(UserItem.wIndex);
          if (StdItem <> nil) and (CompareText(sItemName, StdItem.Name) = 0) then begin
            if not IsEnoughBag then Break;
            PlayObject.m_StorageItemList.Delete(I);
            m_ItemList.Add(UserItem);
            SendAddItem(UserItem);
            Inc(nItemCount);
            if nItemCount >= nCount then Break;
          end;
        end;
      end;
  end;
end;

procedure TPlayObject.CmdDeleteItem(Cmd: pTGameCmd; sHumanName, sItemName: string; nCount: Integer);
var
  I: Integer;
  PlayObject: TPlayObject;
  nItemCount: Integer;
  StdItem: pTStdItem;
  UserItem: pTUserItem;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or (sItemName = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name Item Quantity)', c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  nItemCount := 0;
  for I := PlayObject.m_ItemList.Count - 1 downto 0 do begin
    if PlayObject.m_ItemList.Count <= 0 then Break;
    UserItem := PlayObject.m_ItemList.Items[I];
    StdItem := UserEngine.GetStdItem(UserItem.wIndex);
    if (StdItem <> nil) and (CompareText(sItemName, StdItem.Name) = 0) then begin
      PlayObject.SendDelItems(UserItem);
      PlayObject.m_ItemList.Delete(I);
      Dispose(UserItem);
      Inc(nItemCount);
      if nItemCount >= nCount then
        Break;
    end;
  end;
end;

procedure TPlayObject.CmdDelGold(Cmd: pTGameCmd; sHumName: string; nCount: Integer);
var
  PlayObject: TPlayObject;
  nServerIndex: Integer;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumName = '') or (nCount <= 0) then Exit;
  PlayObject := UserEngine.GetPlayObject(sHumName);
  if PlayObject <> nil then begin
    if PlayObject.m_nGold > nCount then begin
      Dec(PlayObject.m_nGold, nCount);
    end else begin
      nCount := PlayObject.m_nGold;
      PlayObject.m_nGold := 0;
    end;
    PlayObject.GoldChanged();
    SysMsg(sHumName + 'gold has been reduced ' + IntToStr(nCount) + '.', c_Green, t_Hint);
    if g_boGameLogGold then
      AddGameDataLog('13' + #9 +
        m_sMapName + #9 +
        IntToStr(m_nCurrX) + #9 +
        IntToStr(m_nCurrY) + #9 +
        m_sCharName + #9 +
        sSTRING_GOLDNAME + #9 +
        IntToStr(nCount) + #9 +
        '1' + #9 +
        sHumName);
  end else begin
    FrontEngine.AddChangeGoldList(t_Gold, m_sCharName, sHumName, -nCount);
    SysMsg(sHumName + 'is not online, so its gold will automatically reduce', c_Green, t_Hint);
  end;
end;

procedure TPlayObject.CmdDelGuild(Cmd: pTGameCmd; sGuildName: string);
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if nServerIndex <> 0 then begin
    SysMsg('This command can only be used on the Main Server.', c_Red, t_Hint);
    Exit;
  end;
  if sGuildName = '' then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' GuildName', c_Red, t_Hint);
    Exit;
  end;
  if g_GuildManager.DELGUILD(sGuildName) then begin
    //UserEngine.SendServerGroupMsg(SS_206, nServerIndex, sGuildName);
  end else begin
    SysMsg('Guild ' + sGuildName + ' was not found.', c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdDelNpc(sCmd: string; nPermission: Integer; sParam1: string);
var
  BaseObject: TActorObject;
  I: Integer;
resourcestring
  sDelOK = 'NPC successfully removed.';
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, '']), c_Red, t_Hint);
    Exit;
  end;
  if (sParam1 = '') then begin
    BaseObject := GetPoseCreate();
    if BaseObject <> nil then begin
      for I := 0 to UserEngine.m_MerchantList.Count - 1 do begin
        if TActorObject(UserEngine.m_MerchantList.Items[I]) = BaseObject then begin
          BaseObject.m_boGhost := True;
          BaseObject.m_dwGhostTick := GetTickCount();
          BaseObject.SendRefMsg(RM_DISAPPEAR, 0, 0, 0, 0, '');
          SysMsg(sDelOK, c_Red, t_Hint);
          Exit;
        end;
      end;
      for I := 0 to UserEngine.QuestNPCList.Count - 1 do begin
        if TActorObject(UserEngine.QuestNPCList.Items[I]) = BaseObject then begin
          BaseObject.m_boGhost := True;
          BaseObject.m_dwGhostTick := GetTickCount();
          BaseObject.SendRefMsg(RM_DISAPPEAR, 0, 0, 0, 0, '');
          SysMsg(sDelOK, c_Red, t_Hint);
          Exit;
        end;
      end;
    end;
    SysMsg(g_sGameCommandDelNpcMsg, c_Red, t_Hint);
  end else begin
    for I := 0 to UserEngine.m_MerchantList.Count - 1 do begin
      BaseObject := TActorObject(UserEngine.m_MerchantList.Items[I]);
      if CompareText(BaseObject.m_sCharName, sParam1) = 0 then begin
        BaseObject.m_boGhost := True;
        BaseObject.m_dwGhostTick := GetTickCount();
        BaseObject.SendRefMsg(RM_DISAPPEAR, 0, 0, 0, 0, '');
        SysMsg(sDelOK, c_Red, t_Hint);
        Exit;
      end;
    end;
    for I := 0 to UserEngine.QuestNPCList.Count - 1 do begin
      BaseObject := TActorObject(UserEngine.QuestNPCList.Items[I]);
      if CompareText(BaseObject.m_sCharName, sParam1) = 0 then begin
        BaseObject.m_boGhost := True;
        BaseObject.m_dwGhostTick := GetTickCount();
        BaseObject.SendRefMsg(RM_DISAPPEAR, 0, 0, 0, 0, '');
        SysMsg(sDelOK, c_Red, t_Hint);
        Exit;
      end;
    end;
    SysMsg(g_sGameCommandDelNpcMsg1, c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdDelSkill(Cmd: pTGameCmd; sHumanName, sSkillName: string);
var
  I: Integer;
  PlayObject: TPlayObject;
  boDelAll: Boolean;
  UserMagic: pTUserMagic;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or (sSkillName = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name Skill)', c_Red, t_Hint);
    Exit;
  end;
  if CompareText(sSkillName, 'All') = 0 then boDelAll := True
  else boDelAll := False;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;

  for I := PlayObject.m_MagicList.Count - 1 downto 0 do begin
    if PlayObject.m_MagicList.Count <= 0 then Break;
    UserMagic := PlayObject.m_MagicList.Items[I];
    if boDelAll then begin
      Dispose(UserMagic);
      PlayObject.m_MagicList.Delete(I);
    end else begin
      if CompareText(UserMagic.MagicInfo.sMagicName, sSkillName) = 0 then begin
        PlayObject.SendDelMagic(UserMagic);
        PlayObject.m_MagicList.Delete(I);
        Dispose(UserMagic);
        PlayObject.SysMsg(Format('%s has been removed', [sSkillName]), c_Green, t_Hint);
        SysMsg(Format('%s skill %s has been removed', [sHumanName, sSkillName]), c_Green, t_Hint);
        Break;
      end;
    end;
  end;
end;

procedure TPlayObject.CmdDenyAccountLogon(Cmd: pTGameCmd; sAccount, sFixDeny: string);
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if sAccount = '' then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' AccountID Setting(0,1) - 1 to block login, 0 to allow.', c_Red, t_Hint);
    Exit;
  end;
  g_DenyAccountList.Lock;
  try
    if (sFixDeny <> '') and (sFixDeny[1] = '1') then begin
      g_DenyAccountList.AddObject(sAccount, TObject(1));
      SaveDenyAccountList();
      SysMsg(sAccount + ' added to blocked list.', c_Green, t_Hint);
    end else begin
      g_DenyAccountList.AddObject(sAccount, TObject(0));
      SysMsg(sAccount + 'Account has been added to the Temp banned list.', c_Green, t_Hint);
    end;
  finally
    g_DenyAccountList.UnLock;
  end;
end;

procedure TPlayObject.CmdDenyCharNameLogon(Cmd: pTGameCmd; sCharName, sFixDeny: string);
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if sCharName = '' then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name Setting(0,1) - 1 to ban Character, 0 to unban.', c_Red, t_Hint);
    Exit;
  end;
  g_DenyChrNameList.Lock;
  try
    if (sFixDeny <> '') and (sFixDeny[1] = '1') then begin
      g_DenyChrNameList.AddObject(sCharName, TObject(1));
      SaveDenyChrNameList();
      SysMsg(sCharName + ' has joined the list of banned names', c_Green, t_Hint);
    end else begin
      g_DenyChrNameList.AddObject(sCharName, TObject(0));
      SysMsg(sCharName + ' has been banned.', c_Green, t_Hint);
    end;
  finally
    g_DenyChrNameList.UnLock;
  end;
end;

procedure TPlayObject.CmdDenyIPaddrLogon(Cmd: pTGameCmd; sIPaddr, sFixDeny: string);
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if sIPaddr = '' then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' IPAddress Setting(0,1) - 1 ban IPAddress, 0 allow IPAddress.', c_Red, t_Hint);
    Exit;
  end;
  g_DenyIPAddrList.Lock;
  try
    if (sFixDeny <> '') and (sFixDeny[1] = '1') then begin
      g_DenyIPAddrList.AddObject(sIPaddr, TObject(1));
      SaveDenyIPAddrList();
      SysMsg(sIPaddr + ' added to blocked list.', c_Green, t_Hint);
    end else begin
      g_DenyIPAddrList.AddObject(sIPaddr, TObject(0));
      SysMsg(sIPaddr + ' has been banned.', c_Green, t_Hint);
    end;
  finally
    g_DenyIPAddrList.UnLock;
  end;
end;

procedure TPlayObject.CmdDisableFilter(sCmd, sParam1: string);
begin
  if (m_btPermission < 6) then Exit;
  if (sParam1 <> '') and (sParam1[1] = '?') then begin
    SysMsg('Enable/Disable Word Filter.', c_Red, t_Hint);
    Exit;
  end;
  boFilterWord := not boFilterWord;
  if boFilterWord then begin
    SysMsg('Text Filter is enabled.', c_Green, t_Hint);
  end else begin
    SysMsg('Text Filter is disabled.', c_Green, t_Hint);
  end;
end;

procedure TPlayObject.CmdDelDenyAccountLogon(Cmd: pTGameCmd; sAccount,
  sFixDeny: string);
var
  I: Integer;
  boDelete: Boolean;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if sAccount = '' then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' AccountID', c_Red, t_Hint);
    Exit;
  end;
  boDelete := False;
  g_DenyAccountList.Lock;
  try
    for I := 0 to g_DenyAccountList.Count - 1 do begin
      if CompareText(sAccount, g_DenyAccountList.Strings[I]) = 0 then begin
        if Integer(g_DenyAccountList.Objects[I]) <> 0 then
          SaveDenyAccountList;
        g_DenyAccountList.Delete(I);
        SysMsg(sAccount + ' removed from banned list.', c_Green, t_Hint);
        boDelete := True;
        Break;
      end;
    end;
  finally
    g_DenyAccountList.UnLock;
  end;
  if not boDelete then
    SysMsg(sAccount + 'Login is not prohibited.', c_Green, t_Hint);
end;

procedure TPlayObject.CmdDelDenyCharNameLogon(Cmd: pTGameCmd; sCharName,
  sFixDeny: string);
var
  I: Integer;
  boDelete: Boolean;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if sCharName = '' then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' CharacterName', c_Red, t_Hint);
    Exit;
  end;
  boDelete := False;
  g_DenyChrNameList.Lock;
  try
    for I := 0 to g_DenyChrNameList.Count - 1 do begin
      if CompareText(sCharName, g_DenyChrNameList.Strings[I]) = 0 then begin
        if Integer(g_DenyChrNameList.Objects[I]) <> 0 then
          SaveDenyChrNameList;
        g_DenyChrNameList.Delete(I);
        SysMsg(sCharName + ' removed from banned list.', c_Green, t_Hint);
        boDelete := True;
        Break;
      end;
    end;
  finally
    g_DenyChrNameList.UnLock;
  end;
  if not boDelete then
    SysMsg(sCharName + 'Login is not prohibited.', c_Green, t_Hint);
end;

procedure TPlayObject.CmdDelDenyIPaddrLogon(Cmd: pTGameCmd; sIPaddr,
  sFixDeny: string);
var
  I: Integer;
  boDelete: Boolean;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if sIPaddr = '' then begin
    SysMsg('Usage @' + Cmd.sCmd + ' IPAddress', c_Red, t_Hint);
    Exit;
  end;
  boDelete := False;
  g_DenyIPAddrList.Lock;
  try
    for I := g_DenyIPAddrList.Count - 1 downto 0 do begin
      if g_DenyIPAddrList.Count <= 0 then Break;
      if CompareText(sIPaddr, g_DenyIPAddrList.Strings[I]) = 0 then begin
        if Integer(g_DenyIPAddrList.Objects[I]) <> 0 then
          SaveDenyIPAddrList;
        g_DenyIPAddrList.Delete(I);
        SysMsg(sIPaddr + 'Login IP banned from the list.', c_Green, t_Hint);
        boDelete := True;
        Break;
      end;
    end;
  finally
    g_DenyIPAddrList.UnLock;
  end;
  if not boDelete then
    SysMsg(sIPaddr + 'Login is not prohibited.', c_Green, t_Hint);
end;

procedure TPlayObject.CmdShowDenyAccountLogon(Cmd: pTGameCmd; sAccount,
  sFixDeny: string);
var
  I: Integer;
begin
  if (m_btPermission < 6) then Exit;
  g_DenyAccountList.Lock;
  try
    if g_DenyAccountList.Count <= 0 then begin
      SysMsg('List is empty.', c_Green, t_Hint);
      Exit;
    end;
    for I := 0 to g_DenyAccountList.Count - 1 do begin
      SysMsg(g_DenyAccountList.Strings[I], c_Green, t_Hint);
    end;
  finally
    g_DenyAccountList.UnLock;
  end;
end;

procedure TPlayObject.CmdShowDenyCharNameLogon(Cmd: pTGameCmd; sCharName,
  sFixDeny: string);
var
  I: Integer;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  g_DenyChrNameList.Lock;
  try
    if g_DenyChrNameList.Count <= 0 then begin
      SysMsg('List is empty', c_Green, t_Hint);
      Exit;
    end;
    for I := 0 to g_DenyChrNameList.Count - 1 do begin
      SysMsg(g_DenyChrNameList.Strings[I], c_Green, t_Hint);
    end;
  finally
    g_DenyChrNameList.UnLock;
  end;
end;

procedure TPlayObject.CmdShowDenyIPaddrLogon(Cmd: pTGameCmd; sIPaddr,
  sFixDeny: string);
var
  I: Integer;
  nCount: Integer;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  g_DenyIPAddrList.Lock;
  try
    nCount := g_DenyIPAddrList.Count;
    if g_DenyIPAddrList.Count <= 0 then begin
      SysMsg('List is empty.', c_Green, t_Hint);
    end;
    if nCount > 0 then begin
      for I := 0 to g_DenyIPAddrList.Count - 1 do begin
        SysMsg(g_DenyIPAddrList.Strings[I], c_Green, t_Hint);
      end;
    end;
  finally
    g_DenyIPAddrList.UnLock;
  end;
end;

procedure TPlayObject.CmdDisableSendMsg(Cmd: pTGameCmd; sHumanName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if sHumanName = '' then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name', c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    PlayObject.m_boFilterSendMsg := True;
  end;
  g_DisableSendMsgList.Add(sHumanName);
  SaveDisableSendMsgList();
  SysMsg(sHumanName + ' can no longer speak.', c_Green, t_Hint);
end;

procedure TPlayObject.CmdDisableSendMsgList(Cmd: pTGameCmd);
var
  I: Integer;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if g_DisableSendMsgList.Count <= 0 then begin
    SysMsg('List is empty.', c_Red, t_Hint);
    Exit;
  end;

  SysMsg('Gag List:', c_Blue, t_Hint);
  for I := 0 to g_DisableSendMsgList.Count - 1 do begin
    SysMsg(g_DisableSendMsgList.Strings[I], c_Green, t_Hint);
  end;
end;

procedure TPlayObject.CmdEnableSendMsg(Cmd: pTGameCmd; sHumanName: string);
var
  I: Integer;
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if sHumanName = '' then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name', c_Red, t_Hint);
    Exit;
  end;
  for I := g_DisableSendMsgList.Count - 1 downto 0 do begin
    if g_DisableSendMsgList.Count <= 0 then Break;
    if CompareText(sHumanName, g_DisableSendMsgList.Strings[I]) = 0 then begin
      PlayObject := UserEngine.GetPlayObject(sHumanName);
      if PlayObject <> nil then begin
        PlayObject.m_boFilterSendMsg := False;
      end;
      g_DisableSendMsgList.Delete(I);
      SaveDisableSendMsgList();
      SysMsg(sHumanName + ' can now speak.', c_Green, t_Hint);
      Exit;
    end;
  end;
  SysMsg(sHumanName + ' was not found.', c_Red, t_Hint);
end;

procedure TPlayObject.CmdEndGuild;
begin
  if (m_MyGuild <> nil) then begin
    if (m_nGuildRankNo > 1) then begin
      if TGUild(m_MyGuild).IsMember(m_sCharName) and TGUild(m_MyGuild).DelMember(m_sCharName) then begin
        //UserEngine.SendServerGroupMsg(SS_207, nServerIndex, TGUild(m_MyGuild).sGuildName);
        m_MyGuild := nil;
        RefRankInfo(0, '');
        RefShowName(); //10/31
        SysMsg('You have quit the Guild.', c_Green, t_Hint);
      end;
    end else begin
      SysMsg('Guild leader can not quit.', c_Red, t_Hint);
    end;
  end else begin
    SysMsg('You are not a member of a Guild', c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdFireBurn(nInt, nTime, nN: Integer);
var
  FireBurnEvent: TFireBurnEvent;
begin
  if (m_btPermission < 6) then Exit;
  if (nInt = 0) or (nTime = 0) or (nN = 0) then begin
    SysMsg('Usage: @' + g_GameCommand.FIREBURN.sCmd + ' nInt nTime nN', c_Red, t_Hint);
    Exit;
  end;
  FireBurnEvent := TFireBurnEvent.Create(Self, m_nCurrX, m_nCurrY, nInt, nTime, nN);
  g_EventManager.AddEvent(FireBurnEvent);
end;

procedure TPlayObject.CmdForcedWallconquestWar(Cmd: pTGameCmd; sCASTLENAME: string);
var
  Castle: TUserCastle;
  s20: string;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;

  if sCASTLENAME = '' then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Castle', c_Red, t_Hint);
    Exit;
  end;

  Castle := g_CastleManager.Find(sCASTLENAME);
  if Castle <> nil then begin
    Castle.m_boUnderWar := not Castle.m_boUnderWar;
    if Castle.m_boUnderWar then begin
      Castle.m_dwStartCastleWarTick := GetTickCount();
      Castle.StartWallconquestWar();

      //UserEngine.SendServerGroupMsg(SS_212, nServerIndex, '');
      s20 := '[' + Castle.m_sName + ' war has started]';
      UserEngine.SendBroadCastMsg(s20, t_System);
      //UserEngine.SendServerGroupMsg(SS_204, nServerIndex, s20);
      Castle.MainDoorControl(True);
    end else begin
      Castle.StopWallconquestWar();
    end;
  end else begin
    SysMsg(Format(g_sGameCommandSbkGoldCastleNotFoundMsg, [sCASTLENAME]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdFreePenalty(Cmd: pTGameCmd; sHumanName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandFreePKHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject.m_nPkPoint := 0;
  PlayObject.RefNameColor();
  PlayObject.SysMsg(g_sGameCommandFreePKHumanMsg, c_Green, t_Hint);
  SysMsg(Format(g_sGameCommandFreePKMsg, [sHumanName]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdGroupRecall(sCmd: string);
var
  I: Integer;
  dwValue: LongWord;
  PlayObject: TPlayObject;
begin
  if m_boRecallSuite or (m_btPermission >= 6) then begin
    if not m_PEnvir.m_boNORECALL then begin
      dwValue := (GetTickCount - m_dwGroupRcallTick) div 1000;
      m_dwGroupRcallTick := m_dwGroupRcallTick + dwValue * 1000;
      if m_btPermission >= 6 then m_wGroupRcallTime := 0;

      if m_wGroupRcallTime > dwValue then begin
        Dec(m_wGroupRcallTime, dwValue);
      end else m_wGroupRcallTime := 0;
      if m_wGroupRcallTime = 0 then begin
        if m_GroupOwner = Self then begin
          for I := 1 to m_GroupMembers.Count - 1 do begin
            PlayObject := TPlayObject(m_GroupMembers.Objects[I]);
            if PlayObject.m_boAllowGroupReCall then begin
              if PlayObject.m_PEnvir.m_boNORECALL then begin
                SysMsg(Format('%s is not allowed to be recalled on this map.', [PlayObject.m_sCharName]), c_Red, t_Hint);
              end else begin
                RecallHuman(PlayObject.m_sCharName);
              end;
            end else begin
              SysMsg(Format('%s is not allowed', [PlayObject.m_sCharName]), c_Red, t_Hint);
            end;
          end;
          m_dwGroupRcallTick := GetTickCount();
          m_wGroupRcallTime := g_Config.nGroupRecallTime;
        end;
      end else begin
        SysMsg(Format('%d seconds before you can use this feature again.', [m_wGroupRcallTime]), c_Red, t_Hint);
      end;
    end else begin
      SysMsg('Not allowed on this Map.', c_Red, t_Hint);
    end;
  end else begin
    SysMsg('You cannot use this feature.', c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdGuildRecall(sCmd, sParam: string);
var
  I, II: Integer;
  dwValue: LongWord;
  PlayObject: TPlayObject;
  GuildRank: pTGuildRank;
  nRecallCount, nNoRecallCount: Integer;
  Castle: TUserCastle;
begin
  if (sParam <> '') and (sParam[1] = '?') then begin
    SysMsg('This command will recall all Guild Members.', c_Red, t_Hint);
    Exit;
  end;

  if not m_boGuildMove and (m_btPermission < 6) then begin
    SysMsg('You cannot use this feature.', c_Red, t_Hint);
    Exit;
  end;
  if not IsGuildMaster then begin
    SysMsg('Only Guild Leader can use this feature.', c_Red, t_Hint);
    Exit;
  end;
  if m_PEnvir.m_boNOGUILDRECALL then begin
    SysMsg('Guild Recall is not allowed on this Map.', c_Red, t_Hint);
    Exit;
  end;
  Castle := g_CastleManager.InCastleWarArea(Self);

  //if UserCastle.m_boUnderWar and UserCastle.InCastleWarArea(m_PEnvir,m_nCurrX,m_nCurrY) then begin
  if (Castle <> nil) and Castle.m_boUnderWar then begin
    SysMsg('Cannot us this feature during a war.', c_Red, t_Hint);
    Exit;
  end;
  nRecallCount := 0;
  nNoRecallCount := 0;
  dwValue := (GetTickCount - m_dwGroupRcallTick) div 1000;
  m_dwGroupRcallTick := m_dwGroupRcallTick + dwValue * 1000;
  if m_btPermission >= 6 then m_wGroupRcallTime := 0;
  if m_wGroupRcallTime > dwValue then begin
    Dec(m_wGroupRcallTime, dwValue);
  end else m_wGroupRcallTime := 0;

  if m_wGroupRcallTime > 0 then begin
    SysMsg(Format('%d seconds before you can use this feature again.', [m_wGroupRcallTime]), c_Red, t_Hint);
    Exit;
  end;

  for I := 0 to TGUild(m_MyGuild).m_RankList.Count - 1 do begin
    GuildRank := TGUild(m_MyGuild).m_RankList.Items[I];
    if GuildRank = nil then Continue;
    for II := 0 to GuildRank.MemberList.Count - 1 do begin
      PlayObject := TPlayObject(GuildRank.MemberList.Objects[II]);
      if PlayObject <> nil then begin
        if PlayObject = Self then begin
          //          Inc(nNoRecallCount);
          Continue;
        end;
        if PlayObject.m_boAllowGuildReCall then begin
          if PlayObject.m_PEnvir.m_boNORECALL then begin
            SysMsg(Format('%s is not allowed recall on this Map.', [PlayObject.m_sCharName]), c_Red, t_Hint);
          end else begin
            RecallHuman(PlayObject.m_sCharName);
            Inc(nRecallCount);
          end;
        end else begin
          Inc(nNoRecallCount);
          SysMsg(Format('%s will not allow!', [PlayObject.m_sCharName]), c_Red, t_Hint);
        end;
      end;
    end;
  end;
  //  SysMsg('已传送' + IntToStr(nRecallCount) + '个成员，' + IntToStr(nNoRecallCount) + '个成员未被传送。',c_Green,t_Hint);
  SysMsg(Format('%d have been recalled, %d have not been recalled.', [nRecallCount, nNoRecallCount]), c_Green, t_Hint);
  m_dwGroupRcallTick := GetTickCount();
  m_wGroupRcallTime := g_Config.nGuildRecallTime;
end;

procedure TPlayObject.CmdGuildWar(sCmd, sGuildName: string);
begin
  if (m_btPermission < 6) then Exit;
end;

procedure TPlayObject.CmdHair(Cmd: pTGameCmd; sHumanName: string; nHair: Integer);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or (nHair < 0) then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Name Hair', c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    PlayObject.m_btHair := nHair;
    PlayObject.FeatureChanged();
    SysMsg(sHumanName + ' has been changed.', c_Green, t_Hint);
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdHumanInfo(Cmd: pTGameCmd; sHumanName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandInfoHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  SysMsg(PlayObject.GeTActorObjectInfo(), c_Green, t_Hint);
end;

procedure TPlayObject.CmdHumanLocal(Cmd: pTGameCmd; sHumanName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandHumanLocalHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  SysMsg(Format(g_sGameCommandHumanLocalMsg, [sHumanName, m_sIPLocal {GetIPLocal(PlayObject.m_sIPaddr)}]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdHunger(sCmd, sHumanName: string; nHungerPoint: Integer);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < 6) then Exit;
  if (sHumanName = '') or (nHungerPoint < 0) then begin
    SysMsg('Usage: @' + sCmd + ' Name HungerPoints', c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    PlayObject.m_nHungerStatus := nHungerPoint;
    PlayObject.SendMsg(PlayObject, RM_MYSTATUS, 0, 0, 0, 0, '');
    PlayObject.RefMyStatus();
    SysMsg(sHumanName + ' hunger has been set.', c_Green, t_Hint);
  end else begin
    SysMsg(sHumanName + ' is not online.', c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdIncPkPoint(Cmd: pTGameCmd; sHumanName: string; nPoint: Integer);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandIncPkPointHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  Inc(PlayObject.m_nPkPoint, nPoint);
  PlayObject.RefNameColor();
  if nPoint > 0 then
    SysMsg(Format(g_sGameCommandIncPkPointAddPointMsg, [sHumanName, nPoint]), c_Green, t_Hint)
  else
    SysMsg(Format(g_sGameCommandIncPkPointDecPointMsg, [sHumanName, -nPoint]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdKickHuman(Cmd: pTGameCmd; sHumName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumName = '') or ((sHumName <> '') and (sHumName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandKickHumanHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumName);
  if PlayObject <> nil then begin
    PlayObject.m_boKickFlag := True;
    PlayObject.m_boEmergencyClose := True;
    PlayObject.m_boPlayOffLine := False;
    PlayObject.m_boNotOnlineAddExp := False;
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdKill(Cmd: pTGameCmd; sHumanName: string);
var
  BaseObject: TActorObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if sHumanName <> '' then begin
    BaseObject := UserEngine.GetPlayObject(sHumanName);
    if BaseObject = nil then begin
      SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
      Exit;
    end;
  end else begin
    BaseObject := GetPoseCreate();
    if BaseObject = nil then begin
      SysMsg('You must face person to kill them.', c_Red, t_Hint);
      Exit;
    end;
  end;
  BaseObject.Die;
end;

procedure TPlayObject.CmdLockLogin(Cmd: pTGameCmd);
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if not g_Config.boLockHumanLogin then begin
    SysMsg('Server does not allow Login locks.', c_Red, t_Hint);
    Exit;
  end;

  if m_boLockLogon and not m_boLockLogoned then begin
    SysMsg('Your login is not locked.', c_Red, t_Hint);
    Exit;
  end;

  m_boLockLogon := not m_boLockLogon;
  if m_boLockLogon then begin
    SysMsg('Lock has been removed.', c_Green, t_Hint);
  end else begin
    SysMsg('Account has been locked.', c_Green, t_Hint);
  end;
end;

procedure TPlayObject.CmdLotteryTicket(sCmd: string; nPermission: Integer;
  sParam1: string);
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sParam1 = '') or ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, '']), c_Red, t_Hint);
    Exit;
  end;
  SysMsg(Format(g_sGameCommandLotteryTicketMsg, [g_Config.nWinLotteryCount,
    g_Config.nNoWinLotteryCount,
      g_Config.nWinLotteryLevel1,
      g_Config.nWinLotteryLevel2,
      g_Config.nWinLotteryLevel3,
      g_Config.nWinLotteryLevel4,
      g_Config.nWinLotteryLevel5,
      g_Config.nWinLotteryLevel6]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdLuckPoint(sCmd: string; nPermission: Integer;
  sHumanName, sCtr, sPoint: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, g_sGameCommandLuckPointHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  if sCtr = '' then begin
    SysMsg(Format(g_sGameCommandLuckPointMsg, [sHumanName, PlayObject.m_nBodyLuckLevel, PlayObject.m_dBodyLuck, PlayObject.m_nLuck]), c_Green, t_Hint);
    Exit;
  end;
end;

procedure TPlayObject.CmdMakeItem(Cmd: pTGameCmd; sItemName: string; nCount: Integer; sParam: string);
var
  I: Integer;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sItemName = '') then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGamecommandMakeHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  if (nCount <= 0) then nCount := 1;
  if (nCount > 10) then nCount := 10;
  if (m_btPermission < Cmd.nPermissionMax) then begin
    if not CanMakeItem(sItemName) then begin
      SysMsg(g_sGamecommandMakeItemNameOrPerMissionNot, c_Red, t_Hint);
      Exit;
    end;
    //if UserCastle.InCastleWarArea(m_PEnvir,m_nCurrX,m_nCurry) then begin
    if g_CastleManager.InCastleWarArea(Self) <> nil then begin
      SysMsg(g_sGamecommandMakeInCastleWarRange, c_Red, t_Hint);
      Exit;
    end;
    if not InSafeZone then begin
      SysMsg(g_sGamecommandMakeInSafeZoneRange, c_Red, t_Hint);
      Exit;
    end;
    nCount := 1;
  end;

  for I := 0 to nCount - 1 do begin
    if {(m_ItemList.Count >= MAXBAGITEM) or }(not IsEnoughBag) then Exit;

    New(UserItem);
    if UserEngine.CopyToUserItemFromName(sItemName, UserItem) then begin
      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if (StdItem.Price >= 15000) and not g_Config.boTestServer and (m_btPermission < 5) then begin
        Dispose(UserItem);
      end else begin
        if Random(g_Config.nMakeRandomAddValue {10}) = 0 then
          UserEngine.RandomUpgradeItem(UserItem);

        UserEngine._RandomItemLimitDay(UserItem, g_Config.nMakeRandomNotLimit);
        if Random(g_Config.nMakeRandomNewAddValue {10}) = 0 then
          UserEngine._RandomUpgradeItem(UserItem);

        if Random(g_Config.nMakeRandomAddPoint) = 0 then
          UserEngine.RandomItemAddPoint(UserItem);

      end;
      if StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26] then begin
        if StdItem.Shape in [130, 131, 132] then begin
          UserEngine.GetUnknowItemValue(UserItem);

          UserEngine._GetUnknowItemValue(UserItem);

        end;
      end;
      if m_btPermission >= Cmd.nPermissionMax then begin
        UserItem.MakeIndex := GetItemNumberEx(); //制造的物品另行取得物品ID
      end;

      if StdItem.StdMode = 49 then begin //聚灵珠
        UserItem.Dura := Str_ToInt(sParam, 0);
      end;

      m_ItemList.Add(UserItem);
      SendAddItem(UserItem);
      if g_Config.boShowMakeItemMsg and (m_btPermission >= 6) then
        MainOutMessage('[Item] ' + m_sCharName + ' ' + sItemName + '(' + IntToStr(UserItem.MakeIndex) + ')');
      if StdItem.NeedIdentify = 1 then
        AddGameDataLog('5' + #9 +
          m_sMapName + #9 +
          IntToStr(m_nCurrX) + #9 +
          IntToStr(m_nCurrY) + #9 +
          m_sCharName + #9 +
          //UserEngine.GetStdItemName(UserItem.wIndex) + #9 +
          StdItem.Name + #9 +
          IntToStr(UserItem.MakeIndex) + #9 +
          '1' + #9 +
          '0');
    end else begin //004CD114
      Dispose(UserItem);
      SysMsg(Format(g_sGamecommandMakeItemNameNotFound, [sItemName]), c_Red, t_Hint);
      Break;
    end;
  end;
end;

procedure TPlayObject.CmdMapMove(Cmd: pTGameCmd; sMapName: string);
var
  Envir: TEnvirnoment;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sMapName = '') or ((sMapName <> '') and (sMapName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandMoveHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  Envir := g_MapManager.FindMap(sMapName);
  if (Envir = nil) then begin
    SysMsg(Format(g_sTheMapNotFound, [sMapName]) { + ' 此地图号不存在！！！'}, c_Red, t_Hint);
    Exit;
  end;
  if (m_btPermission >= Cmd.nPermissionMax) or CanMoveMap(sMapName) then begin
    SendRefMsg(RM_SPACEMOVE_FIRE, 0, 0, 0, 0, '');
    MapRandomMove(sMapName, 0);
  end else begin
    SysMsg(Format(g_sTheMapDisableMove, [sMapName, Envir.sMapDesc]) {'地图 ' + sParam1 + ' 不允许传送！！！'}, c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdPositionMove(Cmd: pTGameCmd; sMapName, sX, sY: string);
var
  Envir: TEnvirnoment;
  nX, nY: Integer;
begin
  try
    if (m_btPermission < Cmd.nPermissionMin) then begin
      SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
      Exit;
    end;
    if (sMapName = '') or (sX = '') or (sY = '') or ((sMapName <> '') and (sMapName[1] = '?')) then begin
      SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandPositionMoveHelpMsg]), c_Red, t_Hint);
      Exit;
    end;
    if (m_btPermission >= Cmd.nPermissionMax) or CanMoveMap(sMapName) then begin
      Envir := g_MapManager.FindMap(sMapName);
      if Envir <> nil then begin
        nX := Str_ToInt(sX, 0);
        nY := Str_ToInt(sY, 0);
        if Envir.CanWalk(nX, nY, True) {and m_PEnvir.CanWalkOfEvent(Self, nX, nY)} then begin
          SpaceMove(sMapName, nX, nY, 0);
        end else begin
          SysMsg(Format(g_sGameCommandPositionMoveCanotMoveToMap, [sMapName, sX, sY]), c_Green, t_Hint);
        end;
      end;
    end else begin
      SysMsg(Format(g_sTheMapDisableMove, [sMapName, Envir.sMapDesc]), c_Red, t_Hint);
    end;
  except
    on E: Exception do begin
      MainOutMessage('[Exceptioin] TPlayObject.CmdPositionMove');
      MainOutMessage(E.Message);
    end;
  end;
end;

procedure TPlayObject.CmdMapMoveHuman(Cmd: pTGameCmd; sSrcMap, sDenMap: string);
var
  SrcEnvir, DenEnvir: TEnvirnoment;
  HumanList: TList;
  I: Integer;
  MoveHuman: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sDenMap = '') or (sSrcMap = '') or ((sSrcMap <> '') and (sSrcMap[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandMapMoveHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  SrcEnvir := g_MapManager.FindMap(sSrcMap);
  DenEnvir := g_MapManager.FindMap(sDenMap);
  if (SrcEnvir = nil) then begin
    SysMsg(Format(g_sGameCommandMapMoveMapNotFound, [sSrcMap]), c_Red, t_Hint);
    Exit;
  end;
  if (DenEnvir = nil) then begin
    SysMsg(Format(g_sGameCommandMapMoveMapNotFound, [sDenMap]), c_Red, t_Hint);
    Exit;
  end;
  HumanList := TList.Create;
  UserEngine.GetMapRageHuman(SrcEnvir, SrcEnvir.m_nWidth div 2, SrcEnvir.m_nHeight div 2, 1000, HumanList);
  for I := 0 to HumanList.Count - 1 do begin
    MoveHuman := TPlayObject(HumanList.Items[I]);
    if MoveHuman <> Self then
      MoveHuman.MapRandomMove(sDenMap, 0);
  end;
  HumanList.Free;
end;

procedure TPlayObject.CmdUserCmd(sLable: string);
begin
  if g_FunctionNPC <> nil then begin
    m_nScriptGotoCount := 0;
    g_FunctionNPC.GotoLable(Self, sLable, False);
  end;
end;

procedure TPlayObject.CmdMemberFunction(sCmd, sParam: string);
begin
  if (sParam <> '') and (sParam[1] = '?') then begin
    SysMsg('Opens Member Function NPC', c_Red, t_Hint);
    Exit;
  end;
  if g_ManageNPC <> nil then begin
    m_nScriptGotoCount := 0;
    g_ManageNPC.GotoLable(Self, '@Member', False);
  end;
end;

procedure TPlayObject.CmdMemberFunctionEx(sCmd, sParam: string);
begin
  if (sParam <> '') and (sParam[1] = '?') then begin
    SysMsg('Opens Member Function EX Npc', c_Red, t_Hint);
    Exit;
  end;
  if g_FunctionNPC <> nil then begin
    m_nScriptGotoCount := 0;
    g_FunctionNPC.GotoLable(Self, '@Member', False);
  end;
end;

procedure TPlayObject.CmdMission(Cmd: pTGameCmd; sX, sY: string); //004CCA08
var
  nX, nY: Integer;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sX = '') or (sY = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' X  Y', c_Red, t_Hint);
    Exit;
  end;
  nX := Str_ToInt(sX, 0);
  nY := Str_ToInt(sY, 0);
  g_boMission := True;
  g_sMissionMap := m_sMapName;
  g_nMissionX := nX;
  g_nMissionY := nY;
  SysMsg('Mob Mission has been set: ' + m_sMapName + '(' + IntToStr(g_nMissionX) + ':' + IntToStr(g_nMissionY) + ')', c_Green, t_Hint);
end;

procedure TPlayObject.CmdMob(Cmd: pTGameCmd; sMonName: string; nCount, nLevel: Integer); //004CC7F4
var
  I: Integer;
  nX, nY: Integer;
  Monster: TActorObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sMonName = '') or ((sMonName <> '') and (sMonName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandMobHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  if nCount <= 0 then nCount := 1;
  if not (nLevel in [0..10]) then nLevel := 0;

  nCount := _MIN(64, nCount);
  GetFrontPosition(nX, nY);
  for I := 0 to nCount - 1 do begin
    Monster := UserEngine.RegenMonsterByName(nil, m_PEnvir.sMapName, nX, nY, sMonName);
    if Monster <> nil then begin
      Monster.m_btSlaveMakeLevel := nLevel;
      Monster.m_btSlaveExpLevel := nLevel;
      Monster.RecalcAbilitys;
      Monster.RefNameColor;
    end else begin
      SysMsg(g_sGameCommandMobMsg, c_Red, t_Hint);
      Break;
    end;
  end;
end;

procedure TPlayObject.CmdMobCount(Cmd: pTGameCmd; sMapName: string);
var
  Envir: TEnvirnoment;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sMapName = '') or ((sMapName <> '') and (sMapName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandMobCountHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  Envir := g_MapManager.FindMap(sMapName);
  if Envir = nil then begin
    SysMsg(g_sGameCommandMobCountMapNotFound, c_Red, t_Hint);
    Exit;
  end;
  SysMsg(Format(g_sGameCommandMobCountMonsterCount, [UserEngine.GetMapMonster(Envir, nil)]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdHumanCount(Cmd: pTGameCmd; sMapName: string);
var
  Envir: TEnvirnoment;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sMapName = '') or ((sMapName <> '') and (sMapName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandHumanCountHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  Envir := g_MapManager.FindMap(sMapName);
  if Envir = nil then begin
    SysMsg(g_sGameCommandMobCountMapNotFound, c_Red, t_Hint);
    Exit;
  end;
  SysMsg(Format(g_sGameCommandMobCountMonsterCount, [UserEngine.GetMapHuman(sMapName)]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdMobFireBurn(Cmd: pTGameCmd; sMAP, sX, sY, sType,
  sTime, sPoint: string);
var
  nX, nY, nType, nTime, nPoint: Integer;
  FireBurnEvent: TFireBurnEvent;
  Envir: TEnvirnoment;
  OldEnvir: TEnvirnoment;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sMAP = '') or ((sMAP <> '') and (sMAP[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandMobFireBurnHelpMsg, [Cmd.sCmd, sMAP, sX, sY, sType, sTime, sPoint]), c_Red, t_Hint);
    Exit;
  end;

  nX := Str_ToInt(sX, -1);
  nY := Str_ToInt(sY, -1);
  nType := Str_ToInt(sType, -1);
  nTime := Str_ToInt(sTime, -1);
  nPoint := Str_ToInt(sPoint, -1);
  if nPoint < 0 then nPoint := 1;

  if (sMAP = '') or (nX < 0) or (nY < 0) or (nType < 0) or (nTime < 0) or (nPoint < 0) then begin
    SysMsg(Format(g_sGameCommandMobFireBurnHelpMsg, [Cmd.sCmd, sMAP, sX, sY, sType, sTime, sPoint]), c_Red, t_Hint);
    Exit;
  end;
  Envir := g_MapManager.FindMap(sMAP);
  if Envir <> nil then begin
    OldEnvir := m_PEnvir;
    m_PEnvir := Envir;
    FireBurnEvent := TFireBurnEvent.Create(Self, nX, nY, nType, nTime * 1000, nPoint);
    g_EventManager.AddEvent(FireBurnEvent);
    m_PEnvir := OldEnvir;
    Exit;
  end;
  SysMsg(Format(g_sGameCommandMobFireBurnMapNotFountMsg, [Cmd.sCmd, sMAP]), c_Red, t_Hint);
end;

procedure TPlayObject.CmdMobLevel(Cmd: pTGameCmd; Param: string); //004CFD5C
var
  I: Integer;
  BaseObjectList: TList;
  BaseObject: TActorObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((Param <> '') and (Param[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, '']), c_Red, t_Hint);
    Exit;
  end;

  BaseObjectList := TList.Create;
  m_PEnvir.GetRangeActorObject(m_nCurrX, m_nCurrY, 2, True, BaseObjectList);
  for I := 0 to BaseObjectList.Count - 1 do begin
    BaseObject := TActorObject(BaseObjectList.Items[I]);
    SysMsg(BaseObject.GeTActorObjectInfo(), c_Green, t_Hint);
  end;
  BaseObjectList.Free;
end;

procedure TPlayObject.CmdMobNpc(sCmd: string; nPermission: Integer; sParam1, sParam2, sParam3, sParam4: string);
var
  nAppr: Integer;
  boIsCastle: Boolean;
  Merchant: TMerchant;
  nX, nY: Integer;
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sParam1 = '') or (sParam2 = '') or ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, g_sGameCommandMobNpcHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  nAppr := Str_ToInt(sParam3, 0);
  boIsCastle := (Str_ToInt(sParam4, 0) = 1);
  if sParam1 = '' then begin
    SysMsg('Usage: @' + sCmd + ' NPCName TextFileName Type(0,1)', c_Red, t_Hint);
    Exit;
  end;
  Merchant := TMerchant.Create;
  Merchant.m_sCharName := sParam1;
  Merchant.m_sMapName := m_sMapName;
  Merchant.m_PEnvir := m_PEnvir;
  Merchant.m_wAppr := nAppr;
  Merchant.m_nFlag := 0;
  Merchant.m_boCastle := boIsCastle;
  Merchant.m_sScript := sParam2;
  GetFrontPosition(nX, nY);
  Merchant.m_nCurrX := nX;
  Merchant.m_nCurrY := nY;
  Merchant.Initialize();
  UserEngine.AddMerchant(Merchant);
end;

procedure TPlayObject.CmdMobPlace(Cmd: pTGameCmd; sX, sY, sMonName, sCount: string);
var
  I: Integer;
  nCount, nX, nY: Integer;
  MEnvir: TEnvirnoment;
  mon: TActorObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  nCount := _MIN(500, Str_ToInt(sCount, 0));
  nX := Str_ToInt(sX, 0);
  nY := Str_ToInt(sY, 0);
  MEnvir := g_MapManager.FindMap(g_sMissionMap);
  if (nX <= 0) or (nY <= 0) or (sMonName = '') or (nCount <= 0) then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' X  Y MonsterName MonsterAmount', c_Red, t_Hint);
    Exit;
  end;
  if not g_boMission or (MEnvir = nil) then begin
    SysMsg('Focus point for Monster has not been set', c_Red, t_Hint);
    SysMsg('Use the command ' + g_GameCommand.Mission.sCmd + ' to create a Mission for mobs.', c_Red, t_Hint);
    Exit;
  end;

  for I := 0 to nCount - 1 do begin
    mon := UserEngine.RegenMonsterByName(nil, g_sMissionMap, nX, nY, sMonName);
    if mon <> nil then begin
      mon.m_boMission := True;
      mon.m_nMissionX := g_nMissionX;
      mon.m_nMissionY := g_nMissionY;
    end else Break;
  end;
  SysMsg(IntToStr(nCount) + sMonName + ' have been added to ' + g_sMissionMap + ' ' + IntToStr(g_nMissionX) + ':' + IntToStr(g_nMissionY), c_Green, t_Hint);
end;

procedure TPlayObject.CmdNpcScript(sCmd: string; nPermission: Integer; sParam1, sParam2, sParam3: string);
var
  BaseObject: TActorObject;
  nNPCType: Integer;
  I: Integer;
  sScriptFileName: string;
  Merchant: TMerchant;
  NormNpc: TNormNpc;
  LoadList: TStringList;
  sScriptLine: string;
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sParam1 = '') or ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, g_sGameCommandNpcScriptHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  nNPCType := -1;
  BaseObject := GetPoseCreate();
  if BaseObject <> nil then begin
    for I := 0 to UserEngine.m_MerchantList.Count - 1 do begin
      if TActorObject(UserEngine.m_MerchantList.Items[I]) = BaseObject then begin
        nNPCType := 0;
        Break;
      end;
    end;
    for I := 0 to UserEngine.QuestNPCList.Count - 1 do begin
      if TActorObject(UserEngine.QuestNPCList.Items[I]) = BaseObject then begin
        nNPCType := 1;
        Break;
      end;
    end;
  end;
  if nNPCType < 0 then begin
    SysMsg('You must face the NPC to use this command', c_Red, t_Hint);
    Exit;
  end;

  if sParam1 = '' then begin
    if nNPCType = 0 then begin
      Merchant := TMerchant(BaseObject);
      sScriptFileName := g_Config.sEnvirDir + sMarket_Def + Merchant.m_sScript + '-' + Merchant.m_sMapName + '.txt';
    end;
    if nNPCType = 1 then begin
      NormNpc := TNormNpc(BaseObject);
      sScriptFileName := g_Config.sEnvirDir + sNpc_def + NormNpc.m_sCharName + '-' + NormNpc.m_sMapName + '.txt';
    end;
    if FileExists(sScriptFileName) then begin
      LoadList := TStringList.Create;
      try
        LoadList.LoadFromFile(sScriptFileName);
      except
        SysMsg('Error reading script: ' + sScriptFileName, c_Red, t_Hint);
      end;
      for I := 0 to LoadList.Count - 1 do begin
        sScriptLine := Trim(LoadList.Strings[I]);
        sScriptLine := ReplaceChar(sScriptLine, ' ', ',');
        SysMsg(IntToStr(I) + ',' + sScriptLine, c_Blue, t_Hint);
      end;
      LoadList.Free;
    end;
  end;
end;

procedure TPlayObject.CmdOPDeleteSkill(sHumanName, sSkillName: string);
begin
  if (m_btPermission < 6) then Exit;
end;

procedure TPlayObject.CmdOPTraining(sHumanName, sSkillName: string;
  nLevel: Integer);
begin
  if (m_btPermission < 6) then Exit;
end;

procedure TPlayObject.CmdPKpoint(Cmd: pTGameCmd; sHumanName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandPKPointHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  SysMsg(Format(g_sGameCommandPKPointMsg, [sHumanName, PlayObject.m_nPkPoint]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdPrvMsg(sCmd: string; nPermission: Integer;
  sHumanName: string);
var
  I: Integer;
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, g_sGameCommandPrvMsgHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  for I := m_BlockWhisperList.Count - 1 downto 0 do begin
    if m_BlockWhisperList.Count <= 0 then Break;
    if CompareText(m_BlockWhisperList.Strings[I], sHumanName) = 0 then begin
      m_BlockWhisperList.Delete(I);
      SysMsg(Format(g_sGameCommandPrvMsgUnLimitMsg, [sHumanName]), c_Green, t_Hint);
      Exit;
    end;
  end;
  m_BlockWhisperList.Add(sHumanName);
  SysMsg(Format(g_sGameCommandPrvMsgLimitMsg, [sHumanName]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdReAlive(Cmd: pTGameCmd; sHumanName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandReAliveHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  //MainOutMessage('TPlayObject.CmdReAlive ');
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject.ReAlive;
  PlayObject.m_WAbil.HP := PlayObject.m_WAbil.MaxHP;
  PlayObject.SendMsg(PlayObject, RM_ABILITY, 0, 0, 0, 0, '');

  SysMsg(Format(g_sGameCommandReAliveMsg, [sHumanName]), c_Green, t_Hint);
  SysMsg(sHumanName + ' Has been reborn.', c_Green, t_Hint);
end;

procedure TPlayObject.CmdRecallHuman(Cmd: pTGameCmd; sHumanName: string); //004CE250
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandRecallHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  RecallHuman(sHumanName);
end;

procedure TPlayObject.CmdRecallMob(Cmd: pTGameCmd; sMonName: string; nCount, nLevel, nAutoChangeColor, nFixColor: Integer); //004CC8C4
var
  I: Integer;
  n10, n14: Integer;
  mon: TActorObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sMonName = '') or ((sMonName <> '') and (sMonName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandRecallMobHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  if nLevel >= 10 then nLevel := 0;
  if nCount <= 0 then nCount := 1;
  for I := 0 to nCount - 1 do begin
    if m_SlaveList.Count >= 20 then Break;
    GetFrontPosition(n10, n14);
    mon := UserEngine.RegenMonsterByName(nil, m_PEnvir.sMapName, n10, n14, sMonName);
    if mon <> nil then begin
      mon.m_Master := Self;
      mon.m_dwMasterRoyaltyTick := GetTickCount + 24 * 60 * 60 * 1000;
      mon.m_btSlaveMakeLevel := 3;
      mon.m_btSlaveExpLevel := nLevel;
      if nAutoChangeColor = 1 then begin
        mon.m_boAutoChangeColor := True;
      end else
        if nFixColor > 0 then begin
        mon.m_boFixColor := True;
        mon.m_nFixColorIdx := nFixColor - 1;
      end;

      mon.RecalcAbilitys();
      mon.RefNameColor();
      m_SlaveList.Add(mon);
    end;
  end;
end;

procedure TPlayObject.CmdReconnection(sCmd, sIPaddr, sPort: string);
//004CE380
begin
  if (m_btPermission < 10) then Exit;
  if (sIPaddr <> '') and (sIPaddr[1] = '?') then begin
    SysMsg('This command is used to change the IP Address used for Client connections.', c_Blue, t_Hint);
    Exit;
  end;

  if (sIPaddr = '') or (sPort = '') then begin
    SysMsg('Usage: @' + sCmd + ' IPAddress Port', c_Red, t_Hint);
    Exit;
  end;
  if (sIPaddr <> '') and (sPort <> '') then begin
    SendMsg(Self, RM_RECONNECTION, 0, 0, 0, 0, sIPaddr + '/' + sPort);
  end;
end;

procedure TPlayObject.CmdRefineWeapon(Cmd: pTGameCmd; nDc, nMc, nSc, nHit: Integer); //004CD1C4
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (nDc + nMc + nSc) > 10 then Exit;
  if m_UseItems[U_WEAPON].wIndex <= 0 then Exit;
  m_UseItems[U_WEAPON].btValue[0] := nDc;
  m_UseItems[U_WEAPON].btValue[1] := nMc;
  m_UseItems[U_WEAPON].btValue[2] := nSc;
  m_UseItems[U_WEAPON].btValue[5] := nHit;
  SendUpdateItem(@m_UseItems[U_WEAPON]);
  RecalcAbilitys();
  SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
  SendMsg(Self, RM_SUBABILITY, 0, 0, 0, 0, '');
  MainOutMessage('[Weapon]' + m_sCharName + ' DC:' + IntToStr(nDc) + ' MC' + IntToStr(nMc) + ' SC' + IntToStr(nSc) + ' HIT:' + IntToStr(nHit));
end;

procedure TPlayObject.CmdReGotoHuman(Cmd: pTGameCmd; sHumanName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandReGotoHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  SpaceMove(PlayObject.m_PEnvir.sMapName, PlayObject.m_nCurrX, PlayObject.m_nCurrY, 0);
end;

procedure TPlayObject.CmdReloadAbuse(sCmd: string; nPermission: Integer;
  sParam1: string);
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, '']), c_Red, t_Hint);
    Exit;
  end;
end;

procedure TPlayObject.CmdReLoadAdmin(sCmd: string);
begin
  if (m_btPermission < 6) then Exit;
  FrmDB.LoadAdminList();
  //UserEngine.SendServerGroupMsg(213, nServerIndex, '');
  SysMsg('Admin list reloaded.', c_Green, t_Hint);
end;

procedure TPlayObject.CmdReloadGuild(sCmd: string; nPermission: Integer;
  sParam1: string);
var
  Guild: TGUild;
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sParam1 = '') or ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, g_sGameCommandReloadGuildHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  if nServerIndex <> 0 then begin
    SysMsg(g_sGameCommandReloadGuildOnMasterserver, c_Red, t_Hint);
    Exit;
  end;
  Guild := g_GuildManager.FindGuild(sParam1);
  if Guild = nil then begin
    SysMsg(Format(g_sGameCommandReloadGuildNotFoundGuildMsg, [sParam1]), c_Red, t_Hint);
    Exit;
  end;
  Guild.LoadGuild();
  SysMsg(Format(g_sGameCommandReloadGuildSuccessMsg, [sParam1]), c_Red, t_Hint);
  //UserEngine.SendServerGroupMsg(SS_207, nServerIndex, sParam1);
end;

procedure TPlayObject.CmdReloadGuildAll;
begin
  if (m_btPermission < 6) then Exit;
end;

procedure TPlayObject.CmdReloadLineNotice(sCmd: string;
  nPermission: Integer; sParam1: string);
begin
  if (m_btPermission < nPermission) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [sCmd, '']), c_Red, t_Hint);
    Exit;
  end;
  if LoadLineNotice(g_Config.sNoticeDir + 'LineNotice.txt') then begin
    SysMsg(g_sGameCommandReloadLineNoticeSuccessMsg, c_Green, t_Hint);
  end else begin
    SysMsg(g_sGameCommandReloadLineNoticeFailMsg, c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdReloadManage(Cmd: pTGameCmd; sParam: string);
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sParam <> '') and (sParam[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, '']), c_Red, t_Hint);
    Exit;
  end;
  if sParam = '' then begin
    if g_ManageNPC <> nil then begin
      g_ManageNPC.ClearScript();
      g_ManageNPC.LoadNpcScript();
      SysMsg('Logon script reloaded', c_Green, t_Hint);
    end else begin
      SysMsg('Failed to read Logon Script', c_Green, t_Hint);
    end;
  end else begin
    if g_FunctionNPC <> nil then begin
      g_FunctionNPC.ClearScript();
      g_FunctionNPC.LoadNpcScript();
      SysMsg('Q-Function reloaded.', c_Green, t_Hint);
    end else begin
      SysMsg('Failed to read Q-Function.', c_Green, t_Hint);
    end;
  end;
end;

procedure TPlayObject.CmdReloadRobot;
begin
  RobotManage.RELOADROBOT();
  SysMsg('Robot reloaded', c_Green, t_Hint);
end;

procedure TPlayObject.CmdReloadRobotManage;
begin
  if (m_btPermission < 6) then Exit;
  if g_RobotNPC <> nil then begin
    g_RobotNPC.ClearScript();
    g_RobotNPC.LoadNpcScript();
    SysMsg('Robot scripts reloaded.', c_Green, t_Hint);
  end else begin
    SysMsg('Failed to read Robot scripts.', c_Green, t_Hint);
  end;
end;

procedure TPlayObject.CmdReloadMonItems;
var
  I: Integer;
  Monster: pTMonInfo;
begin
  if (m_btPermission < 6) then Exit;
  try
    for I := 0 to UserEngine.MonsterList.Count - 1 do begin
      Monster := UserEngine.MonsterList.Items[I];
      FrmDB.LoadMonitems(Monster.sName, Monster.ItemList);
    end;
    SysMsg('MonItems reloaded.', c_Green, t_Hint);
  except
    SysMsg('Failed to reload MonItems.', c_Green, t_Hint);
  end;
end;

procedure TPlayObject.CmdReloadNpc(sParam: string);
var
  I: Integer;
  TmpList: TList;
  Merchant: TMerchant;
  NPC: TNormNpc;
begin
  if (m_btPermission < 6) then Exit;
  if CompareText('all', sParam) = 0 then begin
    FrmDB.ReLoadMerchants();
    UserEngine.ReloadMerchantList();
    SysMsg('NPCs reloaded.', c_Red, t_Hint);
    UserEngine.ReloadNpcList();
    SysMsg('Failed to reload NPCs', c_Red, t_Hint);
    Exit;
  end;
  TmpList := TList.Create;
  if UserEngine.GetMerchantList(m_PEnvir, m_nCurrX, m_nCurrY, 9, TmpList) > 0 then begin
    for I := 0 to TmpList.Count - 1 do begin
      Merchant := TMerchant(TmpList.Items[I]);
      Merchant.ClearScript;
      Merchant.LoadNpcScript;
      SysMsg(Merchant.m_sCharName + 'reloaded.', c_Green, t_Hint);
    end; // for
  end else begin
    SysMsg('No NPCs found in Area', c_Red, t_Hint);
  end;
  TmpList.Clear;
  if UserEngine.GetNpcList(m_PEnvir, m_nCurrX, m_nCurrY, 9, TmpList) > 0 then begin
    for I := 0 to TmpList.Count - 1 do begin
      NPC := TNormNpc(TmpList.Items[I]);
      NPC.ClearScript;
      NPC.LoadNpcScript;
      SysMsg(NPC.m_sCharName + 'reloaded.', c_Green, t_Hint);
    end; // for
  end else begin
    SysMsg('No NPCs found in area.', c_Red, t_Hint);
  end;
  TmpList.Free;
end;

procedure TPlayObject.CmdSearchHuman(sCmd, sHumanName: string);
var
  PlayObject: TPlayObject;
begin
  if m_boProbeNecklace or (m_btPermission >= 6) then begin
    if (sHumanName = '') then begin
      SysMsg('Usage: @' + sCmd + ' Name', c_Red, t_Hint);
      Exit;
    end;
    if ((GetTickCount - m_dwProbeTick) > 10000) or (m_btPermission >= 3) then begin
      m_dwProbeTick := GetTickCount();
      PlayObject := UserEngine.GetPlayObject(sHumanName);
      if PlayObject <> nil then begin
        SysMsg(sHumanName + ' is located on ' + PlayObject.m_PEnvir.sMapDesc + '(' + PlayObject.m_PEnvir.sMapName + ') ' + IntToStr(PlayObject.m_nCurrX) + ':' + IntToStr(PlayObject.m_nCurrY), c_Blue, t_Hint);
      end else begin
        SysMsg(sHumanName + ' is not online.', c_Red, t_Hint);
      end;
    end else begin
      SysMsg(IntToStr((GetTickCount - m_dwProbeTick) div 1000 - 10) + ' seconds before you can use this command again.', c_Red, t_Hint);
    end;
  end else begin
    SysMsg('You cannot use this feature.', c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdSearchMon(sCmd, sMonName: string);
var
  PlayObject: TPlayObject;
  ActorObject: TActorObject;
  MonList: TList;
  I: Integer;
begin
  if m_boProbeNecklace or (m_btPermission >= 6) then begin
    if (sMonName = '') then begin
      SysMsg('Usage: @' + sCmd + ' Monster', c_Red, t_Hint);
      Exit;
    end;
    if ((GetTickCount - m_dwProbeTick) > 10000) or (m_btPermission >= 3) then begin
      m_dwProbeTick := GetTickCount();
      MonList := TList.Create;
      if UserEngine.GetMonsterByName(sMonName, MonList) > 0 then begin
        for I := 0 to MonList.Count - 1 do begin
          ActorObject := TActorObject(MonList.Items[I]);
          SysMsg(IntToStr(I + 1) + '. ' + sMonName + ' is located on ' + ActorObject.m_PEnvir.sMapDesc + '(' + ActorObject.m_PEnvir.sMapName + ') ' + IntToStr(ActorObject.m_nCurrX) + ':' + IntToStr(ActorObject.m_nCurrY), c_Blue, t_Hint);
        end;
      end else begin
        SysMsg(sMonName + ' was not found', c_Red, t_Hint);
      end;
      MonList.Free;
    end else begin
      SysMsg(IntToStr((GetTickCount - m_dwProbeTick) div 1000 - 10) + ' seconds before you can use this command again.', c_Red, t_Hint);
    end;
  end else begin
    SysMsg('You cannot use this feature', c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdShowSbkGold(Cmd: pTGameCmd; sCASTLENAME, sCtr, sGold: string);
var
  I: Integer;
  Ctr: Char;
  nGold: Integer;
  Castle: TUserCastle;
  List: TStringList;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sCASTLENAME <> '') and (sCASTLENAME[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, '']), c_Red, t_Hint);
    Exit;
  end;
  if sCASTLENAME = '' then begin
    List := TStringList.Create;
    g_CastleManager.GetCastleGoldInfo(List);
    for I := 0 to List.Count - 1 do begin
      SysMsg(List.Strings[I], c_Green, t_Hint);
    end;
    List.Free;
    Exit;
  end;
  Castle := g_CastleManager.Find(sCASTLENAME);
  if Castle = nil then begin
    SysMsg(Format(g_sGameCommandSbkGoldCastleNotFoundMsg, [sCASTLENAME]), c_Red, t_Hint);
    Exit;
  end;

  Ctr := sCtr[1];
  nGold := Str_ToInt(sGold, -1);
  if not (Ctr in ['=', '-', '+']) or (nGold < 0) or (nGold > 100000000) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandSbkGoldHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  case Ctr of
    '=': Castle.m_nTotalGold := nGold;
    '-': Dec(Castle.m_nTotalGold);
    '+': Inc(Castle.m_nTotalGold, nGold);
  end;
  if Castle.m_nTotalGold < 0 then Castle.m_nTotalGold := 0;
end;

procedure TPlayObject.CmdShowUseItemInfo(Cmd: pTGameCmd;
  sHumanName: string);
var
  I: Integer;
  PlayObject: TPlayObject;
  UserItem: pTUserItem;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandShowUseItemInfoHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  for I := Low(PlayObject.m_UseItems) to High(PlayObject.m_UseItems) do begin
    UserItem := @PlayObject.m_UseItems[I];
    if UserItem.wIndex = 0 then Continue;
    SysMsg(Format('%s [%s] IDX [%d] serial number [%d] sustained [%d-%d]',
      [GetUseItemName(I),
      UserEngine.GetStdItemName(UserItem.wIndex),
        UserItem.wIndex,
        UserItem.MakeIndex,
        UserItem.Dura,
        UserItem.DuraMax]),
        c_Blue, t_Hint);
  end;
end;

procedure TPlayObject.CmdBindUseItem(Cmd: pTGameCmd; sHumanName, sItem,
  sType, sLight: string);
var
  I: Integer;
  PlayObject: TPlayObject;
  UserItem: pTUserItem;
  nItem, nBind: Integer;
  ItemBind: pTItemBind;
  nItemIdx, nMakeIdex: Integer;
  sBindName: string;
  boFind: Boolean;
  boLight: Boolean;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  nBind := -1;
  nItem := GetUseItemIdx(sItem);
  if CompareText(sType, 'Account number') = 0 then nBind := 0;
  if CompareText(sType, 'Figure') = 0 then nBind := 1;
  if CompareText(sType, 'IP') = 0 then nBind := 2;
  boLight := sLight = '1';
  if (nItem < 0) or (nBind < 0) or (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandBindUseItemHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  UserItem := @PlayObject.m_UseItems[nItem];
  if UserItem.wIndex = 0 then begin
    SysMsg(Format(g_sGameCommandBindUseItemNoItemMsg, [sHumanName, sItem]), c_Red, t_Hint);
    Exit;
  end;
  nItemIdx := UserItem.wIndex;
  nMakeIdex := UserItem.MakeIndex;
  case nBind of
    0: begin
        boFind := False;
        sBindName := PlayObject.m_sUserID;
        g_ItemBindAccount.Lock;
        try
          for I := 0 to g_ItemBindAccount.Count - 1 do begin
            ItemBind := g_ItemBindAccount.Items[I];
            if (ItemBind.nItemIdx = nItemIdx) and (ItemBind.nMakeIdex = nMakeIdex) then begin
              SysMsg(Format(g_sGameCommandBindUseItemAlreadBindMsg, [sHumanName, sItem]), c_Red, t_Hint);
              boFind := True;
              Break;
            end;
          end;
          if not boFind then begin
            New(ItemBind);
            ItemBind.nItemIdx := nItemIdx;
            ItemBind.nMakeIdex := nMakeIdex;
            ItemBind.sBindName := sBindName;
            g_ItemBindAccount.Insert(0, ItemBind);
          end;
        finally
          g_ItemBindAccount.UnLock;
        end;
        if boFind then Exit;
        SaveItemBindAccount();
        SysMsg(Format('%s[%s]IDX[%d]Series No. [%d] sustained [%d-%d], bind to %s success.',
          [GetUseItemName(nItem),
          UserEngine.GetStdItemName(UserItem.wIndex),
            UserItem.wIndex,
            UserItem.MakeIndex,
            UserItem.Dura,
            UserItem.DuraMax,
            sBindName]),
            c_Blue, t_Hint);
        PlayObject.SysMsg(Format('Item %s[%s] has already been bound %s[%s].',
          [GetUseItemName(nItem),
          UserEngine.GetStdItemName(UserItem.wIndex),
            sType,
            sBindName
            ]), c_Blue, t_Hint);
        PlayObject.SendMsg(PlayObject, RM_SENDUSEITEMS, 0, 0, 0, 0, '');
      end;
    1: begin
        sBindName := PlayObject.m_sCharName;
        boFind := False;
        g_ItemBindCharName.Lock;
        try
          for I := 0 to g_ItemBindCharName.Count - 1 do begin
            ItemBind := g_ItemBindCharName.Items[I];
            if (ItemBind.nItemIdx = nItemIdx) and (ItemBind.nMakeIdex = nMakeIdex) then begin
              SysMsg(Format(g_sGameCommandBindUseItemAlreadBindMsg, [sHumanName, sItem]), c_Red, t_Hint);
              boFind := True;
              Break;
            end;
          end;
          if not boFind then begin
            New(ItemBind);
            ItemBind.nItemIdx := nItemIdx;
            ItemBind.nMakeIdex := nMakeIdex;
            ItemBind.sBindName := sBindName;
            g_ItemBindCharName.Insert(0, ItemBind);
          end;
        finally
          g_ItemBindCharName.UnLock;
        end;
        if boFind then Exit;
        SaveItemBindCharName();
        SysMsg(Format('%s[%s]IDX[%d]Series No. [%d] sustained [%d-%d], bind to %s success.',
          [GetUseItemName(nItem),
          UserEngine.GetStdItemName(UserItem.wIndex),
            UserItem.wIndex,
            UserItem.MakeIndex,
            UserItem.Dura,
            UserItem.DuraMax,
            sBindName]),
            c_Blue, t_Hint);
        PlayObject.SysMsg(Format('Item %s[%s] has already been bound %s[%s].',
          [GetUseItemName(nItem),
          UserEngine.GetStdItemName(UserItem.wIndex),
            sType,
            sBindName
            ]), c_Blue, t_Hint);
        //PlayObject.SendUpdateItem(UserItem);
        PlayObject.SendMsg(PlayObject, RM_SENDUSEITEMS, 0, 0, 0, 0, '');
      end;
    2: begin
        boFind := False;
        sBindName := PlayObject.m_sIPaddr;
        g_ItemBindIPaddr.Lock;
        try
          for I := 0 to g_ItemBindIPaddr.Count - 1 do begin
            ItemBind := g_ItemBindIPaddr.Items[I];
            if (ItemBind.nItemIdx = nItemIdx) and (ItemBind.nMakeIdex = nMakeIdex) then begin
              SysMsg(Format(g_sGameCommandBindUseItemAlreadBindMsg, [sHumanName, sItem]), c_Red, t_Hint);
              boFind := True;
              Break;
            end;
          end;
          if not boFind then begin
            New(ItemBind);
            ItemBind.nItemIdx := nItemIdx;
            ItemBind.nMakeIdex := nMakeIdex;
            ItemBind.sBindName := sBindName;
            g_ItemBindIPaddr.Insert(0, ItemBind);
          end;
        finally
          g_ItemBindIPaddr.UnLock;
        end;
        if boFind then Exit;
        SaveItemBindIPaddr();
        SysMsg(Format('%s[%s]IDX[%d]Series No. [%d] sustained [%d-%d], bind to %s success.',
          [GetUseItemName(nItem),
          UserEngine.GetStdItemName(UserItem.wIndex),
            UserItem.wIndex,
            UserItem.MakeIndex,
            UserItem.Dura,
            UserItem.DuraMax,
            sBindName]),
            c_Blue, t_Hint);
        PlayObject.SysMsg(Format('Item %s[%s] has already been bound %s[%s].',
          [GetUseItemName(nItem),
          UserEngine.GetStdItemName(UserItem.wIndex),
            sType,
            sBindName
            ]), c_Blue, t_Hint);
        //PlayObject.SendUpdateItem(UserItem);
        PlayObject.SendMsg(PlayObject, RM_SENDUSEITEMS, 0, 0, 0, 0, '');
      end;
  end;
end;

procedure TPlayObject.CmdUnBindUseItem(Cmd: pTGameCmd; sHumanName, sItem: string);
var
  I: Integer;
  PlayObject: TPlayObject;
  UserItem: pTUserItem;
  nItem: Integer;
  ItemBind: pTItemBind;
  nItemIdx, nMakeIdex: Integer;
  sBindName: string;
  boFind: Boolean;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  nItem := GetUseItemIdx(sItem);

  if (nItem < 0) or (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandUnBindUseItemHelpMsg]), c_Red, t_Hint);
    Exit;
  end;

  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject = nil then begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
    Exit;
  end;
  UserItem := @PlayObject.m_UseItems[nItem];
  if UserItem.wIndex = 0 then begin
    SysMsg(Format(g_sGameCommandBindUseItemNoItemMsg, [sHumanName, sItem]), c_Red, t_Hint);
    Exit;
  end;
  nItemIdx := UserItem.wIndex;
  nMakeIdex := UserItem.MakeIndex;

  boFind := False;
  sBindName := PlayObject.m_sUserID;
  g_ItemBindAccount.Lock;
  try
    for I := g_ItemBindAccount.Count - 1 downto 0 do begin
      ItemBind := g_ItemBindAccount.Items[I];
      if (CompareText(ItemBind.sBindName, sBindName) = 0) and (ItemBind.nItemIdx = nItemIdx) and (ItemBind.nMakeIdex = nMakeIdex) then begin
        Dispose(ItemBind);
        g_ItemBindAccount.Delete(I);
        boFind := True;
        Break;
      end;
    end;
  finally
    g_ItemBindAccount.UnLock;
  end;

  if boFind then begin
    SaveItemBindAccount();
    PlayObject.SendMsg(PlayObject, RM_SENDUSEITEMS, 0, 0, 0, 0, '');
    SysMsg(Format(g_sGameCommandUnBindUseItemAlreadBindMsg, [sHumanName, sItem]), c_Red, t_Hint);
  end;

  boFind := False;
  sBindName := PlayObject.m_sCharName;
  g_ItemBindCharName.Lock;
  try
    for I := g_ItemBindCharName.Count - 1 downto 0 do begin
      ItemBind := g_ItemBindCharName.Items[I];
      if (CompareText(ItemBind.sBindName, sBindName) = 0) and (ItemBind.nItemIdx = nItemIdx) and (ItemBind.nMakeIdex = nMakeIdex) then begin
        Dispose(ItemBind);
        g_ItemBindCharName.Delete(I);
        boFind := True;
        Break;
      end;
    end;
  finally
    g_ItemBindCharName.UnLock;
  end;

  if boFind then begin
    SaveItemBindCharName();
    PlayObject.SendMsg(PlayObject, RM_SENDUSEITEMS, 0, 0, 0, 0, '');
    SysMsg(Format(g_sGameCommandUnBindUseItemAlreadBindMsg, [sHumanName, sItem]), c_Red, t_Hint);
  end;

  boFind := False;
  sBindName := PlayObject.m_sIPaddr;
  g_ItemBindIPaddr.Lock;
  try
    for I := g_ItemBindIPaddr.Count - 1 downto 0 do begin
      ItemBind := g_ItemBindIPaddr.Items[I];
      if (CompareText(ItemBind.sBindName, sBindName) = 0) and (ItemBind.nItemIdx = nItemIdx) and (ItemBind.nMakeIdex = nMakeIdex) then begin
        Dispose(ItemBind);
        g_ItemBindIPaddr.Delete(I);
        boFind := True;
        Break;
      end;
    end;
  finally
    g_ItemBindIPaddr.UnLock;
  end;

  if boFind then begin
    SaveItemBindIPaddr();
    PlayObject.SendMsg(PlayObject, RM_SENDUSEITEMS, 0, 0, 0, 0, '');
    SysMsg(Format(g_sGameCommandUnBindUseItemAlreadBindMsg, [sHumanName, sItem]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdShutup(Cmd: pTGameCmd; sHumanName, sTime: string);
var
  dwTime: LongWord;
  nIndex: Integer;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sTime = '') or (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandShutupHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  dwTime := Str_ToInt(sTime, 5);
  g_DenySayMsgList.Lock;
  try
    nIndex := g_DenySayMsgList.GetIndex(sHumanName);
    if nIndex >= 0 then begin
      g_DenySayMsgList.Objects[nIndex] := TObject(GetTickCount + dwTime * 60 * 1000);
    end else begin
      g_DenySayMsgList.AddRecord(sHumanName, GetTickCount + dwTime * 60 * 1000);
    end;
  finally
    g_DenySayMsgList.UnLock;
  end;
  SysMsg(Format(g_sGameCommandShutupHumanMsg, [sHumanName, dwTime]), c_Red, t_Hint);
end;

procedure TPlayObject.CmdShutupList(Cmd: pTGameCmd; sParam1: string);
var
  I, nCount: Integer;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if ((sParam1 <> '') and (sParam1[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, '']), c_Red, t_Hint);
    Exit;
  end;

  if (m_btPermission < 6) then Exit;
  g_DenySayMsgList.Lock;
  try
    nCount := g_DenySayMsgList.Count;
    if g_DenySayMsgList.Count <= 0 then begin
      SysMsg(g_sGameCommandShutupListIsNullMsg, c_Green, t_Hint);

    end;
    if nCount > 0 then begin
      for I := 0 to g_DenySayMsgList.Count - 1 do begin
        SysMsg(g_DenySayMsgList.Strings[I] + ' ' + IntToStr((LongWord(g_DenySayMsgList.Objects[I]) - GetTickCount) div 60000), c_Green, t_Hint);
      end;
    end;
  finally
    g_DenySayMsgList.UnLock;
  end;
end;

procedure TPlayObject.CmdShutupRelease(Cmd: pTGameCmd; sHumanName: string; boAll: Boolean);
var
  I: Integer;
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandShutupReleaseHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  g_DenySayMsgList.Lock;
  try
    I := g_DenySayMsgList.GetIndex(sHumanName);
    if I >= 0 then begin
      g_DenySayMsgList.Delete(I);
      PlayObject := UserEngine.GetPlayObject(sHumanName);
      if PlayObject <> nil then begin
        PlayObject.SysMsg(g_sGameCommandShutupReleaseCanSendMsg, c_Red, t_Hint);
      end;
      if boAll then begin
        //UserEngine.SendServerGroupMsg(SS_210, nServerIndex, sHumanName);
      end;
      SysMsg(Format(g_sGameCommandShutupReleaseHumanCanSendMsg, [sHumanName]), c_Green, t_Hint);
    end;
  finally
    g_DenySayMsgList.UnLock;
  end;
end;

procedure TPlayObject.CmdSmakeItem(Cmd: pTGameCmd; nWhere, nValueType, nValue: Integer);
var
  sShowMsg: string;
  StdItem: pTStdItem;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;

  if (nWhere in [0..18]) and (nValueType in [0..15]) and (nValue in [0..255]) then begin
    if m_UseItems[nWhere].wIndex > 0 then begin
      StdItem := UserEngine.GetStdItem(m_UseItems[nWhere].wIndex);
      if StdItem = nil then Exit;

      if nValueType > 13 then begin
        nValue := _MIN(65, nValue);
        if nValueType = 14 then m_UseItems[nWhere].Dura := nValue * 1000;
        if nValueType = 15 then m_UseItems[nWhere].DuraMax := nValue * 1000;
      end else begin
        m_UseItems[nWhere].btValue[nValueType] := nValue;
      end;
      RecalcAbilitys();
      SendUpdateItem(@m_UseItems[nWhere]);
      sShowMsg := IntToStr(m_UseItems[nWhere].wIndex) + '-' + IntToStr(m_UseItems[nWhere].MakeIndex) + ' ' +
        IntToStr(m_UseItems[nWhere].Dura) + '/' + IntToStr(m_UseItems[nWhere].DuraMax) + ' ' +
        IntToStr(m_UseItems[nWhere].btValue[0]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[1]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[2]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[3]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[4]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[5]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[6]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[7]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[8]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[9]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[10]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[11]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[12]) + '/' +
        IntToStr(m_UseItems[nWhere].btValue[13]);
      SysMsg(sShowMsg, c_Blue, t_Hint);
      if g_Config.boShowMakeItemMsg then
        MainOutMessage('[Item] ' + m_sCharName + '(' + StdItem.Name + ' -> ' + sShowMsg + ')');
    end else begin
      SysMsg(g_sGamecommandSuperMakeHelpMsg, c_Red, t_Hint);
    end;
  end;
end;

procedure TPlayObject.CmdSpirtStart(sCmd, sParam1: string);
var
  nTime: Integer;
  dwTime: LongWord;
begin
  if (m_btPermission < 6) then Exit;
  if (sParam1 <> '') and (sParam1[1] = '?') then begin
    SysMsg('Start a pet rebellion.', c_Red, t_Hint);
    Exit;
  end;
  nTime := Str_ToInt(sParam1, -1);
  if nTime > 0 then begin
    dwTime := LongWord(nTime) * 1000;
  end else begin
    dwTime := g_Config.dwSpiritMutinyTime;
  end;
  g_dwSpiritMutinyTick := GetTickCount + dwTime;
  SysMsg('Pet rebellion started for ' + IntToStr(dwTime div 1000) + ' seconds.', c_Green, t_Hint);
end;

procedure TPlayObject.CmdSpirtStop(sCmd, sParam1: string);
begin
  if (m_btPermission < 6) then Exit;
  if (sParam1 <> '') and (sParam1[1] = '?') then begin
    SysMsg('Stop the Pet rebellion.', c_Red, t_Hint);
    Exit;
  end;
  g_dwSpiritMutinyTick := 0;
  SysMsg('Pet rebellion stopped.', c_Green, t_Hint);
end;

procedure TPlayObject.CmdStartQuest(Cmd: pTGameCmd; sQuestName: string);
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sQuestName = '') then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' QuestName', c_Red, t_Hint);
    Exit;
  end;
  UserEngine.SendQuestMsg(sQuestName);
end;

procedure TPlayObject.CmdSuperTing(Cmd: pTGameCmd; sHumanName, sRange: string);
var
  I: Integer;
  PlayObject: TPlayObject;
  MoveHuman: TPlayObject;
  nRange: Integer;
  HumanList: TList;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sRange = '') or (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandSuperTingHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  nRange := _MAX(10, Str_ToInt(sRange, 2));
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    HumanList := TList.Create;
    UserEngine.GetMapRageHuman(PlayObject.m_PEnvir, PlayObject.m_nCurrX, PlayObject.m_nCurrY, nRange, HumanList);
    for I := 0 to HumanList.Count - 1 do begin
      MoveHuman := TPlayObject(HumanList.Items[I]);
      if MoveHuman <> Self then
        MoveHuman.MapRandomMove(MoveHuman.m_sHomeMap, 0);
    end;
    HumanList.Free;
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdTakeOffHorse(sCmd, sParam: string);
begin
  if (sParam <> '') and (sParam[1] = '?') then begin
    SysMsg('Unmount your horse command', c_Red, t_Hint);
    SysMsg(Format('Usage: @%s', [sCmd]), c_Red, t_Hint);
    Exit;
  end;
  if not m_boOnHorse then Exit;
  m_boOnHorse := False;
  FeatureChanged();
end;

procedure TPlayObject.CmdTakeOnHorse(sCmd, sParam: string);
begin
  if (sParam <> '') and (sParam[1] = '?') then begin
    SysMsg('Mount Horse command.', c_Red, t_Hint);
    SysMsg(Format('Usage: @%s', [sCmd]), c_Red, t_Hint);
    Exit;
  end;
  if m_boOnHorse then Exit;
  if (m_btHorseType = 0) then begin
    SysMsg('You do not have a horse.', c_Red, t_Hint);
    Exit;
  end;
  m_boOnHorse := True;
  FeatureChanged();
  if m_boOnHorse then begin
    try
      g_FunctionNPC.GotoLable(Self, '@OnHorse', False);
    except

    end;
  end;
end;

procedure TPlayObject.CmdTestFire(sCmd: string; nRange, nType, nTime, nPoint: Integer);
var
  nX, nY: Integer;
  FireBurnEvent: TFireBurnEvent;
  nMinX, nMaxX, nMinY, nMaxY: Integer;
begin
  nMinX := m_nCurrX - nRange;
  nMaxX := m_nCurrX + nRange;
  nMinY := m_nCurrY - nRange;
  nMaxY := m_nCurrY + nRange;
  for nX := nMinX to nMaxX do begin
    for nY := nMinY to nMaxY do begin
      if ((nX < nMaxX) and (nY = nMinY)) or
        ((nY < nMaxY) and (nX = nMinX)) or
        (nX = nMaxX) or (nY = nMaxY) then begin
        FireBurnEvent := TFireBurnEvent.Create(Self, nX, nY, nType, nTime * 1000, nPoint);
        g_EventManager.AddEvent(FireBurnEvent);
      end;
    end;
  end;
end;

procedure TPlayObject.CmdTestGetBagItems(Cmd: pTGameCmd; sParam: string);
var
  btDc, btSc, btMc, btDura: Byte;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sParam <> '') and (sParam[1] = '?') then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandTestGetBagItemsHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  btDc := 0;
  btSc := 0;
  btMc := 0;
  btDura := 0;
  GetBagUseItems(btDc, btSc, btMc, btDura);
  SysMsg(Format('DC:%d SC:%d MC:%d DURA:%d', [btDc, btSc, btMc, btDura]), c_Blue, t_Hint);
end;

procedure TPlayObject.CmdTestSpeedMode(Cmd: pTGameCmd);
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  m_boTestSpeedMode := not m_boTestSpeedMode;
  if m_boTestSpeedMode then begin
    SysMsg('Speed Test Mode started.', c_Red, t_Hint);
  end else begin
    SysMsg('Speed Test Mode stopped.', c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdTestStatus(sCmd: string; nType, nTime: Integer);
begin
  if (m_btPermission < 6) then Exit;
  if (not (nType in [Low(TStatusTime)..High(TStatusTime)])) or (nTime < 0) then begin
    SysMsg('Usage: @' + sCmd + ' Type(0..11) Time', c_Red, t_Hint);
    Exit;
  end;
  m_wStatusTimeArr[nType] := nTime * 1000;
  m_dwStatusArrTick[nType] := GetTickCount();
  m_nCharStatus := GetCharStatus();
  StatusChanged();
  SysMsg(Format('Status:%d Time: %d seconds', [nType, nTime]), c_Green, t_Hint);
end;

procedure TPlayObject.CmdTing(Cmd: pTGameCmd; sHumanName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandTingHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    PlayObject.MapRandomMove(m_sHomeMap, 0);
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdTraining(sSkillName: string; nLevel: Integer); //004CC414
begin
  if (m_btPermission < 6) then Exit;
end;

procedure TPlayObject.CmdUserMoveXY(sCmd, sX, sY: string);
var
  Envir: TEnvirnoment;
  nX, nY: Integer;
begin
  if m_boTeleport then begin
    nX := Str_ToInt(sX, -1);
    nY := Str_ToInt(sY, -1);
    {
    if (nX < 0) or (nY < 0) then begin
      SysMsg('命令格式: @' + sCMD + ' 座标X 座标Y',c_Red,t_Hint);
      exit;
    end;
    }
    if not m_PEnvir.m_boNOPOSITIONMOVE then begin
      if m_PEnvir.CanWalkOfItem(nX, nY, g_Config.boUserMoveCanDupObj, g_Config.boUserMoveCanOnItem) then begin
        if (GetTickCount - m_dwTeleportTick) > g_Config.dwUserMoveTime * 1000 {10000} then begin
          m_dwTeleportTick := GetTickCount();
          if (m_UseItems[U_BUJUK].wIndex > 0) and (m_UseItems[U_BUJUK].Dura > 0) then begin //增加传送符功能
            if m_UseItems[U_BUJUK].Dura > 100 then begin
              Dec(m_UseItems[U_BUJUK].Dura, 100);
            end else begin
              m_UseItems[U_BUJUK].Dura := 0;
            end;
            SendMsg(Self, RM_DURACHANGE, U_BUJUK, m_UseItems[U_BUJUK].Dura, m_UseItems[U_BUJUK].DuraMax, 0, '');
            SendRefMsg(RM_SPACEMOVE_FIRE, 0, 0, 0, 0, '');
            if (nX < 0) or (nY < 0) then begin
              RandomMove;
            end else begin
              if m_PEnvir.CanWalk(nX, nY, False) then
                SpaceMove(m_sMapName, nX, nY, 0);
            end;
            Exit;
          end;
          SendRefMsg(RM_SPACEMOVE_FIRE, 0, 0, 0, 0, '');
          if (nX < 0) or (nY < 0) then begin
            RandomMove;
          end else begin
            if m_PEnvir.CanWalk(nX, nY, False) then
              SpaceMove(m_sMapName, nX, nY, 0);
          end;
        end else begin
          SysMsg(IntToStr(g_Config.dwUserMoveTime - (GetTickCount - m_dwTeleportTick) div 1000) + 'Seconds before you can then use this function!', c_Red, t_Hint);
        end;
      end else begin
        SysMsg(Format(g_sGameCommandPositionMoveCanotMoveToMap, [m_sMapName, sX, sY]), c_Green, t_Hint);
      end;
    end else begin
      SysMsg('Cannot use command on this Map.', c_Red, t_Hint);
    end;
  end else begin
    SysMsg('You cannot use this feature.', c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdViewDiary(sCmd: string; nFlag: Integer);
begin

end;

procedure TPlayObject.CmdViewWhisper(Cmd: pTGameCmd; sCharName, sParam2: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sCharName = '') or ((sCharName <> '') and (sCharName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandViewWhisperHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sCharName);
  if PlayObject <> nil then begin
    if PlayObject.m_GetWhisperHuman = Self then begin
      PlayObject.m_GetWhisperHuman := nil;
      SysMsg(Format(g_sGameCommandViewWhisperMsg1, [sCharName]), c_Green, t_Hint);
    end else begin
      PlayObject.m_GetWhisperHuman := Self;
      SysMsg(Format(g_sGameCommandViewWhisperMsg2, [sCharName]), c_Green, t_Hint);
    end;
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sCharName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdClearCopyItem(Cmd: pTGameCmd; sHumanName: string);
var
  PlayObject: TPlayObject;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  if (sHumanName = '') or ((sHumanName <> '') and (sHumanName[1] = '?')) then begin
    SysMsg(Format(g_sGameCommandParamUnKnow, [Cmd.sCmd, g_sGameCommandTingHelpMsg]), c_Red, t_Hint);
    Exit;
  end;
  PlayObject := UserEngine.GetPlayObject(sHumanName);
  if PlayObject <> nil then begin
    PlayObject.ClearCopyItems();
    SysMsg('Clear completed.', c_Red, t_Hint);
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumanName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.CmdRangeClearCopyItem(Cmd: pTGameCmd; sRange: string);
var
  I: Integer;
  nRange: Integer;
  PlayObject: TPlayObject;
  HumanList: TList;
  ItemList: TList;
begin
  if (m_btPermission < Cmd.nPermissionMin) then begin
    SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
    Exit;
  end;
  nRange := Str_ToInt(sRange, -1);
  if (nRange < 0) then begin
    SysMsg('Usage: @' + Cmd.sCmd + ' Range(>0)', c_Red, t_Hint);
    Exit;
  end;
  ItemList := TList.Create;
  HumanList := TList.Create;
  UserEngine.GetMapRageHuman(m_PEnvir, m_nCurrX, m_nCurrY, nRange, HumanList);
  for I := 0 to HumanList.Count - 1 do begin
    PlayObject := TPlayObject(HumanList.Items[I]);
    if PlayObject <> Self then
      PlayObject.AddCheckItems(ItemList);
  end;
  HumanList.Free;

  ClearCopyItems(ItemList);

  ItemList.Free;

  SysMsg('Clear completed.', c_Red, t_Hint);
end;

constructor TPlayObject.Create;
begin
  inherited;
  m_btRaceServer := RC_PLAYOBJECT;
  m_boEmergencyClose := False;
  m_boSwitchData := False;
  m_boReconnection := False;
  m_boKickFlag := False;
  m_boSoftClose := False;
  m_boReadyRun := False;

  m_dwSaveRcdTick := GetTickCount();
  m_boWantRefMsg := True;
  m_boRcdSaved := False;
  m_boDieInFight3Zone := False;
  m_Script := nil;
  m_boTimeRecall := False;
  m_sMoveMap := '';
  m_nMoveX := 0;
  m_nMoveY := 0;
  m_sLastMapName := ''; //人物死亡地图
  m_nLastCurrX := 0; //人物死亡地图所在座标X
  m_nLastCurrY := 0; //人物死亡地图所在座标Y

  m_boDealing := False;
  m_DealCreat := nil;

  m_boDueling := False;
  m_DuelCreat := nil;
  m_StartDuelCreat := nil;
  m_DuelItemList := TList.Create;
  m_nDuelGolds := 0;
  m_boAllowDuel := True;

  m_GroupOwner := nil;
  m_GroupMembers := TStringList.Create;
  m_boHearWhisper := True;
  m_boBanShout := True;
  m_boBanGuildChat := True;
  m_boAllowDeal := True;
  m_boAllowGroupReCall := False;
  m_BlockWhisperList := TStringList.Create;
  m_wGroupRcallTime := 0;
  m_dwGroupRcallTick := GetTickCount();

  m_GroupLevel            := 1;

  m_VisibleEvent := TList.Create;
  m_DealItemList := TList.Create;

  m_nDealGolds := 0;
  m_StorageItemList := TList.Create;
  m_boAllowGroup := False;
  m_boAllowGuild := False;
  m_dwShoutMsgTick := 0;

  m_dwRunTick := GetTickCount();
  m_nRunTime := 250;
  m_dwSearchTime := 1000;
  m_dwSearchTick := GetTickCount();
  m_nViewRange := 12;
  m_boNewHuman := False;
  m_boLoginNoticeOK := False;
  bo6AB := False;
  m_boExpire := False;
  m_boSendNotice := False;
  m_dwCheckDupObjTick := GetTickCount();
  dwTick578 := GetTickCount();
  dwTick57C := GetTickCount();
  m_boInSafeArea := False;

  m_dwMagicAttackTick := GetTickCount();
  m_dwMagicAttackInterval := 0;
  m_dwAttackTick := GetTickCount();
  m_dwMoveTick := GetTickCount();
  m_dwTurnTick := GetTickCount();
  m_dwActionTick := GetTickCount();
  m_dwAttackCount := 0;
  m_dwAttackCountA := 0;
  m_dwMagicAttackCount := 0;
  m_dwMoveCount := 0;
  m_dwMoveCountA := 0;
  m_nOverSpeedCount := 0;

  m_sOldSayMsg := '';
  m_dwSayMsgTick := GetTickCount();
  m_boDisableSayMsg := False;
  m_dwDisableSayMsgTick := GetTickCount();
  m_dLogonTime := Now();
  m_dwLogonTick := GetTickCount();

  m_boSwitchData := False;
  m_boSwitchDataSended := False;
  m_nWriteChgDataErrCount := 0;
  m_dwShowLineNoticeTick := GetTickCount();
  m_nShowLineNoticeIdx := 0;
  m_nShowLineNoticeIdx1 := 0;
  m_nSoftVersionDateEx := 0;
  m_CanJmpScriptLableList := TStringList.Create;
  m_nKillMonExpMultiple := 1;
  m_nKillMonExpRate := 100;
  m_dwRateTick := GetTickCount();
  m_nPowerRate := 100;
  m_boSetStoragePwd := False;
  m_boReConfigPwd := False;
  m_boCheckOldPwd := False;
  m_boUnLockPwd := False;
  m_boUnLockStoragePwd := False;
  m_boPasswordLocked := False; //锁仓库
  m_btPwdFailCount := 0;
  m_sTempPwd := '';
  m_sStoragePwd := ''; ;
  m_boFilterSendMsg := False;
  m_boCanDeal := True;
  m_boCanDrop := True;
  m_boCanGetBackItem := True;
  m_boCanWalk := True;
  m_boCanRun := True;
  m_boCanHit := True;
  m_boCanSpell := True;
  m_boCanUseItem := True;
  m_nMemberType := 0;
  m_nMemberLevel := 0;
  m_boCanDuel := True;

  m_nGameGold := 0;
  m_boDecGameGold := False;
  m_nDecGameGold := 1;
  m_dwDecGameGoldTick := GetTickCount();
  m_dwDecGameGoldTime := 60 * 1000;

  m_boIncGameGold := False;
  m_nIncGameGold := 1;
  m_dwIncGameGoldTick := GetTickCount();
  m_dwIncGameGoldTime := 60 * 1000;

  m_nGamePoint := 0;
  m_dwIncGamePointTick := GetTickCount();

  m_nPayMentPoint := 0;

  m_DearHuman := nil;
  m_MasterHuman := nil;
  m_MasterList := TList.Create;
  m_boSendMsgFlag := False;
  m_boChangeItemNameFlag := False;

  m_boCanMasterRecall := False;
  m_boCanDearRecall := False;
  m_dwDearRecallTick := GetTickCount();
  m_dwMasterRecallTick := GetTickCount();
  m_btReColorIdx := 0;
  m_GetWhisperHuman := nil;
  m_boOnHorse := False;
  m_wContribution := 0;
  m_sRankLevelName := g_sRankLevelName;
  m_boFixedHideMode := True;
  m_nStep := 0;
  FillChar(m_nMval, SizeOf(m_nMval), #0);
  m_nClientFlagMode := -1;
  m_dwAutoGetExpTick := GetTickCount;
  m_nAutoGetExpPoint := 0;
  m_AutoGetExpEnvir := nil;
  m_dwHitIntervalTime := g_Config.dwHitIntervalTime; //攻击间隔
  m_dwMagicHitIntervalTime := g_Config.dwMagicHitIntervalTime; //魔法间隔
  m_dwRunIntervalTime := g_Config.dwRunIntervalTime; //走路间隔
  m_dwWalkIntervalTime := g_Config.dwWalkIntervalTime; //走路间隔
  m_dwTurnIntervalTime := g_Config.dwTurnIntervalTime; //换方向间隔
  m_dwActionIntervalTime := g_Config.dwActionIntervalTime; //组合操作间隔
  m_dwRunLongHitIntervalTime := g_Config.dwRunLongHitIntervalTime; //组合操作间隔
  m_dwRunHitIntervalTime := g_Config.dwRunHitIntervalTime; //组合操作间隔
  m_dwWalkHitIntervalTime := g_Config.dwWalkHitIntervalTime; //组合操作间隔
  m_dwRunMagicIntervalTime := g_Config.dwRunMagicIntervalTime; //跑位魔法间隔
  m_DynamicVarList := TList.Create;
  m_SessInfo := nil;
  m_boTestSpeedMode := False;
  m_boLockLogon := True;
  m_boLockLogoned := False;

  m_boRemoteMsg := False; //是否允许接受消息

  m_boNotOnlineAddExp := False; //是否是离线挂机人物
  m_boStartAutoAddExpPoint := False; //是否开始增加经验
  m_dwStartNotOnlineAddExpTime := 0; //离线挂机开始时间
  m_dwNotOnlineAddExpTime := 0; //离线挂机时长
  m_nNotOnlineAddExpPoint := 0; //离线挂机每分钟增加经验值
  m_dwAutoAddExpPointTick := GetTickCount;
  m_dwAutoAddExpPointTimeTick := GetTickCount;
  m_sAutoSendMsg := ''; //自动回复信息
  m_boKickAutoAddExpUser := False;
  m_boTimeGoto := False;
  m_dwTimeGotoTick := GetTickCount;
  m_sTimeGotoLable := '';
  m_TimeGotoNPC := nil;
  m_nBigStoragePage := 0; //无限仓库的当前页数
  m_nDealGoldPose := 0;
  m_boPlayOffLine := True; //是否允许下线触发
  m_dwDedingUseTick := GetTickCount - g_Config.nDedingUseTime * 1000;
  m_dwSkillUltimateEnhancerDelayTimeTick := GetTickCount - g_Config.nSkill50DelayTime * 1000;
  m_nCopyHumanLevel := 0;
  m_boAllowReAlive := False; //是否允许复活
  m_nShowMessagePosition := 0;

  m_boRunPlayRobotManage := False; //是否运行个人机器人
  m_RobotRunTicks := nil;
  //m_PlayRobotManage := TPlayRobotManage.Create; //人物个人机器人
  //m_PlayRobotManage.PlayObject := Self;

  FillChar(m_nInteger, SizeOf(m_nInteger), #0);
  SafeFillChar(m_sString, SizeOf(m_sString), #0);

  m_wMasterCount := 0; //徒弟数
  //m_PlayOrderList := TStringList.Create; //人物排行
  m_nSelPlayOrderType := 0; //选择排行类型
  m_nPlayOrderPage := 0; //选择人物排行页数
  m_boGetMyLevelOrder := False;

  //m_BigStorageItemList := nil; //无限仓库

  m_btHearMsgFColor := $00; //喊话字体颜色
  m_btWhisperMsgFColor := $FC; //私聊字体颜色

  m_MyHero := nil; //我的英雄
  m_boHasHero := False;
  m_sHeroCharName := '';
  m_boWaitHeroDate := False;
  m_boHeroVersion := False;

  m_SuperItemBox := nil;
  m_sUpgradeItemName := '';
  m_nUpgradeItemIndex := -1;
  m_nItemIndex := -1;
  m_boUpgradeNPC := False;
  m_dwRecallHeroTick := 0;

  m_boSayAdvertise := True;
  m_dwSayAdvertiseTick := GetTickCount;
  m_dwCanUseItemTick := GetTickCount;
  m_dwMagicMovePositionTick := GetTickCount;
  m_boStore := False;
  m_StoreItemList := TList.Create;

  m_dwClientLastTick := 0;
  m_dwServerLastTick := 0;
  m_TimeLabelList := TList.Create;
  m_boSerieMagicTime := False; //是否在连击
  m_dwSerieMagicTimeTick := GetTickCount - g_Config.nSerieMagicTime * 1000;
  m_dwQueryItemBagTick := GetTickCount - 3000;

  m_nRememberItemIndex := -1;
  m_sRememberItemName := '';

  m_dwTick30 := GetTickCount;

  m_IntegerList := TQuickList.Create; //N
  m_StringList := TValueList.Create; //S
  m_boShowRankLevelName := False;
  if Assigned(PlugInEngine.PlayObjectCreate) then begin
    PlugInEngine.PlayObjectCreate(Self);
  end;
end;

destructor TPlayObject.Destroy;
var
  I: Integer;
begin
  for I := 0 to m_VisibleEvent.Count - 1 do begin
    Dispose(pTVisibleMapEvent(m_VisibleEvent.Items[I]));
  end;
  m_VisibleEvent.Free;

  for I := 0 to m_DealItemList.Count - 1 do begin
    Dispose(pTUserItem(m_DealItemList.Items[I]));
  end;
  m_DealItemList.Free;

  for I := 0 to m_StorageItemList.Count - 1 do begin
    Dispose(pTUserItem(m_StorageItemList.Items[I]));
  end;
  m_StorageItemList.Free;

  for I := 0 to m_DuelItemList.Count - 1 do begin
    Dispose(pTUserItem(m_DuelItemList.Items[I]));
  end;
  m_DuelItemList.Free;

  m_GroupMembers.Free;
  m_BlockWhisperList.Free;
  m_MasterList.Free;

  for I := 0 to m_DynamicVarList.Count - 1 do begin
    Dispose(pTDynamicVar(m_DynamicVarList.Items[I]));
  end;
  m_DynamicVarList.Free;

  m_CanJmpScriptLableList.Free;
  m_boRunPlayRobotManage := False;
  m_RobotRunTicks := nil;

  if m_SuperItemBox <> nil then begin
    Dispose(m_SuperItemBox);
    m_SuperItemBox := nil;
  end;

  for I := 0 to m_StoreItemList.Count - 1 do begin
    Dispose(pTStoreServerItem(m_StoreItemList.Items[I]));
  end;
  m_StoreItemList.Free;

  for I := 0 to m_TimeLabelList.Count - 1 do begin
    Dispose(pTTimeLabel(m_TimeLabelList.Items[I]));
  end;

  m_TimeLabelList.Free;

  m_IntegerList.Free; //N
  m_StringList.Free; //S

  if Assigned(PlugInEngine.PlayObjectDestroy) then begin
    PlugInEngine.PlayObjectDestroy(Self);
  end;
  //g_TestList.Add('Handle:'+IntToStr(Integer(Self))+' TPlayObject.Destroy:' + m_sCharName);
  inherited;
end;

procedure TPlayObject.ClearTimeLabel(nType: Integer);
var
  I: Integer;
  TimeLabel: pTTimeLabel;
begin
  for I := m_TimeLabelList.Count - 1 downto 0 do begin
    TimeLabel := m_TimeLabelList.Items[I];
    if (TimeLabel.nType = nType) then begin
      TimeLabel.boDelete := True;
    end;
  end;
end;

procedure TPlayObject.ClearAllDelayLabel;
var
  I: Integer;
  TimeLabel: pTTimeLabel;
begin
  for I := 0 to m_TimeLabelList.Count - 1 do begin
    TimeLabel := m_TimeLabelList.Items[I];
    Dispose(TimeLabel);
  end;
  m_TimeLabelList.Clear;
end;

procedure TPlayObject.DealCancel;
begin
  GetBackDealItems();
  if not m_boDealing then Exit;
  m_boDealing := False;
  SendDefMessage(SM_DEALCANCEL, 0, 0, 0, 0, '');
  if m_DealCreat <> nil then begin
    m_DealCreat.DealCancel;
  end;
  m_DealCreat := nil;
  GetBackDealItems();
  if m_boDealType then begin
    SysMsg(g_sDealActionCancelMsg {'交易取消'}, c_Green, t_Hint);
  end else begin
    SysMsg(g_sDuelActionCancelMsg {'挑战取消'}, c_Green, t_Hint);
  end;
  m_DealLastTick := GetTickCount();
end;

procedure TPlayObject.DealCancelA;
begin
  m_Abil.HP := m_WAbil.HP;
  DealCancel();
end;

procedure TPlayObject.DuelCancel;
begin
  GetBackDuelItems;
  if not m_boDueling then Exit;
  m_boDueling := False;
  SendDefMessage(SM_DUELCANCEL, 0, 0, 0, 0, '');
  if m_DuelCreat <> nil then begin
    m_DuelCreat.DuelCancel;
  end;
  m_DuelCreat := nil;
  m_StartDuelCreat := nil;
  GetBackDuelItems();
  SysMsg(g_sDuelActionCancelMsg {'挑战取消'}, c_Green, t_Hint);
  m_DuelLastTick := GetTickCount();
end;

procedure TPlayObject.DuelCancelA;
begin
  m_Abil.HP := m_WAbil.HP;
  DuelCancel();
end;

procedure TPlayObject.StoreCancel;
begin
  GetBackStoreItems();
  if not m_boStore then Exit;
  m_boStore := False;
  SendDefMessage(SM_SENDSTOPSTORE_OK, 0, 0, 0, 0, '');
  SendRefMsg(RM_SENDSTORE, m_btDirection, m_nCurrX, m_nCurrY, Integer(m_boStore), '');
end;

function TPlayObject.DecGold(nGold: Integer): Boolean;
begin
  Result := False;
  if m_nGold >= nGold then begin
    Dec(m_nGold, nGold);
    Result := True;
  end;
end;

procedure TPlayObject.DecGameGold(nGameGold: Integer);
begin
  if m_nGameGold >= nGameGold then begin
    Dec(m_nGameGold, nGameGold);
  end else m_nGameGold := 0;
end;

procedure TPlayObject.DecGamePoint(nGamePoint: Integer);
begin
  if m_nGamePoint >= nGamePoint then begin
    Dec(m_nGamePoint, nGamePoint);
  end else m_nGamePoint := 0;
end;

procedure TPlayObject.DropUseItems(BaseObject: TActorObject);
var
  I: Integer;
  nRate: Integer;
  StdItem: pTStdItem;
  DelList: TStringList;
  boDropall: Boolean;
  sCheckItemName: string;

  PlayObject: TPlayObject;
  ActorObject, ActorObject18: TActorObject;
  nC, n10: Integer;
resourcestring
  sExceptionMsg = '[Exception] TPlayObject::DropUseItems';
begin
  DelList := nil;
  if m_boAngryRing or m_boNoDropUseItem or (not m_boDropUseItem) then Exit;
  PlayObject := nil;
  if (BaseObject <> nil) and (BaseObject.m_btRaceServer = RC_PLAYOBJECT) then
    PlayObject := TPlayObject(BaseObject)
  else begin
    ActorObject18 := nil;
    n10 := 9999;
    for I := 0 to m_VisibleActors.Count - 1 do begin
      ActorObject := TActorObject(pTVisibleBaseObject(m_VisibleActors.Items[I]).BaseObject);
      if (ActorObject.m_btRaceServer = RC_PLAYOBJECT) then begin
        if (not ActorObject.m_boHideMode or m_boCoolEye) then begin
          nC := abs(m_nCurrX - ActorObject.m_nCurrX) + abs(m_nCurrY - ActorObject.m_nCurrY);
          if nC < n10 then begin
            n10 := nC;
            ActorObject18 := ActorObject;
          end;
        end;
      end;
    end;
    if ActorObject18 <> nil then
      PlayObject := TPlayObject(ActorObject18)
    else
      PlayObject := Self;
  end;

  try
    for I := Low(THumanUseItems) to High(THumanUseItems) do begin
      StdItem := UserEngine.GetStdItem(m_UseItems[I].wIndex);
      if StdItem <> nil then begin
        if (StdItem.Reserved and 8 <> 0) then begin
          if DelList = nil then DelList := TStringList.Create;
          DelList.AddObject(StdItem.Name, TObject(m_UseItems[I].MakeIndex));
          //004BB885
          if StdItem.NeedIdentify = 1 then
            AddGameDataLog('16' + #9 +
              m_sMapName + #9 +
              IntToStr(m_nCurrX) + #9 +
              IntToStr(m_nCurrY) + #9 +
              m_sCharName + #9 +
              //UserEngine.GetStdItemName(m_UseItems[I].wIndex) + #9 +
              StdItem.Name + #9 +
              IntToStr(m_UseItems[I].MakeIndex) + #9 +
              BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
              '0');
          m_UseItems[I].wIndex := 0;
        end;
      end;
    end;

    if PKLevel > 2 then nRate := g_Config.nDieRedDropUseItemRate {15}
    else nRate := g_Config.nDieDropUseItemRate {30};

    for I := Low(THumanUseItems) to High(THumanUseItems) do begin
      if m_UseItems[I].wIndex <= 0 then Continue;
      boDropall := False;
      StdItem := UserEngine.GetStdItem(m_UseItems[I].wIndex);
      if StdItem = nil then Continue;

      if InDisableTakeOffList(m_UseItems[I].wIndex) then Continue; //检查是否在禁止取下列表,如果在列表中则不掉此物品

      if (m_UseItems[I].AddValue[0] = 1) and (GetDayCount(m_UseItems[I].MaxDate, Now) <= 0) then begin //删除到期装备
        m_UseItems[I].wIndex := 0;
        Continue;
      end;

      if g_Config.boBindItemNoScatter and (CheckItemBindUse(@m_UseItems[I], False) = 2) then Continue; //绑定物品禁止爆

      if Assigned(PlugInEngine.CheckCanDieScatterItem) then begin //死亡必爆
        sCheckItemName := StdItem.Name;
        if PlugInEngine.CheckCanDieScatterItem(Self, PChar(sCheckItemName), False) then begin
          boDropall := True;
        end;
      end;

      if Assigned(PlugInEngine.CheckNotCanScatterItem) then begin //禁止爆出
        sCheckItemName := StdItem.Name;
        if PlugInEngine.CheckNotCanScatterItem(Self, PChar(sCheckItemName), False) then begin
          Continue;
        end;
      end;

      if (not boDropall) and (Random(nRate) <> 0) then Continue;

      if DropItemDown(@m_UseItems[I], 2, True, BaseObject, Self) then begin
        if (PlayObject <> nil) and (g_FunctionNPC <> nil) then begin
          PlayObject.m_sScatterItemName := StdItem.Name;
          PlayObject.m_sScatterItemOwnerName := m_sCharName;
          PlayObject.m_sScatterItemMapName := m_sMapName;
          PlayObject.m_sScatterItemMapDesc := m_PEnvir.sMapDesc;
          PlayObject.m_nScatterItemX := m_nScatterItemX;
          PlayObject.m_nScatterItemY := m_nScatterItemY;

          if IsAllowScatterItem(PlayObject.m_sScatterItemName) then begin
            try
              g_FunctionNPC.GotoLable(PlayObject, '@DropUseItems', False);
            except
              MainOutMessage(sExceptionMsg + ' FunctionNPC::GotoLable');
            end;
          end;
        end;

        //StdItem := UserEngine.GetStdItem(m_UseItems[I].wIndex);
        //if StdItem <> nil then begin
        if (StdItem.Reserved and 10 = 0) then begin
          if m_btRaceServer = RC_PLAYOBJECT then begin
              //if StdItem.Reserved = 10 then Continue; //增加不允许掉落装备
            if DelList = nil then DelList := TStringList.Create;
            DelList.AddObject(UserEngine.GetStdItemName(m_UseItems[I].wIndex), TObject(m_UseItems[I].MakeIndex));
          end;
          m_UseItems[I].wIndex := 0;
        end;
      end;
      //end;
    end;
    if DelList <> nil then
      SendMsg(Self, RM_SENDDELITEMLIST, 0, Integer(DelList), 0, 0, '');
  except
    MainOutMessage(sExceptionMsg);
  end;
end;

procedure TPlayObject.GainExp(dwExp: LongWord);
var
  I, n, sumlv, nExp, nHighLevel, nLowLevel: Integer;
  PlayObject: TPlayObject;
  nCheckCode: Integer;
resourcestring
  sExceptionMsg = '[Exception] TPlayObject::GainExp';
const
  bonus: array[0..GROUPMAX] of real = (1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.1, 2.2);
  lvlexp: array[1..10] of Integer = (5000*1,5000*2,5000*3,5000*4,5000*5,5000*6,5000*7,5000*8,5000*9,5000*10);
  lvlbonus:Real = 0.2;
begin
   try
    if m_GroupOwner <> nil then begin
      sumlv := 0;
      n := 0;
      If (m_GroupOwner.m_GroupLevel < GROUPMAX) and (m_GroupOwner.m_GroupLevel <> 10) then begin
        Inc(m_GroupOwner.m_GroupExp,Round(dwExp*lvlbonus));
        if (m_GroupOwner.m_GroupLevel in [1..10]) then
          if m_GroupOwner.m_GroupExp >= lvlexp[m_GroupOwner.m_GroupLevel] then begin
          m_GroupOwner.m_GroupExp := 0;
          Inc(m_GroupOwner.m_GroupLevel);
          for i:=0 to m_GroupOwner.m_GroupMembers.Count-1 do begin
           PlayObject:=TPlayObject(m_GroupOwner.m_GroupMembers.Objects[i]);
           PlayObject.SysMsg('The group has now reached level: ' + IntToStr(m_GroupOwner.m_GroupLevel) + ' ..!',c_Blue,t_Hint);
           IncHealthSpell(2000, 2000);
           PlayObject.SendMsg(Self,RM_GROUPINFO,0,0,0,0,'');
           PlayObject.RecalcAbilitys;
           RecalcAbilitys;
          end;
          end;
        SendMsg(Self,RM_GROUPINFO,0,0,0,0,'');
      end;
      for i:=0 to m_GroupOwner.m_GroupMembers.Count-1 do begin
        PlayObject:=TPlayObject(m_GroupOwner.m_GroupMembers.Objects[i]);
        if not PlayObject.m_boDeath and (m_PEnvir = PlayObject.m_PEnvir) and (abs(m_nCurrX-PlayObject.m_nCurrX) <= 12) and (abs(m_nCurrX - PlayObject.m_nCurrX) <= 12) then begin
          sumlv := sumlv + PlayObject.m_Abil.Level;
           Inc (n);
        end;
      end;
      if (sumlv > 0) and (n > 1) and (m_GroupOwner.m_boGroupShareExp) then begin
        if n in [0..GROUPMAX] then
          dwExp := Round (dwExp * bonus[n]);
        for i:=0 to m_GroupOwner.m_GroupMembers.Count-1 do begin
          PlayObject:= TPlayObject(m_GroupOwner.m_GroupMembers.Objects[i]);
          if not PlayObject.m_boDeath and (m_PEnvir = PlayObject.m_PEnvir) and (abs(m_nCurrX - PlayObject.m_nCurrX) <= 12) and (abs(m_nCurrX - PlayObject.m_nCurrX) <= 12) then begin
            if g_Config.boHighLevelKillMonFixExp then begin 
              PlayObject.WinExp(Round(dwExp / n));
            end else begin
              PlayObject.WinExp(Round(dwExp / sumlv * PlayObject.m_Abil.Level));
            end;
          end;
        end;
      end else WinExp(dwExp);
    end else WinExp(dwExp);
  except
    MainOutMessage (sExceptionMsg);
  end;
end;
{  try
    if dwExp > 0 then begin
      nCheckCode := 0;
      nHighLevel := 0;
      nLowLevel := High(Word);

      if (m_GroupOwner <> nil) and (m_GroupOwner.m_GroupMembers <> nil) and (m_GroupOwner.m_GroupMembers.Count > 0) then begin
        sumlv := 0;
        n := 0;
        nCheckCode := 1;
        for I := 0 to m_GroupOwner.m_GroupMembers.Count - 1 do begin
          PlayObject := TPlayObject(m_GroupOwner.m_GroupMembers.Objects[I]);
          if PlayObject <> nil then begin
            if not PlayObject.m_boDeath and (m_PEnvir = PlayObject.m_PEnvir) and (abs(m_nCurrX - PlayObject.m_nCurrX) <= 12) and (abs(m_nCurrX - PlayObject.m_nCurrX) <= 12) then begin
              sumlv := sumlv + PlayObject.m_Abil.Level;
              if PlayObject.m_Abil.Level > nHighLevel then nHighLevel := PlayObject.m_Abil.Level;
              if PlayObject.m_Abil.Level < nLowLevel then nLowLevel := PlayObject.m_Abil.Level;
              Inc(n);
            end;
          end;
        end;

        nCheckCode := 2;
        if (sumlv > 0) and (n > 1) then begin
          if n in [0..GROUPMAX] then
            dwExp := Round(dwExp * bonus[n]);

          nCheckCode := 3;
          for I := 0 to m_GroupOwner.m_GroupMembers.Count - 1 do begin
            PlayObject := TPlayObject(m_GroupOwner.m_GroupMembers.Objects[I]);
            if PlayObject <> nil then begin
              if not PlayObject.m_boDeath and (m_PEnvir = PlayObject.m_PEnvir) and (abs(m_nCurrX - PlayObject.m_nCurrX) <= 12) and (abs(m_nCurrX - PlayObject.m_nCurrX) <= 12) then begin
                if g_Config.boHighLevelKillMonFixExp and g_Config.boHighLevelGroupFixExp then begin //02/08 增加，在高等级经验不变时，把组队的经验平均分配
                  nCheckCode := 4;
                  PlayObject.WinExp(Round(dwExp / n));
                  nCheckCode := 5;
                end else
                  if g_Config.boHighLevelGroupFixExp then begin
                  nCheckCode := 6;
                  PlayObject.WinExp(Round(dwExp / sumlv * PlayObject.m_Abil.Level));
                  nCheckCode := 7;
                end else
                  if m_Abil.Level > (nLowLevel + 5) then begin //大号杀怪组里人经验不变
                  PlayObject.WinExp(Round(dwExp / sumlv * PlayObject.m_Abil.Level));
                  //PlayObject.WinExp(dwExp);
                end else
                  if m_Abil.Level <= (nLowLevel + 5) then begin
                  if PlayObject.m_Abil.Level > (nLowLevel + 5) then begin //小号杀怪经验组里人大号经验改变
                    nExp := Round(dwExp / PlayObject.m_Abil.Level);
                    if nExp <= 0 then nExp := 1;
                    PlayObject.WinExp(nExp);
                  end else begin
                    PlayObject.WinExp(Round(dwExp / sumlv * PlayObject.m_Abil.Level)); //小号杀怪经验组里人小号经验不变
                  end;
                end;
              end;
            end;
          end;
        end else WinExp(dwExp);
      end else WinExp(dwExp);
    end;
  except
    MainOutMessage(sExceptionMsg + ' ' + m_sCharName + ' ' + IntToStr(nCheckCode));
  end;
end;  }

procedure TPlayObject.GameTimeChanged;
begin
  if m_nBright <> g_nGameTime then begin
    m_nBright := g_nGameTime;
    SendMsg(Self, RM_DAYCHANGING, 0, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.GetBackDealItems;
var
  I: Integer;
begin
  if m_DealItemList.Count > 0 then begin
    for I := 0 to m_DealItemList.Count - 1 do begin
      m_ItemList.Add(m_DealItemList.Items[I]);
    end;
    m_DealItemList.Clear;
  end;

  if m_boDealType then
    Inc(m_nGold, m_nDealGolds)
  else
    Inc(m_nGameGold, m_nDealGolds);

  m_nDealGolds := 0;
  m_boDealOK := False;
end;

procedure TPlayObject.GetBackDuelItems;
var
  I: Integer;
begin
  if m_DuelItemList.Count > 0 then begin
    for I := 0 to m_DuelItemList.Count - 1 do begin
      m_ItemList.Add(m_DuelItemList.Items[I]);
    end;
    m_DuelItemList.Clear;
  end;

  Inc(m_nGameGold, m_nDuelGolds);
  m_nDuelGolds := 0;
  m_boDuelOK := False;
end;

procedure TPlayObject.GetBackStoreItems();
var
  I: Integer;
  StoreServerItem: pTStoreServerItem;
begin
  if m_StoreItemList.Count > 0 then begin
    for I := 0 to m_StoreItemList.Count - 1 do begin
      StoreServerItem := pTStoreServerItem(m_StoreItemList.Items[I]);
      //m_ItemList.Add(StoreServerItem.UserItem);
      Dispose(StoreServerItem);
    end;
    m_StoreItemList.Clear;
  end;
end;

procedure TPlayObject.GetBagUseItems(var btDc, btSc, btMc, btDura: Byte);
var
  I, II: Integer;
  DuraList: TList;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
  StdItem80: TStdItem;
  DelItemList: TStringList;
  nDc, nSc, nMc, nDcMin, nDcMax, nScMin, nScMax, nMcMin, nMcMax, nDura, nItemCount: Integer;
begin
  nDcMin := 0;
  nDcMax := 0;
  nScMin := 0;
  nScMax := 0;
  nMcMin := 0;
  nMcMax := 0;
  nDura := 0;
  nItemCount := 0;
  DelItemList := nil;
  DuraList := TList.Create;
  for I := m_ItemList.Count - 1 downto 0 do begin
    if m_ItemList.Count <= 0 then Break;
    UserItem := m_ItemList.Items[I];
    //if UserItem = nil then Continue;
    if UserEngine.GetStdItemName(UserItem.wIndex) = g_Config.sBlackStone then begin
      DuraList.Add(Pointer(Round(UserItem.Dura / 1.0E3)));
      if DelItemList = nil then DelItemList := TStringList.Create;
      DelItemList.AddObject(g_Config.sBlackStone, TObject(UserItem.MakeIndex));
      m_ItemList.Delete(I);
      Dispose(UserItem);
    end else begin
      if IsUseItem(UserItem.wIndex) then begin
        StdItem := UserEngine.GetStdItem(UserItem.wIndex);
        if StdItem <> nil then begin
          StdItem80 := StdItem^;
          ItemUnit.GetItemAddValue(UserItem, StdItem80);
          nDc := 0;
          nSc := 0;
          nMc := 0;
          case StdItem80.StdMode of
            19, 20, 21: begin //004A0421
                nDc := HiWord(StdItem80.DC) + LoWord(StdItem80.DC);
                nSc := HiWord(StdItem80.SC) + LoWord(StdItem80.SC);
                nMc := HiWord(StdItem80.MC) + LoWord(StdItem80.MC);
              end;
            22, 23: begin //004A046E
                nDc := HiWord(StdItem80.DC) + LoWord(StdItem80.DC);
                nSc := HiWord(StdItem80.SC) + LoWord(StdItem80.SC);
                nMc := HiWord(StdItem80.MC) + LoWord(StdItem80.MC);
              end;
            24, 26: begin
                nDc := HiWord(StdItem80.DC) + LoWord(StdItem80.DC) + 1;
                nSc := HiWord(StdItem80.SC) + LoWord(StdItem80.SC) + 1;
                nMc := HiWord(StdItem80.MC) + LoWord(StdItem80.MC) + 1;
              end;
          end;
          if nDcMin < nDc then begin
            nDcMax := nDcMin;
            nDcMin := nDc;
          end else begin
            if nDcMax < nDc then nDcMax := nDc;
          end;
          if nScMin < nSc then begin
            nScMax := nScMin;
            nScMin := nSc;
          end else begin
            if nScMax < nSc then nScMax := nSc;
          end;
          if nMcMin < nMc then begin
            nMcMax := nMcMin;
            nMcMin := nMc;
          end else begin
            if nMcMax < nMc then nMcMax := nMc;
          end;
          if DelItemList = nil then DelItemList := TStringList.Create;
          DelItemList.AddObject(StdItem.Name, TObject(UserItem.MakeIndex));
          //004A06DB
          if StdItem.NeedIdentify = 1 then
            AddGameDataLog('26' + #9 +
              m_sMapName + #9 +
              IntToStr(m_nCurrX) + #9 +
              IntToStr(m_nCurrY) + #9 +
              m_sCharName + #9 +
              //UserEngine.GetStdItemName(UserItem.wIndex) + #9 +
              StdItem.Name + #9 +
              IntToStr(UserItem.MakeIndex) + #9 +
              '1' + #9 +
              '0');
          m_ItemList.Delete(I);
          Dispose(UserItem);
        end;
      end;
    end;
  end; // for
  for I := 0 to DuraList.Count - 1 do begin
    if DuraList.Count <= 0 then Break;
    for II := DuraList.Count - 1 downto I + 1 do begin
      if Integer(DuraList.Items[II]) > Integer(DuraList.Items[II - 1]) then
        DuraList.Exchange(II, II - 1);
    end; // for
  end; // for
  for I := 0 to DuraList.Count - 1 do begin
    nDura := nDura + Integer(DuraList.Items[I]);
    Inc(nItemCount);
    if nItemCount >= 5 then Break;
  end;
  btDura := Round(_MIN(5, nItemCount) + _MIN(5, nItemCount) * ((nDura / nItemCount) / 5.0));
  btDc := nDcMin div 5 + nDcMax div 3;
  btSc := nScMin div 5 + nScMax div 3;
  btMc := nMcMin div 5 + nMcMax div 3;
  if DelItemList <> nil then
    SendMsg(Self, RM_SENDDELITEMLIST, 0, Integer(DelItemList), 0, 0, '');
  if DuraList <> nil then DuraList.Free;
end;

function TPlayObject.GeTActorObjectInfo: string;
begin
  Result := m_sCharName +
    ' Logo:' + IntToHex(Integer(Self), 2) +
    ' Permission Level: ' + IntToStr(m_btPermission) +
    ' Management: ' + BoolToCStr(m_boAdminMode) +
    ' Stealth mode: ' + BoolToCStr(m_boObMode) +
    ' Superman: ' + BoolToCStr(m_boSuperMan) +
    ' Map:' + m_sMapName + '(' + m_PEnvir.sMapDesc + ')' +
    ' Cords:' + IntToStr(m_nCurrX) + ':' + IntToStr(m_nCurrY) +
    ' Level:' + IntToStr(m_Abil.Level) +
    ' Relevel:' + IntToStr(m_btReLevel) +
    ' Exp:' + IntToStr(m_Abil.Exp) +
    ' HP: ' + IntToStr(m_WAbil.HP) + '-' + IntToStr(m_WAbil.MaxHP) +
    ' MP: ' + IntToStr(m_WAbil.MP) + '-' + IntToStr(m_WAbil.MaxMP) +
    ' DC: ' + IntToStr(LoWord(m_WAbil.DC)) + '-' + IntToStr(HiWord(m_WAbil.DC)) +
    ' MC: ' + IntToStr(LoWord(m_WAbil.MC)) + '-' + IntToStr(HiWord(m_WAbil.MC)) +
    ' SC: ' + IntToStr(LoWord(m_WAbil.SC)) + '-' + IntToStr(HiWord(m_WAbil.SC)) +
    ' AC: ' + IntToStr(LoWord(m_WAbil.AC)) + '-' + IntToStr(HiWord(m_WAbil.AC)) +
    ' MAC: ' + IntToStr(LoWord(m_WAbil.MAC)) + '-' + IntToStr(HiWord(m_WAbil.MAC)) +
    ' HitPoint:' + IntToStr(m_btHitPoint) +
    ' SpeedPoint:' + IntToStr(m_btSpeedPoint) +
    ' 速度:' + IntToStr(m_nHitSpeed) +
    ' StorePass:' + m_sStoragePwd +
    ' LocalIP:' + m_sIPaddr + '(' + m_sIPLocal {GetIPLocal(m_sIPaddr)} + ')' +
    ' UserID:' + m_sUserID +
    ' Login Time:' + DateTimeToStr(m_dLogonTime) +
    ' Online Time (minutes):' + IntToStr((GetTickCount - m_dwLogonTick) div 60000) +
    ' Payment:' + IntToStr(m_nPayMent) +
    ' ' + g_Config.sGameGoldName + ':' + IntToStr(m_nGameGold) +
    ' ' + g_Config.sGamePointName + ':' + IntToStr(m_nGamePoint) +
    ' ' + g_Config.sPayMentPointName + ':' + IntToStr(m_nPayMentPoint) +
    ' Member:' + IntToStr(m_nMemberType) +
    ' MemberLevel:' + IntToStr(m_nMemberLevel) +
    ' Mon exp:' + CurrToStr(m_nKillMonExpRate / 100) +
    ' Power Rate:' + CurrToStr(m_nPowerRate / 100) +
    ' Creditpoint:' + IntToStr(m_btCreditPoint);
end;

function TPlayObject.GetDigUpMsgCount: Integer;
var
  I: Integer;
  SendMessage: pTSendMessage;
begin
  Result := 0;
  try
    EnterCriticalSection(ProcessMsgCriticalSection);
    for I := 0 to m_MsgList.Count - 1 do begin
      SendMessage := m_MsgList.Items[I];
      if SendMessage <> nil then begin
        if (SendMessage.wIdent = CM_BUTCH) then begin
          Inc(Result);
        end;
      end;
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
end;

procedure TActorObject.UseLamp;
var
  nOldDura: Integer;
  nDura: Integer;
  PlayObject: TPlayObject;
  StdItem: pTStdItem;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::UseLamp';
begin
  try
    if m_UseItems[U_RIGHTHAND].wIndex > 0 then begin
      StdItem := UserEngine.GetStdItem(m_UseItems[U_RIGHTHAND].wIndex);
      if (StdItem = nil) or (StdItem.Source <> 0) then Exit;
      if (StdItem <> nil) and (StdItem.Shape > 50) then Exit;
      nOldDura := Round(m_UseItems[U_RIGHTHAND].Dura / 1000);
      if g_Config.boDecLampDura then begin
        nDura := m_UseItems[U_RIGHTHAND].Dura - 1;
      end else begin
        nDura := m_UseItems[U_RIGHTHAND].Dura;
      end;
      if nDura <= 0 then begin
        m_UseItems[U_RIGHTHAND].Dura := 0;
        if m_btRaceServer = RC_PLAYOBJECT then begin
          PlayObject := TPlayObject(Self);
          PlayObject.SendDelItems(@m_UseItems[U_RIGHTHAND]);
        end;
        m_UseItems[U_RIGHTHAND].wIndex := 0;
        m_nLight := 0;
        SendRefMsg(RM_CHANGELIGHT, 0, 0, 0, 0, '');
        SendMsg(Self, RM_LAMPCHANGEDURA, 0, m_UseItems[U_RIGHTHAND].Dura, 0, 0, '');
        RecalcAbilitys();
        //        FeatureChanged(); 01/21 取消 蜡烛是本人才可以看到的，不需要发送广播信息
      end else m_UseItems[U_RIGHTHAND].Dura := nDura;
      if nOldDura <> Round(nDura / 1000) then begin
        SendMsg(Self, RM_LAMPCHANGEDURA, 0, m_UseItems[U_RIGHTHAND].Dura, 0, 0, '');
      end;
    end;
  except
    MainOutMessage(sExceptionMsg);
  end;
end;

function TActorObject.GetPoseCreate: TActorObject;
var
  nX, nY: Integer;
begin
  Result := nil;
  if GetFrontPosition(nX, nY) then begin
    Result := TActorObject(m_PEnvir.GetMovingObject(nX, nY, True));
  end;
end;

procedure TPlayObject.ClientQueryBagItems;
var
  I, nCount: Integer;
  Item: pTStdItem;
  sSENDMSG: string;
  ClientItem: TClientItem;
  OClientItem: TOClientItem;
  StdItem: TStdItem;
  UserItem: pTUserItem;
  sUserItemName: string;
  ItemBagList: TStringList;
begin
  if Assigned(PlugInEngine.ClientQueryBagItems) then begin
    PlugInEngine.ClientQueryBagItems(Self);
  end else begin
    if not m_boHeroVersion then begin
      sSENDMSG := '';
      for I := 0 to m_ItemList.Count - 1 do begin
        UserItem := m_ItemList.Items[I];
        Item := UserEngine.GetStdItem(UserItem.wIndex);
        //sItemNewName:=GetItemName(UserItem.MakeIndex);
        if Item <> nil then begin
          StdItem := Item^;
          ItemUnit.GetItemAddValue(UserItem, StdItem);
          //      Move(StdItem,ClientItem.S,SizeOf(TStdItem));
          CopyStdItemToOStdItem(@StdItem, @OClientItem.s);

          //取自定义物品名称
          sUserItemName := '';
          if UserItem.btValue[13] = 1 then
            sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);

          if sUserItemName <> '' then
            OClientItem.s.Name := sUserItemName;

          OClientItem.Dura := UserItem.Dura;
          OClientItem.DuraMax := UserItem.DuraMax;
          OClientItem.MakeIndex := UserItem.MakeIndex;
          if StdItem.StdMode = 50 then begin
            OClientItem.s.Name := OClientItem.s.Name + ' #' + IntToStr(UserItem.Dura);
          end;
          sSENDMSG := sSENDMSG + EncodeBuffer(@OClientItem, SizeOf(TOClientItem)) + '/';
        end;
      end;
      if sSENDMSG <> '' then begin
        m_DefMsg := MakeDefaultMsg(SM_BAGITEMS, Integer(Self), 0, 0, m_ItemList.Count);
        SendSocket(@m_DefMsg, sSENDMSG);
      end;
    end else begin
      sSENDMSG := '';
      //if m_ItemList.Count <= 20 then begin
      for I := 0 to m_ItemList.Count - 1 do begin
        UserItem := m_ItemList.Items[I];
        //sItemNewName:=GetItemName(UserItem.MakeIndex);
        Item := UserEngine.GetStdItem(UserItem.wIndex);
        if Item <> nil then begin
          StdItem := Item^;
          ItemUnit.GetItemAddValue(UserItem, StdItem);
          Move(StdItem, ClientItem.s, SizeOf(TStdItem));

          //取自定义物品名称
          sUserItemName := '';
          if UserItem.btValue[13] = 1 then
            sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
          if sUserItemName <> '' then
            ClientItem.s.Name := sUserItemName;

          if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
            ClientItem.s.Name := '( )' + ClientItem.s.Name;
          end;

          ClientItem.Dura := UserItem.Dura;
          ClientItem.DuraMax := UserItem.DuraMax;
          ClientItem.MakeIndex := UserItem.MakeIndex;

          ClientItem.s.AddValue := UserItem.AddValue;
          ClientItem.s.AddPoint := UserItem.AddPoint;
          ClientItem.s.MaxDate := UserItem.MaxDate;
          //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

          if StdItem.StdMode = 50 then begin
            ClientItem.s.Name := ClientItem.s.Name + ' #' + IntToStr(UserItem.Dura);
          end;
          //Mainoutmessage('sSENDMSG:'+IntToStr(Length(EncodeBuffer(@ClientItem, SizeOf(TClientItem))))+' '+EncodeBuffer(@ClientItem, SizeOf(TClientItem)));
          sSENDMSG := sSENDMSG + EncodeBuffer(@ClientItem, SizeOf(TClientItem)) + '/';
        end;
      end;
      if sSENDMSG <> '' then begin
        m_DefMsg := MakeDefaultMsg(SM_BAGITEMS, Integer(Self), 0, 0, m_ItemList.Count);
        SendSocket(@m_DefMsg, sSENDMSG);
      end;
    end;
  end;
end;

procedure TPlayObject.ClientQueryUserSet(ProcessMsg: pTProcessMessage);
var
  sPassword: string;
begin
  {sPassword := Md5.EncryptString(ProcessMsg.sMsg);
  if sPassword <> DecodeString('NbA_VsaSTRucMbAjUl') then begin
    MainOutMessage('Fail');
    exit;
  end;
  m_nClientFlagMode := ProcessMsg.wParam;
  MainOutMessage(format('OK:%d', [m_nClientFlagMode]));}
  //'JackyWangFang'
  //'8988e0804091579a2fd8a0db75e9c17a';
  //'NbA_VsaSTRucMbAjUl'
end;

procedure TPlayObject.ClientQueryUserState(BaseObject: TActorObject; nX, nY: Integer);
var
  I: Integer;
  UserState: TUserStateInfo;
  OUserState: TOUserStateInfo;
  StdItem: pTStdItem;
  StdItem24: TStdItem;
  ClientItem: TClientItem;
  OClientItem: TOClientItem;
  sUserItemName: string;
  UserItem: pTUserItem;
  boQuery: Boolean;
begin
  if not CretInNearXY(BaseObject, nX, nY) then Exit;
  boQuery := not BaseObject.m_boIsUnknowActor;
  if (not boQuery) and (m_btPermission >= 10) then boQuery := True;
  if (not boQuery) and (BaseObject.m_Master <> nil) and (BaseObject.Master = Self) then boQuery := True;

  {if BaseObject.m_UseItems[U_HELMET].wIndex > 0 then begin
    StdItem := UserEngine.GetStdItem(BaseObject.m_UseItems[U_HELMET].wIndex);
    if StdItem <> nil then begin
      case StdItem.Shape of
        1, 2: boQuery := False;
      end;
      if (not boQuery) and (m_btPermission >= 10) then boQuery := True;
      if (not boQuery) and (BaseObject.m_Master <> nil) and (BaseObject.Master = Self) then boQuery := True;
    end;
  end; }

  if boQuery then begin
    if (not m_boHeroVersion) and (m_dwClientTick = 0) then begin
      FillChar(OUserState, SizeOf(TOUserStateInfo), #0);
      OUserState.feature := BaseObject.GetFeature(Self);
      OUserState.UserName := BaseObject.m_sCharName;
      OUserState.NAMECOLOR := GetCharColor(BaseObject);
      if BaseObject.m_MyGuild <> nil then begin
        OUserState.GuildName := TGUild(BaseObject.m_MyGuild).sGuildName;
      end;
      OUserState.GuildRankName := BaseObject.m_sGuildRankName;

      for I := Low(THumItems) to High(THumItems) do begin
        UserItem := @BaseObject.m_UseItems[I];
        if UserItem.wIndex > 0 then begin
          StdItem := UserEngine.GetStdItem(BaseObject.m_UseItems[I].wIndex);
          if StdItem = nil then Continue;
          StdItem24 := StdItem^;
          ItemUnit.GetItemAddValue(@BaseObject.m_UseItems[I], StdItem24);
        //Move(StdItem24,ClientItem.S,SizeOf(TStdItem));
          CopyStdItemToOStdItem(@StdItem24, @OClientItem.s);

        //取自定义物品名称
          sUserItemName := '';
          if UserItem.btValue[13] = 1 then
            sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
          if sUserItemName <> '' then
            OClientItem.s.Name := sUserItemName;
          OClientItem.MakeIndex := BaseObject.m_UseItems[I].MakeIndex;
          OClientItem.Dura := BaseObject.m_UseItems[I].Dura;
          OClientItem.DuraMax := BaseObject.m_UseItems[I].DuraMax;
          OUserState.UseItems[I] := OClientItem;
        end;
      end;
      m_DefMsg := MakeDefaultMsg(SM_SENDUSERSTATE, 0, 0, 0, 0);
      SendSocket(@m_DefMsg, EncodeBuffer(@OUserState, SizeOf(TOUserStateInfo)));
    end else begin
      //if not CretInNearXY(BaseObject, nX, nY) then Exit;
      FillChar(UserState, SizeOf(TUserStateInfo), #0);
      UserState.feature := BaseObject.GetFeature(Self);
      UserState.UserName := BaseObject.m_sCharName;
      UserState.NAMECOLOR := GetCharColor(BaseObject);
      if BaseObject.m_btRaceServer = RC_HEROOBJECT then begin
        if (BaseObject.m_Master <> nil) then begin
          UserState.GuildName := BaseObject.m_Master.m_sCharName + '''s';
          UserState.GuildRankName := 'Hero';
        end;
      end else begin
        if BaseObject.m_MyGuild <> nil then begin
          UserState.GuildName := TGUild(BaseObject.m_MyGuild).sGuildName;
        end;
        UserState.GuildRankName := BaseObject.m_sGuildRankName;
      end;

      for I := Low(THumanUseItems) to High(THumanUseItems) do begin
        UserItem := @BaseObject.m_UseItems[I];
        if UserItem.wIndex > 0 then begin
          StdItem := UserEngine.GetStdItem(BaseObject.m_UseItems[I].wIndex);
          if StdItem = nil then Continue;
          StdItem24 := StdItem^;
          ItemUnit.GetItemAddValue(@BaseObject.m_UseItems[I], StdItem24);
          Move(StdItem24, ClientItem.s, SizeOf(TStdItem));

        //取自定义物品名称
          sUserItemName := '';
          if UserItem.btValue[13] = 1 then
            sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
          if sUserItemName <> '' then
            ClientItem.s.Name := sUserItemName;

          if BaseObject.m_btRaceServer = RC_HEROOBJECT then begin
            if (BaseObject.m_Master <> nil) and (TPlayObject(BaseObject.m_Master).CheckItemBindUse(UserItem, False) = 2) then begin //绑定物品
              ClientItem.s.Name := '( )' + ClientItem.s.Name;
            end;
          end else
            if BaseObject.m_btRaceServer = RC_PLAYOBJECT then begin
            if (TPlayObject(BaseObject).CheckItemBindUse(UserItem, False) = 2) then begin //绑定物品
              ClientItem.s.Name := '( )' + ClientItem.s.Name;
            end;
          end;

          ClientItem.MakeIndex := BaseObject.m_UseItems[I].MakeIndex;
          ClientItem.Dura := BaseObject.m_UseItems[I].Dura;
          ClientItem.DuraMax := BaseObject.m_UseItems[I].DuraMax;

          ClientItem.s.AddValue := UserItem.AddValue;
          ClientItem.s.AddPoint := UserItem.AddPoint;
          ClientItem.s.MaxDate := UserItem.MaxDate;
          //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

          UserState.UseItems[I] := ClientItem;
        end;
      end;
      m_DefMsg := MakeDefaultMsg(SM_SENDUSERSTATE, 0, 0, 0, 0);
      SendSocket(@m_DefMsg, EncodeBuffer(@UserState, SizeOf(TUserStateInfo)));
    end;
  end;
end;

procedure TPlayObject.ClientQueryUserItem(nLabel: Integer; sData: string);
var
  sUserName, sItemName, sItemMakeIdx: string;
  nItemMakeIdx: Integer;
  PlayObject: TPlayObject;
  I: Integer;
  StdItem: pTStdItem;
  StdItem24: TStdItem;
  ClientItem: TClientItem;
  OClientItem: TOClientItem;
  sUserItemName: string;
  UserItem: pTUserItem;
  boFind: Boolean;
begin
  boFind := False;

  sData := GetValidStr3(sData, sUserName, ['|']);
  sData := GetValidStr3(sData, sItemName, ['|']);
  sData := GetValidStr3(sData, sItemMakeIdx, ['|']);
  nItemMakeIdx := Str_ToInt(sItemMakeIdx, 0);

  if (sUserName <> '') and (sItemName <> '') and (nItemMakeIdx <> 0) then begin
    PlayObject := UserEngine.GetPlayObject(sUserName);
    if PlayObject <> nil then begin
      for I := 0 to PlayObject.m_ItemList.Count - 1 do begin
        UserItem := PlayObject.m_ItemList.Items[I];
        if (UserItem.wIndex > 0) and (UserItem.MakeIndex = nItemMakeIdx) then begin

          StdItem := UserEngine.GetStdItem(UserItem.wIndex);
          if StdItem = nil then Continue;
          StdItem24 := StdItem^;
          ItemUnit.GetItemAddValue(UserItem, StdItem24);
          Move(StdItem24, ClientItem.s, SizeOf(TStdItem));

        //取自定义物品名称
          sUserItemName := '';
          if UserItem.btValue[13] = 1 then
            sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
          if sUserItemName <> '' then
            ClientItem.s.Name := sUserItemName;

          if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
            ClientItem.s.Name := '( )' + ClientItem.s.Name;
          end;

          if CompareText(ClientItem.s.Name, sItemName) = 0 then begin
            ClientItem.MakeIndex := UserItem.MakeIndex;
            ClientItem.Dura := UserItem.Dura;
            ClientItem.DuraMax := UserItem.DuraMax;

            ClientItem.s.AddValue := UserItem.AddValue;
            ClientItem.s.AddPoint := UserItem.AddPoint;
            ClientItem.s.MaxDate := UserItem.MaxDate;
            //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);
            boFind := True;
            Break;
          end;
        end;
      end;

      if not boFind then begin
        for I := Low(THumanUseItems) to High(THumanUseItems) do begin
          UserItem := @PlayObject.m_UseItems[I];
          if (UserItem.wIndex > 0) and (UserItem.MakeIndex = nItemMakeIdx) then begin

            StdItem := UserEngine.GetStdItem(UserItem.wIndex);
            if StdItem = nil then Continue;
            StdItem24 := StdItem^;
            ItemUnit.GetItemAddValue(UserItem, StdItem24);
            Move(StdItem24, ClientItem.s, SizeOf(TStdItem));

        //取自定义物品名称
            sUserItemName := '';
            if UserItem.btValue[13] = 1 then
              sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
            if sUserItemName <> '' then
              ClientItem.s.Name := sUserItemName;

            if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
              ClientItem.s.Name := '( )' + ClientItem.s.Name;
            end;

            if CompareText(ClientItem.s.Name, sItemName) = 0 then begin
              ClientItem.MakeIndex := UserItem.MakeIndex;
              ClientItem.Dura := UserItem.Dura;
              ClientItem.DuraMax := UserItem.DuraMax;

              ClientItem.s.AddValue := UserItem.AddValue;
              ClientItem.s.AddPoint := UserItem.AddPoint;
              ClientItem.s.MaxDate := UserItem.MaxDate;
              //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);
              boFind := True;
              Break;
            end;
          end;
        end;
      end;

      if not boFind then begin
        for I := 0 to PlayObject.m_StorageItemList.Count - 1 do begin
          UserItem := PlayObject.m_StorageItemList.Items[I];
          if (UserItem.wIndex > 0) and (UserItem.MakeIndex = nItemMakeIdx) then begin

            StdItem := UserEngine.GetStdItem(UserItem.wIndex);
            if StdItem = nil then Continue;
            StdItem24 := StdItem^;
            ItemUnit.GetItemAddValue(UserItem, StdItem24);
            Move(StdItem24, ClientItem.s, SizeOf(TStdItem));

        //取自定义物品名称
            sUserItemName := '';
            if UserItem.btValue[13] = 1 then
              sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
            if sUserItemName <> '' then
              ClientItem.s.Name := sUserItemName;

            if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
              ClientItem.s.Name := '(Tie)' + ClientItem.s.Name;
            end;

            if CompareText(ClientItem.s.Name, sItemName) = 0 then begin
              ClientItem.MakeIndex := UserItem.MakeIndex;
              ClientItem.Dura := UserItem.Dura;
              ClientItem.DuraMax := UserItem.DuraMax;

              ClientItem.s.AddValue := UserItem.AddValue;
              ClientItem.s.AddPoint := UserItem.AddPoint;
              ClientItem.s.MaxDate := UserItem.MaxDate;
              //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);
              boFind := True;
              Break;
            end;
          end;
        end;
      end;

    end;
  end;
  if boFind then begin
    m_DefMsg := MakeDefaultMsg(SM_SENDFINDITEMINFO_OK, nLabel, 0, 0, 0);
    SendSocket(@m_DefMsg, EncodeBuffer(@ClientItem, SizeOf(TClientItem)));
  end;
end;

procedure TPlayObject.ClientMerchantDlgSelect(nParam1: Integer; sMsg: string);
var
  NPC: TNormNpc;
begin
  if {m_boDeath or}  m_boGhost then Exit; //修改死亡后可以点击NPC
  //MainOutMessage('TPlayObject.ClientMerchantDlgSelect1 ' + Trim(sMsg));
  NPC := UserEngine.FindMerchant(TObject(nParam1));
  if NPC = nil then NPC := UserEngine.FindNPC(TObject(nParam1));
  if NPC = nil then Exit;
  if ((NPC.m_PEnvir = m_PEnvir) and
    (abs(NPC.m_nCurrX - m_nCurrX) < 15) and
    (abs(NPC.m_nCurrY - m_nCurrY) < 15)) or (NPC.m_boIsHide) then
    NPC.UserSelect(Self, Trim(sMsg));
  //MainOutMessage('TPlayObject.ClientMerchantDlgSelect2 ' + Trim(sMsg));
  //if NPC <> nil then MainOutMessage('NPC <> nil ' + NPC.m_sCharName);
end;

procedure TPlayObject.ClientMerchantQuerySellPrice(nParam1, nMakeIndex: Integer;
  sMsg: string);
var
  I: Integer;
  UserItem: pTUserItem;
  UserItem18: pTUserItem;
  Merchant: TMerchant;
  sUserItemName: string;
begin
  UserItem18 := nil;
  for I := 0 to m_ItemList.Count - 1 do begin
    UserItem := m_ItemList.Items[I];
    if UserItem.MakeIndex = nMakeIndex then begin
      //取自定义物品名称
      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName = '' then
        sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

      if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        sUserItemName := '(Tie)' + sUserItemName;
      end;

      if CompareText(sUserItemName, sMsg) = 0 then begin
        UserItem18 := UserItem;
        Break;
      end;
    end;
  end;
  if UserItem18 = nil then Exit;
  Merchant := UserEngine.FindMerchant(TObject(nParam1));
  if Merchant = nil then Exit;
  if ((Merchant.m_PEnvir = m_PEnvir) and
    (Merchant.m_boSell) and
    (abs(Merchant.m_nCurrX - m_nCurrX) < 15) and
    (abs(Merchant.m_nCurrY - m_nCurrY) < 15)) then
    Merchant.ClientQuerySellPrice(Self, UserItem18);
end;

procedure TPlayObject.ClientUserChangeItem(nParam1, nMakeIndex: Integer; sItemName: string);
var
  I: Integer;
  UserItem: pTUserItem;
  Merchant: TMerchant;
  sUserItemName: string;
  n01: Integer;
begin
  n01 := -1;
  m_sUpgradeItemName := '';
  m_nUpgradeItemIndex := -1;
  m_nItemIndex := -1;
  m_boUpgradeNPC := True;
  Merchant := nil;
  Merchant := UserEngine.FindMerchant(TObject(nParam1));
  if Merchant <> nil then begin
    for I := m_ItemList.Count - 1 downto 0 do begin
      UserItem := m_ItemList.Items[I];
      if (UserItem <> nil) and (UserItem.MakeIndex = nMakeIndex) then begin
      //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName = '' then
          sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

        if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
          sUserItemName := '(绑)' + sUserItemName;
        end;

        if CompareText(sUserItemName, sItemName) = 0 then begin
          n01 := -2;
          if not CanUpgradeItem(sItemName) then begin
            n01 := 0;
            if (Merchant <> nil) and
              (Merchant.m_boUpgradeItem) and
              ((Merchant.m_PEnvir = m_PEnvir) and
              (abs(Merchant.m_nCurrX - m_nCurrX) < 15) and
              (abs(Merchant.m_nCurrY - m_nCurrY) < 15)) then begin
              m_sUpgradeItemName := sItemName;
              m_nUpgradeItemIndex := nMakeIndex;
              m_boUpgradeNPC := True;
              m_nItemIndex := I;
              n01 := 1;
            end;
          end;
          Break;
        end;
      end;
    end;
  end;
  if n01 = 1 then begin
    if (Merchant <> nil) then (Merchant.GotoLable(Self, '@ChangeItems', False));
    //MainOutMessage('Merchant.GotoLable '+sItemName);
  end else begin
    m_sUpgradeItemName := '';
    m_nUpgradeItemIndex := -1;
    m_nItemIndex := -1;
    m_boUpgradeNPC := True;
    SendChangeItemFail();
  end;
  if n01 = -1 then begin
    if (Merchant <> nil) then Merchant.SendMsgToUser(Self, 'package is not found.. the item!');
  end else
    if n01 = -2 then begin
    if (Merchant <> nil) then Merchant.SendMsgToUser(Self, 'The items are prohibited to upgrade!');
  end else begin

  end;
end;

procedure TPlayObject.ClientUserSellOffItem(nParam1, nMakeIndex: Integer; sMsg: string); //拍卖
var
  I: Integer;
  UserItem: pTUserItem;
  Merchant: TMerchant;
  sUserItemName: string;
  sSellGold: string;
  nSellGold: Integer;
  SellOffInfo: pTSellOffInfo;
  StdItem: pTStdItem;
begin
  //清除复制装备
  ClearCopyItems();
  for I := m_ItemList.Count - 1 downto 0 do begin
    UserItem := m_ItemList.Items[I];
    if (UserItem <> nil) and (UserItem.MakeIndex = nMakeIndex) then begin
      //取自定义物品名称

      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName = '' then
        sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

      if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        sUserItemName := '(Tie)' + sUserItemName;
      end;

      sMsg := GetValidStr3(sMsg, sSellGold, ['/']);
      nSellGold := Str_ToInt(sSellGold, -1);

      if CompareText(sUserItemName, sMsg) = 0 then begin
        Merchant := UserEngine.FindMerchant(TObject(nParam1));
        if (Merchant <> nil) and
          (Merchant.m_boSellOff) and
          ((Merchant.m_PEnvir = m_PEnvir) and
          (abs(Merchant.m_nCurrX - m_nCurrX) < 15) and
          (abs(Merchant.m_nCurrY - m_nCurrY) < 15)) and (nSellGold > 0) then begin
          if Merchant.ClientSellOffItem(Self, UserItem, nSellGold) then begin
            StdItem := UserEngine.GetStdItem(UserItem.wIndex);
            if StdItem <> nil then begin
              if StdItem.NeedIdentify = 1 then
                AddGameDataLog('32' + #9 +
                  m_sMapName + #9 +
                  IntToStr(m_nCurrX) + #9 +
                  IntToStr(m_nCurrY) + #9 +
                  m_sCharName + #9 +
              //UserEngine.GetStdItemName(m_UseItems[I].wIndex) + #9 +
                  StdItem.Name + #9 +
                  IntToStr(UserItem.MakeIndex) + #9 +
                  BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
                  Merchant.m_sCharName);
            end;

            if UserItem.btValue[13] = 1 then begin
              ItemUnit.DelCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
              UserItem.btValue[13] := 0;
            end;
            m_ItemList.Delete(I);
            Dispose(UserItem);
            WeightChanged();
          end;
        end;
        Break;
      end;
    end;
  end;
end;

procedure TPlayObject.ClientBuySellOffItem(sItemName: string; nInt: Integer);
var
  bo29: Boolean;
  List20: TSellList;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
  n1C, nPrice: Integer;
  sUserItemName: string;
  SellOffInfo: pTSellOffInfo;
  AddSellOffInfo: pTSellOffInfo;
  OnlinePlayObject: TPlayObject;
begin
  //n1C = 1 物品已经拍卖了  n1C = 2 无法携带更多的物品 n1C = 3 没有足够的元宝购买物品
  n1C := 1;
  bo29 := False;
  SellOffInfo := nil;
  if (g_SellList.GetItem(nInt, sItemName, SellOffInfo) >= 0) and (SellOffInfo <> nil) then begin
    StdItem := UserEngine.GetStdItem(SellOffInfo.UserItem.wIndex);
    if StdItem <> nil then begin
      if IsAddWeightAvailable(StdItem.Weight) then begin
      //MainOutMessage('SellOffInfo.sCharName '+SellOffInfo.sCharName);
        if CompareText(m_sCharName, SellOffInfo.sCharName) = 0 then begin
          if IsEnoughBag then begin

            if (SellOffInfo.UserItem.AddValue[0] = 1) and (GetDayCount(SellOffInfo.UserItem.MaxDate, Now) <= 0) then begin //删除到期装备
              g_SellList.Delete(SellOffInfo, True);
            end else begin
              New(UserItem);
              UserItem^ := SellOffInfo.UserItem;
              if g_SellList.Delete(SellOffInfo, True) then begin
                m_ItemList.Add(UserItem);
                SendAddItem(UserItem);
                bo29 := True;
                n1C := 0;

                if StdItem.NeedIdentify = 1 then
                  AddGameDataLog('33' + #9 +
                    m_sMapName + #9 +
                    IntToStr(m_nCurrX) + #9 +
                    IntToStr(m_nCurrY) + #9 +
                    m_sCharName + #9 +
              //UserEngine.GetStdItemName(m_UseItems[I].wIndex) + #9 +
                    StdItem.Name + #9 +
                    IntToStr(UserItem.MakeIndex) + #9 +
                    BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
                    '0');
              end else begin
                Dispose(UserItem);
              end;
            end;

          end else begin
            n1C := 2;
          end;

        end else
          if (m_nGold >= SellOffInfo.nSellGold) and (SellOffInfo.nSellGold > 0) then begin
          if IsEnoughBag then begin

            if (SellOffInfo.UserItem.AddValue[0] = 1) and (GetDayCount(SellOffInfo.UserItem.MaxDate, Now) <= 0) then begin //删除到期装备
              g_SellList.Delete(SellOffInfo, True);
            end else begin
              New(AddSellOffInfo);
              Move(SellOffInfo^, AddSellOffInfo^, SizeOf(TSellOffInfo));
            //AddSellOffInfo^ := SellOffInfo^;
              if g_SellList.Delete(SellOffInfo, True) then begin
                if g_GoldList.Add(AddSellOffInfo) then begin
                  New(UserItem);
                  UserItem^ := AddSellOffInfo.UserItem;
                  AddItemToBag(UserItem);
                  SendAddItem(UserItem);
                  Dec(m_nGold, AddSellOffInfo.nSellGold);
                  GoldChanged;
                  OnlinePlayObject := UserEngine.GetPlayObject(AddSellOffInfo.sCharName);
                  if OnlinePlayObject <> nil then begin
                    OnlinePlayObject.SysMsg(m_sCharName + ' Buy your ' + sItemName, c_Red, t_Hint);
                  end;
                  n1C := 0;
                  if StdItem.NeedIdentify = 1 then
                    AddGameDataLog('33' + #9 +
                      m_sMapName + #9 +
                      IntToStr(m_nCurrX) + #9 +
                      IntToStr(m_nCurrY) + #9 +
                      m_sCharName + #9 +
              //UserEngine.GetStdItemName(m_UseItems[I].wIndex) + #9 +
                      StdItem.Name + #9 +
                      IntToStr(UserItem.MakeIndex) + #9 +
                      BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
                      '0');
                  if g_boGameLogGameGold then begin
                    AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEGOLD,
                      m_sMapName,
                        m_nCurrX,
                        m_nCurrY,
                        m_sCharName,
                        g_Config.sGameGoldName,
                        AddSellOffInfo.nSellGold,
                        '-',
                        m_sCharName]));
                  end;
                end else g_SellList.Add(AddSellOffInfo);
              end else Dispose(AddSellOffInfo);
            end;

          end else n1C := 2;
        end else n1C := 3;
        bo29 := True;
      end else n1C := 2;
    end;
  end;
  if n1C = 0 then begin
    SendMsg(Self, RM_SENDBUYSELLOFFITEM_OK, 0, m_nGold, nInt, 0, '');
  end else begin
    SendMsg(Self, RM_SENDBUYSELLOFFITEM_FAIL, 0, n1C, 0, 0, '');
  end;
end;

procedure TPlayObject.ClientUserGetSellOffGold(nInt: Integer; sMsg: string); //取款
var
  nSellGold: Integer;
  nRate: Integer;
  nSellGoldCount: Integer;
  nRateCount: Integer;
  s1C: string;
  SellOffInfo: pTSellOffInfo;
  n18: Integer;
  nIndex: Integer;
  nIndex1: Integer;
  List20: TSellList;
begin
  nSellGoldCount := 0;
  nRateCount := 0;
  s1C := '';
  nIndex := g_GoldList.GetGoldListByCharName(m_sCharName, List20);
  if nIndex >= 0 then begin
    nIndex1 := List20.GetItem(nInt, sMsg, SellOffInfo);
    if nIndex1 >= 0 then begin
      if g_Config.nUserSellOffTax > 0 then begin
        nRate := SellOffInfo.nSellGold * g_Config.nUserSellOffTax div 100;
        nSellGold := SellOffInfo.nSellGold - nRate;
      end else begin
        nSellGold := SellOffInfo.nSellGold;
        nRate := 0;
      end;
      if List20.m_SellEngine.UpDate(SellOffInfo, False) then begin
        s1C := 'Goods:' + UserEngine.GetStdItemName(SellOffInfo.UserItem.wIndex) + ' Amount:' + IntToStr(nSellGold) + ' Tax:' + IntToStr(nRate) + g_Config.sGameGoldName + ' Date:' + DateTimeToStr(SellOffInfo.dSellDateTime);
        SysMsg(s1C, c_Green, t_Hint);
        Inc(m_nGold, nSellGold);
        GoldChanged;
        if g_boGameLogGameGold then begin
          AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEGOLD,
            m_sMapName,
              m_nCurrX,
              m_nCurrY,
              m_sCharName,
              g_Config.sGameGoldName,
              nSellGold,
              '+',
              m_sCharName]));
        end;

        List20.Delete(nIndex1);
        Dispose(SellOffInfo);
        if List20.Count <= 0 then begin
          List20.Free;
          g_GoldList.Delete(nIndex);
        end;
      end;
    end;
  end;
end;

procedure TPlayObject.ClientUserBuySellOffItem(nIdent, nParam1, nInt, nZz: Integer; sMsg: string); //购买寄售物品
var
  Merchant: TMerchant;
begin
  try
    if m_boDealing or m_boDueling then Exit;
    if not m_boHeroVersion then begin
      Merchant := UserEngine.FindMerchant(TObject(nParam1));
      if (Merchant = nil) or
        (not Merchant.m_boBuyOff) or
        (Merchant.m_PEnvir <> m_PEnvir) or
        (abs(Merchant.m_nCurrX - m_nCurrX) > 15) or
        (abs(Merchant.m_nCurrY - m_nCurrY) > 15) then Exit;
      if nIdent = CM_SENDBUYSELLOFFITEM then begin //购买寄售物品
        Merchant.ClientBuySellOffItem(Self, sMsg, nInt);

  //清除复制装备
        ClearCopyItems();
      end;
      //MainOutMessage('ClientUserBuySellOffItem nZz:' + IntToStr(nZz) + ' nInt:' + IntToStr(nInt));
      if nIdent = CM_SENDQUERYSELLOFFITEM then begin //发送寄售物品列表
        Merchant.ClientGetDetailSellGoodsList(Self, sMsg, nZz);
      end;
    end else begin
      if nIdent = CM_SENDBUYSELLOFFITEM then begin //购买寄售物品
        ClientBuySellOffItem(sMsg, nInt);
  //清除复制装备
        ClearCopyItems();
      end;
    end;
  except
    on E: Exception do begin
      MainOutMessage('TPlayObject::ClientUserBuySellOffItem wIdent = ' + IntToStr(nIdent));
      MainOutMessage(E.Message);
    end;
  end;
end;

procedure TPlayObject.ClientUserSellItem(nParam1, nMakeIndex: Integer; sMsg: string);
var
  I: Integer;
  UserItem: pTUserItem;
  Merchant: TMerchant;
  sUserItemName: string;
begin
  //清除复制装备
  ClearCopyItems();
  for I := m_ItemList.Count - 1 downto 0 do begin
    UserItem := m_ItemList.Items[I];
    if (UserItem <> nil) and (UserItem.MakeIndex = nMakeIndex) then begin
      //取自定义物品名称
      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName = '' then
        sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

      if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        sUserItemName := '( )' + sUserItemName;
      end;

      if CompareText(sUserItemName, sMsg) = 0 then begin
        Merchant := UserEngine.FindMerchant(TObject(nParam1));
        if (Merchant <> nil) and
          (Merchant.m_boSell) and
          ((Merchant.m_PEnvir = m_PEnvir) and
          (abs(Merchant.m_nCurrX - m_nCurrX) < 15) and
          (abs(Merchant.m_nCurrY - m_nCurrY) < 15)) then begin
          if Merchant.ClientSellItem(Self, UserItem) then begin
            if UserItem.btValue[13] = 1 then begin
              ItemUnit.DelCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
              UserItem.btValue[13] := 0;
            end;
            m_ItemList.Delete(I);
            WeightChanged();
          end;
        end;
        Break;
      end;
    end;
  end; // for
end;

procedure TPlayObject.ClientUserBuyItem(nIdent, nParam1, nInt, nZz: Integer; sMsg: string);
var
  Merchant: TMerchant;
begin
  try
    if m_boDealing or m_boDueling then Exit;
    Merchant := UserEngine.FindMerchant(TObject(nParam1));
    if (Merchant = nil) or
      ((not Merchant.m_boBuy) and (not Merchant.m_boBuyOff)) or
      (Merchant.m_PEnvir <> m_PEnvir) or
      (abs(Merchant.m_nCurrX - m_nCurrX) > 15) or
      (abs(Merchant.m_nCurrY - m_nCurrY) > 15) then Exit;

    if nIdent = CM_USERBUYITEM then begin
      Merchant.ClientBuyItem(Self, sMsg, nInt);
    //清除复制装备
      ClearCopyItems();
    end;
    //MainOutMessage('ClientUserBuyItem nZz:' + IntToStr(nZz) + ' nInt:' + IntToStr(nInt));
    if nIdent = CM_USERGETDETAILITEM then begin
      if Merchant.m_boBuyOff then begin
        Merchant.ClientGetDetailSellGoodsList(Self, sMsg, nZz);
      end else begin
        Merchant.ClientGetDetailGoodsList(Self, sMsg, nZz);
      end;
    end;
  except
    on E: Exception do begin
      MainOutMessage('TPlayObject::ClientUserBuyItem wIdent = ' + IntToStr(nIdent));
      MainOutMessage(E.Message);
    end;
  end;
end;

function TPlayObject.ClientDropGold(nGold: Integer): Boolean;
begin
  Result := False;
  if g_Config.boInSafeDisableDrop and InSafeZone then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sCanotDropInSafeZoneMsg);
    Exit;
  end;

  if g_Config.boControlDropItem and (nGold < g_Config.nCanDropGold) then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sCanotDropGoldMsg);
    Exit;
  end;

  if not m_boCanDrop then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sCanotDropItemMsg);
    Exit;
  end;
  if nGold >= m_nGold then Exit;
  Dec(m_nGold, nGold);
  if not DropGoldDown(nGold, False, nil, Self) then Inc(m_nGold, nGold);
  GoldChanged();
  Result := True;
end;

function TPlayObject.ClientDropItem(sItemName: string;
  nItemIdx: Integer): Boolean;
  function FindBagItem(UserItem: pTUserItem): Boolean;
  var
    I: Integer;
  begin
    Result := False;
    for I := 0 to m_ItemList.Count - 1 do begin
      if UserItem = m_ItemList.Items[I] then begin
        Result := True;
        break;
      end;
    end;
  end;
var
  I: Integer;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
  sUserItemName: string;
  sCheckItemName: string;
begin
  Result := False;
  {if not m_boClientFlag then begin
    if m_nStep = 8 then Inc(m_nStep)
    else m_nStep := 0;
  end;}

  //清除复制装备
  ClearCopyItems();

  if g_Config.boInSafeDisableDrop and InSafeZone then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sCanotDropInSafeZoneMsg);
    Exit;
  end;

  if not m_boCanDrop then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sCanotDropItemMsg);
    Exit;
  end;

  if Pos(' ', sItemName) > 0 then begin //折分物品名称(信件物品的名称后面加了使用次数)
    GetValidStr3(sItemName, sItemName, [' ']);
  end;

  if (GetTickCount - m_DealLastTick) > 3000 then begin
    for I := m_ItemList.Count - 1 downto 0 do begin
      if m_ItemList.Count <= 0 then Break;
      UserItem := m_ItemList.Items[I];
      if (UserItem <> nil) and (UserItem.MakeIndex = nItemIdx) then begin

        if (UserItem.AddValue[0] = 1) and (GetDayCount(UserItem.MaxDate, Now) <= 0) then begin //删除到期装备
          m_ItemList.Delete(I);
          Dispose(UserItem);
          Result := True;
          Break;
        end;

        StdItem := UserEngine.GetStdItem(UserItem.wIndex);
        if StdItem = nil then Continue;
        //sItem:=UserEngine.GetStdItemName(UserItem.wIndex);
        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName = '' then
          sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

        if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
          sUserItemName := '( )' + sUserItemName;
        end;

        if CompareText(sUserItemName, sItemName) = 0 then begin
          if g_Config.boBindItemNoDrop and (CheckItemBindUse(UserItem, True) = 2) then Break; //绑定物品禁止扔

          if Assigned(PlugInEngine.CheckCanDropItem) then begin
            sCheckItemName := StdItem.Name;
            if not PlugInEngine.CheckCanDropItem(Self, PChar(sCheckItemName), True) then Break;
          end;

          if g_Config.boStartMapEvent and (m_btRaceServer = RC_PLAYOBJECT) and StartMapEventOfDropItem(sUserItemName) then begin //扔物品事件
            //DelBagItem(UserItem);
            if FindBagItem(UserItem) then begin
              m_ItemList.Delete(I);
              Dispose(UserItem);
            end;
            Result := True;
            Break;
          end;

          if g_Config.boControlDropItem and (StdItem.Price < g_Config.nCanDropPrice) then begin
            m_ItemList.Delete(I);
            Dispose(UserItem);

            Result := True;
            Break;
          end;

          if DropItemDown(UserItem, 1, False, nil, Self) then begin
            m_ItemList.Delete(I);
            Dispose(UserItem);
            Result := True;
            Break;
          end;

        end;
      end;
    end;
    if Result then WeightChanged();
  end;
end;

procedure TPlayObject.GoldChange(sChrName: string; nGold: Integer);
var
  s10, s14: string;
begin
  if nGold > 0 then begin
    s10 := '14';
    s14 := 'To cut.';
  end else begin
    s10 := '13';
    s14 := 'To cut';
  end;
  SysMsg(sChrName + ' Gold coins ' + IntToStr(nGold) + ' Gold' + s14, c_Green, t_Hint);
  if g_boGameLogGold then
    AddGameDataLog(s10 + #9 +
      m_sMapName + #9 +
      IntToStr(m_nCurrX) + #9 +
      IntToStr(m_nCurrY) + #9 +
      m_sCharName + #9 +
      sSTRING_GOLDNAME + #9 +
      IntToStr(nGold) + #9 +
      '1' + #9 +
      sChrName);
end;

procedure TPlayObject.ClearStatusTime;
begin
  FillChar(m_wStatusTimeArr, SizeOf(TStatusTime), #0);
end;

procedure TPlayObject.SendMapDescription;
var
  nMUSICID: Integer;
  sMUSINAME: string;
begin
  nMUSICID := -1;
  if not m_boHeroVersion then begin
    if m_PEnvir.m_boMUSIC then
      nMUSICID := m_PEnvir.m_nMUSICID;
    SendDefMessage(SM_MAPDESCRIPTION, nMUSICID, 0, 0, 0, m_PEnvir.sMapDesc);
  end else begin
    if m_PEnvir.m_boMUSIC then begin
      nMUSICID := m_PEnvir.m_nMUSICID;
      sMUSINAME := m_PEnvir.m_sMUSICName;
    end;
    if nMUSICID >= 0 then begin
      SendDefMessage(SM_MAPDESCRIPTION, nMUSICID, 0, 0, 0, m_PEnvir.sMapDesc);
    end else begin
      SendDefMessage(SM_MAPDESCRIPTION, -1, 0, 0, 0, m_PEnvir.sMapDesc + #13 + sMUSINAME);
    end;
  end;
  if not m_PEnvir.m_boHorse then begin //不允许骑马
    if m_boOnHorse then begin
      m_boOnHorse := not m_boOnHorse;
      FeatureChanged();
    end;
  end;
  if m_PEnvir.m_boNoRecallHero then begin //不允许召唤英雄
    if m_MyHero <> nil then begin
      THeroObject(m_MyHero).LogOut;

    end;
    SysMsg(g_sNoRecallHero, c_Green, t_Hint);
  end;

  //发送雪
  SendDefMessage(SM_SENDSNOW, m_PEnvir.m_nSNOWLEVEL, Integer(m_PEnvir.m_boSNOW), 0, 0, '');

  SendDefMessage(SM_SENDRAIN, m_PEnvir.m_nRAINLEVEL, Integer(m_PEnvir.m_boRAIN), 0, 0, '');
end;

procedure TPlayObject.SendNotice;
var
  LoadList: TStringList;
  I: Integer;
  sNoticeMsg: string;
begin
  LoadList := TStringList.Create;
  NoticeManager.GetNoticeMsg('Notice', LoadList);
  sNoticeMsg := '';
  for I := 0 to LoadList.Count - 1 do begin
    sNoticeMsg := sNoticeMsg + LoadList.Strings[I] + #$20#$1B;
  end;
  LoadList.Free;
  SendDefMessage(SM_SENDNOTICE, 2000, 0, 0, 0, sNoticeMsg);
end;

procedure TPlayObject.UserLogon;
var
  I: Integer;
  II: Integer;
  UserItem: pTUserItem;
  UserItem1: pTUserItem;
  StdItem: pTStdItem;
  s14: string;
  sItem: string;
  sIPaddr: string;

  wErrorCode: Word;
  PlayObject: TPlayObject;

  resourcestring
  sExceptionMsg = '[Exception] TPlayObject::UserLogon';
  sCheckIPaddrFail = 'Log IP addresses do not match!';
begin
  sIPaddr := '';
  try
    if g_Config.boTestServer then begin
      if m_Abil.Level < g_Config.nTestLevel then m_Abil.Level := g_Config.nTestLevel;
      if m_nGold < g_Config.nTestGold then m_nGold := g_Config.nTestGold;
    end;
    if g_Config.boTestServer or (g_Config.boServiceMode) then m_nPayMent := 3;
    m_dwMapMoveTick := GetTickCount();
    m_dLogonTime := Now();
    m_dwLogonTick := GetTickCount();

    if not m_boAI then Initialize();

    SendMsg(Self, RM_LOGON, 0, 0, 0, 0, '');
    if m_Abil.Level <= 7 then begin
      if GetRangeHumanCount >= 80 then begin
        MapRandomMove(m_PEnvir.sMapName, 0);
      end;
    end;

    if m_boDieInFight3Zone then begin
      MapRandomMove(m_PEnvir.sMapName, 0);
    end;

    if UserEngine.GetHumPermission(m_sCharName, sIPaddr, m_btPermission) then begin
{$IF VEROWNER = WL}
      if not CompareIPaddr(m_sIPaddr, sIPaddr) then begin
        SysMsg(sCheckIPaddrFail, c_Red, t_Hint);
        m_boEmergencyClose := True;
        m_boPlayOffLine := False;
      end;
{$IFEND}
    end;

    GetStartPoint();

    for I := 0 to m_MagicList.Count - 1 do begin
      if m_MagicList.Count <= 0 then Break;
      if pTUserMagic(m_MagicList.Items[I]) <> nil then
        sub_4C713C(pTUserMagic(m_MagicList.Items[I]));
    end;

    //给新人增加新人物品
    if m_boNewHuman then begin
      New(UserItem);
      if UserEngine.CopyToUserItemFromName(g_Config.sCandle, UserItem) then begin
        m_ItemList.Add(UserItem);
      end else Dispose(UserItem);
      New(UserItem);
      if UserEngine.CopyToUserItemFromName(g_Config.sBasicDrug, UserItem) then begin
        m_ItemList.Add(UserItem);
      end else Dispose(UserItem);
      New(UserItem);
      if UserEngine.CopyToUserItemFromName(g_Config.sWoodenSword, UserItem) then begin
        m_ItemList.Add(UserItem);
      end else Dispose(UserItem);

      New(UserItem);
      if m_btGender = 0 then
        sItem := g_Config.sClothsMan
      else
        sItem := g_Config.sClothsWoman;

      if UserEngine.CopyToUserItemFromName(sItem, UserItem) then begin
        m_ItemList.Add(UserItem);
      end else Dispose(UserItem);
    end;

    //检查背包中的物品是否合法
    for I := m_ItemList.Count - 1 downto 0 do begin
      if m_ItemList.Count <= 0 then Break;
      UserItem := m_ItemList.Items[I];
      if UserEngine.GetStdItemName(UserItem.wIndex) = '' then begin
        Dispose(pTUserItem(m_ItemList.Items[I]));
        m_ItemList.Delete(I);
      end;
    end;

    //检查人物身上的物品是否符合使用规则
    if g_Config.boCheckUserItemPlace then begin
      for I := Low(THumanUseItems) to High(THumanUseItems) do begin
        if m_UseItems[I].wIndex > 0 then begin
          StdItem := UserEngine.GetStdItem(m_UseItems[I].wIndex);
          if StdItem <> nil then begin
            if not CheckUserItems(I, StdItem) then begin
              New(UserItem);
              UserItem^ := m_UseItems[I];
              if not AddItemToBag(UserItem) then begin
                m_ItemList.Insert(0, UserItem);
              end;
              m_UseItems[I].wIndex := 0;
            end;
          end else m_UseItems[I].wIndex := 0;
        end;
      end;
    end;

  //清除复制装备
    ClearCopyItems();

    for I := Low(m_dwStatusArrTick) to High(m_dwStatusArrTick) do begin
      if m_wStatusTimeArr[I] > 0 then
        m_dwStatusArrTick[I] := GetTickCount();
    end;

    m_nCharStatus := GetCharStatus();
    RecalcLevelAbilitys();
    RecalcAbilitys();
    m_Abil.MaxExp := GetLevelExp(m_Abil.Level);
    if btB2 = 0 then begin
      m_nPkPoint := 0;
      Inc(btB2);
    end;
    if (m_nGold > g_Config.nHumanMaxGold * 2) and (g_Config.nHumanMaxGold > 0) then m_nGold := g_Config.nHumanMaxGold * 2;
    if not bo6AB then begin
      if (m_nSoftVersionDate <> g_Config.nSoftVersionDate) then begin
        SysMsg(sClientSoftVersionError, c_Red, t_Hint);
        SysMsg(sDownLoadNewClientSoft, c_Red, t_Hint);
        SysMsg(sForceDisConnect, c_Red, t_Hint);
        m_boEmergencyClose := True;
        m_boPlayOffLine := False;
        Exit;
      end;
      if (not m_boHeroVersion) and g_Config.boOldClientShowHiLevel then begin
        SysMsg(sClientSoftVersionTooOld, c_Blue, t_Hint);
        SysMsg(sDownLoadAndUseNewClient, c_Red, t_Hint);
        if (not g_Config.boCanOldClientLogon) then begin
          SysMsg(sClientSoftVersionError, c_Red, t_Hint);
          SysMsg(sDownLoadNewClientSoft, c_Red, t_Hint);
          SysMsg(sForceDisConnect, c_Red, t_Hint);
          m_boEmergencyClose := True;
          m_boPlayOffLine := False;
          Exit;
        end;
      end;
      case m_btAttatckMode of
        HAM_ALL: SysMsg(sAttackModeOfAll, c_Green, t_Hint); //[攻击模式: 全体攻击]
        HAM_PEACE: SysMsg(sAttackModeOfPeaceful, c_Green, t_Hint); //[攻击模式: 和平攻击]
        HAM_DEAR: SysMsg(sAttackModeOfDear, c_Green, t_Hint); //[攻击模式: 和平攻击]
        HAM_MASTER: SysMsg(sAttackModeOfMaster, c_Green, t_Hint); //[攻击模式: 和平攻击]
        HAM_GROUP: SysMsg(sAttackModeOfGroup, c_Green, t_Hint); //[攻击模式: 编组攻击]
        HAM_GUILD: SysMsg(sAttackModeOfGuild, c_Green, t_Hint); //[攻击模式: 行会攻击]
        HAM_PKATTACK: SysMsg(sAttackModeOfRedWhite, c_Green, t_Hint); //[攻击模式: 红名攻击]
      end;
      SysMsg(sStartChangeAttackModeHelp, c_Green, t_Hint); //使用组合快捷键 CTRL-H 更改攻击...
      if g_Config.boTestServer then
        SysMsg(sStartNoticeMsg, c_Green, t_Hint); //欢迎进入本服务器进行游戏...
      if UserEngine.PlayObjectCount > g_Config.nTestUserLimit then begin
        if m_btPermission < 2 then begin
          SysMsg(sOnlineUserFull, c_Red, t_Hint);
          SysMsg(sForceDisConnect, c_Red, t_Hint);
          m_boEmergencyClose := True;
          m_boPlayOffLine := False;
        end;
      end;
    end;
    m_nBright := g_nGameTime;
    m_Abil.MaxExp := GetLevelExp(m_Abil.Level); //jacky 2004/09/15 登录重新取得升级所需经验值
    SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
    SendMsg(Self, RM_SUBABILITY, 0, 0, 0, 0, '');
    SendMsg(Self, RM_ADJUST_BONUS, 0, 0, 0, 0, '');
    SendMsg(Self, RM_DAYCHANGING, 0, 0, 0, 0, '');
    SendMsg(Self, RM_SENDUSEITEMS, 0, 0, 0, 0, '');
    SendMsg(Self, RM_SENDMYMAGIC, 0, 0, 0, 0, '');

    if m_MyHero <> nil then begin //发送英雄信息 人物是离线挂机状态
      SendDelayMsg(Self, RM_REFHEROLOGON, 0, 0, 0, 0, '', 1000);
      //THeroObject(m_MyHero).LogOn();
      //THeroObject(m_MyHero).ClientQueryBagItems;
      {SendDelayMsg(m_MyHero, RM_RECALLHERO, 0, Integer(m_MyHero), 0, 0, '', 1000);

      m_MyHero.SendDelayMsg(m_MyHero, RM_SENDUSEITEMS, 0, 0, 0, 0, '', 1200); //发送英雄身上装备
      m_MyHero.SendDelayMsg(m_MyHero, RM_SENDMYMAGIC, 0, 0, 0, 0, '', 1200); //发送英雄使用魔法
      //THeroObject(m_MyHero).ClientQueryBagItems; //发送英雄包裹
      m_MyHero.SendDelayMsg(m_MyHero, RM_QUERYHEROBAGCOUNT, 0, THeroObject(m_MyHero).m_nItemBagCount, 0, 0, '', 1400); //发送英雄包裹容量

      m_MyHero.SendDelayMsg(m_MyHero, RM_ABILITY, 0, 0, 0, 0, '', 1400); //发送英雄属性
      m_MyHero.SendDelayMsg(m_MyHero, RM_SUBABILITY, 0, 0, 0, 0, '', 1400); //发送英雄属性
      m_MyHero.SendDelayMsg(m_MyHero, RM_QUERYHEROBAG, 0, 0, 0, 0, '', 2600); //发送英雄包裹
      m_MyHero.SendMsg(m_MyHero, RM_FIRDRAGONPOINT, g_Config.nMaxFirDragonPoint, THeroObject(m_MyHero).m_nFirDragonPoint, 0, 0, '');
      case THeroObject(m_MyHero).m_btStatus of
        0: m_MyHero.SysMsg('状态：跟随', c_Green, t_Hint);
        1: m_MyHero.SysMsg('状态：攻击', c_Green, t_Hint);
        2: m_MyHero.SysMsg('状态：休息', c_Green, t_Hint);
      end;
      m_MyHero.SysMsg(g_sHeroLoginMsg, c_Green, t_Hint); }
    end;

    if m_NewStatus <> sNone then begin
      SetNewStatus(m_NewStatus);
    end;

    m_MyGuild := g_GuildManager.MemberOfGuild(m_sCharName);
    if m_MyGuild <> nil then begin
      m_sGuildRankName := TGUild(m_MyGuild).GetRankName(Self, m_nGuildRankNo);
      for I := 0 to TGUild(m_MyGuild).GuildWarList.Count - 1 do begin
        SysMsg(TGUild(m_MyGuild).GuildWarList.Strings[I] + ' is in the War list.', c_Green, t_Hint);
      end;
    end;

    RefShowName();

    if (m_nPayMent = 1) then begin
      if not bo6AB then SysMsg(sYouNowIsTryPlayMode, c_Red, t_Hint);
      //    m_nGoldMax:=100000;
      m_nGoldMax := g_Config.nHumanTryModeMaxGold;
      if m_Abil.Level > g_Config.nTryModeLevel then begin
        SysMsg('TestMode can be used up to level  ' + IntToStr(g_Config.nTryModeLevel), c_Red, t_Hint);
        SysMsg('See www.legendofmirheroes.com for details', c_Red, t_Hint);
        m_boEmergencyClose := True;
        m_boPlayOffLine := False;
      end;
    end;

    if (m_nPayMent = 3) and not bo6AB then
      SysMsg(g_sNowIsFreePlayMode {'当前服务器运行于测试模式.'}, c_Green, t_Hint);
    if g_Config.boVentureServer then
      SysMsg('Server is in Venture Mode.', c_Green, t_Hint);

    if (m_MagicErgumSkill <> nil) and (not m_boUseThrusting) then begin
      m_boUseThrusting := True;
      SendSocket(nil, '+LNG/' + IntToStr(GetTickCount));
    end;

    if m_PEnvir.m_boNORECONNECT then
      MapRandomMove(m_PEnvir.sNoReconnectMap, 0);

    if CheckDenyLogon() then begin //如果人物在禁止登录列表里则直接掉线而不执行下面内容
      m_boEmergencyClose := True; //2006-11-18 修正禁止人物登陆不下线的问题
      m_boPlayOffLine := False;
      Exit;
    end;

    //if m_nSoftVersionDateEx <> 100000000 then begin
    if not m_boHeroVersion then begin
      SysMsg(sClientSoftVersionTooOld, c_Red, t_Hint);
      SysMsg(sDownLoadAndUseNewClient, c_Red, t_Hint);
      if g_Config.boCheckClientVersion then begin
        SysMsg(sForceDisConnect, c_Red, t_Hint);
        m_boEmergencyClose := True;
        m_boPlayOffLine := False;
        Exit;
      end;
    end;

    {==============================================================================}
    for I := 0 to m_DynamicVarList.Count - 1 do begin //清除离线人物的变量错误
      Dispose(pTDynamicVar(m_DynamicVarList.Items[I]));
    end;
    m_DynamicVarList.Clear;

    m_boRemoteMsg := False; //是否允许接受消息

    m_boStartAutoAddExpPoint := False; //是否开始增加经验
    m_dwStartNotOnlineAddExpTime := 0; //离线挂机开始时间
    m_dwNotOnlineAddExpTime := 0; //离线挂机时长
    m_nNotOnlineAddExpPoint := 0; //离线挂机每分钟增加经验值
    m_dwAutoAddExpPointTick := GetTickCount;
    m_dwAutoAddExpPointTimeTick := GetTickCount;
    m_sAutoSendMsg := ''; //自动回复信息
    m_boKickAutoAddExpUser := False;

    //MainOutMessage(m_sCharName + ' TPlayObject::m_WAbil.Level0:' + IntToStr(m_WAbil.Level));

    if g_ManageNPC <> nil then begin
      m_nScriptGotoCount := 0;
      g_ManageNPC.GotoLable(Self, '@Login', False);
    end;
    //MainOutMessage(m_sCharName + ' TPlayObject::m_WAbil.Level0:' + IntToStr(m_WAbil.Level));

    m_boFixedHideMode := False; //01/21 增加

    // PlayObject.Create 过程里被置为True，在执行完登录脚本后再置False
    if m_sDearName <> '' then CheckMarry();
    CheckMaster();

    SendMsg(Self, RM_USERCASTLE, 0, GetObjectCastle, 0, 0, ''); //发送 沙行会状态

    m_boFilterSendMsg := GetDisableSendMsgList(m_sCharName);

    //密码保护系统
    if g_Config.boPasswordLockSystem then begin
      if m_boPasswordLocked then begin
        m_boCanGetBackItem := not g_Config.boLockGetBackItemAction;
      end;

      if g_Config.boLockHumanLogin and m_boLockLogon and m_boPasswordLocked then begin
        m_boCanDeal := not g_Config.boLockDealAction;
        m_boCanDrop := not g_Config.boLockDropAction;
        m_boCanUseItem := not g_Config.boLockUserItemAction;
        m_boCanWalk := not g_Config.boLockWalkAction;
        m_boCanRun := not g_Config.boLockRunAction;
        m_boCanHit := not g_Config.boLockHitAction;
        m_boCanSpell := not g_Config.boLockSpellAction;
        m_boCanSendMsg := not g_Config.boLockSendMsgAction;
        m_boObMode := g_Config.boLockInObModeAction;
        //m_boAdminMode := g_Config.boLockInObModeAction;
        SysMsg(g_sActionIsLockedMsg + ' Command: @' + g_GameCommand.UnLock.sCmd, c_Red, t_Hint);
        SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sActionIsLockedMsg + '\ \'
          + 'Unlock command: @' + g_GameCommand.UnLock.sCmd + '\'
          + 'Lock command: @' + g_GameCommand.Lock.sCmd + '\'
          + 'Set Password command: @' + g_GameCommand.SETPASSWORD.sCmd + '\'
          + 'Change Password command: @' + g_GameCommand.CHGPASSWORD.sCmd);
      end;
(*
{$IF VEROWNER = WL}
        SysMsg(g_sActionIsLockedMsg + ' 开锁命令: @' + g_GameCommand.LOCKLOGON.sCmd, c_Red, t_Hint);
        SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sActionIsLockedMsg + '\ \'
          + '密码命令: @' + g_GameCommand.PASSWORDLOCK.sCmd);
      end;
      if not m_boPasswordLocked then begin
        SysMsg(Format(g_sPasswordNotSetMsg, [g_GameCommand.PASSWORDLOCK.sCmd]), c_Red, t_Hint);
      end;
      if not m_boLockLogon and m_boPasswordLocked then begin
        SysMsg(Format(g_sNotPasswordProtectMode, [g_GameCommand.LOCKLOGON.sCmd]), c_Red, t_Hint);
      end;
{$ELSE}
      SysMsg(g_sActionIsLockedMsg + ' 开锁命令: @' + g_GameCommand.UnLock.sCmd, c_Red, t_Hint);
      SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sActionIsLockedMsg + '\ \'
        + '开锁命令: @' + g_GameCommand.UnLock.sCmd + '\'
        + '加锁命令: @' + g_GameCommand.Lock.sCmd + '\'
        + '设置密码命令: @' + g_GameCommand.SETPASSWORD.sCmd + '\'
        + '修改密码命令: @' + g_GameCommand.CHGPASSWORD.sCmd);
    end;
{$IFEND}
*)
    end;

  //重置泡点方面计时
    m_dwIncGamePointTick := GetTickCount();
    m_dwIncGameGoldTick := GetTickCount();
    m_dwAutoGetExpTick := GetTickCount();

  except
    on E: Exception do begin
      MainOutMessage(sExceptionMsg);
      MainOutMessage(E.Message);
    end;
  end;
end;

procedure TPlayObject.ReadAllBook();
begin

end;

procedure TPlayObject.SendGoldInfo(boSendName: Boolean);
var
  sMsg: string;
begin
  //if not m_boHeroVersion then Exit;
  if boSendName then
    sMsg := g_Config.sGameGoldName + #13 + g_Config.sGamePointName;

  SendDefMessage(SM_GAMEGOLDNAME,
    m_nGameGold,
    LoWord(m_nGamePoint),
    HiWord(m_nGamePoint),
    0,
    sMsg);
end;

procedure TPlayObject.SendLogon;
var
  MessageBodyWL: TMessageBodyWL;
  nRecog: Integer;
begin
  m_DefMsg := MakeDefaultMsg(SM_LOGON, Integer(Self), m_nCurrX, m_nCurrY, MakeWord(m_btDirection, m_nLight));
  MessageBodyWL.lParam1 := GetFeatureToLong();
  MessageBodyWL.lParam2 := m_nCharStatus;
  if m_boAllowGroup then MessageBodyWL.lTag1 := MakeLong(MakeWord(1, 0), GetFeatureEx)
  else MessageBodyWL.lTag1 := 0;
  MessageBodyWL.lTag2 := 0;
  SendSocket(@m_DefMsg, EncodeBuffer(@MessageBodyWL, SizeOf(TMessageBodyWL)));

  nRecog := GetFeatureToLong();
  SendDefMessage(SM_FEATURECHANGED,
    Integer(Self),
    LoWord(nRecog),
    HiWord(nRecog),
    GetFeatureEx,
    '');
end;

procedure TPlayObject.SendServerConfig;
var
  nRecog, nParam: Integer;
  nRunHuman, nRunMon, nRunNpc, nWarRunAll: Integer;
  ServerConfig: TServerConfig;
  sMsg: string;
begin
  if not m_boHeroVersion then Exit;

  ServerConfig.nFireDelayTime := 10000;
  ServerConfig.nKTZDelayTime := g_Config.nSkill58DelayTime * 1000;
  ServerConfig.nPKJDelayTime := g_Config.nSkill43DelayTime * 1000;
  ServerConfig.nSkill50DelayTime := g_Config.nSkill50DelayTime * 1000;
  ServerConfig.nZRJFDelayTime := g_Config.nSkillZRJFDelayTime * 1000;
  ServerConfig.nGreenNumber := g_Config.nGreenNumber;
  ServerConfig.boChgSpeed := g_Config.boAllowClientChgSpeed;
  //ServerConfig.btGameOptionType := g_Config.nClientGameOptionType;
  ServerConfig.btShowClientItemStyle := g_Config.nShowClientItemStyle;
  ServerConfig.boCheckSpeedHack := g_Config.boCheckSpeedHack;
  ServerConfig.boAllowItemAddValue := g_Config.boAllowItemAddValue;
  ServerConfig.boAllowItemTime := g_Config.boAllowItemTime;
  ServerConfig.boAllowItemAddPoint := g_Config.boAllowItemAddPoint;
  ServerConfig.nMaxLevel := g_Config.nMaxLevel;


  nRunHuman := 0;
  nRunMon := 0;
  nRunNpc := 0;
  nWarRunAll := 0;

  if g_Config.boDiableHumanRun or ((m_btPermission > 9) and g_Config.boGMRunAll) or (g_Config.boSafeAreaLimited and InSafeZone) then begin
    nRunHuman := 1;
    nRunMon := 1;
    nRunNpc := 1;
    nWarRunAll := 1;
  end else begin
    if g_Config.boRUNHUMAN or m_PEnvir.m_boRUNHUMAN then nRunHuman := 1;
    if g_Config.boRUNMON or m_PEnvir.m_boRUNMON then nRunMon := 1;
    if g_Config.boRunNpc then nRunNpc := 1;
    //if g_Config.boWarDisHumRun then nWarRunAll := 1;
  end;

  sMsg := EncodeBuffer(@ServerConfig, SizeOf(TServerConfig));
  nRecog := MakeLong(MakeWord(nRunHuman, nRunMon), MakeWord(nRunNpc, nWarRunAll));
  nParam := MakeWord(5, 0);

  m_DefMsg := MakeDefaultMsg(SM_SERVERCONFIG, nRecog, nParam, 0, 0);
  SendSocket(@m_DefMsg, sMsg);
end;

procedure TPlayObject.SendServerStatus;
begin
  if m_btPermission < 10 then Exit;
  SysMsg(IntToStr(CalcFileCRC(Application.ExeName)), c_Red, t_Hint);
end;
//检查角色的座标是否在指定误差范围以内
//TargeTActorObject 为要检查的角色，nX,nY 为比较的座标
//检查角色是否在指定座标的1x1 范围以内，如果在则返回True 否则返回 False

function TActorObject.CretInNearXY(TargeTActorObject: TActorObject; nX,
  nY: Integer): Boolean;
var
  I: Integer;
  nCX, nCY: Integer;
  MapCellInfo: pTMapCellinfo;
  BaseObject: TBaseObject;
  ActorObject: TActorObject;
begin
  Result := False;
  if m_PEnvir = nil then begin
    MainOutMessage('CretInNearXY nil PEnvir');
    Exit;
  end;
  for nCX := nX - 1 to nX + 1 do begin
    for nCY := nY - 1 to nY + 1 do begin
      if m_PEnvir.GetMapCellInfo(nCX, nCY, MapCellInfo) and (MapCellInfo.ObjList <> nil) then begin
        for I := 0 to MapCellInfo.ObjList.Count - 1 do begin
          BaseObject := TBaseObject(MapCellInfo.ObjList.Items[I]);
          if BaseObject.m_ObjType = t_Actor then begin
            ActorObject := TActorObject(MapCellInfo.ObjList.Items[I]);
            if ActorObject <> nil then begin
              if (not ActorObject.m_boGhost) and (not ActorObject.m_boFixedHideMode) and (ActorObject = TargeTActorObject) then begin
                Result := True;
                Exit;
              end;
            end;
          end;
        end;
      end;
    end;
  end;
end;

function TActorObject.CretInNearXY(TargeTActorObject: TActorObject; nX,
  nY: Integer; nRange: Integer): Boolean;
var
  I: Integer;
  nCX, nCY: Integer;
  MapCellInfo: pTMapCellinfo;
  BaseObject: TBaseObject;
  ActorObject: TActorObject;
begin
  Result := False;
  if m_PEnvir = nil then begin
    MainOutMessage('CretInNearXY nil PEnvir');
    Exit;
  end;
  for nCX := nX - nRange to nX + nRange do begin
    for nCY := nY - nRange to nY + nRange do begin
      if m_PEnvir.GetMapCellInfo(nCX, nCY, MapCellInfo) and (MapCellInfo.ObjList <> nil) then begin
        for I := 0 to MapCellInfo.ObjList.Count - 1 do begin
          BaseObject := TBaseObject(MapCellInfo.ObjList.Items[I]);
          if BaseObject.m_ObjType = t_Actor then begin
            ActorObject := TActorObject(MapCellInfo.ObjList.Items[I]);
            if ActorObject <> nil then begin
              if (not ActorObject.m_boGhost) and (not ActorObject.m_boFixedHideMode) and (ActorObject = TargeTActorObject) then begin
                Result := True;
                Exit;
              end;
            end;
          end;
        end;
      end;
    end;
  end;
end;

procedure TPlayObject.SendUseitems;
var
  I: Integer;
  Item: pTStdItem;
  sSENDMSG: string;
  ClientItem: TClientItem;
  OClientItem: TOClientItem;
  StdItem: TStdItem;
  sUserItemName: string;
begin
  if Assigned(PlugInEngine.SendUseitemsMsg) then begin
    PlugInEngine.SendUseitemsMsg(Self);
  end else begin
    if m_boNotOnlineAddExp or m_boAI then Exit;
    if (not m_boHeroVersion) and (m_dwClientTick = 0) then begin
      sSENDMSG := '';
      for I := Low(THumItems) to High(THumItems) do begin
        if m_UseItems[I].wIndex > 0 then begin
          Item := UserEngine.GetStdItem(m_UseItems[I].wIndex);
          if Item <> nil then begin
            StdItem := Item^;
            ItemUnit.GetItemAddValue(@m_UseItems[I], StdItem);
            CopyStdItemToOStdItem(@StdItem, @OClientItem.s);

            //取自定义物品名称
            sUserItemName := '';
            if m_UseItems[I].btValue[13] = 1 then
              sUserItemName := ItemUnit.GetCustomItemName(m_UseItems[I].MakeIndex, m_UseItems[I].wIndex);
            if sUserItemName <> '' then
              OClientItem.s.Name := sUserItemName;

            OClientItem.Dura := m_UseItems[I].Dura;
            OClientItem.DuraMax := m_UseItems[I].DuraMax;
            OClientItem.MakeIndex := m_UseItems[I].MakeIndex;
            sSENDMSG := sSENDMSG + IntToStr(I) + '/' + EncodeBuffer(@OClientItem, SizeOf(TOClientItem)) + '/';
          end;
        end;
      end;
      if sSENDMSG <> '' then begin
        m_DefMsg := MakeDefaultMsg(SM_SENDUSEITEMS, 0, 0, 0, 0);
        SendSocket(@m_DefMsg, sSENDMSG);
      end;
    end else begin
      sSENDMSG := '';
      for I := Low(THumanUseItems) to High(THumanUseItems) do begin
        if m_UseItems[I].wIndex > 0 then begin
          //sItemNewName:=GetItemName(m_UseItems[i].MakeIndex);
          Item := UserEngine.GetStdItem(m_UseItems[I].wIndex);
          if Item <> nil then begin
            StdItem := Item^;
            ItemUnit.GetItemAddValue(@m_UseItems[I], StdItem);
            Move(StdItem, ClientItem.s, SizeOf(TStdItem));

            //取自定义物品名称
            sUserItemName := '';
            if m_UseItems[I].btValue[13] = 1 then
              sUserItemName := ItemUnit.GetCustomItemName(m_UseItems[I].MakeIndex, m_UseItems[I].wIndex);
            if sUserItemName <> '' then
              ClientItem.s.Name := sUserItemName;

            if CheckItemBindUse(@m_UseItems[I], False) = 2 then begin //绑定物品
              ClientItem.s.Name := '( )' + ClientItem.s.Name;
            end;

            ClientItem.Dura := m_UseItems[I].Dura;
            ClientItem.DuraMax := m_UseItems[I].DuraMax;
            ClientItem.MakeIndex := m_UseItems[I].MakeIndex;

            ClientItem.s.AddValue := m_UseItems[I].AddValue;
            ClientItem.s.AddPoint := m_UseItems[I].AddPoint;
            ClientItem.s.MaxDate := m_UseItems[I].MaxDate;
            //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, @m_UseItems[I]);

            sSENDMSG := sSENDMSG + IntToStr(I) + '/' + EncodeBuffer(@ClientItem, SizeOf(TClientItem)) + '/';
          end;
        end;
      end;
      if sSENDMSG <> '' then begin
        m_DefMsg := MakeDefaultMsg(SM_SENDUSEITEMS, 0, 0, 0, 0);
        SendSocket(@m_DefMsg, sSENDMSG);
      end;
    end;
  end;
end;

procedure TPlayObject.SendUseMagic;
var
  I: Integer;
  sSENDMSG: string;
  UserMagic: pTUserMagic;
  ClientMagic: TClientMagic;
begin
  if Assigned(PlugInEngine.SendUseMagicMsg) then begin
    PlugInEngine.SendUseMagicMsg(Self);
  end else begin
    if m_boNotOnlineAddExp or m_boAI then Exit;
    sSENDMSG := '';
    for I := 0 to m_MagicList.Count - 1 do begin
      UserMagic := m_MagicList.Items[I];
      if UserMagic <> nil then begin
        ClientMagic.Key := Chr(UserMagic.btKey);
        ClientMagic.Level := UserMagic.btLevel;
        ClientMagic.CurTrain := UserMagic.nTranPoint;
        ClientMagic.Def := UserMagic.MagicInfo^;
        sSENDMSG := sSENDMSG + EncodeBuffer(@ClientMagic, SizeOf(TClientMagic)) + '/';
      end;
    end;
    if sSENDMSG <> '' then begin
      m_DefMsg := MakeDefaultMsg(SM_SENDMYMAGIC, 0, 0, 0, m_MagicList.Count);
      SendSocket(@m_DefMsg, sSENDMSG);
    end;
  end;
end;

function TPlayObject.ClientChangeDir(wIdent: Word; nX, nY, nDir: Integer; var dwDelayTime: LongWord): Boolean; //4CAEB8
var
  dwCheckTime: LongWord;
begin
  Result := False;
  if m_boDeath or (m_wStatusTimeArr[POISON_STONE {5}] {0x6A} <> 0) then Exit; //防麻
  if not CheckActionStatus(wIdent, dwDelayTime) then begin
    m_boFilterAction := False;
    Exit;
  end;
  m_boFilterAction := True;
  dwCheckTime := GetTickCount - m_dwTurnTick;
  if dwCheckTime < g_Config.dwTurnIntervalTime then begin
    dwDelayTime := g_Config.dwTurnIntervalTime - dwCheckTime;
    {
    if dwCheckTime <= g_Config.dwTurnIntervalTime div 2 then begin
      SysMsg('ClientChangeDir ' + IntToStr(dwCheckTime);
      m_boEmergencyClose:=True;
      Result:=True;
    end;
    }
    Exit;
  end;

  if (nX = m_nCurrX) and (nY = m_nCurrY) then begin
    m_btDirection := nDir;
    if Walk(RM_TURN) then begin
      m_dwTurnTick := GetTickCount();
      Result := True;
    end;
  end;
end;

function TPlayObject.ClientSitDownHit(nX, nY, nDir: Integer; var dwDelayTime: LongWord): Boolean; //004CC248
var
  dwCheckTime: LongWord;
begin
  //SetProcessName('TPlayObject.ClientSitDownHit');
  Result := False;
  if m_boDeath or (m_wStatusTimeArr[POISON_STONE {5}] {0x6A} <> 0) then Exit; //防麻

  dwCheckTime := GetTickCount - m_dwTurnTick;

  if dwCheckTime < g_Config.dwTurnIntervalTime then begin
    dwDelayTime := g_Config.dwTurnIntervalTime - dwCheckTime;
    Exit;
  end;
  m_dwTurnTick := GetTickCount;
  SendRefMsg(RM_POWERHIT, 0, 0, 0, 0, '');
  Result := True;
end;

procedure TPlayObject.ClientOpenDoor(nX, nY: Integer);
var
  Door: TDoorObject;
  Castle: TUserCastle;
begin
  Door := m_PEnvir.GetDoor(nX, nY);

  if Door = nil then Exit;
  Castle := g_CastleManager.IsCastleEnvir(m_PEnvir);
  if (Castle = nil) or
    (Castle.m_DoorStatus <> Door.m_Status) or
    (m_btRaceServer <> RC_PLAYOBJECT) or
    Castle.CheckInPalace(m_nCurrX, m_nCurrY, Self) then begin
    UserEngine.OpenDoor(m_PEnvir, nX, nY);
  end;
  {
  if (UserCastle.m_MapCastle <> m_PEnvir) or
     (UserCastle.m_DoorStatus <> Door.Status) or
     (m_btRaceServer <> RC_PLAYOBJECT) or
     UserCastle.CheckInPalace(m_nCurrX,m_nCurrY,Self) then begin

    UserEngine.OpenDoor(m_PEnvir,nX,nY);
  end;
  }
end;

procedure TPlayObject.ClientQueryRanking(nTabelPage, nPageType, nPage: Integer; boSearchSelf: Boolean);
var
  sData: string;
begin
  sData := '';
  if boSearchSelf then begin
    if LoadRanking(nTabelPage, nPageType, nPage, m_sCharName, @m_DefMsg, sData) then begin
      if sData <> '' then begin
        m_DefMsg.Ident := SM_SENGRANKING;
        SendSocket(@m_DefMsg, sData);
        //MainOutMessage('TPlayObject.ClientQueryRanking1:'+sData);
      end else begin
        m_DefMsg := MakeDefaultMsg(SM_SENGMYRANKING_FAIL, 0, 0, 0, 0);
        SendSocket(@m_DefMsg, '');
        //MainOutMessage('TPlayObject.ClientQueryRanking2:'+sData);
      end;
    end else begin
      m_DefMsg := MakeDefaultMsg(SM_SENGMYRANKING_FAIL, 0, 0, 0, 0);
      SendSocket(@m_DefMsg, '');
      //MainOutMessage('TPlayObject.ClientQueryRanking3:'+sData);
    end;
  end else begin
    if LoadRanking(nTabelPage, nPageType, nPage, '', @m_DefMsg, sData) then begin
      //if sData <> '' then begin

      //end;
    end;
    m_DefMsg.Ident := SM_SENGRANKING;
    SendSocket(@m_DefMsg, sData);
  end;
  //MainOutMessage('TPlayObject.ClientQueryRanking:'+sData);
end;

procedure TPlayObject.ClientHeroLogOn();
var
  nSec: Integer;
  UserOpenInfo: pTUserOpenInfo;
begin
  if m_boNotOnlineAddExp or m_boWaitHeroDate or m_boGhost or m_boDeath or m_PEnvir.m_boNoRecallHero then Exit;
  if (m_sHeroCharName <> '') and (m_MyHero = nil) then begin
    if m_PEnvir.m_boNoRecallHero then begin
      SysMsg(g_sNoRecallHero, c_Green, t_Hint);
      Exit;
    end;
    if (GetTickCount - m_dwRecallHeroTick > g_Config.nRecallHeroTime * 1000) then begin
      if not m_boAI then begin
        FrontEngine.AddToLoadHeroRcdList(Self, nil, l_Load);
      end else begin
        //if not UserEngine.FindHeroOpenInfo(m_sHeroCharName) then begin
        New(UserOpenInfo);
        UserOpenInfo.nResult := 1;
        UserOpenInfo.sChrName := m_sHeroCharName;
        UserOpenInfo.LoadUser.PlayObject := Self;
        UserOpenInfo.LoadUser.HeroData := l_Load;
        UserEngine.AddHeroOpenInfo(UserOpenInfo);
        m_boWaitHeroDate := True;
        //end;
      end;
    end else begin
      nSec := abs(g_Config.nRecallHeroTime - (GetTickCount - m_dwRecallHeroTick) div 1000);
      SysMsg(Format(g_Config.sRecallHeroHint, [nSec]), c_Green, t_Hint);
    end;
  end;
end;

procedure TPlayObject.ClientHeroLogOut(BaseObject: TActorObject);
begin
  if m_boNotOnlineAddExp or m_boWaitHeroDate or m_boGhost then Exit;
  if (m_sHeroCharName <> '') and (m_MyHero <> nil) and (BaseObject = m_MyHero) then begin
    if m_boAI then begin
      THeroObject(m_MyHero).LogOut;
    end else begin
      if THeroObject(m_MyHero).m_btHeroGroup > 0 then begin
        THeroObject(m_MyHero).UnHeroGroup;
        Exit;
      end;
      THeroObject(m_MyHero).LogOut;
    end;
  end;
end;

procedure TPlayObject.ClientHeroAutoAttack(); //英雄挂机
begin
  //MainOutMessage('TPlayObject.ClientHeroAutoAttack();');
  if m_boNotOnlineAddExp or m_boWaitHeroDate or m_boGhost or m_boDeath then Exit;
  if (m_MyHero <> nil) and (THeroObject(m_MyHero).m_boHeroLogOut or m_MyHero.m_boDeath or m_MyHero.m_boGhost or (THeroObject(m_MyHero).m_btHeroGroup > 0)) then Exit;

  if THeroObject(m_MyHero) <> nil then begin
    THeroObject(m_MyHero).m_boAutoAttack := not THeroObject(m_MyHero).m_boAutoAttack;
    if THeroObject(m_MyHero).m_boAutoAttack then begin
      THeroObject(m_MyHero).SysMsg('Start Attacking.', 251, 249, t_Hint);
      //THeroObject(m_MyHero).m_btLastDir := THeroObject(m_MyHero).m_btDirection;
    end else begin
      THeroObject(m_MyHero).SysMsg('Stop Attacking.', 251, 249, t_Hint);
    end;
  end;
end;

procedure TPlayObject.ClientHeroTarget(BaseObject: TActorObject; nX, nY: Integer); //锁定
var
  sMsg: string;
  boTarget: Boolean;
begin
  if m_boNotOnlineAddExp or m_boWaitHeroDate or m_boGhost or m_boDeath or (m_MyHero = nil) then Exit;
  if (m_MyHero <> nil) and (m_MyHero.m_boDeath or m_MyHero.m_boGhost or (THeroObject(m_MyHero).m_btHeroGroup > 0)) then Exit;
  if (BaseObject = nil) and (m_MyHero.m_TargetCret <> nil) then BaseObject := m_MyHero.m_TargetCret;
  if (BaseObject = nil) and (m_TargetCret <> nil) then BaseObject := m_TargetCret;
  if (BaseObject = nil) then BaseObject := TActorObject(m_PEnvir.GetMovingObject(nX, nY, False));
  if CretInNearXY(BaseObject, nX, nY) then begin
    if (BaseObject <> Self) and (BaseObject <> m_MyHero) and (m_MyHero.Master <> BaseObject.Master) then begin
      if (BaseObject.m_btRaceServer < RC_NPC) or (BaseObject.m_btRaceServer > RC_PEACENPC) then begin
        if (BaseObject.m_btRaceServer <> 110) and
          (BaseObject.m_btRaceServer <> 111) then begin
          if g_Config.boSafeNoAllowTargetBB and (BaseObject.m_Master <> nil) and BaseObject.InSafeZone and IsProperFriend(BaseObject.Master) then Exit;
          //if not IsProperTarget(BaseObject) then Exit;
          //if (abs(THeroObject(m_MyHero).m_nCurrX - BaseObject.m_nCurrX) <= 8) and (abs(THeroObject(m_MyHero).m_nCurrY - BaseObject.m_nCurrY) <= 8) then begin
          if (abs(m_nCurrX - BaseObject.m_nCurrX) <= 12) and (abs(m_nCurrY - BaseObject.m_nCurrY) <= 12) then begin
            if (BaseObject.m_Master <> nil) and ((BaseObject.Master.m_btRaceServer = RC_PLAYOBJECT)) then begin
              if not IsAttackTarget(BaseObject.Master) then Exit;
            end;

            {if (BaseObject.m_btRaceServer = RC_PLAYOBJECT) then begin
              SetTargetCreat(BaseObject);
              THeroObject(m_MyHero).SetTargetCreat(BaseObject);
              SetTargetCreat(BaseObject);
            end;}

            // and IsProperTarget(BaseObject)

            //THeroObject(m_MyHero).ChgStatus(1);
            boTarget := THeroObject(m_MyHero).m_boTarget;
            try
              THeroObject(m_MyHero).m_boTarget := False;
              m_MyHero.SetTargetCreat(BaseObject);
            finally
              THeroObject(m_MyHero).m_boTarget := boTarget;
            end;

            SetTargetCreat(BaseObject);
            BaseObject.SetTargetCreat(m_MyHero);
            if (BaseObject.m_Master <> nil) and (BaseObject.m_Master.m_TargetCret = nil) then
              BaseObject.m_Master.SetTargetCreat(m_MyHero);

            THeroObject(m_MyHero).m_nTargetX := BaseObject.m_nCurrX;
            THeroObject(m_MyHero).m_nTargetY := BaseObject.m_nCurrY;
            THeroObject(m_MyHero).m_boTarget := True;
            sMsg := '<Hero %s> is now attacking :%s(Coordinates %d/%d)';
            if THeroObject(m_MyHero).m_btStatus <> 0 then THeroObject(m_MyHero).m_btStatus := 0;
            SysMsg(Format(sMsg, [THeroObject(m_MyHero).m_sCharName, BaseObject.m_sCharName, BaseObject.m_nCurrX, BaseObject.m_nCurrY]), c_Green, t_Hint);
            Exit;
          end;
        end;
      end;
    end;
  end;
  THeroObject(m_MyHero).m_boTarget := False;
end;

procedure TPlayObject.ClientHeroGroupAttack(BaseObject: TActorObject; nX, nY: Integer); //合击
begin
  if m_boWaitHeroDate or m_boGhost or m_boDeath or (m_MyHero = nil) then Exit;
  if (m_MyHero <> nil) and (m_MyHero.m_boDeath or m_MyHero.m_boGhost or (THeroObject(m_MyHero).m_btHeroGroup > 0)) then Exit;

  if (THeroObject(m_MyHero).m_btStatus <> 2) and (not THeroObject(m_MyHero).m_boProtectStatus) and (m_MyHero.m_PEnvir = m_PEnvir) and (abs(m_MyHero.m_nCurrX - m_nCurrX) <= 12) and (abs(m_MyHero.m_nCurrY - m_nCurrY) <= 12) then begin

    if THeroObject(m_MyHero).m_nAngryValue >= g_Config.nMaxAngryValue then begin
      if THeroObject(m_MyHero).FindGroupMagic <> nil then begin
        if not THeroObject(m_MyHero).m_boTarget then begin
          if (m_MyHero.m_TargetCret <> nil) and (m_TargetCret = nil) then begin
            SetTargetCreat(m_TargetCret);
            THeroObject(m_MyHero).m_nTargetX := m_MyHero.m_TargetCret.m_nCurrX;
            THeroObject(m_MyHero).m_nTargetY := m_MyHero.m_TargetCret.m_nCurrY;
            THeroObject(m_MyHero).m_boUseGroupSpell := True;
          end else
            if (m_MyHero.m_TargetCret = nil) and (m_TargetCret <> nil) and (abs(m_MyHero.m_nCurrX - m_TargetCret.m_nCurrX) <= 12) and (abs(m_MyHero.m_nCurrY - m_TargetCret.m_nCurrY) <= 12) then begin
            THeroObject(m_MyHero).SetTargetCreat(m_TargetCret);
            THeroObject(m_MyHero).m_nTargetX := m_TargetCret.m_nCurrX;
            THeroObject(m_MyHero).m_nTargetY := m_TargetCret.m_nCurrY;
            THeroObject(m_MyHero).m_boUseGroupSpell := True;
          end else
            if (m_MyHero.m_TargetCret <> nil) and (m_TargetCret <> nil) and (abs(m_MyHero.m_nCurrX - m_TargetCret.m_nCurrX) <= 12) and (abs(m_MyHero.m_nCurrY - m_TargetCret.m_nCurrY) <= 12) then begin
            THeroObject(m_MyHero).SetTargetCreat(m_TargetCret);
            THeroObject(m_MyHero).m_nTargetX := m_TargetCret.m_nCurrX;
            THeroObject(m_MyHero).m_nTargetY := m_TargetCret.m_nCurrY;
            THeroObject(m_MyHero).m_boUseGroupSpell := True;
          end else begin
            if (BaseObject <> nil) and (not IsProperFriend(BaseObject)) and (abs(m_MyHero.m_nCurrX - BaseObject.m_nCurrX) <= 12) and (abs(m_MyHero.m_nCurrY - BaseObject.m_nCurrY) <= 12) then begin
              SetTargetCreat(BaseObject);
              THeroObject(m_MyHero).SetTargetCreat(BaseObject);
              THeroObject(m_MyHero).m_nTargetX := m_TargetCret.m_nCurrX;
              THeroObject(m_MyHero).m_nTargetY := m_TargetCret.m_nCurrY;
              THeroObject(m_MyHero).m_boUseGroupSpell := True;
            end;
          end;
        end else begin
          if m_MyHero.m_TargetCret <> nil then begin
            SetTargetCreat(m_MyHero.m_TargetCret);
            THeroObject(m_MyHero).m_nTargetX := m_MyHero.m_TargetCret.m_nCurrX;
            THeroObject(m_MyHero).m_nTargetY := m_MyHero.m_TargetCret.m_nCurrY;
            THeroObject(m_MyHero).m_boUseGroupSpell := True;
          end;
        end;
      end;
    end;
  end;
end;

procedure TPlayObject.ClientHeroProtect(nX, nY: Integer); //守护
begin
  if m_boWaitHeroDate or m_boGhost or m_boDeath or (m_MyHero = nil) then Exit;
  if (m_MyHero <> nil) and (m_MyHero.m_boDeath or m_MyHero.m_boGhost or (THeroObject(m_MyHero).m_btHeroGroup > 0)) then Exit;
  //if (m_MyHero.m_WAbil.Level >= g_Config.nNeedGuardLevel) then begin
  THeroObject(m_MyHero).m_boProtectStatus := not THeroObject(m_MyHero).m_boProtectStatus;
  if THeroObject(m_MyHero).m_boProtectStatus then begin
    THeroObject(m_MyHero).m_nProtectTargetX := nX;
    THeroObject(m_MyHero).m_nProtectTargetY := nY;
    THeroObject(m_MyHero).SysMsg(Format('Your Hero is now Protecting the area at (%d/%d).', [nX, nY]), c_Green, t_Hint)
  end else begin
    THeroObject(m_MyHero).m_nProtectTargetX := -1;
    THeroObject(m_MyHero).m_nProtectTargetY := -1;
    THeroObject(m_MyHero).SysMsg('Hero has stopped protecting this area.', c_Green, t_Hint);
  end;
  //end;
end;

procedure TPlayObject.ClientTakeOnItemsFormBag(nIndex: Integer; btWhere: Byte; nItemIdx: Integer; sItemName: string);
var
  I, n14, n18: Integer;
  UserItem, TakeOffItem: pTUserItem;
  StdItem, StdItem20: pTStdItem;
  StdItem58: TStdItem;
  sUserItemName: string;
label FailExit;
begin
  StdItem := nil;
  UserItem := nil;
  n14 := -1;
  if (m_boNotOnlineAddExp) or (m_MyHero = nil) or m_boWaitHeroDate or m_boStore or ((m_MyHero <> nil) and (THeroObject(m_MyHero).m_boHeroLogOut or m_MyHero.m_boDeath or m_MyHero.m_boGhost)) then begin
    goto FailExit;
  end;

  case nIndex of
    CM_HEROTAKEONITEMFROMMASTER: begin
        for I := 0 to m_ItemList.Count - 1 do begin
          UserItem := m_ItemList.Items[I];
          if (UserItem <> nil) and (UserItem.MakeIndex = nItemIdx) then begin
            //取自定义物品名称
            sUserItemName := '';
            if UserItem.btValue[13] = 1 then
              sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
            if sUserItemName = '' then
              sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

            if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
              sUserItemName := '( )' + sUserItemName;
            end;

            StdItem := UserEngine.GetStdItem(UserItem.wIndex);
            if StdItem <> nil then begin
              if CompareText(sUserItemName, sItemName) = 0 then begin
                n14 := I;
                Break;
              end;
            end;
          end;
          UserItem := nil;
        end;
        n18 := 0;
        if (StdItem <> nil) and (UserItem <> nil) then begin
          if CheckUserItems(btWhere, StdItem) then begin
            StdItem58 := StdItem^;
            ItemUnit.GetItemAddValue(UserItem, StdItem58);
            if THeroObject(m_MyHero).CheckTakeOnItems(btWhere, StdItem58) then begin
              TakeOffItem := nil;
              if btWhere in [0..18] then begin
                if m_MyHero.m_UseItems[btWhere].wIndex > 0 then begin
                  StdItem20 := UserEngine.GetStdItem(m_MyHero.m_UseItems[btWhere].wIndex);
                  if (StdItem20 <> nil) and
                    (StdItem20.StdMode in [15, 19, 20, 21, 22, 23, 24, 26]) then begin
                    if (not m_boUserUnLockDurg) and (m_MyHero.m_UseItems[btWhere].btValue[7] <> 0) then begin
                      THeroObject(m_MyHero).SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                      n18 := -4;
                      goto FailExit;
                    end;
                  end;
                  if not m_boUserUnLockDurg and ((StdItem20.Reserved and 2) <> 0) then begin
                    THeroObject(m_MyHero).SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                    n18 := -4;
                    goto FailExit;
                  end;
                  if (StdItem20.Reserved and 4) <> 0 then begin
                    m_MyHero.SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                    n18 := -4;
                    goto FailExit;
                  end;
                  if InDisableTakeOffList(m_MyHero.m_UseItems[btWhere].wIndex) then begin
                    m_MyHero.SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                    goto FailExit;
                  end;
                  New(TakeOffItem);
                  TakeOffItem^ := m_MyHero.m_UseItems[btWhere];
                end; //004DAEC7 if m_UseItems[btWhere].wIndex > 0 then begin

                m_MyHero.GetGroupItemList;
                if (StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26]) and
                  (UserItem.btValue[8] <> 0) then
                  UserItem.btValue[8] := 0;

                m_MyHero.m_UseItems[btWhere] := UserItem^;
                DelBagItem(n14);
                if TakeOffItem <> nil then begin
                  if AddItemToBag(TakeOffItem) then begin
                    SendAddItem(TakeOffItem);
                  end else Dispose(TakeOffItem);
                end;
                THeroObject(m_MyHero).RecalcAbilitys();
                THeroObject(m_MyHero).SendMsg(m_MyHero, RM_ABILITY, 0, 0, 0, 0, '');
                THeroObject(m_MyHero).SendMsg(m_MyHero, RM_SUBABILITY, 0, 0, 0, 0, '');
                THeroObject(m_MyHero).WeightChanged();
                SendDefMessage(SM_HEROTAKEONITEMFROMMASTER_OK, m_MyHero.GetFeatureToLong, m_MyHero.GetFeatureEx, 0, nIndex, '');
                THeroObject(m_MyHero).FeatureChanged();
                if btWhere = U_HELMET then THeroObject(m_MyHero).RefShowName;

                m_MyHero.SendGroupItemMsg();
                n18 := 1;
              end;
            end else n18 := -1;
          end else n18 := -1;
        end;
      end;
    CM_TAKEONITEMFROMHERO: begin
        for I := 0 to m_MyHero.m_ItemList.Count - 1 do begin
          UserItem := m_MyHero.m_ItemList.Items[I];
          if (UserItem <> nil) and (UserItem.MakeIndex = nItemIdx) then begin
            //取自定义物品名称
            sUserItemName := '';
            if UserItem.btValue[13] = 1 then
              sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
            if sUserItemName = '' then
              sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

            if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
              sUserItemName := '( )' + sUserItemName;
            end;

            StdItem := UserEngine.GetStdItem(UserItem.wIndex);
            if StdItem <> nil then begin
              if CompareText(sUserItemName, sItemName) = 0 then begin
                n14 := I;
                Break;
              end;
            end;
          end;
          UserItem := nil;
        end;
        n18 := 0;
        if (StdItem <> nil) and (UserItem <> nil) then begin
          if CheckUserItems(btWhere, StdItem) then begin
            StdItem58 := StdItem^;
            ItemUnit.GetItemAddValue(UserItem, StdItem58);
            if CheckTakeOnItems(btWhere, StdItem58) then begin
              TakeOffItem := nil;
              if btWhere in [0..18] then begin
                if m_UseItems[btWhere].wIndex > 0 then begin
                  StdItem20 := UserEngine.GetStdItem(m_UseItems[btWhere].wIndex);
                  if (StdItem20 <> nil) and
                    (StdItem20.StdMode in [15, 19, 20, 21, 22, 23, 24, 26]) then begin
                    if (not m_boUserUnLockDurg) and (m_MyHero.m_UseItems[btWhere].btValue[7] <> 0) then begin
                      SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                      n18 := -4;
                      goto FailExit;
                    end;
                  end;
                  if not m_boUserUnLockDurg and ((StdItem20.Reserved and 2) <> 0) then begin
                    SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                    n18 := -4;
                    goto FailExit;
                  end;
                  if ((StdItem20.Reserved and 4) <> 0) then begin
                    SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                    n18 := -4;
                    goto FailExit;
                  end;
                  if InDisableTakeOffList(m_MyHero.m_UseItems[btWhere].wIndex) then begin
                    SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                    goto FailExit;
                  end;
                  New(TakeOffItem);
                  TakeOffItem^ := m_UseItems[btWhere];
                end; //004DAEC7 if m_UseItems[btWhere].wIndex > 0 then begin

                GetGroupItemList;
                if (StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26]) and
                  (UserItem.btValue[8] <> 0) then
                  UserItem.btValue[8] := 0;

                m_UseItems[btWhere] := UserItem^;
                THeroObject(m_MyHero).DelBagItem(n14);

                if TakeOffItem <> nil then begin
                  if THeroObject(m_MyHero).AddItemToBag(TakeOffItem) then begin
                    THeroObject(m_MyHero).SendAddItem(TakeOffItem);
                  end else Dispose(TakeOffItem);
                end;
                RecalcAbilitys();
                SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
                SendMsg(Self, RM_SUBABILITY, 0, 0, 0, 0, '');
                SendDefMessage(SM_TAKEONITEMFROMHERO_OK, GetFeatureToLong, GetFeatureEx, 0, nIndex, '');
                FeatureChanged();
                if btWhere = U_HELMET then RefShowName;
                n18 := 1;

                SendGroupItemMsg();

                if g_FunctionNPC <> nil then begin
                  m_nScriptGotoCount := 0;
                  g_FunctionNPC.GotoLable(Self, '@TakeOn' + IntToStr(btWhere), False);
                end;

                //TakeOnGroupItem(@m_UseItems[btWhere]);
              end;
            end else n18 := -1;
          end else n18 := -1;
        end;
      end;
  end;

  FailExit:
  case nIndex of
    CM_HEROTAKEONITEMFROMMASTER: begin
        if n18 <= 0 then
          SendDefMessage(SM_HEROTAKEONITEMFROMMASTER_FAIL, n18, 0, 0, nIndex, '');
      end;
    CM_TAKEONITEMFROMHERO: begin
        if n18 <= 0 then
          SendDefMessage(SM_TAKEONITEMFROMHERO_FAIL, n18, 0, 0, nIndex, '');
      end;
  end;
end;

procedure TPlayObject.ClientTakeOffItemsToBag(nIndex: Integer; btWhere: Byte; nItemIdx: Integer; sItemName: string);
var
  n10: Integer;
  StdItem: pTStdItem;
  UserItem: pTUserItem;
  sUserItemName: string;
label FailExit;
begin
  n10 := 0;
  if m_boNotOnlineAddExp or (m_MyHero = nil) or m_boWaitHeroDate or m_boStore or ((m_MyHero <> nil) and (THeroObject(m_MyHero).m_boHeroLogOut or m_MyHero.m_boDeath or m_MyHero.m_boGhost)) then begin
    goto FailExit;
  end;
  case nIndex of
    CM_TAKEOFFITEMTOHERO: begin //装备脱下到英雄包裹
        if (not m_boDealing) and (not m_boDueling) and (btWhere < 13) then begin
          if m_UseItems[btWhere].wIndex > 0 then begin
            if m_UseItems[btWhere].MakeIndex = nItemIdx then begin
              StdItem := UserEngine.GetStdItem(m_UseItems[btWhere].wIndex);
              if (StdItem <> nil) and
                (StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26]) then begin
                if (not m_boUserUnLockDurg) and (m_MyHero.m_UseItems[btWhere].btValue[7] <> 0) then begin
                  SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                  n10 := -4;
                  goto FailExit;
                end;
              end;
              if not m_boUserUnLockDurg and ((StdItem.Reserved and 2) <> 0) then begin
                SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                n10 := -4;
                goto FailExit;
              end;
              if ((StdItem.Reserved and 4) <> 0) then begin
                SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                n10 := -4;
                goto FailExit;
              end;
              if InDisableTakeOffList(m_UseItems[btWhere].wIndex) then begin
                SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                goto FailExit;
              end;
              //取自定义物品名称
              sUserItemName := '';
              if m_UseItems[btWhere].btValue[13] = 1 then
                sUserItemName := ItemUnit.GetCustomItemName(m_UseItems[btWhere].MakeIndex, m_UseItems[btWhere].wIndex);
              if sUserItemName = '' then
                sUserItemName := UserEngine.GetStdItemName(m_UseItems[btWhere].wIndex);

              if CheckItemBindUse(@(m_UseItems[btWhere]), False) = 2 then begin //绑定物品
                sUserItemName := '( )' + sUserItemName;
              end;

              if CompareText(sUserItemName, sItemName) = 0 then begin
                New(UserItem);
                UserItem^ := m_UseItems[btWhere];
                if THeroObject(m_MyHero).AddItemToBag(UserItem) then begin
                  THeroObject(m_MyHero).SendAddItem(UserItem);
                  m_UseItems[btWhere].wIndex := 0;
                  RecalcAbilitys();
                  SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
                  SendMsg(Self, RM_SUBABILITY, 0, 0, 0, 0, '');
                  SendDefMessage(SM_TAKEOFFITEMTOHERO_OK, GetFeatureToLong, GetFeatureEx, 0, nIndex, '');
                  FeatureChanged();
                  n10 := 1;
                  if btWhere = U_HELMET then RefShowName;
                  ClearCopyItems();
                  if g_FunctionNPC <> nil then begin
                    m_nScriptGotoCount := 0;
                    g_FunctionNPC.GotoLable(Self, '@TakeOff' + IntToStr(btWhere), False);
                  end;
                end else begin
                  Dispose(UserItem);
                  n10 := -3;
                end;
              end;
            end;
          end else n10 := -2;
        end else n10 := -1;
      end;

    CM_HEROTAKEOFFITEMTOMASTER: begin //装备脱下到主人包裹
        if (not m_boDealing) and (not m_boDueling) and (btWhere < 13) then begin
          if m_MyHero.m_UseItems[btWhere].wIndex > 0 then begin
            if m_MyHero.m_UseItems[btWhere].MakeIndex = nItemIdx then begin
              StdItem := UserEngine.GetStdItem(m_MyHero.m_UseItems[btWhere].wIndex);
              if (StdItem <> nil) and
                (StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26]) then begin
                if {(not m_boUserUnLockDurg) and}(m_MyHero.m_UseItems[btWhere].btValue[7] <> 0) then begin
                  THeroObject(m_MyHero).SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                  n10 := -4;
                  goto FailExit;
                end;
              end;

              if not m_boUserUnLockDurg and ((StdItem.Reserved and 2) <> 0) then begin
                SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                n10 := -4;
                goto FailExit;
              end;

              if ((StdItem.Reserved and 4) <> 0) then begin
                THeroObject(m_MyHero).SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                n10 := -4;
                goto FailExit;
              end;
              if InDisableTakeOffList(m_MyHero.m_UseItems[btWhere].wIndex) then begin
                THeroObject(m_MyHero).SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                goto FailExit;
              end;
              //取自定义物品名称
              sUserItemName := '';
              if m_MyHero.m_UseItems[btWhere].btValue[13] = 1 then
                sUserItemName := ItemUnit.GetCustomItemName(m_MyHero.m_UseItems[btWhere].MakeIndex, m_MyHero.m_UseItems[btWhere].wIndex);
              if sUserItemName = '' then
                sUserItemName := UserEngine.GetStdItemName(m_MyHero.m_UseItems[btWhere].wIndex);

              if CheckItemBindUse(@(m_MyHero.m_UseItems[btWhere]), False) = 2 then begin //绑定物品
                sUserItemName := '( )' + sUserItemName;
              end;

              if CompareText(sUserItemName, sItemName) = 0 then begin
                New(UserItem);
                UserItem^ := m_MyHero.m_UseItems[btWhere];
                if AddItemToBag(UserItem) then begin
                  SendAddItem(UserItem);
                  WeightChanged();
                  m_MyHero.m_UseItems[btWhere].wIndex := 0;
                  THeroObject(m_MyHero).RecalcAbilitys();
                  THeroObject(m_MyHero).WeightChanged();
                  THeroObject(m_MyHero).SendMsg(m_MyHero, RM_ABILITY, 0, 0, 0, 0, '');
                  THeroObject(m_MyHero).SendMsg(m_MyHero, RM_SUBABILITY, 0, 0, 0, 0, '');
                  SendDefMessage(SM_HEROTAKEOFFITEMTOMASTER_OK, THeroObject(m_MyHero).GetFeatureToLong, THeroObject(m_MyHero).GetFeatureEx, 0, nIndex, '');
                  THeroObject(m_MyHero).FeatureChanged();
                  if btWhere = U_HELMET then THeroObject(m_MyHero).RefShowName;
                  n10 := 1;
                  ClearCopyItems();
                end else begin
                  Dispose(UserItem);
                  n10 := -3;
                end;
              end;
            end;
          end else n10 := -2;
        end else n10 := -1;
      end;
  end;
  FailExit:
  case nIndex of
    CM_TAKEOFFITEMTOHERO: begin //装备脱下到英雄包裹
        if n10 <= 0 then
          SendDefMessage(SM_TAKEOFFITEMTOHERO_FAIL, n10, 0, 0, nIndex, '');
      end;
    CM_HEROTAKEOFFITEMTOMASTER: begin //装备脱下到主人包裹
        if n10 <= 0 then
          SendDefMessage(SM_HEROTAKEOFFITEMTOMASTER_FAIL, n10, 0, 0, nIndex, '');
      end;
  end;
end;

procedure TPlayObject.ClientMasterBagToHeroBag(nItemIdx: Integer; sItemName: string);
var
  I: Integer;
  UserItem: pTUserItem;
  sUserItemName: string;
begin
  //MainOutMessage('TPlayObject.ClientMasterBagToHeroBag1');
  if m_boNotOnlineAddExp or m_boWaitHeroDate or m_boGhost or (m_MyHero = nil) then begin
    SendDefMessage(SM_MASTERBAGTOHEROBAG_FAIL, 0, 0, 0, 0, '');
  end else begin
    if THeroObject(m_MyHero).IsEnoughBag then begin
      for I := 0 to m_ItemList.Count - 1 do begin
        UserItem := m_ItemList.Items[I];
        if UserItem.MakeIndex = nItemIdx then begin
        //取自定义物品名称
          sUserItemName := '';
          if UserItem.btValue[13] = 1 then
            sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
          if sUserItemName = '' then
            sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

          if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
            sUserItemName := '( )' + sUserItemName;
          end;

          if CompareText(sUserItemName, sItemName) = 0 then begin
            if m_MyHero.AddItemToBag(UserItem) then begin
              m_ItemList.Delete(I);
              SendDefMessage(SM_MASTERBAGTOHEROBAG_OK, 0, 0, 0, 0, '');
              Exit;
            end;
            break;
          end;
        end;
      end;
    end;
    SendDefMessage(SM_MASTERBAGTOHEROBAG_FAIL, 0, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.ClientHeroBagToMasterBag(nItemIdx: Integer; sItemName: string);
var
  I: Integer;
  UserItem: pTUserItem;
  sUserItemName: string;
begin
  if m_boNotOnlineAddExp or m_boWaitHeroDate or m_boGhost or (m_MyHero = nil) then begin
    SendDefMessage(SM_HEROBAGTOMASTERBAG_FAIL, 0, 0, 0, 0, '');
  end else begin
    if IsEnoughBag then begin
      for I := 0 to m_MyHero.m_ItemList.Count - 1 do begin
        UserItem := m_MyHero.m_ItemList.Items[I];
        if UserItem.MakeIndex = nItemIdx then begin
        //取自定义物品名称
          sUserItemName := '';
          if UserItem.btValue[13] = 1 then
            sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
          if sUserItemName = '' then
            sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

          if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
            sUserItemName := '( )' + sUserItemName;
          end;

          if CompareText(sUserItemName, sItemName) = 0 then begin
            if AddItemToBag(UserItem) then begin
              m_MyHero.m_ItemList.Delete(I);
              SendDefMessage(SM_HEROBAGTOMASTERBAG_OK, 0, 0, 0, 0, '');
              Exit;
            end;
            break;
          end;
        end;
      end;
    end;
    SendDefMessage(SM_HEROBAGTOMASTERBAG_FAIL, 0, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.ClientGetSellGoodList(nType, nPage: Integer; sItemName: string);
  function GetPageCount(ListCount: Integer): Integer;
  begin
    if ListCount >= 10 then begin
      Result := ListCount div 10;
      if ListCount mod 10 > 0 then Inc(Result);
    end else begin
      if ListCount > 0 then
        Result := 1
      else Result := 0;
    end;
  end;
var
  ItemList: TSellList;
  SellList: TSellList;
  SellItem: TClientSellItem;
  SellOffInfo: pTSellOffInfo;
  StdItem: pTStdItem;
  StdItem01: TStdItem;
  nPageCount: Integer;
  n01: Integer;
  n02: Integer;
  I: Integer;
  sSendText: string;
begin
  nPageCount := 0;
  n01 := 0;
  n02 := 0;
  sSendText := '';
  case nType of
    0: begin
        nPageCount := GetPageCount(g_SellList.m_SellList.Count);
        if nPage <= nPageCount - 1 then begin
          for I := nPage * 10 to g_SellList.m_SellList.Count - 1 do begin
            SellOffInfo := pTSellOffInfo(g_SellList.m_SellList.Objects[I]);
            StdItem := UserEngine.GetStdItem(SellOffInfo.UserItem.wIndex);
            if StdItem <> nil then begin
              if (SellOffInfo.UserItem.AddValue[0] = 1) and (GetDayCount(SellOffInfo.UserItem.MaxDate, Now) <= 0) then Continue;
              {SafeFillChar(SellItem, SizeOf(TClientSellItem), #0);
              SafeFillChar(SellItem.SellItem, SizeOf(TClientItem), #0); }
              SellItem.sCharName := SellOffInfo.sCharName;
              SellItem.dSellDateTime := SellOffInfo.dSellDateTime;
              SellItem.SellItem.MakeIndex := SellOffInfo.UserItem.MakeIndex;
              SellItem.SellItem.Dura := SellOffInfo.UserItem.Dura;
              SellItem.SellItem.DuraMax := SellOffInfo.UserItem.DuraMax;
              SellItem.SellItem.s := StdItem^;
              ItemUnit.GetItemAddValue(@SellOffInfo.UserItem, SellItem.SellItem.s);
              SellItem.SellItem.s.Price := SellOffInfo.nSellGold;
              SellItem.SellItem.s.AddValue := SellOffInfo.UserItem.AddValue;
              SellItem.SellItem.s.AddPoint := SellOffInfo.UserItem.AddPoint;

              SellItem.SellItem.s.MaxDate := SellOffInfo.UserItem.MaxDate;
              //SellItem.SellItem.s.sDescr := UserEngine.GetStdItemDescr(SellItem.SellItem.s, @SellOffInfo.UseItems);
              if SellItem.sCharName = m_sCharName then SellItem.SellItem.s.Price := -SellItem.SellItem.s.Price;
              sSendText := sSendText + EncodeBuffer(@SellItem, SizeOf(TClientSellItem)) + '/';
              Inc(n01);
              if n01 >= 10 then Break;
            end;
          end;
        end;
      end;
    1..5: begin
        SellList := g_SellList.GetTypeItemListA(nType);
        if SellList <> nil then begin
          nPageCount := GetPageCount(SellList.Count);
          if nPage <= nPageCount - 1 then begin
            for I := nPage * 10 to SellList.Count - 1 do begin
              SellOffInfo := pTSellOffInfo(SellList.Objects[I]);
              StdItem := UserEngine.GetStdItem(SellOffInfo.UserItem.wIndex);
              if StdItem <> nil then begin
                if (SellOffInfo.UserItem.AddValue[0] = 1) and (GetDayCount(SellOffInfo.UserItem.MaxDate, Now) <= 0) then Continue;
                //SafeFillChar(SellItem, SizeOf(TClientSellItem), #0);
                //SafeFillChar(SellItem.SellItem, SizeOf(TClientItem), #0);
                SellItem.sCharName := SellOffInfo.sCharName;
                SellItem.dSellDateTime := SellOffInfo.dSellDateTime;
                SellItem.SellItem.MakeIndex := SellOffInfo.UserItem.MakeIndex;
                SellItem.SellItem.Dura := SellOffInfo.UserItem.Dura;
                SellItem.SellItem.DuraMax := SellOffInfo.UserItem.DuraMax;
                SellItem.SellItem.s := StdItem^;
                ItemUnit.GetItemAddValue(@SellOffInfo.UserItem, SellItem.SellItem.s);
                SellItem.SellItem.s.Price := SellOffInfo.nSellGold;
                SellItem.SellItem.s.AddValue := SellOffInfo.UserItem.AddValue;
                SellItem.SellItem.s.AddPoint := SellOffInfo.UserItem.AddPoint;
                SellItem.SellItem.s.MaxDate := SellOffInfo.UserItem.MaxDate;
                //SellItem.SellItem.s.sDescr := UserEngine.GetStdItemDescr(SellItem.SellItem.s, @SellOffInfo.UseItems);
                if SellItem.sCharName = m_sCharName then SellItem.SellItem.s.Price := -SellItem.SellItem.s.Price;
                sSendText := sSendText + EncodeBuffer(@SellItem, SizeOf(TClientSellItem)) + '/';
                Inc(n01);
                if n01 >= 10 then Break;
              end;
            end;
          end;
        end;
      end;
    6: begin
        SellList := nil;
        if (g_SellList.GetSellListByCharName(m_sCharName, SellList) >= 0) and (SellList <> nil) then begin
          nPageCount := GetPageCount(SellList.Count);
          if nPage <= nPageCount - 1 then begin
            for I := nPage * 10 to SellList.Count - 1 do begin
              SellOffInfo := pTSellOffInfo(SellList.Objects[I]);
              StdItem := UserEngine.GetStdItem(SellOffInfo.UserItem.wIndex);
              if StdItem <> nil then begin
                if (SellOffInfo.UserItem.AddValue[0] = 1) and (GetDayCount(SellOffInfo.UserItem.MaxDate, Now) <= 0) then Continue;
                SellItem.sCharName := SellOffInfo.sCharName;
                SellItem.dSellDateTime := SellOffInfo.dSellDateTime;
                SellItem.SellItem.MakeIndex := SellOffInfo.UserItem.MakeIndex;
                SellItem.SellItem.Dura := SellOffInfo.UserItem.Dura;
                SellItem.SellItem.DuraMax := SellOffInfo.UserItem.DuraMax;
                SellItem.SellItem.s := StdItem^;
                ItemUnit.GetItemAddValue(@SellOffInfo.UserItem, SellItem.SellItem.s);
                SellItem.SellItem.s.Price := SellOffInfo.nSellGold;
                SellItem.SellItem.s.AddValue := SellOffInfo.UserItem.AddValue;
                SellItem.SellItem.s.AddPoint := SellOffInfo.UserItem.AddPoint;
                SellItem.SellItem.s.MaxDate := SellOffInfo.UserItem.MaxDate;
                //SellItem.SellItem.s.sDescr := UserEngine.GetStdItemDescr(SellItem.SellItem.s, @SellOffInfo.UseItems);
                if SellItem.sCharName = m_sCharName then SellItem.SellItem.s.Price := -SellItem.SellItem.s.Price;
                sSendText := sSendText + EncodeBuffer(@SellItem, SizeOf(TClientSellItem)) + '/';
                Inc(n01);
                if n01 >= 10 then Break;
              end;
            end;
          end;
        end;
      end;
    7: begin
        SellList := nil;
        if (g_GoldList.GetGoldListByCharName(m_sCharName, SellList) >= 0) and (SellList <> nil) then begin
          nPageCount := GetPageCount(SellList.Count);
          if nPage <= nPageCount - 1 then begin
            for I := nPage * 10 to SellList.Count - 1 do begin
              SellOffInfo := pTSellOffInfo(SellList.Objects[I]);
              StdItem := UserEngine.GetStdItem(SellOffInfo.UserItem.wIndex);
              if StdItem <> nil then begin
                if (SellOffInfo.UserItem.AddValue[0] = 1) and (GetDayCount(SellOffInfo.UserItem.MaxDate, Now) <= 0) then Continue;
                SellItem.sCharName := SellOffInfo.sCharName;
                SellItem.dSellDateTime := SellOffInfo.dSellDateTime;
                SellItem.SellItem.MakeIndex := SellOffInfo.UserItem.MakeIndex;
                SellItem.SellItem.Dura := SellOffInfo.UserItem.Dura;
                SellItem.SellItem.DuraMax := SellOffInfo.UserItem.DuraMax;
                SellItem.SellItem.s := StdItem^;
                ItemUnit.GetItemAddValue(@SellOffInfo.UserItem, SellItem.SellItem.s);
                SellItem.SellItem.s.Price := SellOffInfo.nSellGold;
                SellItem.SellItem.s.AddValue := SellOffInfo.UserItem.AddValue;
                SellItem.SellItem.s.AddPoint := SellOffInfo.UserItem.AddPoint;
                SellItem.SellItem.s.MaxDate := SellOffInfo.UserItem.MaxDate;
                //SellItem.SellItem.s.sDescr := UserEngine.GetStdItemDescr(SellItem.SellItem.s, @SellOffInfo.UseItems);
                if SellItem.sCharName = m_sCharName then SellItem.SellItem.s.Price := -SellItem.SellItem.s.Price;
                sSendText := sSendText + EncodeBuffer(@SellItem, SizeOf(TClientSellItem)) + '/';
                Inc(n01);
                if n01 >= 10 then Break;
              end;
            end;
          end;
        end;
      end;
    8: begin //查询
        if (g_SellList.GetSellListByItemName(sItemName, ItemList) >= 0) and (ItemList <> nil) then begin
          nPageCount := GetPageCount(ItemList.Count);
          if nPage <= nPageCount - 1 then begin
            for I := nPage * 10 to ItemList.Count - 1 do begin
              SellOffInfo := pTSellOffInfo(ItemList.Objects[I]);
              StdItem := UserEngine.GetStdItem(SellOffInfo.UserItem.wIndex);
              if StdItem <> nil then begin
                if (SellOffInfo.UserItem.AddValue[0] = 1) and (GetDayCount(SellOffInfo.UserItem.MaxDate, Now) <= 0) then Continue;
                FillChar(SellItem, SizeOf(TClientSellItem), #0);
                FillChar(SellItem.SellItem, SizeOf(TClientItem), #0);
                SellItem.sCharName := SellOffInfo.sCharName;
                SellItem.dSellDateTime := SellOffInfo.dSellDateTime;
                SellItem.SellItem.MakeIndex := SellOffInfo.UserItem.MakeIndex;
                SellItem.SellItem.Dura := SellOffInfo.UserItem.Dura;
                SellItem.SellItem.DuraMax := SellOffInfo.UserItem.DuraMax;
                SellItem.SellItem.s := StdItem^;
                ItemUnit.GetItemAddValue(@SellOffInfo.UserItem, SellItem.SellItem.s);
                SellItem.SellItem.s.Price := SellOffInfo.nSellGold;
                SellItem.SellItem.s.AddValue := SellOffInfo.UserItem.AddValue;
                SellItem.SellItem.s.AddPoint := SellOffInfo.UserItem.AddPoint;
                SellItem.SellItem.s.MaxDate := SellOffInfo.UserItem.MaxDate;
                //SellItem.SellItem.s.sDescr := UserEngine.GetStdItemDescr(SellItem.SellItem.s, @SellOffInfo.UseItems);
                if SellItem.sCharName = m_sCharName then SellItem.SellItem.s.Price := -SellItem.SellItem.s.Price;
                sSendText := sSendText + EncodeBuffer(@SellItem, SizeOf(TClientSellItem)) + '/';
                Inc(n01);
                if n01 >= 10 then Break;
              end;
            end;
          end;
        end;
      end;
  end;
  SendMsg(Self, RM_SENDSELLOFFGOODSLIST, 0, nType, nPage, nPageCount, sSendText);
end;

procedure TPlayObject.ClientGetBackBox();
var
  boGetBackOK: Boolean;
  StdItem: pTStdItem;
  UserItem: pTUserItem;
begin
  boGetBackOK := False;
  if (m_SuperItemBox <> nil) and (m_SuperItemBox.BoxStatus = b_ShowBox) and (m_SuperItemBox.UserItem.wIndex > 0) then begin
    StdItem := UserEngine.GetStdItem(m_SuperItemBox.UserItem.wIndex);
    if StdItem <> nil then begin
      boGetBackOK := True;
      if IsEnoughBag then begin
        New(UserItem);
        UserItem^ := m_SuperItemBox.UserItem;
        m_ItemList.Add(UserItem);
        SendAddItem(UserItem);
      end else begin
        DropItemDown(@(m_SuperItemBox.UserItem), 3, False, Self, nil);
      end;
      if StdItem.NeedIdentify = 1 then
        AddGameDataLog('9' + #9 +
          m_sMapName + #9 +
          IntToStr(m_nCurrX) + #9 +
          IntToStr(m_nCurrY) + #9 +
          m_sCharName + #9 +
          StdItem.Name + #9 +
          IntToStr(UserItem.MakeIndex) + #9 +
          '1' + #9 +
          m_SuperItemBox.sOpenBoxName);
    end;
    Dispose(m_SuperItemBox);
    m_SuperItemBox := nil;
  end;
  if boGetBackOK then begin
    m_DefMsg := MakeDefaultMsg(SM_GETBACKITEMBOX_OK, 0, 0, 0, 0);
    SendSocket(@m_DefMsg, '');
  end else begin
    m_DefMsg := MakeDefaultMsg(SM_GETBACKITEMBOX_FAIL, 0, 0, 0, 0);
    SendSocket(@m_DefMsg, '');
  end;
end;

procedure TPlayObject.ClientGetBoxIndex();
var
  nCount: Integer;
begin
  if m_SuperItemBox = nil then Exit;
  if m_SuperItemBox.BoxStatus = b_OpenBox then begin
    m_SuperItemBox.BoxStatus := b_BoxIndex;
    nCount := 0;
    while True do begin
      if nCount > 50 then begin
        m_SuperItemBox.btGiveBoxIndex := 10;
        Break;
      end;
      m_SuperItemBox.btGiveBoxIndex := Random(8);
      if (m_SuperItemBox.btGiveBoxIndex <> 4) and m_SuperItemBox.BoxItemArray[m_SuperItemBox.btGiveBoxIndex].boGive then Break;
      Inc(nCount);
    end;
    if m_SuperItemBox.btGiveBoxIndex <> 10 then
      SendDelayMsg(Self, RM_GETSELBOXITEMNUM, 0, m_SuperItemBox.btGiveBoxIndex, 0, 0, '', 5000);
  end;
end;

procedure TPlayObject.ClientGetBoxItem(nIdx: Integer);
var
  UserItem: pTUserItem;
  StdItem: pTStdItem;
  sItemName: string;
  nItemCount: Integer;
begin
  if m_SuperItemBox = nil then Exit;
  if ((m_SuperItemBox.BoxStatus = b_BoxIndex) or (m_SuperItemBox.BoxStatus = b_OpenBox)) and (nIdx = m_SuperItemBox.btGiveBoxIndex) and (m_SuperItemBox.BoxItemArray[m_SuperItemBox.btGiveBoxIndex].StdItem.Name <> '') then begin
    sItemName := m_SuperItemBox.BoxItemArray[m_SuperItemBox.btGiveBoxIndex].StdItem.Name;
    m_SuperItemBox.BoxItemArray[m_SuperItemBox.btGiveBoxIndex].StdItem.Name := '';
    m_SuperItemBox.sItemName := sItemName;
    if UserEngine.GetStdItemIdx(sItemName) > 0 then begin
      if g_FunctionNPC <> nil then begin
        m_nScriptGotoCount := 0;
        g_FunctionNPC.GotoLable(Self, '@OpenBox', False);
      end;

      if m_SuperItemBox.BoxItemArray[m_SuperItemBox.btGiveBoxIndex].StdItem.StdMode = 8 then begin
        nItemCount := m_SuperItemBox.BoxItemArray[m_SuperItemBox.btGiveBoxIndex].StdItem.Need;
        if CompareLStr(sItemName, sSTRING_GOLDNAME, Length(sSTRING_GOLDNAME)) then begin
          IncGold(nItemCount);
          GoldChanged();
          SysMsg(Format('From the chest to get %d %s !', [nItemCount, sSTRING_GOLDNAME]), c_Red, t_Hint);
        end else
          if CompareLStr(sItemName, g_Config.sGameGoldName, Length(g_Config.sGameGoldName)) then begin
          IncGameGold(nItemCount);
          GameGoldChanged();
          SysMsg(Format('Obtained from the chest of %d %s !', [nItemCount, g_Config.sGameGoldName]), c_Red, t_Hint);
        end else
          if CompareLStr(sItemName, g_Config.sGamePointName, Length(g_Config.sGamePointName)) then begin
          IncGamePoint(nItemCount);
          GameGoldChanged();
          SysMsg(Format('From the chest to get %d point %s !', [nItemCount, g_Config.sGamePointName]), c_Red, t_Hint);
        end else
          if CompareLStr(sItemName, 'Prestige' {g_Config.sCreditPointName}, Length('Prestige' {g_Config.sCreditPointName})) then begin
          if m_btCreditPoint + LongWord(nItemCount) > High(Integer) then begin
            m_btCreditPoint := High(Integer);
          end else begin
            Inc(m_btCreditPoint, LongWord(nItemCount));
          end;
          SysMsg(Format('From the chest to get %d reputation point!', [LongWord(nItemCount)]), c_Red, t_Hint);
        end else
   {   if CompareLStr(sItemName, g_Config.sGameDiamondName, Length(g_Config.sGameDiamondName)) then begin //金刚石
      IncGameDiamond(nItemCount);
      NewGamePointChanged();
      SysMsg(Format('从宝箱中获得%d个金刚石！！！', [nItemCount]), c_Red, t_Hint);
    end else
      if CompareLStr(sItemName, g_Config.sGameGirdName, Length(g_Config.sGameGirdName)) then begin //灵符
      IncGameGird(nItemCount);
      NewGamePointChanged();
      SysMsg(Format('从宝箱中获得%d张灵符！！！', [nItemCount]), c_Red, t_Hint);
    end else  }
          if CompareLStr(sItemName, 'Experience', Length('Experience')) then begin
          GetExp(nItemCount);
          IncBeadExp(nItemCount);
          SysMsg(Format('%d from the chest to get the experience!', [nItemCount]), c_Red, t_Hint);
        end;
      end else begin
      //SysMsg(m_sCharName + '在开启' + m_SuperItemBox.sOpenBoxName + '获得了' + sItemName, c_Red, t_Hint);
        if IsEnoughBag then begin
          New(UserItem);
          if UserEngine.CopyToUserItemFromName(sItemName, UserItem) then begin
            m_ItemList.Add(UserItem);
            SendAddItem(UserItem);
            StdItem := UserEngine.GetStdItem(UserItem.wIndex);
            if StdItem.NeedIdentify = 1 then
              AddGameDataLog('9' + #9 +
                m_sMapName + #9 +
                IntToStr(m_nCurrX) + #9 +
                IntToStr(m_nCurrY) + #9 +
                m_sCharName + #9 +
                sItemName + #9 +
                IntToStr(UserItem.MakeIndex) + #9 +
                '1' + #9 +
                m_SuperItemBox.sOpenBoxName);
          end else Dispose(UserItem);
        end else begin
          New(UserItem);
          if UserEngine.CopyToUserItemFromName(sItemName, UserItem) then begin
            StdItem := UserEngine.GetStdItem(UserItem.wIndex);
            if StdItem.NeedIdentify = 1 then
              AddGameDataLog('9' + #9 +
                m_sMapName + #9 +
                IntToStr(m_nCurrX) + #9 +
                IntToStr(m_nCurrY) + #9 +
                m_sCharName + #9 +
                sItemName + #9 +
                IntToStr(UserItem.MakeIndex) + #9 +
                '1' + #9 +
                m_SuperItemBox.sOpenBoxName);
            DropItemDown(UserItem, 3, False, Self, nil);
          end;
          Dispose(UserItem);
        end;
      end;
    end;
  end;
  m_SuperItemBox.BoxStatus := b_None;
  if m_SuperItemBox <> nil then begin
    Dispose(m_SuperItemBox);
    m_SuperItemBox := nil;
  end;
end;

procedure TPlayObject.RandomGetUserBoxItem(ItemList: TStringList);
var
  I, II, nIndex, nGiveIndex: Integer;
  CenterItemList, GiveItemList, FillItemList: TStringList;
  BoxItem: pTBoxItem;
  StdItem: pTStdItem;
begin
  CenterItemList := TStringList.Create;
  GiveItemList := TStringList.Create;
  FillItemList := TStringList.Create;
  for I := 0 to ItemList.Count - 1 do begin
    BoxItem := pTBoxItem(ItemList.Objects[I]);
    case BoxItem.ItemType of
      i_Center: CenterItemList.AddObject(ItemList.Strings[I], ItemList.Objects[I]);
      i_Give: GiveItemList.AddObject(ItemList.Strings[I], ItemList.Objects[I]);
      i_Fill: FillItemList.AddObject(ItemList.Strings[I], ItemList.Objects[I]);
    end;
  end;
//获取中间的物品
  if CenterItemList.Count > 0 then begin
    nIndex := Random(CenterItemList.Count - 1);
    BoxItem := pTBoxItem(CenterItemList.Objects[nIndex]);
    StdItem := UserEngine.GetStdItem(BoxItem.sItemName);
    if StdItem <> nil then begin
      m_SuperItemBox.BoxItemArray[4].boGive := True;
      m_SuperItemBox.BoxItemArray[4].boCenter := True;
      m_SuperItemBox.BoxItemArray[4].StdItem := StdItem^;
      if m_SuperItemBox.BoxItemArray[4].StdItem.StdMode = 8 then
        m_SuperItemBox.BoxItemArray[4].StdItem.Need := BoxItem.nItemCount;
    end;
  end;
//获取允许获取物品
  nGiveIndex := 4;
  if GiveItemList.Count > 0 then begin
    nIndex := Random(GiveItemList.Count - 1);
    BoxItem := pTBoxItem(GiveItemList.Objects[nIndex]);
    StdItem := UserEngine.GetStdItem(BoxItem.sItemName);
    if StdItem <> nil then begin
      while True do begin
        nGiveIndex := Random(8);
        if nGiveIndex <> 4 then break;
      end;
      m_SuperItemBox.BoxItemArray[nGiveIndex].boGive := True;
      m_SuperItemBox.BoxItemArray[nGiveIndex].boCenter := False;
      m_SuperItemBox.BoxItemArray[nGiveIndex].StdItem := StdItem^;
      if m_SuperItemBox.BoxItemArray[nGiveIndex].StdItem.StdMode = 8 then
        m_SuperItemBox.BoxItemArray[nGiveIndex].StdItem.Need := BoxItem.nItemCount;
    end;
  end;
//获取填充物品
  if FillItemList.Count > 0 then begin
    for I := 0 to 8 do begin
      if (I <> 4) and (I <> nGiveIndex) then begin
        nIndex := Random(FillItemList.Count - 1);
        BoxItem := pTBoxItem(FillItemList.Objects[nIndex]);
        StdItem := UserEngine.GetStdItem(BoxItem.sItemName);
        if StdItem <> nil then begin
          m_SuperItemBox.BoxItemArray[I].boGive := False;
          m_SuperItemBox.BoxItemArray[I].boCenter := False;
          m_SuperItemBox.BoxItemArray[I].StdItem := StdItem^;
          if m_SuperItemBox.BoxItemArray[I].StdItem.StdMode = 8 then
            m_SuperItemBox.BoxItemArray[I].StdItem.Need := BoxItem.nItemCount;
        end;
      end;
    end;
  end;
  CenterItemList.Free;
  GiveItemList.Free;
  FillItemList.Free;
end;

procedure TPlayObject.ClientOpenBox(nItemIdx: Integer; sItemName: string); //打开宝箱
var
  I, n14, n18: Integer;
  UserItem: pTUserItem;
  UserItem01: TUserItem;
  StdItem, StdItem20: pTStdItem;
  boFind: Boolean;
  ItemBox: pTItemBox;
  ClientItem: TClientItem;
  sSENDMSG: string;
begin
  if m_SuperItemBox = nil then Exit;
  StdItem := nil;
  StdItem20 := nil;
  UserItem := nil;
  UserItem01.wIndex := 0;
  boFind := False;
  n14 := -1;
  n18 := 0;
  //MainOutMessage('ClientOpenBox1:' + sItemName);
  if m_SuperItemBox.BoxStatus = b_ShowBox then begin
    for I := 0 to m_ItemList.Count - 1 do begin
      UserItem := m_ItemList.Items[I];
      if UserItem.MakeIndex = nItemIdx then begin
        StdItem := UserEngine.GetStdItem(UserItem.wIndex);
        if StdItem <> nil then begin
          if CompareText(StdItem.Name, sItemName) = 0 then begin
            n14 := I;
            StdItem20 := StdItem;
            UserItem01 := UserItem^;
            boFind := True;
            Break;
          end;
        end;
      end;
      UserItem := nil;
    end;
    if boFind and (StdItem20 <> nil) and (UserItem01.wIndex > 0) then begin
      if (StdItem20.Shape - 12 = m_SuperItemBox.btBoxType) and (StdItem20.StdMode = 40) then begin
        ItemBox := FindItemBox(m_SuperItemBox.sOpenBoxName);
        if (ItemBox <> nil) and (ItemBox.StdItem.Shape - 12 = m_SuperItemBox.btBoxType) then begin
          m_SuperItemBox.btGiveBoxIndex := 4;
          RandomGetUserBoxItem(ItemBox.ItemList);
          DelBagItem(n14);
          m_SuperItemBox.BoxStatus := b_OpenBox;
          //m_SuperItemBox.sOpenBoxName := '';
          sSENDMSG := '';
          for I := Low(m_SuperItemBox.BoxItemArray) to High(m_SuperItemBox.BoxItemArray) do begin
            if m_SuperItemBox.BoxItemArray[I].StdItem.Name <> '' then begin
              Move(m_SuperItemBox.BoxItemArray[I].StdItem, ClientItem.s, SizeOf(TStdItem));
              ClientItem.Dura := m_SuperItemBox.BoxItemArray[I].StdItem.DuraMax;
              ClientItem.DuraMax := m_SuperItemBox.BoxItemArray[I].StdItem.DuraMax;
              ClientItem.MakeIndex := 0;
              FillChar(ClientItem.s.AddValue, SizeOf(TValue), #0);
              FillChar(ClientItem.s.AddPoint, SizeOf(TValue), #0);
   { ClientItem.btValue := UserItem.btValue[14];
    ClientItem.AddValue := UserItem.AddValue;
    ClientItem.nValue := UserItem.nValue;
    ClientItem.sValue := UserItem.sValue;}
              sSENDMSG := sSENDMSG + IntToStr(I) + '/' + EncodeBuffer(@ClientItem, SizeOf(TClientItem)) + '/';
            end;
          end;
          m_DefMsg := MakeDefaultMsg(SM_OPENITEMBOX_OK, m_SuperItemBox.btBoxType, 0, 0, 0);
          SendSocket(@m_DefMsg, sSENDMSG);
          n18 := 1;
        end else n18 := -1;
      end else n18 := -1;
    end else n18 := -1;
  end else n18 := -1;
  if n18 <= 0 then begin
    SendDefMessage(SM_OPENITEMBOX_FAIL, n18, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.ClientOpenBox(sItemName: string);
var
  I, n14, n18: Integer;
  ItemBox: pTItemBox;
  ClientItem: TClientItem;
  sSENDMSG: string;
begin
  if m_SuperItemBox <> nil then begin
    Dispose(m_SuperItemBox);
    m_SuperItemBox := nil;
  end;
  ItemBox := FindItemBox(sItemName);
  if ItemBox <> nil then begin
    New(m_SuperItemBox);
    m_SuperItemBox.BoxStatus := b_OpenBox;
    m_SuperItemBox.btBoxType := ItemBox.StdItem.Shape - 12;
    m_SuperItemBox.btGiveBoxIndex := 4;
    m_SuperItemBox.sOpenBoxName := sItemName;
    for I := Low(m_SuperItemBox.BoxItemArray) to High(m_SuperItemBox.BoxItemArray) do begin
      m_SuperItemBox.BoxItemArray[I].StdItem.Name := '';
    end;
    RandomGetUserBoxItem(ItemBox.ItemList);
    sSENDMSG := '';
    for I := Low(m_SuperItemBox.BoxItemArray) to High(m_SuperItemBox.BoxItemArray) do begin
      if m_SuperItemBox.BoxItemArray[I].StdItem.Name <> '' then begin
        Move(m_SuperItemBox.BoxItemArray[I].StdItem, ClientItem.s, SizeOf(TStdItem));
        ClientItem.Dura := m_SuperItemBox.BoxItemArray[I].StdItem.DuraMax;
        ClientItem.DuraMax := m_SuperItemBox.BoxItemArray[I].StdItem.DuraMax;
        ClientItem.MakeIndex := 0;
        FillChar(ClientItem.s.AddValue, SizeOf(TValue), #0);
        FillChar(ClientItem.s.AddPoint, SizeOf(TValue), #0);
   { ClientItem.btValue := UserItem.btValue[14];
    ClientItem.AddValue := UserItem.AddValue;
    ClientItem.nValue := UserItem.nValue;
    ClientItem.sValue := UserItem.sValue;}
        sSENDMSG := sSENDMSG + IntToStr(I) + '/' + EncodeBuffer(@ClientItem, SizeOf(TClientItem)) + '/';
      end;
    end;
    m_DefMsg := MakeDefaultMsg(SM_OPENITEMBOX_OK, m_SuperItemBox.btBoxType, 1, 0, 0);
    SendSocket(@m_DefMsg, sSENDMSG);
    n18 := 1;
  end;
end;

procedure TPlayObject.ClientGetUpgradeItems(sData: string);
  function FindItem(nItemIdx: Integer): Integer;
  var
    I: Integer;
    UserItem: pTUserItem;
  begin
    Result := -1;
    for I := 0 to m_ItemList.Count - 1 do begin
      UserItem := m_ItemList.Items[I];
      if UserItem.MakeIndex = nItemIdx then begin
        Result := I;
        Break;
      end;
    end;
  end;

var
  I, n14, n18: Integer;
  UserItem: pTUserItem;
  UserItem01: pTUserItem;
  UserItem02: pTUserItem;
  UserItem03: pTUserItem;
  UpgradeItemIndexs: TUpgradeItemIndexs;
  ItemIndexs: TUpgradeItemIndexs;
  boFind: Boolean;
  StdItem: pTStdItem;
  StdItem01: pTStdItem;
  StdItem02: pTStdItem;
  StdItem03: pTStdItem;
  StdItem80: TStdItem;
  ClientItem: TClientItem;
  nRate: Integer;
  sSENDMSG: string;
  sUserItemName: string;
  sCheckItemName: string;
begin
  n14 := 0;
  sSENDMSG := '';
  DecodeBuffer(sData, @UpgradeItemIndexs, SizeOf(TUpgradeItemIndexs));
  boFind := True;
  for I := 0 to 2 do begin
    ItemIndexs[I] := FindItem(UpgradeItemIndexs[I]);
    if ItemIndexs[I] < 0 then begin
      boFind := False;
      Break;
    end;
  end;
  //摆摊禁止操作
  if boFind and (not m_boStore) then begin
    UserItem := pTUserItem(m_ItemList.Items[ItemIndexs[0]]);
    UserItem01 := pTUserItem(m_ItemList.Items[ItemIndexs[1]]);
    UserItem02 := pTUserItem(m_ItemList.Items[ItemIndexs[2]]);

    StdItem := UserEngine.GetStdItem(UserItem.wIndex);
    StdItem01 := UserEngine.GetStdItem(UserItem01.wIndex);
    StdItem02 := UserEngine.GetStdItem(UserItem02.wIndex);

    if (StdItem <> nil) and (StdItem01 <> nil) and (StdItem02 <> nil) then begin
      if (StdItem.StdMode = 55) then begin //1=55 2=56
        if StdItem01.StdMode = 56 then begin
          StdItem03 := StdItem;
          StdItem := StdItem02;
          StdItem02 := StdItem03;

          UserItem03 := UserItem;
          UserItem := UserItem02;
          UserItem02 := UserItem03;
        end else begin
          StdItem03 := StdItem;
          StdItem := StdItem01;
          StdItem01 := StdItem03;

          UserItem03 := UserItem;
          UserItem := UserItem01;
          UserItem01 := UserItem03;
        end;
      end else
        if StdItem.StdMode = 56 then begin
        if StdItem01.StdMode = 55 then begin
          StdItem03 := StdItem;
          StdItem := StdItem02;
          StdItem02 := StdItem03;

          UserItem03 := UserItem;
          UserItem := UserItem02;
          UserItem02 := UserItem03;
        end else begin
          StdItem03 := StdItem;
          StdItem := StdItem01;
          StdItem01 := StdItem03;

          UserItem03 := UserItem;
          UserItem := UserItem01;
          UserItem01 := UserItem03;
        end;
      end;

      if StdItem01.StdMode = 56 then begin
        StdItem03 := StdItem01;
        StdItem01 := StdItem02;
        StdItem02 := StdItem03;

        UserItem03 := UserItem01;
        UserItem01 := UserItem02;
        UserItem02 := UserItem03;
      end;

      if UserEngine._AllowUpgradeItem(UserItem) then begin
        if (UserItem <> UserItem01) and (UserItem <> UserItem02) and (UserItem01 <> UserItem02) and (UserItem.AddValue[13] < g_Config.nItemMaxStarCount) then begin

          if g_Config.boCheckCanUpgradeStarItem and Assigned(PlugInEngine.CheckCanUpgradeItem) then begin //禁止升级
            sCheckItemName := StdItem.Name;
            if PlugInEngine.CheckCanUpgradeItem(Self, PChar(sCheckItemName), True) then begin
              m_DefMsg := MakeDefaultMsg(SM_SENDUPGRADEITEM_FAIL, n14, 0, 0, 0);
              SendSocket(@m_DefMsg, sSENDMSG);
              Exit;
            end;
          end;

          if (StdItem01.StdMode = 55) and ((StdItem01.Shape = 0) or (StdItem01.Shape = StdItem.StdMode)) and ((StdItem01.Need = 0) or (StdItem01.Need >= UserItem.AddValue[13])) and
            (StdItem02.StdMode = 56) and ((StdItem02.Shape = 0) or (StdItem02.Shape = StdItem.StdMode)) then begin
         { if StdItem01.DuraMax > 100 then begin
            nRate := (StdItem01.DuraMax - 100) div 2;
          end else begin
            nRate := (100 - StdItem01.DuraMax) div 2;
          end; }

            if StdItem.NeedIdentify = 1 then
              AddGameDataLog('31' + #9 +
                m_sMapName + #9 +
                IntToStr(m_nCurrX) + #9 +
                IntToStr(m_nCurrY) + #9 +
                m_sCharName + #9 +
              //UserEngine.GetStdItemName(m_UseItems[I].wIndex) + #9 +
                StdItem.Name + #9 +
                IntToStr(UserItem.MakeIndex) + #9 +
                BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
                '0');

            nRate := StdItem01.DuraMax;
            if Random(nRate) = 0 then begin //升级成功
              n14 := 1;
              UserItem.AddValue[13] := _MIN(UserItem.AddValue[13] + 1, 255);
              if StdItem01.AniCount > 0 then begin
                UserEngine.RandomUpgradeItem_(UserItem, StdItem01.AniCount, 0);
                if StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26] then begin
                  if (StdItem.Shape = 130) or (StdItem.Shape = 131) or (StdItem.Shape = 132) then begin
                    UserEngine.GetUnknowItemValue_(UserItem, StdItem01.AniCount, 0);
                  end;
                end;
              end else begin
                ItemUnit.UpgradeItems_(UserItem, StdItem01, 0);

              end;
            //FillChar(ClientItem, SizeOf(TClientItem), #0);
              ClientItem.s := StdItem^;
              ItemUnit.GetItemAddValue(UserItem, ClientItem.s);
            //Move(StdItem80, ClientItem.s, SizeOf(TStdItem));
              sUserItemName := '';
              if UserItem.btValue[13] = 1 then
                sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
              if sUserItemName <> '' then
                ClientItem.s.Name := sUserItemName;

              if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
                ClientItem.s.Name := '( )' + ClientItem.s.Name;
              end;

              ClientItem.MakeIndex := UserItem.MakeIndex;
              ClientItem.Dura := UserItem.Dura;
              ClientItem.DuraMax := UserItem.DuraMax;
              ClientItem.s.AddValue := UserItem.AddValue;
              ClientItem.s.AddPoint := UserItem.AddPoint;
              ClientItem.s.MaxDate := UserItem.MaxDate;

              if StdItem.StdMode = 50 then begin
                ClientItem.s.Name := ClientItem.s.Name + ' #' + IntToStr(UserItem.Dura);
              end;
              sSENDMSG := EncodeBuffer(@ClientItem, SizeOf(TClientItem));
              DelBagItem(UserItem01.MakeIndex, StdItem01.Name);
              DelBagItem(UserItem02.MakeIndex, StdItem02.Name);
            end else begin //升级失败
              n14 := -2;
          //减属性
              if StdItem02.AniCount > 0 then begin
                UserEngine.RandomUpgradeItem_(UserItem, StdItem02.AniCount, 1);
                if StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26] then begin
                  if (StdItem.Shape = 130) or (StdItem.Shape = 131) or (StdItem.Shape = 132) then begin
                    UserEngine.GetUnknowItemValue_(UserItem, StdItem02.AniCount, 1);
                  end;
                end;
              end else begin
                ItemUnit.UpgradeItems_(UserItem, StdItem02, 1);
              end;
            {if StdItem02.DuraMax > 100 then begin
              nRate := (StdItem02.DuraMax - 100) div 2;
            end else begin
              nRate := (100 - StdItem02.DuraMax) div 2;
            end; }
              nRate := StdItem02.DuraMax;
              if Random(nRate) = 0 then begin //破碎
                n14 := -1;
              //Dispose(UserItem);
              //DelBagItem(ItemIndexs[0]);
                DelBagItem(UserItem.MakeIndex, StdItem.Name);
              end else begin
              {if StdItem02.NeedLevel > 100 then begin
                nRate := (StdItem02.NeedLevel - 100) div 2;
              end else begin
                nRate := (100 - StdItem02.NeedLevel) div 2;
              end;}
                nRate := StdItem02.NeedLevel;
                if Random(nRate) = 0 then begin //降级
                  UserItem.AddValue[13] := _MAX(UserItem.AddValue[13] - StdItem02.Need, 0);
                end;

                ClientItem.s := StdItem^;
                ItemUnit.GetItemAddValue(UserItem, ClientItem.s);
              //Move(StdItem80, ClientItem.s, SizeOf(TStdItem));
                sUserItemName := '';
                if UserItem.btValue[13] = 1 then
                  sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
                if sUserItemName <> '' then
                  ClientItem.s.Name := sUserItemName;

                if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
                  ClientItem.s.Name := '(绑)' + ClientItem.s.Name;
                end;

                ClientItem.MakeIndex := UserItem.MakeIndex;
                ClientItem.Dura := UserItem.Dura;
                ClientItem.DuraMax := UserItem.DuraMax;
                ClientItem.s.AddValue := UserItem.AddValue;
                ClientItem.s.AddPoint := UserItem.AddPoint;
                ClientItem.s.MaxDate := UserItem.MaxDate;
                //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

                if StdItem.StdMode = 50 then begin
                  ClientItem.s.Name := ClientItem.s.Name + ' #' + IntToStr(UserItem.Dura);
                end;
                sSENDMSG := EncodeBuffer(@ClientItem, SizeOf(TClientItem));
              end;
           { DelBagItem(ItemIndexs[1]);
            DelBagItem(ItemIndexs[2]); }
              DelBagItem(UserItem01.MakeIndex, StdItem01.Name);
              DelBagItem(UserItem02.MakeIndex, StdItem02.Name);
           { Dispose(UserItem01);
            Dispose(UserItem02);}
            end;
          end;
        end;
      end;
    end;
  end;
  if n14 <= 0 then begin
    m_DefMsg := MakeDefaultMsg(SM_SENDUPGRADEITEM_FAIL, n14, 0, 0, 0);
    SendSocket(@m_DefMsg, sSENDMSG);
  end else begin
    m_DefMsg := MakeDefaultMsg(SM_SENDUPGRADEITEM_OK, 0, 0, 0, 0);
    SendSocket(@m_DefMsg, sSENDMSG);
  end;
end;

procedure TPlayObject.AddCheckItems(ItemList: TList);
var
  I: Integer;
  CopyItem: pTCopyItem;
  StorageList: TStorageList;
  SellList: TSellList;
  ActorObject: TActorObject;
  //HeroObject: THeroObject;
begin
  if (not g_Config.boCopyItemClear) then begin
    for I := 0 to ItemList.Count - 1 do begin
      Dispose(pTCopyItem(ItemList.Items[I]));
    end;
    ItemList.Clear;
    Exit;
  end;

  for I := Low(m_UseItems) to High(m_UseItems) do begin
    if m_UseItems[I].wIndex > 0 then begin
      New(CopyItem);
      CopyItem.ItemType := t_UseItem;
      CopyItem.OwnerObj := Self;
      CopyItem.OwnerAddr := @m_UseItems[I];
      CopyItem.UserItem := m_UseItems[I];
      ItemList.Add(CopyItem);
    end;
  end;

  for I := 0 to m_ItemList.Count - 1 do begin
    New(CopyItem);
    CopyItem.ItemType := t_BagItem;
    CopyItem.OwnerObj := Self;
    CopyItem.UserItem := pTUserItem(m_ItemList.Items[I])^;
    ItemList.Add(CopyItem);
  end;

  for I := 0 to m_StorageItemList.Count - 1 do begin
    New(CopyItem);
    CopyItem.ItemType := t_StorageItem;
    CopyItem.OwnerObj := Self;
    CopyItem.OwnerAddr := nil;
    CopyItem.UserItem := pTUserItem(m_StorageItemList.Items[I])^;
    ItemList.Add(CopyItem);
  end;

  if (m_MyHero <> nil) and (not THeroObject(m_MyHero).m_boHeroLogOut) and (not m_MyHero.m_boDeath) and (not m_MyHero.m_boGhost) then begin
    for I := Low(m_MyHero.m_UseItems) to High(m_MyHero.m_UseItems) do begin
      if m_MyHero.m_UseItems[I].wIndex > 0 then begin
        New(CopyItem);
        CopyItem.ItemType := t_UseItem;
        CopyItem.OwnerObj := m_MyHero;
        CopyItem.OwnerAddr := @m_MyHero.m_UseItems[I];
        CopyItem.UserItem := m_MyHero.m_UseItems[I];
        ItemList.Add(CopyItem);
      end;
    end;

    for I := 0 to m_MyHero.m_ItemList.Count - 1 do begin
      New(CopyItem);
      CopyItem.ItemType := t_BagItem;
      CopyItem.OwnerObj := m_MyHero;
      CopyItem.UserItem := pTUserItem(m_MyHero.m_ItemList.Items[I])^;
      ItemList.Add(CopyItem);
    end;
  end;

  StorageList := nil;
  if (g_Storage.GetStorageList(m_sCharName, StorageList) >= 0) and (StorageList <> nil) then begin
    for I := 0 to StorageList.Count - 1 do begin
      New(CopyItem);
      CopyItem.ItemType := t_BigStorageItem;
      CopyItem.OwnerObj := Self;
      CopyItem.OwnerAddr := StorageList.Items[I];
      CopyItem.UserItem := pTBigStorage(StorageList.Items[I]).UserItem;
      ItemList.Add(CopyItem);
    end;
  end;

  SellList := nil;
  if (g_SellList.GetSellListByCharName(m_sCharName, SellList) >= 0) and (SellList <> nil) then begin
    for I := 0 to SellList.Count - 1 do begin
      New(CopyItem);
      CopyItem.ItemType := t_SellOffItem;
      CopyItem.OwnerObj := Self;
      CopyItem.OwnerAddr := SellList.Objects[I];
      CopyItem.UserItem := pTSellOffInfo(SellList.Objects[I]).UserItem;
      ItemList.Add(CopyItem);
    end;
  end;
end;

procedure TPlayObject.ClearCopyItems(ItemList: TList);
var
  I, II, nIndex: Integer;
  CopyItem: TCopyItem;
  CopyItem01: TCopyItem;
  StorageList: TStorageList;
  SellList: TSellList;
  ActorObject: TActorObject;
  PlayObject: TPlayObject;
  HeroObject: THeroObject;
begin
  if (not g_Config.boCopyItemClear) { and (ItemList.Count = 0) } then begin
    for I := 0 to ItemList.Count - 1 do begin
      Dispose(pTCopyItem(ItemList.Items[I]));
    end;
    ItemList.Clear;
    Exit;
  end;

  for I := 0 to ItemList.Count - 1 do begin
    //Application.ProcessMessages;
    CopyItem := pTCopyItem(ItemList.Items[I])^;
    Dispose(pTCopyItem(ItemList.Items[I]));

    if CopyItem.OwnerObj = nil then Continue;
    for II := I + 1 to ItemList.Count - 2 do begin
      //Application.ProcessMessages;
      CopyItem01 := pTCopyItem(ItemList.Items[II])^;
      if CopyItem.UserItem.MakeIndex = CopyItem01.UserItem.MakeIndex then begin
        case CopyItem01.ItemType of
          t_UseItem: begin
              ActorObject := TActorObject(CopyItem01.OwnerObj);
              case ActorObject.m_btRaceServer of
                RC_PLAYOBJECT: begin
                    TPlayObject(ActorObject).SendDelItems(@CopyItem01.UserItem);
                    MainOutMessage('(Body) Duplicate Items removed:' + ActorObject.m_sCharName + ' Equipment Name:' + UserEngine.GetStdItemName(CopyItem01.UserItem.wIndex) + ' MakeIndex:' + IntToStr(CopyItem01.UserItem.MakeIndex));
                  end;
                RC_HEROOBJECT: begin
                    THeroObject(ActorObject).SendDelItems(@CopyItem01.UserItem);
                    MainOutMessage('(Body) Duplicate Hero Items removed:' + ActorObject.m_sCharName + ' Equipment Name:' + UserEngine.GetStdItemName(CopyItem01.UserItem.wIndex) + ' MakeIndex:' + IntToStr(CopyItem01.UserItem.MakeIndex));
                  end;
              end;
              if CopyItem01.OwnerAddr <> nil then
                pTUserItem(CopyItem01.OwnerAddr).wIndex := 0;
              CopyItem01.UserItem.wIndex := 0;
              CopyItem01.OwnerObj := nil;
            end;
          t_BagItem: begin
              ActorObject := TActorObject(CopyItem01.OwnerObj);
              case ActorObject.m_btRaceServer of
                RC_PLAYOBJECT: begin
                    MainOutMessage('(Bag) Duplicate Item removed:' + ActorObject.m_sCharName + ' Equipment Name:' + UserEngine.GetStdItemName(CopyItem01.UserItem.wIndex) + ' MakeIndex:' + IntToStr(CopyItem01.UserItem.MakeIndex));
                    TPlayObject(ActorObject).SendDelItems(@CopyItem01.UserItem);
                    TPlayObject(ActorObject).DelBagItem(CopyItem01.UserItem.MakeIndex, UserEngine.GetStdItemName(CopyItem01.UserItem.wIndex));
                  end;
                RC_HEROOBJECT: begin
                    MainOutMessage('(Bag) Duplicate Hero Item removed:' + ActorObject.m_sCharName + ' Equipment Name:' + UserEngine.GetStdItemName(CopyItem01.UserItem.wIndex) + ' MakeIndex:' + IntToStr(CopyItem01.UserItem.MakeIndex));
                    THeroObject(ActorObject).SendDelItems(@CopyItem01.UserItem);
                    THeroObject(ActorObject).DelBagItem(CopyItem01.UserItem.MakeIndex, UserEngine.GetStdItemName(CopyItem01.UserItem.wIndex));
                  end;
              end;
              CopyItem01.OwnerObj := nil;
            end;
          t_StorageItem: begin
              ActorObject := TActorObject(CopyItem01.OwnerObj);
              MainOutMessage('(Storage) Duplicate item removed:' + ActorObject.m_sCharName + ' Equipment Name:' + UserEngine.GetStdItemName(CopyItem01.UserItem.wIndex) + ' MakeIndex:' + IntToStr(CopyItem01.UserItem.MakeIndex));
              case ActorObject.m_btRaceServer of
                RC_PLAYOBJECT: begin
                    TPlayObject(ActorObject).DelStorageItem(CopyItem01.UserItem.MakeIndex, UserEngine.GetStdItemName(CopyItem01.UserItem.wIndex));
                  end;
              end;
              CopyItem01.OwnerObj := nil;
            end;
          t_BigStorageItem: begin
              ActorObject := TActorObject(CopyItem01.OwnerObj);
              MainOutMessage('(Big Storage) Duplicate item removed:' + ActorObject.m_sCharName + ' Equipment Name:' + UserEngine.GetStdItemName(CopyItem01.UserItem.wIndex) + ' MakeIndex:' + IntToStr(CopyItem01.UserItem.MakeIndex));
              g_Storage.Delete(ActorObject.m_sCharName, pTBigStorage(CopyItem01.OwnerAddr));
              CopyItem01.OwnerObj := nil;
            end;
          t_SellOffItem: begin
              ActorObject := TActorObject(CopyItem01.OwnerObj);
              MainOutMessage('(Commission) Duplicate item removed:' + ActorObject.m_sCharName + ' Equipment Name:' + UserEngine.GetStdItemName(CopyItem01.UserItem.wIndex) + ' MakeIndex:' + IntToStr(CopyItem01.UserItem.MakeIndex));
              g_SellList.Delete(pTSellOffInfo(CopyItem01.OwnerAddr), True);
              CopyItem01.OwnerObj := nil;
            end;
        end;
      end;
    end;
  end;
  ItemList.Clear;
end;

procedure TPlayObject.ClearCopyItems();
var
  ItemList: TList;
begin
  //清除复制装备
  if g_Config.boCopyItemClear then begin
    ItemList := TList.Create;
    AddCheckItems(ItemList);
    ClearCopyItems(ItemList);
    ItemList.Free;
  end;
end;

procedure TActorObject.GetGroupItemList;
var
  I: Integer;
begin
  m_GroupItemList.Clear;
  for I := 0 to m_GroupItem.Count - 1 do
    m_GroupItemList.Add(m_GroupItem.Items[I]);
end;

procedure TActorObject.SendGroupItemMsg();
var
  I, II: Integer;
  boFind: Boolean;
  GroupItem: pTGroupItem;
begin
  if m_GroupItem.Count > 0 then begin
    if m_GroupItemList.Count <= 0 then begin
      for I := 0 to m_GroupItem.Count - 1 do begin
        GroupItem := m_GroupItem.Items[I];
        if GroupItem.FLD_HINTMSG <> '' then begin
          SysMsg(GroupItem.FLD_HINTMSG, c_Red, t_Hint);
        end;
      end;
    end else begin
      for I := 0 to m_GroupItem.Count - 1 do begin
        GroupItem := m_GroupItem.Items[I];
        boFind := False;
        for II := 0 to m_GroupItemList.Count - 1 do begin
          if GroupItem = m_GroupItemList.Items[II] then begin
            boFind := True;
            break;
          end;
        end;
        if not boFind then begin
          if GroupItem.FLD_HINTMSG <> '' then begin
            SysMsg(GroupItem.FLD_HINTMSG, c_Red, t_Hint);
          end;
        end;
      end;
    end;
  end;
end;

procedure TPlayObject.ClientTakeOnItemsEx(btWhere: Byte; nItemIdx: Integer; sItemName: string);
var
  I, n14: Integer;
  UserItem, TakeOffItem: pTUserItem;
  UserItem01: TUserItem;
  StdItem, StdItem20: pTStdItem;
  StdItem58: TStdItem;
  sUserItemName: string;
  boFind: Boolean;
begin
  StdItem := nil;
  UserItem := nil;
  UserItem01.wIndex := 0;
  boFind := False;
  n14 := -1;
  if m_boStore then begin
    Exit;
  end;
  for I := 0 to m_ItemList.Count - 1 do begin
    UserItem := m_ItemList.Items[I];
    if UserItem.MakeIndex = nItemIdx then begin
      //取自定义物品名称
      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName = '' then
        sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

      if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        sUserItemName := '( )' + sUserItemName;
      end;

      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if StdItem <> nil then begin
        if CompareText(sUserItemName, sItemName) = 0 then begin
          n14 := I;
          UserItem01 := UserItem^;
          boFind := True;
          Break;
        end;
      end;
    end;
    UserItem := nil;
  end;

  if (StdItem <> nil) and (UserItem <> nil) and boFind then begin
    if CheckUserItems(btWhere, StdItem) then begin
      StdItem58 := StdItem^;
      ItemUnit.GetItemAddValue(UserItem, StdItem58);
      if CheckTakeOnItems(btWhere, StdItem58) and (CheckItemBindUse(UserItem, True) <> 1) then begin
        TakeOffItem := nil;
        if btWhere in [0..18] then begin
          if m_UseItems[btWhere].wIndex > 0 then begin
            StdItem20 := UserEngine.GetStdItem(m_UseItems[btWhere].wIndex);
            if (StdItem20 <> nil) and
              (StdItem20.StdMode in [15, 19, 20, 21, 22, 23, 24, 26]) then begin
              if (not m_boUserUnLockDurg) and (m_UseItems[btWhere].btValue[7] <> 0) then begin
                SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                Exit;
              end;
            end;
            if not m_boUserUnLockDurg and ((StdItem20.Reserved and 2) <> 0) then begin
              SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
              Exit;
            end;
            if ((StdItem20.Reserved and 4) <> 0) then begin
              SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
              Exit;
            end;
            if InDisableTakeOffList(m_UseItems[btWhere].wIndex) then begin
              SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
              Exit;
            end;
            New(TakeOffItem);
            TakeOffItem^ := m_UseItems[btWhere];
          end; //004DAEC7 if m_UseItems[btWhere].wIndex > 0 then begin

          GetGroupItemList;
          if (StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26]) and
            (UserItem.btValue[8] <> 0) then
            UserItem.btValue[8] := 0;

          m_UseItems[btWhere] := UserItem^;
          DelBagItem(n14);
          //DelBagItem(UserItem01.MakeIndex, StdItem.Name);
          if TakeOffItem <> nil then begin
            if AddItemToBag(TakeOffItem) then begin
              SendAddItem(TakeOffItem);
            end else Dispose(TakeOffItem);
          end;
          RecalcAbilitys();
          SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
          SendMsg(Self, RM_SUBABILITY, 0, 0, 0, 0, '');
          SendDefMessage(SM_TAKEONITEM, nItemIdx, btWhere, 0, 0, sItemName);
          //SendDefMessage(SM_TAKEON_OK, GetFeatureToLong, GetFeatureEx, 0, 0, IntToStr(btWhere) + '/' + sItemName + '/' + IntToStr(nItemIdx));
          FeatureChanged();
          if btWhere = U_HELMET then RefShowName;

          SendGroupItemMsg();

          if g_FunctionNPC <> nil then begin
            g_FunctionNPC.GotoLable(Self, 'TakeOn' + IntToStr(btWhere), False);
          end;
          TakeOnGroupItem(@m_UseItems[btWhere]);
        end;
      end;
    end;
  end;
end;

procedure TPlayObject.ClientTakeOffItemsEx(btWhere: Byte; nItemIdx: Integer; sItemName: string);
var
  StdItem: pTStdItem;
  UserItem: pTUserItem;
  sUserItemName: string;
begin
  if (not m_boNotOnlineAddExp) and (not m_boDealing) and (not m_boDueling) and (not m_boStore) and (btWhere < 13) then begin
    if m_UseItems[btWhere].wIndex > 0 then begin
      if m_UseItems[btWhere].MakeIndex = nItemIdx then begin
        StdItem := UserEngine.GetStdItem(m_UseItems[btWhere].wIndex);
        if (StdItem <> nil) and
          (StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26]) then begin
          if (not m_boUserUnLockDurg) and (m_UseItems[btWhere].btValue[7] <> 0) then begin
            SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
            Exit;
          end;
        end;
        if not m_boUserUnLockDurg and ((StdItem.Reserved and 2) <> 0) then begin
          SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
          Exit;
        end;
        if ((StdItem.Reserved and 4) <> 0) then begin
          SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
          Exit;
        end;
        if InDisableTakeOffList(m_UseItems[btWhere].wIndex) then begin
          SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
          Exit;
        end;
        //取自定义物品名称
        sUserItemName := '';
        if m_UseItems[btWhere].btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(m_UseItems[btWhere].MakeIndex, m_UseItems[btWhere].wIndex);
        if sUserItemName = '' then
          sUserItemName := UserEngine.GetStdItemName(m_UseItems[btWhere].wIndex);

        if CheckItemBindUse(@m_UseItems[btWhere], False) = 2 then begin //绑定物品
          sUserItemName := '( )' + sUserItemName;
        end;

        if CompareText(sUserItemName, sItemName) = 0 then begin
          New(UserItem);
          UserItem^ := m_UseItems[btWhere];
          if AddItemToBag(UserItem) then begin
            m_UseItems[btWhere].wIndex := 0;

            SendAddItem(UserItem);
            RecalcAbilitys();
            SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
            SendMsg(Self, RM_SUBABILITY, 0, 0, 0, 0, '');

            SendDefMessage(SM_TAKEOFFITEM, nItemIdx, btWhere, 0, 0, sItemName);

            //SendDefMessage(SM_TAKEOFF_OK, GetFeatureToLong, GetFeatureEx, 0, 0,''{
              //IntToStr(btWhere) + '/' + sItemName + '/' + IntToStr(nItemIdx)});
            FeatureChanged();
            if btWhere = U_HELMET then RefShowName;
            if g_FunctionNPC <> nil then begin
              g_FunctionNPC.GotoLable(Self, 'TakeOff' + IntToStr(btWhere), False);
            end;
          end else begin
            Dispose(UserItem);
          end;
        end;
      end;
    end;
  end;
end;

procedure TPlayObject.ClientTakeOnItems(btWhere: Byte; nItemIdx: Integer; sItemName: string);
var
  I, n14, n18: Integer;
  UserItem, TakeOffItem: pTUserItem;
  UserItem01: TUserItem;
  StdItem, StdItem20: pTStdItem;
  StdItem58: TStdItem;
  sUserItemName: string;
  boFind: Boolean;
label FailExit;
begin
  StdItem := nil;
  UserItem := nil;
  UserItem01.wIndex := 0;
  boFind := False;
  n14 := -1;
  if m_boStore then begin
    goto FailExit;
  end;
  for I := 0 to m_ItemList.Count - 1 do begin
    UserItem := m_ItemList.Items[I];
    if UserItem.MakeIndex = nItemIdx then begin
      //取自定义物品名称
      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName = '' then
        sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

      if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        sUserItemName := '(绑)' + sUserItemName;
      end;

      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if StdItem <> nil then begin
        if CompareText(sUserItemName, sItemName) = 0 then begin
          n14 := I;
          UserItem01 := UserItem^;
          boFind := True;
          Break;
        end;
      end;
    end;
    StdItem := nil;
    UserItem := nil;
  end;
  n18 := 0;
  if (StdItem <> nil) and (UserItem <> nil) and boFind then begin
    if CheckUserItems(btWhere, StdItem) then begin
      StdItem58 := StdItem^;
      ItemUnit.GetItemAddValue(UserItem, StdItem58);
      if CheckTakeOnItems(btWhere, StdItem58) and (CheckItemBindUse(UserItem, True) <> 1) then begin
        TakeOffItem := nil;
        if btWhere in [0..18] then begin
          if m_UseItems[btWhere].wIndex > 0 then begin
            StdItem20 := UserEngine.GetStdItem(m_UseItems[btWhere].wIndex);
            if (StdItem20 <> nil) and
              (StdItem20.StdMode in [15, 19, 20, 21, 22, 23, 24, 26]) then begin
              if (not m_boUserUnLockDurg) and (m_UseItems[btWhere].btValue[7] <> 0) then begin
                SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
                n18 := -4;
                goto FailExit;
              end;
            end;
            if not m_boUserUnLockDurg and ((StdItem20.Reserved and 2) <> 0) then begin
              SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
              n18 := -4;
              goto FailExit;
            end;
            if ((StdItem20.Reserved and 4) <> 0) then begin
              SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
              n18 := -4;
              goto FailExit;
            end;
            if InDisableTakeOffList(m_UseItems[btWhere].wIndex) then begin
              SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
              goto FailExit;
            end;
            New(TakeOffItem);
            TakeOffItem^ := m_UseItems[btWhere];
          end; //004DAEC7 if m_UseItems[btWhere].wIndex > 0 then begin
          //GroupItem := m_GroupItem;

          GetGroupItemList;

          if (StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26]) and
            (UserItem.btValue[8] <> 0) then
            UserItem.btValue[8] := 0;

          m_UseItems[btWhere] := UserItem^;
          DelBagItem(n14);
          //DelBagItem(UserItem01.MakeIndex, StdItem.Name);
          if TakeOffItem <> nil then begin
            if AddItemToBag(TakeOffItem) then begin
              SendAddItem(TakeOffItem);
            end else Dispose(TakeOffItem);
          end;
          RecalcAbilitys();
          SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
          SendMsg(Self, RM_SUBABILITY, 0, 0, 0, 0, '');
          SendDefMessage(SM_TAKEON_OK, GetFeatureToLong, GetFeatureEx, 0, 0, '' {IntToStr(btWhere) + '/' + sItemName + '/' + IntToStr(nItemIdx)});
          FeatureChanged();
          if btWhere = U_HELMET then RefShowName;

          SendGroupItemMsg();

          n18 := 1;
          if g_FunctionNPC <> nil then begin
            g_FunctionNPC.GotoLable(Self, 'TakeOn' + IntToStr(btWhere), False);
          end;
          TakeOnGroupItem(@m_UseItems[btWhere]);
        end;
      end else n18 := -1;
    end else n18 := -1;
  end;
  FailExit:
  if n18 <= 0 then begin
    SendDefMessage(SM_TAKEON_FAIL, n18, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.ClientTakeOffItems(btWhere: Byte; nItemIdx: Integer; sItemName: string);
var
  n10: Integer;
  StdItem: pTStdItem;
  UserItem: pTUserItem;
  sUserItemName: string;
label FailExit;
begin
  n10 := 0;
  if (not m_boNotOnlineAddExp) and (not m_boDealing) and (not m_boDueling) and (not m_boStore) and (btWhere < 13) then begin
    if m_UseItems[btWhere].wIndex > 0 then begin
      if m_UseItems[btWhere].MakeIndex = nItemIdx then begin
        StdItem := UserEngine.GetStdItem(m_UseItems[btWhere].wIndex);
        if (StdItem <> nil) and
          (StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26]) then begin
          if (not m_boUserUnLockDurg) and (m_UseItems[btWhere].btValue[7] <> 0) then begin
            SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
            n10 := -4;
            goto FailExit;
          end;
        end;
        if not m_boUserUnLockDurg and ((StdItem.Reserved and 2) <> 0) then begin
          SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
          n10 := -4;
          goto FailExit;
        end;
        if ((StdItem.Reserved and 4) <> 0) then begin
          SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
          n10 := -4;
          goto FailExit;
        end;
        if InDisableTakeOffList(m_UseItems[btWhere].wIndex) then begin
          SysMsg(g_sCanotTakeOffItem {'无法取下物品！！！'}, c_Red, t_Hint);
          goto FailExit;
        end;
        //取自定义物品名称
        sUserItemName := '';
        if m_UseItems[btWhere].btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(m_UseItems[btWhere].MakeIndex, m_UseItems[btWhere].wIndex);
        if sUserItemName = '' then
          sUserItemName := UserEngine.GetStdItemName(m_UseItems[btWhere].wIndex);

        if CheckItemBindUse(@m_UseItems[btWhere], False) = 2 then begin //绑定物品
          sUserItemName := '(绑)' + sUserItemName;
        end;

        if CompareText(sUserItemName, sItemName) = 0 then begin
          New(UserItem);
          UserItem^ := m_UseItems[btWhere];
          if AddItemToBag(UserItem) then begin
            m_UseItems[btWhere].wIndex := 0;

            SendAddItem(UserItem);
            RecalcAbilitys();
            SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
            SendMsg(Self, RM_SUBABILITY, 0, 0, 0, 0, '');
            SendDefMessage(SM_TAKEOFF_OK, GetFeatureToLong, GetFeatureEx, 0, 0, ''
              {IntToStr(btWhere) + '/' + sItemName + '/' + IntToStr(nItemIdx)});
            FeatureChanged();
            if btWhere = U_HELMET then RefShowName;
            if g_FunctionNPC <> nil then begin
              g_FunctionNPC.GotoLable(Self, 'TakeOff' + IntToStr(btWhere), False);
            end;
          end else begin
            Dispose(UserItem);
            n10 := -3;
          end;
        end;
      end;
    end else n10 := -2;
  end else n10 := -1;
  FailExit:
  if n10 <= 0 then
    SendDefMessage(SM_TAKEOFF_FAIL, n10, 0, 0, 0, '');
end;

procedure TPlayObject.RepairAllItem();
var
  nWhere: Integer;
  sCheckItemName: string;
  StdItem: pTStdItem;
begin
  for nWhere := Low(THumanUseItems) to High(THumanUseItems) do begin
    if m_UseItems[nWhere].wIndex > 0 then begin
      StdItem := UserEngine.GetStdItem(m_UseItems[nWhere].wIndex);
      if StdItem <> nil then begin
        if (m_UseItems[nWhere].DuraMax > m_UseItems[nWhere].Dura) and (StdItem.StdMode <> 43) then begin
          if Assigned(PlugInEngine.CheckCanRepairItem) then begin
            sCheckItemName := StdItem.Name;
            if not PlugInEngine.CheckCanRepairItem(m_Master, PChar(sCheckItemName), False) then Continue;
          end;
          m_UseItems[nWhere].Dura := m_UseItems[nWhere].DuraMax;
          SendMsg(Self, RM_DURACHANGE, nWhere, m_UseItems[nWhere].Dura, m_UseItems[nWhere].DuraMax, 0, '');
        end;
      end;
    end;
  end;
end;

procedure TPlayObject.RandomMove();
var
  nX, nY: Integer;
begin
  if m_PEnvir = nil then Exit;
  nX := Random(m_PEnvir.m_nWidth);
  nY := Random(m_PEnvir.m_nHeight);
  SendRefMsg(RM_SPACEMOVE_FIRE, 0, 0, 0, 0, '');
  SpaceMove(m_sMapName, nX, nY, 0);
end;

procedure TPlayObject.ClientUseItems(nItemIdx: Integer; sItemName: string);
  function GetUnbindItemName(nShape: Integer): string;
  var
    I: Integer;
  begin
    Result := '';
    for I := 0 to g_UnbindList.Count - 1 do begin
      if Integer(g_UnbindList.Objects[I]) = nShape then begin
        Result := g_UnbindList.Strings[I];
        Break;
      end;
    end;
  end;
  function GetUnBindItems(sItemName: string; nCount: Integer): Boolean;
  var
    I: Integer;
    UserItem: pTUserItem;
  begin
    Result := False;
    for I := 0 to nCount - 1 do begin
      New(UserItem);
      if UserEngine.CopyToUserItemFromName(sItemName, UserItem) then begin
        m_ItemList.Add(UserItem);
        SendAddItem(UserItem);
        Result := True;
      end else begin
        Dispose(UserItem);
        Break;
      end;
    end;
  end;
  function FoundUserItem(Item: pTUserItem): Boolean;
  var
    I: Integer;
    UserItem: pTUserItem;
  begin
    Result := False;
    for I := 0 to m_ItemList.Count - 1 do begin
      UserItem := m_ItemList.Items[I];
      if UserItem = Item then begin
        Result := True;
        Break;
      end;
    end;
  end;
var
  I, II: Integer;
  boEatOK: Boolean;
  boSendUpDate: Boolean;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
  UserItem34: TUserItem;
  //sMapName: string;
//  nCurrX, nCurrY: Integer;
  //nIndex: Integer;
begin
  boEatOK := False;
  boSendUpDate := False;
  StdItem := nil;
  UserItem := nil;
  if m_boCanUseItem and (not m_boStore) then begin
    if (not m_boDeath) and (m_WAbil.HP > 0) and ((GetTickCount - m_dwCanUseItemTick > g_Config.nUseItemSpeed) or (g_Config.nUseItemSpeed <= 0)) then begin //2008-05-16增加物品使用间隔
      m_dwCanUseItemTick := GetTickCount;
      ClearCopyItems(); //清除复制装备
      //nIndex := 0;
      for I := m_ItemList.Count - 1 downto 0 do begin
        if m_ItemList.Count <= 0 then Break;
        UserItem := m_ItemList.Items[I];
        //if UserItem = nil then Continue;
        if {(UserItem <> nil) and}(UserItem.MakeIndex = nItemIdx) then begin
          UserItem34 := UserItem^;
          StdItem := UserEngine.GetStdItem(UserItem.wIndex);
          if (StdItem <> nil) {and (CompareText(StdItem.Name, sItemName) = 0) } then begin
            if not m_PEnvir.AllowStdItems(UserItem.wIndex) then begin
              SysMsg(Format(g_sCanotMapUseItemMsg, [StdItem.Name]), c_Red, t_Hint);
              Break;
            end;
            case StdItem.StdMode of
              0, 1, 3: begin //药
                  if EatItems(StdItem) then begin
                    m_ItemList.Delete(I);
                    Dispose(UserItem);
                    UserItem := nil;
                    boEatOK := True;
                  end;
                  Break;
                end;
              2: begin
                  case StdItem.Shape of //修复神水
                    1: begin
                        if UserItem.Dura > 0 then begin
                          if UserItem.Dura >= 100 then begin
                            Dec(UserItem.Dura, 100);
                          end else begin
                            UserItem.Dura := 0;
                          end;
                          RepairAllItem();
                          boEatOK := True;
                        end;
                        if UserItem.Dura > 0 then begin
                          boSendUpDate := True;
                          boEatOK := False;
                          UserItem34 := UserItem^;
                        end else begin
                          m_ItemList.Delete(I);
                          Dispose(UserItem);
                          UserItem := nil;
                          boEatOK := True;
                        end;

                      end;
                    2: begin //随机传送石
                        if UserItem.Dura > 0 then begin
                          if UserItem.Dura >= 1000 then begin
                            Dec(UserItem.Dura, 1000);
                          end else begin
                            UserItem.Dura := 0;
                          end;
                          boEatOK := True;
                          RandomMove();
                        end;
                        if UserItem.Dura > 0 then begin
                          boSendUpDate := True;
                          boEatOK := False;
                          UserItem34 := UserItem^;
                        end else begin
                          m_ItemList.Delete(I);
                          Dispose(UserItem);
                          UserItem := nil;
                          boEatOK := True;
                        end;
                      end;
                    3: begin //使用次数
                        if UserItem.Dura > 0 then begin
                          if UserItem.Dura >= 1000 then begin
                            Dec(UserItem.Dura, 1000);
                          end else begin
                            UserItem.Dura := 0;
                          end;
                          UseStdmodeFunItem(StdItem);
                          boEatOK := True;
                        end;
                        if FoundUserItem(UserItem) then begin
                          if UserItem.Dura > 0 then begin
                            boSendUpDate := True;
                            boEatOK := False;
                            UserItem34 := UserItem^;
                          end else begin
                            m_ItemList.Delete(I);
                            Dispose(UserItem);
                            UserItem := nil;
                          end;
                        end;
                      end;
                  end;
                end;

              4: begin //书
                  if ReadBook(StdItem) then begin
                    if FoundUserItem(UserItem) then begin
                      m_ItemList.Delete(I);
                      Dispose(UserItem);
                      UserItem := nil;
                    end;
                    boEatOK := True;
                    if (m_MagicErgumSkill <> nil) and (not m_boUseThrusting) then begin
                      ThrustingOnOff(True);
                      SendSocket(nil, '+LNG/' + IntToStr(GetTickCount));
                    end;
                    if (m_MagicBanwolSkill <> nil) and (not m_boUseHalfMoon) then begin
                      HalfMoonOnOff(True);
                      SendSocket(nil, '+WID/' + IntToStr(GetTickCount));
                    end;
                  end;
                end;

              7: begin //使用次数
                  case StdItem.Shape of //修复神水
                    0: begin
                        if UserItem.Dura > 0 then begin
                          if UserItem.Dura >= 1000 then begin
                            Dec(UserItem.Dura, 1000);
                          end else begin
                            UserItem.Dura := 0;
                          end;

                          boEatOK := True;
                        end;
                        if UserItem.Dura > 0 then begin
                          boSendUpDate := True;
                          boEatOK := False;
                          UserItem34 := UserItem^;
                        end else begin
                          if FoundUserItem(UserItem) then begin
                            m_ItemList.Delete(I);
                            Dispose(UserItem);
                            UserItem := nil;
                          end;
                        end;
                      end;
                  end;
                end;

              49: begin //聚灵珠
                  //MainOutMessage(Format('UserItem.Dura:%d UserItem.DuraMax:%d', [UserItem.Dura, UserItem.DuraMax]));
                  if UserItem.Dura >= UserItem.DuraMax then begin
                    case StdItem.AniCount of
                      0: begin //元宝提取经验
                          if m_nGameGold >= StdItem.Need then begin
                            DecGameGold(StdItem.Need);
                            GameGoldChanged;
                            IncExp(UserItem.DuraMax * 10000);
                            if (m_MyHero <> nil) and (not m_MyHero.m_boDeath) then begin //给英雄分配经验
                              m_MyHero.IncExp(abs(UserItem.DuraMax * 10000 * g_Config.nHeroKillMonExpRate div 100) + 1);
                            end;
                            m_ItemList.Delete(I);
                            Dispose(UserItem);
                            UserItem := nil;
                            boEatOK := True;
                           { boSendUpDate := True;
                            boEatOK := False;
                            UserItem.Dura := 0;
                            FillChar(UserItem.btValue, SizeOf(TValue), 0);
                            UserItem34 := UserItem^;}
                          end else begin
                            boEatOK := False;
                            SendMsg(Self, RM_MENU_OK, 0, Integer(Self), 0, 0, 'Not enough' + g_Config.sGameGoldName + 'for this function!');
                          end;
                        end;
                      1: begin //金币提取经验
                          if m_nGold >= StdItem.Need then begin
                            Dec(m_nGold, StdItem.Need);
                            GoldChanged;

                            IncExp(UserItem.DuraMax * 10000);

                            if (m_MyHero <> nil) and (not m_MyHero.m_boDeath) then begin //给英雄分配经验
                              m_MyHero.IncExp(abs(UserItem.DuraMax * 10000 * g_Config.nHeroKillMonExpRate div 100) + 1);
                            end;
                            m_ItemList.Delete(I);
                            Dispose(UserItem);
                            UserItem := nil;
                            boEatOK := True;
                            {boSendUpDate := True;
                            boEatOK := False;
                            UserItem.Dura := 0;
                            FillChar(UserItem.btValue, SizeOf(TValue), 0);
                            UserItem34 := UserItem^; }

                          end else begin

                            boEatOK := False;
                            SendMsg(Self, RM_MENU_OK, 0, Integer(Self), 0, 0, 'Not enough Gold for this function!');
                          end;
                        end;
                      2: begin //声望提取经验

                          if m_btCreditPoint >= StdItem.Need then begin
                            Dec(m_btCreditPoint, StdItem.Need);

                            IncExp(UserItem.DuraMax * 10000);

                            if (m_MyHero <> nil) and (not m_MyHero.m_boDeath) then begin //给英雄分配经验
                              m_MyHero.IncExp(abs(UserItem.DuraMax * 10000 * g_Config.nHeroKillMonExpRate div 100) + 1);
                            end;

                            m_ItemList.Delete(I);
                            Dispose(UserItem);
                            UserItem := nil;
                            boEatOK := True;
                            {boSendUpDate := True;
                            boEatOK := False;
                            UserItem.Dura := 0;
                            FillChar(UserItem.btValue, SizeOf(TValue), 0);
                            UserItem34 := UserItem^;}
                          end else begin
                            boEatOK := False;

                            SendMsg(Self, RM_MENU_OK, 0, Integer(Self), 0, 0, 'Not enough Credit for this function!');
                          end;
                        end;
                      3: begin //活跃提取经验
                          if m_nGamePoint >= StdItem.Need then begin
                            DecGamePoint(StdItem.Need);
                            GameGoldChanged;
                            IncExp(UserItem.DuraMax * 10000);
                            if (m_MyHero <> nil) and (not m_MyHero.m_boDeath) then begin //给英雄分配经验
                              m_MyHero.IncExp(abs(UserItem.DuraMax * 10000 * g_Config.nHeroKillMonExpRate div 100) + 1);
                            end;
                            m_ItemList.Delete(I);
                            Dispose(UserItem);
                            UserItem := nil;
                            boEatOK := True;
                            {boSendUpDate := True;
                            boEatOK := False;
                            UserItem.Dura := 0;
                            FillChar(UserItem.btValue, SizeOf(TValue), 0);
                            UserItem34 := UserItem^;}
                          end else begin
                            boEatOK := False;
                            SendMsg(Self, RM_MENU_OK, 0, Integer(Self), 0, 0,'Not enough' + g_Config.sGamePointName + ' for this function!');
                          end;
                        end;
                    else boEatOK := False;
                    end;
                  end else begin
                    boEatOK := False;
                    SendMsg(Self, RM_MENU_OK, 0, Integer(Self), 0, 0, 'Unable to use.');
                  end;
                end;
              31: begin //解包物品
                  if StdItem.AniCount = 0 then begin
                    case StdItem.Shape of
                      13..16: begin
                          if m_SuperItemBox <> nil then begin
                            Dispose(m_SuperItemBox);
                            m_SuperItemBox := nil;
                          end;
                          New(m_SuperItemBox);
                          m_SuperItemBox.BoxStatus := b_ShowBox;
                          m_SuperItemBox.btBoxType := StdItem.Shape - 12;
                          m_SuperItemBox.sOpenBoxName := StdItem.Name;
                          m_SuperItemBox.btGiveBoxIndex := 10;
                          for II := Low(m_SuperItemBox.BoxItemArray) to High(m_SuperItemBox.BoxItemArray) do begin
                            m_SuperItemBox.BoxItemArray[II].StdItem.Name := '';
                          end;
                          m_SuperItemBox.UserItem := UserItem^;
                          m_ItemList.Delete(I);
                          Dispose(UserItem);
                          UserItem := nil;
                          SendDefMessage(SM_SHOWITEMBOX, m_SuperItemBox.btBoxType, 0, 0, 0, '');
                          boEatOK := True;
                        end;
                    else begin
                        if (m_ItemList.Count + 6 - 1) <= MAXBAGITEM then begin
                          if FoundUserItem(UserItem) then begin
                            m_ItemList.Delete(I);
                            Dispose(UserItem);
                            UserItem := nil;
                          end;
                          GetUnBindItems(GetUnbindItemName(StdItem.Shape), 6);
                          boEatOK := True;
                        end;
                      end;
                    end;
                  end else begin
                    case StdItem.Shape of
                      0: begin
                          if UseStdmodeFunItem(StdItem) then begin
                            if FoundUserItem(UserItem) then begin
                              m_ItemList.Delete(I);
                              Dispose(UserItem);
                              UserItem := nil;
                            end;
                            boEatOK := True;
                          end;
                        end;
                      1: begin
                          m_nRememberItemIndex := UserItem.MakeIndex;
                          m_sRememberItemName := StdItem.Name;
                          if UseStdmodeFunItem(StdItem) then begin //记忆石
                            boEatOK := False;
                            boSendUpDate := False;
                            {
                            if UserItem.Dura > 0 then begin
                              if UserItem.Dura >= 1000 then begin
                                Dec(UserItem.Dura, 1000);
                              end else begin
                                UserItem.Dura := 0;
                              end;
                              boEatOK := True;
                            end;

                            if UserItem.Dura > 0 then begin
                              boSendUpDate := True;
                              boEatOK := False;
                              UserItem34 := UserItem^;
                            end else begin
                              if FoundUserItem(UserItem) then begin
                                m_ItemList.Delete(I);
                                Dispose(UserItem);
                                UserItem := nil;
                              end;
                            end;
                            }
                          end;
                        end;
                      {
                      1: begin
                          if ItemDblClick(StdItem.Name, UserItem, sMapName, nCurrX, nCurrY) then begin
                            if FoundUserItem(UserItem) then begin
                              m_ItemList.Delete(I);
                              Dispose(UserItem);
                              UserItem := nil;
                            end;
                            SpaceMove(sMapName, nCurrX, nCurrY, 0);
                            boEatOK := True;
                          end else begin
                            boSendUpDate := m_boHeroVersion;
                            UserItem34 := UserItem^;
                            SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, '当前地图坐标保存成功！！！\再次双击，将传送到\地图：' + m_PEnvir.sMapDesc + ' 坐标：' + IntToStr(m_nCurrX) + ':' + IntToStr(m_nCurrY));
                          end;
                        end;
                        }
                    end;
                  end;
                end;
            end;
            Break;
          end;
        end;
      end;
    end;
  end else begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sCanotUseItemMsg);
  end;
  if boEatOK then begin
    WeightChanged();
    SendDefMessage(SM_EAT_OK, 0, 0, 0, 0, '');
    if StdItem.NeedIdentify = 1 then
      AddGameDataLog('11' + #9 +
        m_sMapName + #9 +
        IntToStr(m_nCurrX) + #9 +
        IntToStr(m_nCurrY) + #9 +
        m_sCharName + #9 +
        //UserEngine.GetStdItemName(UserItem34.wIndex) + #9 +
        StdItem.Name + #9 +
        IntToStr(UserItem34.MakeIndex) + #9 +
        '1' + #9 +
        '0');
  end else begin
    SendDefMessage(SM_EAT_FAIL, 0, 0, 0, 0, '');
    //MainOutMessage('SM_EAT_FAIL');
  end;
  if boSendUpDate then SendUpdateItem(@UserItem34);
end;

function TPlayObject.UseStdmodeFunItem(StdItem: pTStdItem): Boolean;
begin
  Result := False;
  if g_FunctionNPC <> nil then begin
    m_nScriptGotoCount := 0;
    g_FunctionNPC.GotoLable(Self, '@StdModeFunc' + IntToStr(StdItem.AniCount), False);
    Result := True;
  end;
end;
//人物死亡触发

function TPlayObject.DieFunc: Boolean;
begin
  Result := False;
  if g_FunctionNPC <> nil then begin
    m_nScriptGotoCount := 0;
    g_FunctionNPC.GotoLable(Self, '@PlayDie', False);
    Result := True;
  end;
end;
//人物升级触发

function TPlayObject.LevelUpFunc: Boolean;
begin
  Result := False;
  if g_FunctionNPC <> nil then begin
    m_nScriptGotoCount := 0;
    g_FunctionNPC.GotoLable(Self, '@PlayLevelUp', False);
    Result := True;
  end;
end;
//杀人触发

function TPlayObject.KillPlayFunc: Boolean;
begin
  Result := False;
  if g_FunctionNPC <> nil then begin
    m_nScriptGotoCount := 0;
    g_FunctionNPC.GotoLable(Self, '@KillPlay', False);
    Result := True;
  end;
end;

function TPlayObject.ClientGetButchItem(BaseObject: TActorObject; nX, nY: Integer; btDir: Byte; var dwDelayTime: LongWord): Boolean; //挖物品
var
  n10, n14: Integer;
  dwCheckTime: LongWord;
  boButchItem: Boolean;
begin
  Result := False;
  dwDelayTime := 0;
  dwCheckTime := GetTickCount - m_dwTurnTick;
  if dwCheckTime < g_Config.dwTurnIntervalTime then begin
    dwDelayTime := g_Config.dwTurnIntervalTime - dwCheckTime;
    Exit;
  end;
  m_dwTurnTick := GetTickCount;
  if (abs(nX - m_nCurrX) <= 2) and (abs(nY - m_nCurrY) <= 2) then begin

    if m_PEnvir.IsValidObject(nX, nY, 2, BaseObject) then begin
      if BaseObject.m_boDeath and (not BaseObject.m_boSkeleton) then begin
        if BaseObject.m_boAnimal or BaseObject.m_boButchItem then begin
          boButchItem := True;
          if ((BaseObject.m_btRaceServer in [RC_PLAYMOSTER]) or ((BaseObject.m_btRaceServer in [RC_PLAYOBJECT]) and BaseObject.m_boAI)) and (not BaseObject.m_boButchItemEx) then
            boButchItem := False;
          if (BaseObject.m_btRaceServer = RC_HEROOBJECT) then
            boButchItem := False;

          if boButchItem then begin
            n10 := Random(16) + 5;
            n14 := Random(201) + 100;
            Dec(BaseObject.m_nBodyLeathery, n10);
            Dec(BaseObject.m_nMeatQuality, n14);
            if BaseObject.m_nMeatQuality < 0 then BaseObject.m_nMeatQuality := 0;
            if BaseObject.m_nBodyLeathery <= 0 then begin
              if (BaseObject.m_btRaceServer >= RC_ANIMAL) and (BaseObject.m_btRaceServer < RC_MONSTER) or ({(BaseObject.m_btRaceServer = RC_PLAYMOSTER) and } BaseObject.m_boButchItem) then begin
                BaseObject.m_boSkeleton := True;
                ApplyMeatQuality();
                BaseObject.SendRefMsg(RM_SKELETON, BaseObject.m_btDirection, BaseObject.m_nCurrX, BaseObject.m_nCurrY, 0, '');
              end;
              if not TakeBagItems(BaseObject) then begin
                SysMsg(sYouFoundNothing {未发现任何物品！！！}, c_Red, t_Hint);
              end;
              BaseObject.m_nBodyLeathery := 50;
            end;
            m_dwDeathTick := GetTickCount();
          end;
        end;
      end;
    end;

    m_btDirection := btDir;
  end;
  SendRefMsg(RM_BUTCH, m_btDirection, m_nCurrX, m_nCurrY, 0, '');
end;

procedure TPlayObject.ClientChangeMagicKey(nSkillIdx, nKey: Integer);
var
  I: Integer;
  UserMagic: pTUserMagic;
begin
  for I := 0 to m_MagicList.Count - 1 do begin
    UserMagic := m_MagicList.Items[I];
    if UserMagic.MagicInfo.wMagicId = nSkillIdx then begin
      UserMagic.btKey := nKey;
      Break;
    end;
  end;
end;

procedure TPlayObject.ClientGropuClose;
begin
  if m_GroupOwner = nil then begin
    m_boAllowGroup := False;
    Exit;
  end;
  if m_GroupOwner <> Self then begin
    m_GroupOwner.DelMember(Self);
    m_boAllowGroup := False;
    m_GroupLevel:=1;
    m_GroupExp:=0;
    RecalcAbilitys;
  end else begin
    SysMsg('If you want to quit the group please use the Delete button.', c_Red, t_Hint);
  end;
end;

procedure TPlayObject.ClientCreateGroup(sHumName: string);
var
  PlayObject: TPlayObject;
begin
  PlayObject := UserEngine.GetPlayObject(sHumName);
  if m_GroupOwner <> nil then begin
    SendDefMessage(SM_CREATEGROUP_FAIL, -1, 0, 0, 0, '');
    Exit;
  end;
  if (PlayObject = nil) or (PlayObject = Self) or PlayObject.m_boDeath or PlayObject.m_boGhost then begin
    SendDefMessage(SM_CREATEGROUP_FAIL, -2, 0, 0, 0, '');
    Exit;
  end;
  if (PlayObject.m_GroupOwner <> nil) then begin
    SendDefMessage(SM_CREATEGROUP_FAIL, -3, 0, 0, 0, '');
    Exit;
  end;
  if (not PlayObject.m_boAllowGroup) then begin
    SendDefMessage(SM_CREATEGROUP_FAIL, -4, 0, 0, 0, '');
    Exit;
  end;
  m_GroupMembers.Clear;
  m_GroupLevel:=1;
  m_GroupExp:=0;
  //m_GroupMembers.AddObject(m_sCharName, Self);
  //m_GroupMembers.AddObject(sHumName, PlayObject);

  AddGroupMember(m_sCharName, Self);
  AddGroupMember(sHumName, PlayObject);

  JoinGroup(Self);
  PlayObject.JoinGroup(Self);

  m_boAllowGroup := True;
  SendDefMessage(SM_CREATEGROUP_OK, 0, 0, 0, 0, '');
  SendGroupMembers();
end;

procedure TPlayObject.ClientAddGroupMember(sHumName: string);
var
  PlayObject: TPlayObject;
begin
  PlayObject := UserEngine.GetPlayObject(sHumName);

  if m_GroupOwner <> Self then begin
    SendDefMessage(SM_GROUPADDMEM_FAIL, -1, 0, 0, 0, '');
    Exit;
  end;
  if m_GroupMembers.Count > g_Config.nGroupMembersMax then begin
    SendDefMessage(SM_GROUPADDMEM_FAIL, -5, 0, 0, 0, '');
    Exit;
  end;
  if (PlayObject = nil) or (PlayObject = Self) or PlayObject.m_boDeath or PlayObject.m_boGhost then begin
    SendDefMessage(SM_GROUPADDMEM_FAIL, -2, 0, 0, 0, '');
    Exit;
  end;
  if (PlayObject.m_GroupOwner <> nil) then begin
    SendDefMessage(SM_GROUPADDMEM_FAIL, -3, 0, 0, 0, '');
    Exit;
  end;
  if (not PlayObject.m_boAllowGroup) then begin
    SendDefMessage(SM_GROUPADDMEM_FAIL, -4, 0, 0, 0, '');
    Exit;
  end;

  AddGroupMember(sHumName, PlayObject);
  PlayObject.JoinGroup(Self);
  //m_GroupMembers.AddObject(sHumName, PlayObject);
  SendDefMessage(SM_GROUPADDMEM_OK, 0, 0, 0, 0, '');
  SendGroupMembers();
end;

procedure TPlayObject.ClientDelGroupMember(sHumName: string);
var
  PlayObject: TPlayObject;
begin
  PlayObject := UserEngine.GetPlayObject(sHumName);
  if m_GroupOwner <> Self then begin
    SendDefMessage(SM_GROUPDELMEM_FAIL, -1, 0, 0, 0, '');
    Exit;
  end;
  if PlayObject = nil then begin
    SendDefMessage(SM_GROUPDELMEM_FAIL, -2, 0, 0, 0, '');
    Exit;
  end;
  if not IsGroupMember(PlayObject) then begin
    SendDefMessage(SM_GROUPDELMEM_FAIL, -3, 0, 0, 0, '');
    Exit;
  end;
  DelMember(PlayObject);
  SendDefMessage(SM_GROUPDELMEM_OK, 0, 0, 0, 0, sHumName);
end;

procedure TPlayObject.ClientDealTry(PlayObject: TPlayObject);
var
//  BaseObject: TActorObject;
  ItemList: TList;
  sCharName1, sCharName2: string;
  nIndex1, nIndex2: Integer;
  Envir: TEnvirnoment;

//  nLen, nLen1: Integer;
//  sBuffer: string;
//  UserReg: TUserReg;
begin
  if m_boDeath or m_boGhost or (m_boDealing) or (m_boDueling) or m_boStore then Exit;

  if m_boIsUnknowActor or PlayObject.m_boIsUnknowActor then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, 'Player not found.');
    Exit;
  end;

  m_boDealType := False;

  if g_Config.boDisableDeal then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sDisableDuelItemsMsg);
    Exit;
  end;

  if GetTickCount - m_DealLastTick < g_Config.dwTryDealTime {3000} then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sPleaseTryDuelLaterMsg);
    Exit;
  end;
  if not m_boCanDeal then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sCanotTryDuelMsg);
    Exit;
  end;

  if (PlayObject <> nil) and (PlayObject <> Self) then begin
    if (PlayObject.GetPoseCreate = Self) and (not PlayObject.m_boDealing) and (not PlayObject.m_boDeath) and (not PlayObject.m_boGhost) then begin
      if (PlayObject.m_boAllowDeal and PlayObject.m_boCanDeal) then begin
        PlayObject.m_boDealType := False;
        m_StartDuelCreat := Self;
        PlayObject.m_StartDuelCreat := Self;

        Envir := g_MapManager.GetDuelMap;
        if (Envir = nil) then begin
          SysMsg('No Map available to Dual on, please wait.', c_Red, t_Hint);
          PlayObject.SysMsg('No Map available to Dual on, please wait.', c_Red, t_Hint);
          SendDefMessage(SM_DUELTRY_FAIL, 0, 0, 0, 0, '');
          Exit;
        end;

        PlayObject.SysMsg(m_sCharName + g_sOpenedDuelMsg, c_Green, t_Hint);
        SysMsg(PlayObject.m_sCharName + g_sOpenedDuelMsg, c_Green, t_Hint);

        sCharName1 := m_sCharName;
        sCharName2 := PlayObject.m_sCharName;
        if not g_DuelEngine.GetDuel(0, sCharName1, sCharName2, nIndex1, nIndex2) then begin

          PlayObject.SysMsg(m_sCharName + g_sOpenedDuelMsg, c_Green, t_Hint);
          SysMsg(PlayObject.m_sCharName + g_sOpenedDuelMsg, c_Green, t_Hint);
          TPlayObject(Self).OpenDealDlg(PlayObject);
          PlayObject.OpenDealDlg(Self);

  //清除复制装备
          ItemList := TList.Create;
          TPlayObject(Self).AddCheckItems(ItemList);
          PlayObject.AddCheckItems(ItemList);
          TPlayObject(Self).ClearCopyItems(ItemList);
          ItemList.Free;
        end else begin
          m_DuelTargetCret := PlayObject;
          PlayObject.m_DuelTargetCret := Self;

          m_boStartDuel := True;
          PlayObject.m_boStartDuel := True;

          if g_FunctionNPC <> nil then begin
            m_nScriptGotoCount := 0;
            PlayObject.m_nScriptGotoCount := 0;
            g_FunctionNPC.GotoLable(Self, '@GotoDuelMap', False);
            g_FunctionNPC.GotoLable(PlayObject, '@GotoDuelMap', False);
          end;
        end;
      end else begin
        SysMsg(g_sPoseDisableDuelMsg {'对方禁止进入交易'}, c_Red, t_Hint);
      end;
    end else begin
      SendDefMessage(SM_DEALTRY_FAIL, 0, 0, 0, 0, '');
    end;
  end else begin
    SendDefMessage(SM_DEALTRY_FAIL, 0, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.ClientDealTry(sHumName: string);
var
  BaseObject: TActorObject;
  PlayObject: TPlayObject;
  ItemList: TList;
begin
  if m_boDeath or m_boGhost or (m_boDealing) or (m_boDueling) or m_boStore then Exit;

  m_boDealType := True;

  if g_Config.boDisableDeal then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sDisableDealItemsMsg);
    Exit;
  end;
  if GetTickCount - m_DealLastTick < g_Config.dwTryDealTime {3000} then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sPleaseTryDealLaterMsg);
    Exit;
  end;
  if not m_boCanDeal then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sCanotTryDealMsg);
    Exit;
  end;

  BaseObject := GetPoseCreate();

  if (BaseObject <> nil) and (BaseObject <> Self) and (BaseObject.m_btRaceServer = RC_PLAYOBJECT) then begin
    PlayObject := TPlayObject(BaseObject);
    if (PlayObject <> nil) and (PlayObject.GetPoseCreate = Self) and (not PlayObject.m_boDealing) and (not PlayObject.m_boDeath) and (not PlayObject.m_boGhost) then begin
      if (PlayObject.m_boAllowDeal and PlayObject.m_boCanDeal) then begin
        if m_boIsUnknowActor or PlayObject.m_boIsUnknowActor then begin
          SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, 'Unable to Deal');
          Exit;
        end;
        PlayObject.m_boDealType := True;
        PlayObject.SysMsg(m_sCharName + g_sOpenedDealMsg, c_Green, t_Hint);
        SysMsg(PlayObject.m_sCharName + g_sOpenedDealMsg, c_Green, t_Hint);
        TPlayObject(Self).OpenDealDlg(PlayObject);
        PlayObject.OpenDealDlg(Self);
  //清除复制装备
        ItemList := TList.Create;
        TPlayObject(Self).AddCheckItems(ItemList);
        PlayObject.AddCheckItems(ItemList);
        TPlayObject(Self).ClearCopyItems(ItemList);
        ItemList.Free;
      end else begin
        SysMsg(g_sPoseDisableDealMsg {'对方禁止进入交易'}, c_Red, t_Hint);
      end;
    end else begin
      SendDefMessage(SM_DEALTRY_FAIL, 0, 0, 0, 0, '');
    end;
  end else begin
    SendDefMessage(SM_DEALTRY_FAIL, 0, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.ClientAddDealItem(nItemIdx: Integer; sItemName: string);
var
  I: Integer;
  bo11: Boolean;
  UserItem: pTUserItem;
  sUserItemName: string;
begin
  if m_boDeath or m_boGhost or (m_DealCreat = nil) or (not m_boDealing) then Exit;

  if Pos(' ', sItemName) >= 0 then begin //折分物品名称(信件物品的名称后面加了使用次数)
    GetValidStr3(sItemName, sItemName, [' ']);
  end;
  bo11 := False;
  if not m_DealCreat.m_boDealOK then begin
    for I := m_ItemList.Count - 1 downto 0 do begin
      if m_ItemList.Count <= 0 then Break;
      UserItem := m_ItemList.Items[I];
      if UserItem.MakeIndex = nItemIdx then begin

        if Assigned(PlugInEngine.CheckCanDealItem) then begin //禁止交易物品
          if not PlugInEngine.CheckCanDealItem(Self, PChar(UserEngine.GetStdItemName(UserItem.wIndex)), True) then Break;
        end;

        if g_Config.boBindItemNoDeal and (CheckItemBindUse(UserItem, True) = 2) then Break; //绑定物品禁止交易

        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName = '' then
          sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

        if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
          sUserItemName := '( )' + sUserItemName;
        end;

        if (CompareText(sUserItemName, sItemName) = 0) and
          (m_DealItemList.Count < 12) then begin
          m_DealItemList.Add(UserItem);
          TPlayObject(Self).SendAddDealItem(UserItem);
          m_ItemList.Delete(I);
          bo11 := True;
          Break;
        end;
      end;
    end;
  end;
  if not bo11 then
    SendDefMessage(SM_DEALADDITEM_FAIL, 0, 0, 0, 0, '');
end;

procedure TPlayObject.ClientDelDealItem(nItemIdx: Integer; sItemName: string);
var
  I: Integer;
  bo11: Boolean;
  UserItem: pTUserItem;
  sUserItemName: string;
begin
  if m_boDeath or m_boGhost or (m_DealCreat = nil) or (not m_boDealing) then Exit;

  if g_Config.boCanNotGetBackDeal then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sDealItemsDenyGetBackMsg);
    SendDefMessage(SM_DEALDELITEM_FAIL, 0, 0, 0, 0, '');
    Exit;
  end;

  if Pos(' ', sItemName) >= 0 then begin //折分物品名称(信件物品的名称后面加了使用次数)
    GetValidStr3(sItemName, sItemName, [' ']);
  end;

  bo11 := False;
  if not m_DealCreat.m_boDealOK then begin
    for I := m_DealItemList.Count - 1 downto 0 do begin
      if m_DealItemList.Count <= 0 then Break;
      UserItem := m_DealItemList.Items[I];
      if UserItem.MakeIndex = nItemIdx then begin
        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName = '' then
          sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

        if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
          sUserItemName := '( )' + sUserItemName;
        end;

        if CompareText(sUserItemName, sItemName) = 0 then begin
          m_ItemList.Add(UserItem);
          TPlayObject(Self).SendDelDealItem(UserItem);
          m_DealItemList.Delete(I);
          bo11 := True;
          Break;
        end;
      end;
    end;
  end;
  if not bo11 then
    SendDefMessage(SM_DEALDELITEM_FAIL, 0, 0, 0, 0, '');
end;

procedure TPlayObject.ClientCancelDeal;
begin
  DealCancel();
end;

procedure TPlayObject.ClientChangeDealGold(nGold: Integer);
var
  bo09: Boolean;
begin
  if m_boDeath or m_boGhost or (m_DealCreat = nil) or (not m_boDealing) then Exit;
  //禁止取回放入交易栏内的金币
  if m_boDealType then begin
    if (m_nDealGolds > 0) and g_Config.boCanNotGetBackDeal then begin
      SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sDealItemsDenyGetBackMsg);
      SendDefMessage(SM_DEALDELITEM_FAIL, 0, 0, 0, 0, '');
      Exit;
    end;
  end else begin
    if (m_nDealGolds > 0) and g_Config.boCanNotGetBackDuel then begin
      SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sDuelItemsDenyGetBackMsg);
      SendDefMessage(SM_DEALDELITEM_FAIL, 0, 0, 0, 0, '');
      Exit;
    end;
  end;
  if nGold < 0 then begin
    SendDefMessage(SM_DEALCHGGOLD_FAIL, m_nDealGolds, LoWord(m_nGold), HiWord(m_nGold), 0, '');
    Exit;
  end;
  bo09 := False;
  if (m_DealCreat <> nil) and (GetPoseCreate = m_DealCreat) then begin
    if not m_DealCreat.m_boDealOK then begin
      if m_boDealType then begin
        if (m_nGold + m_nDealGolds) >= nGold then begin
          m_nGold := (m_nGold + m_nDealGolds) - nGold;
          m_nDealGolds := nGold;
          SendDefMessage(SM_DEALCHGGOLD_OK, m_nDealGolds, LoWord(m_nGold), HiWord(m_nGold), 0, '');
          m_DealCreat.SendDefMessage(SM_DEALREMOTECHGGOLD, m_nDealGolds, 0, 0, 0, '');
          m_DealCreat.m_DealLastTick := GetTickCount();
          bo09 := True;
          m_DealLastTick := GetTickCount();
        end;
      end else begin
        if (m_nGameGold + m_nDealGolds) >= nGold then begin
          m_nGameGold := (m_nGameGold + m_nDealGolds) - nGold;
          m_nDealGolds := nGold;
          SendDefMessage(SM_DEALCHGGOLD_OK, m_nDealGolds, LoWord(m_nGold), HiWord(m_nGold), 0, '');
          GameGoldChanged();
          GoldChanged();
          m_DealCreat.SendDefMessage(SM_DEALREMOTECHGGOLD, m_nDealGolds, 0, 0, 0, '');
          m_DealCreat.m_DealLastTick := GetTickCount();
          bo09 := True;
          m_DealLastTick := GetTickCount();
        end;
      end;
    end;
  end;
  if not bo09 then begin
    SendDefMessage(SM_DEALCHGGOLD_FAIL, m_nDealGolds, LoWord(m_nGold), HiWord(m_nGold), 0, '');
  end;
end;

procedure TPlayObject.ClientDealEnd;
var
  I: Integer;
  bo11: Boolean;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
  PlayObject: TPlayObject;
  Envir: TEnvirnoment;
begin
  m_boDealOK := True;
  if (m_DealCreat = nil) or m_boGhost or m_boSoftClose then Exit;
  if (m_DealCreat <> nil) and (m_DealCreat.m_boGhost) then Exit;
  if m_boDealType then begin
    if ((GetTickCount - m_DealLastTick) < g_Config.dwDealOKTime {1000}) or ((GetTickCount - m_DealCreat.m_DealLastTick) < g_Config.dwDealOKTime {1000}) then begin
      SysMsg(g_sDealOKTooFast, c_Red, t_Hint);
      DealCancel();
      Exit;
    end;
  end else begin
    if ((GetTickCount - m_DealLastTick) < g_Config.dwDealOKTime {1000}) or ((GetTickCount - m_DealCreat.m_DealLastTick) < g_Config.dwDealOKTime {1000}) then begin
      SysMsg(g_sDuelOKTooFast, c_Red, t_Hint);
      DealCancel();
      Exit;
    end;
  end;
  if m_DealCreat.m_boDealOK then begin
    bo11 := True;
    if m_boDealType then begin
      if (MAXBAGITEM - m_ItemList.Count) < m_DealCreat.m_DealItemList.Count then begin
        bo11 := False;
        SysMsg(g_sYourBagSizeTooSmall, c_Red, t_Hint);
      end;
      if (m_nGoldMax - m_nGold) < m_DealCreat.m_nDealGolds then begin
        SysMsg(g_sYourGoldLargeThenLimit, c_Red, t_Hint);
        bo11 := False;
      end;
      if (MAXBAGITEM - m_DealCreat.m_ItemList.Count) < m_DealItemList.Count then begin
        SysMsg(g_sDealHumanBagSizeTooSmall, c_Red, t_Hint);
        bo11 := False;
      end;
      if (m_DealCreat.m_nGoldMax - m_DealCreat.m_nGold) < m_nDealGolds then begin
        SysMsg(g_sDealHumanGoldLargeThenLimit, c_Red, t_Hint);
        bo11 := False;
      end;
    end else begin
      Envir := g_MapManager.GetDuelMap;
      if Envir = nil then begin
        SysMsg('No Map available, please wait and try again.', c_Red, t_Hint);
        m_DealCreat.SysMsg('No Map available, please wait and try again.', c_Red, t_Hint);
        DealCancel();
        Exit;
      end;

      if m_StartDuelCreat = Self then begin
        g_DuelEngine.AddDuel(0, m_sCharName, m_DealCreat.m_sCharName, m_nDealGolds, m_DealCreat.m_nDealGolds);
      end else begin
        g_DuelEngine.AddDuel(0, m_DealCreat.m_sCharName, m_sCharName, m_DealCreat.m_nDealGolds, m_nDealGolds);
      end;
    end;

    if bo11 then begin
      if m_boDealType then begin
        for I := 0 to m_DealItemList.Count - 1 do begin
          UserItem := m_DealItemList.Items[I];
          m_DealCreat.AddItemToBag(UserItem);
          m_DealCreat.SendAddItem(UserItem);
          StdItem := UserEngine.GetStdItem(UserItem.wIndex);
          if StdItem <> nil then begin
            if not IsCheapStuff(StdItem.StdMode) then begin
              if StdItem.NeedIdentify = 1 then
                AddGameDataLog('8' + #9 +
                  m_sMapName + #9 +
                  IntToStr(m_nCurrX) + #9 +
                  IntToStr(m_nCurrY) + #9 +
                  m_sCharName + #9 +
                //UserEngine.GetStdItemName(UserItem.wIndex) + #9 +
                  StdItem.Name + #9 +
                  IntToStr(UserItem.MakeIndex) + #9 +
                  '1' + #9 +
                  m_DealCreat.m_sCharName);
            end;
          end;
        end;
        if m_nDealGolds > 0 then begin
          Inc(m_DealCreat.m_nGold, m_nDealGolds);
          m_DealCreat.GoldChanged();
          if g_boGameLogGold then
            AddGameDataLog('8' + #9 +
              m_sMapName + #9 +
              IntToStr(m_nCurrX) + #9 +
              IntToStr(m_nCurrY) + #9 +
              m_sCharName + #9 +
              sSTRING_GOLDNAME + #9 +
              IntToStr(m_nGold) + #9 +
              '1' + #9 +
              m_DealCreat.m_sCharName);
        end;
        for I := 0 to m_DealCreat.m_DealItemList.Count - 1 do begin
          UserItem := m_DealCreat.m_DealItemList.Items[I];
          AddItemToBag(UserItem);
          TPlayObject(Self).SendAddItem(UserItem);
          StdItem := UserEngine.GetStdItem(UserItem.wIndex);
          if StdItem <> nil then begin
            if not IsCheapStuff(StdItem.StdMode) then begin
              if StdItem.NeedIdentify = 1 then
                AddGameDataLog('8' + #9 +
                  m_DealCreat.m_sMapName + #9 +
                  IntToStr(m_DealCreat.m_nCurrX) + #9 +
                  IntToStr(m_DealCreat.m_nCurrY) + #9 +
                  m_DealCreat.m_sCharName + #9 +
                //UserEngine.GetStdItemName(UserItem.wIndex) + #9 +
                  StdItem.Name + #9 +
                  IntToStr(UserItem.MakeIndex) + #9 +
                  '1' + #9 +
                  m_sCharName);
            end;
          end;
        end;

        if m_DealCreat.m_nDealGolds > 0 then begin
          Inc(m_nGold, m_DealCreat.m_nDealGolds);
          GoldChanged();

          if g_boGameLogGold then
            AddGameDataLog('8' + #9 +
              m_DealCreat.m_sMapName + #9 +
              IntToStr(m_DealCreat.m_nCurrX) + #9 +
              IntToStr(m_DealCreat.m_nCurrY) + #9 +
              m_DealCreat.m_sCharName + #9 +
              sSTRING_GOLDNAME + #9 +
              IntToStr(m_DealCreat.m_nGold) + #9 +
              '1' + #9 +
              m_sCharName);
        end;
      end else begin
        for I := 0 to m_DealItemList.Count - 1 do begin
          UserItem := m_DealItemList.Items[I];
          if m_StartDuelCreat = Self then begin
            g_DuelEngine.AddItem(m_sCharName, m_DealCreat.m_sCharName, UserItem);
          end else begin
            g_DuelEngine.AddItem(m_DealCreat.m_sCharName, m_sCharName, UserItem);
          end;
          StdItem := UserEngine.GetStdItem(UserItem.wIndex);
          if StdItem <> nil then begin
            if not IsCheapStuff(StdItem.StdMode) then begin
              if StdItem.NeedIdentify = 1 then
                AddGameDataLog('34' + #9 +
                  m_DealCreat.m_sMapName + #9 +
                  IntToStr(m_DealCreat.m_nCurrX) + #9 +
                  IntToStr(m_DealCreat.m_nCurrY) + #9 +
                  m_DealCreat.m_sCharName + #9 +
                //UserEngine.GetStdItemName(UserItem.wIndex) + #9 +
                  StdItem.Name + #9 +
                  IntToStr(UserItem.MakeIndex) + #9 +
                  '1' + #9 +
                  m_sCharName);
            end;
          end;
          Dispose(UserItem);
        end;
        for I := 0 to m_DealCreat.m_DealItemList.Count - 1 do begin
          UserItem := m_DealCreat.m_DealItemList.Items[I];
          if m_StartDuelCreat = Self then begin
            g_DuelEngine.AddItem(m_sCharName, m_DealCreat.m_sCharName, UserItem);
          end else begin
            g_DuelEngine.AddItem(m_DealCreat.m_sCharName, m_sCharName, UserItem);
          end;
          StdItem := UserEngine.GetStdItem(UserItem.wIndex);
          if StdItem <> nil then begin
            if not IsCheapStuff(StdItem.StdMode) then begin
              if StdItem.NeedIdentify = 1 then
                AddGameDataLog('34' + #9 +
                  m_sMapName + #9 +
                  IntToStr(m_nCurrX) + #9 +
                  IntToStr(m_nCurrY) + #9 +
                  m_sCharName + #9 +
                //UserEngine.GetStdItemName(UserItem.wIndex) + #9 +
                  StdItem.Name + #9 +
                  IntToStr(UserItem.MakeIndex) + #9 +
                  '1' + #9 +
                  m_DealCreat.m_sCharName);
            end;
          end;
          Dispose(UserItem);
        end;
      end;

      PlayObject := TPlayObject(m_DealCreat);
      PlayObject.SendDefMessage(SM_DEALSUCCESS, 0, 0, 0, 0, '');
      if m_boDealType then begin
        PlayObject.SysMsg(g_sDealSuccessMsg {'交易成功...'}, c_Green, t_Hint);
      end else begin
        PlayObject.SysMsg(g_sDuelSuccessMsg {'交易成功...'}, c_Green, t_Hint);
      end;

      PlayObject.m_DealCreat := nil;
      PlayObject.m_StartDuelCreat := nil;
      PlayObject.m_boDealing := False;
      PlayObject.m_DealItemList.Clear;
      PlayObject.m_nDealGolds := 0;
      PlayObject.m_boDealOK := False; //Jacky 增加
      SendDefMessage(SM_DEALSUCCESS, 0, 0, 0, 0, '');
      if m_boDealType then begin
        SysMsg(g_sDealSuccessMsg {'交易成功...'}, c_Green, t_Hint);
      end else begin
        SysMsg(g_sDuelSuccessMsg {'交易成功...'}, c_Green, t_Hint);
      end;

      m_DealCreat := nil;
      m_boDealing := False;
      m_DealItemList.Clear;
      m_nDealGolds := 0;
      m_boDealOK := False; //Jacky 增加

      m_StartDuelCreat := nil;

//进入决斗场地
      if not m_boDealType then begin
        m_DuelTargetCret := PlayObject;
        PlayObject.m_DuelTargetCret := Self;
        m_boStartDuel := True;
        PlayObject.m_boStartDuel := True;
        if g_FunctionNPC <> nil then begin
          m_nScriptGotoCount := 0;
          PlayObject.m_nScriptGotoCount := 0;
          g_FunctionNPC.GotoLable(Self, '@GotoDuelMap', False);
          g_FunctionNPC.GotoLable(PlayObject, '@GotoDuelMap', False);
        end;
      end;
    end else begin
      DealCancel();
    end;
  end else begin
    if m_boDealType then begin
      SysMsg(g_sYouDealOKMsg {'你已经确认交易了'}, c_Green, t_Hint);
      m_DealCreat.SysMsg(g_sPoseDealOKMsg {'对方已经确认交易了'}, c_Green, t_Hint);
    end else begin
      SysMsg(g_sYouDuelOKMsg {'你已经确认交易了'}, c_Green, t_Hint);
      m_DealCreat.SysMsg(g_sPoseDuelOKMsg {'对方已经确认交易了'}, c_Green, t_Hint);
    end;
  end;
  ClearCopyItems(); //清除复制装备
end;

procedure TPlayObject.ClientDuelTry(PlayObject: TPlayObject);
var
  //BaseObject: TActorObject;
  sCharName1, sCharName2: string;
  nIndex1, nIndex2: Integer;
  ItemList: TList;
  Envir: TEnvirnoment;

  {nLen, nLen1: Integer;
  sBuffer: string;
  UserReg: TUserReg;
  dwTickTime: LongWord; }
begin
  if m_boDeath or m_boGhost or m_boStartDuel or m_boDealing or m_boDueling or m_boStore or (m_NewStatus <> sNone) then Exit;
  if m_boIsUnknowActor or PlayObject.m_boIsUnknowActor then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, 'Player not found.');
    Exit;
  end;
  if g_Config.boDisableDuel then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sDisableDuelItemsMsg);
    Exit;
  end;

  if GetTickCount - m_DuelLastTick < g_Config.dwTryDuelTime {3000} then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sPleaseTryDuelLaterMsg);
    Exit;
  end;

  if not m_boCanDuel then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sCanotTryDuelMsg);
    Exit;
  end;

  m_boDealType := False;

  if (PlayObject <> nil) and (PlayObject <> Self) then begin
    if (PlayObject.GetPoseCreate = Self) and (not PlayObject.m_boDueling) and
      (not PlayObject.m_boDeath) and (not PlayObject.m_boGhost) and (not PlayObject.m_boStartDuel) then begin
      if (PlayObject.m_boAllowDuel and PlayObject.m_boCanDuel) then begin

        Envir := g_MapManager.GetDuelMap;
        if (Envir = nil) then begin
          SysMsg('No Map available to Dual on, please wait.', c_Red, t_Hint);
          PlayObject.SysMsg('No Map available to Dual on, please wait.', c_Red, t_Hint);
          SendDefMessage(SM_DUELTRY_FAIL, 0, 0, 0, 0, '');
          Exit;
        end;
        {
        m_DuelCreat := PlayObject;
        PlayObject.m_DuelCreat := Self;
        }
        sCharName1 := m_sCharName;
        sCharName2 := PlayObject.m_sCharName;

        if not g_DuelEngine.GetDuel(0, sCharName1, sCharName2, nIndex1, nIndex2) then begin
          m_StartDuelCreat := Self;
          PlayObject.m_StartDuelCreat := Self;
          PlayObject.SysMsg(m_sCharName + g_sOpenedDuelMsg, c_Green, t_Hint);
          SysMsg(PlayObject.m_sCharName + g_sOpenedDuelMsg, c_Green, t_Hint);
          TPlayObject(Self).OpenDuelDlg(PlayObject);
          PlayObject.OpenDuelDlg(Self);

  //清除复制装备
          ItemList := TList.Create;
          TPlayObject(Self).AddCheckItems(ItemList);
          PlayObject.AddCheckItems(ItemList);
          TPlayObject(Self).ClearCopyItems(ItemList);
          ItemList.Free;
        end else begin
          m_DuelTargetCret := PlayObject;
          PlayObject.m_DuelTargetCret := Self;

          m_boStartDuel := True;
          PlayObject.m_boStartDuel := True;
          //Envir.m_boDueling := True;
          if g_FunctionNPC <> nil then begin
            m_nScriptGotoCount := 0;
            PlayObject.m_nScriptGotoCount := 0;
            g_FunctionNPC.GotoLable(Self, '@GotoDuelMap', False);
            g_FunctionNPC.GotoLable(PlayObject, '@GotoDuelMap', False);
          end;
          {SpaceMove(Envir.MapName, Envir.m_nWidth div 2, Envir.m_nHeight div 2, 0);
          PlayObject.SpaceMove(Envir.MapName, Envir.m_nWidth div 2, Envir.m_nHeight div 2, 0);  }
          //SysMsg('你们的挑战还没有完成，不能在申请挑战了！！！', c_Red, t_Hint);
        end;
      end else begin
        SysMsg(g_sPoseDisableDuelMsg {'对方禁止进入交易'}, c_Red, t_Hint);
      end;
    end else begin
      SendDefMessage(SM_DUELTRY_FAIL, 0, 0, 0, 0, '');
    end;
  end else begin
    SendDefMessage(SM_DUELTRY_FAIL, 0, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.ClientDuelTry(sHumName: string);
var
  BaseObject: TActorObject;
  PlayObject: TPlayObject;
  sCharName1, sCharName2: string;
  nIndex1, nIndex2: Integer;
  ItemList: TList;
  Envir: TEnvirnoment;

  {nLen, nLen1: Integer;
  sBuffer: string;
  UserReg: TUserReg;
  dwTickTime: LongWord; }
begin
  if m_boStore then Exit; //摆摊禁止操作
  if m_boDeath or m_boGhost or m_boStartDuel or m_boDealing or m_boDueling or (m_NewStatus <> sNone) then Exit;
  if g_Config.boDisableDuel then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sDisableDuelItemsMsg);
    Exit;
  end;

  if GetTickCount - m_DuelLastTick < g_Config.dwTryDuelTime {3000} then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sPleaseTryDuelLaterMsg);
    Exit;
  end;

  if not m_boCanDuel then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sCanotTryDuelMsg);
    Exit;
  end;

  m_boDealType := True;

  BaseObject := GetPoseCreate();

  if (BaseObject <> nil) and (BaseObject <> Self) and (BaseObject.m_btRaceServer = RC_PLAYOBJECT) then begin
    PlayObject := TPlayObject(BaseObject);
    if (PlayObject <> nil) and (PlayObject.GetPoseCreate = Self) and (not PlayObject.m_boDueling) and
      (not PlayObject.m_boDeath) and (not PlayObject.m_boGhost) and (not PlayObject.m_boStartDuel) then begin
      if (PlayObject.m_boAllowDuel and PlayObject.m_boCanDuel) then begin
        if m_boIsUnknowActor or PlayObject.m_boIsUnknowActor then begin
          SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, 'Player not found.');
          Exit;
        end;
        Envir := g_MapManager.GetDuelMap;
        if (Envir = nil) then begin
          SysMsg('No Map available for Duel, please wait and try again', c_Red, t_Hint);
          PlayObject.SysMsg('No Map available for Duel, please wait and try again', c_Red, t_Hint);
          SendDefMessage(SM_DUELTRY_FAIL, 0, 0, 0, 0, '');
          Exit;
        end;
        {
        m_DuelCreat := PlayObject;
        PlayObject.m_DuelCreat := Self;
        }
        sCharName1 := m_sCharName;
        sCharName2 := PlayObject.m_sCharName;
        if not g_DuelEngine.GetDuel(0, sCharName1, sCharName2, nIndex1, nIndex2) then begin
          m_StartDuelCreat := Self;
          PlayObject.m_StartDuelCreat := Self;
          PlayObject.SysMsg(m_sCharName + g_sOpenedDuelMsg, c_Green, t_Hint);
          SysMsg(PlayObject.m_sCharName + g_sOpenedDuelMsg, c_Green, t_Hint);
          TPlayObject(Self).OpenDuelDlg(PlayObject);
          PlayObject.OpenDuelDlg(Self);

  //清除复制装备
          ItemList := TList.Create;
          TPlayObject(Self).AddCheckItems(ItemList);
          PlayObject.AddCheckItems(ItemList);
          TPlayObject(Self).ClearCopyItems(ItemList);
          ItemList.Free;
        end else begin
          m_DuelTargetCret := PlayObject;
          PlayObject.m_DuelTargetCret := Self;

          m_boStartDuel := True;
          PlayObject.m_boStartDuel := True;
          //Envir.m_boDueling := True;
          if g_FunctionNPC <> nil then begin
            m_nScriptGotoCount := 0;
            PlayObject.m_nScriptGotoCount := 0;
            g_FunctionNPC.GotoLable(Self, '@GotoDuelMap', False);
            g_FunctionNPC.GotoLable(PlayObject, '@GotoDuelMap', False);
          end;
          {SpaceMove(Envir.MapName, Envir.m_nWidth div 2, Envir.m_nHeight div 2, 0);
          PlayObject.SpaceMove(Envir.MapName, Envir.m_nWidth div 2, Envir.m_nHeight div 2, 0);  }
          //SysMsg('你们的挑战还没有完成，不能在申请挑战了！！！', c_Red, t_Hint);
        end;
      end else begin
        SysMsg(g_sPoseDisableDuelMsg {'对方禁止进入交易'}, c_Red, t_Hint);
      end;
    end else begin
      SendDefMessage(SM_DUELTRY_FAIL, 0, 0, 0, 0, '');
    end;
  end else begin
    SendDefMessage(SM_DUELTRY_FAIL, 0, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.ClientAddDuelItem(nItemIdx: Integer; sItemName: string);
var
  I: Integer;
  bo11: Boolean;
  UserItem: pTUserItem;
  sUserItemName: string;
begin
  if m_boDeath or m_boGhost or (m_DuelCreat = nil) or (not m_boDueling) then Exit;

  if Pos(' ', sItemName) >= 0 then begin //折分物品名称(信件物品的名称后面加了使用次数)
    GetValidStr3(sItemName, sItemName, [' ']);
  end;

  bo11 := False;
  if not m_DuelCreat.m_boDuelOK then begin
    for I := m_ItemList.Count - 1 downto 0 do begin
      if m_ItemList.Count <= 0 then Break;
      UserItem := m_ItemList.Items[I];
      if UserItem.MakeIndex = nItemIdx then begin

        if Assigned(PlugInEngine.CheckCanDealItem) then begin //禁止交易物品
          if not PlugInEngine.CheckCanDealItem(Self, PChar(UserEngine.GetStdItemName(UserItem.wIndex)), True) then Break;
        end;

        if g_Config.boBindItemNoDeal and (CheckItemBindUse(UserItem, True) = 2) then Break; //绑定物品禁止交易

        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName = '' then
          sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

        if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
          sUserItemName := '( )' + sUserItemName;
        end;

        if (CompareText(sUserItemName, sItemName) = 0) and
          (m_DuelItemList.Count < 5) then begin
          m_DuelItemList.Add(UserItem);
          TPlayObject(Self).SendAddDuelItem(UserItem);
          m_ItemList.Delete(I);
          bo11 := True;
          Break;
        end;
      end;
    end;
  end;
  if not bo11 then
    SendDefMessage(SM_DUELADDITEM_FAIL, 0, 0, 0, 0, '');
end;

procedure TPlayObject.ClientDelDuelItem(nItemIdx: Integer; sItemName: string);
var
  I: Integer;
  bo11: Boolean;
  UserItem: pTUserItem;
  sUserItemName: string;
begin
  if g_Config.boCanNotGetBackDuel then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sDuelItemsDenyGetBackMsg);
    SendDefMessage(SM_DUELDELITEM_FAIL, 0, 0, 0, 0, '');
    Exit;
  end;

  if m_boDeath or m_boGhost or (m_DuelCreat = nil) then Exit;

  if Pos(' ', sItemName) >= 0 then begin //折分物品名称(信件物品的名称后面加了使用次数)
    GetValidStr3(sItemName, sItemName, [' ']);
  end;

  bo11 := False;
  if not m_DuelCreat.m_boDuelOK then begin
    for I := m_DuelItemList.Count - 1 downto 0 do begin
      if m_DuelItemList.Count <= 0 then Break;
      UserItem := m_DuelItemList.Items[I];
      if UserItem.MakeIndex = nItemIdx then begin
        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName = '' then
          sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

        if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
          sUserItemName := '( )' + sUserItemName;
        end;

        if CompareText(sUserItemName, sItemName) = 0 then begin
          m_ItemList.Add(UserItem);
          TPlayObject(Self).SendDelDuelItem(UserItem);
          m_DuelItemList.Delete(I);
          bo11 := True;
          Break;
        end;
      end;
    end;
  end;
  if not bo11 then
    SendDefMessage(SM_DUELDELITEM_FAIL, 0, 0, 0, 0, '');
end;

procedure TPlayObject.ClientCancelDuel;
begin
  DuelCancel();
end;

procedure TPlayObject.ClientChangeDuelGold(nGold: Integer);
var
  bo09: Boolean;
begin
  if m_boDeath or m_boGhost or (m_DuelCreat = nil) or (not m_boDueling) then Exit;
  //禁止取回放入交易栏内的金币
  if (m_nDuelGolds > 0) and g_Config.boCanNotGetBackDuel then begin
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sDuelItemsDenyGetBackMsg);
    SendDefMessage(SM_DUELDELITEM_FAIL, 0, 0, 0, 0, '');
    Exit;
  end;
  if nGold < 0 then begin
    SendDefMessage(SM_DUELCHGGOLD_FAIL, m_nDuelGolds, LoWord(m_nGameGold), HiWord(m_nGameGold), 0, '');
    Exit;
  end;
  bo09 := False;

  if (m_DuelCreat <> nil) and (GetPoseCreate = m_DuelCreat) then begin
    if not m_DuelCreat.m_boDuelOK then begin
      if (m_nGameGold + m_nDuelGolds) >= nGold then begin
        m_nGameGold := (m_nGameGold + m_nDuelGolds) - nGold;
        m_nDuelGolds := nGold;
        SendDefMessage(SM_DUELCHGGOLD_OK, m_nDuelGolds, LoWord(m_nGameGold), HiWord(m_nGameGold), 0, '');
        m_DuelCreat.SendDefMessage(SM_DUELREMOTECHGGOLD, m_nDuelGolds, 0, 0, 0, '');
        m_DuelCreat.m_DuelLastTick := GetTickCount();
        bo09 := True;
        m_DuelLastTick := GetTickCount();
        //MainOutMessage('TPlayObject.ClientChangeDuelGold5:'+IntToStr(nGold));
      end;
    end;
  end;
  if not bo09 then begin
   // MainOutMessage('TPlayObject.ClientChangeDuelGold6:'+IntToStr(nGold));
    SendDefMessage(SM_DUELCHGGOLD_FAIL, m_nDuelGolds, LoWord(m_nGameGold), HiWord(m_nGameGold), 0, '');
  end;
end;

procedure TPlayObject.ClientDuelEnd;
var
  I: Integer;
//  bo11: Boolean;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
  PlayObject: TPlayObject;
  Envir: TEnvirnoment;
begin
  m_boDuelOK := True;
  if (m_DuelCreat = nil) or (m_StartDuelCreat = nil) or m_boGhost or m_boSoftClose then Exit;

  if (m_DuelCreat <> nil) and (m_DuelCreat.m_boGhost) then Exit;
  if ((GetTickCount - m_DuelLastTick) < g_Config.dwDuelOKTime {1000}) or ((GetTickCount - m_DuelCreat.m_DuelLastTick) < g_Config.dwDuelOKTime {1000}) then begin
    SysMsg(g_sDuelOKTooFast, c_Red, t_Hint);
    DuelCancel();
    Exit;
  end;
  if m_DuelCreat.m_boDuelOK then begin
    Envir := g_MapManager.GetDuelMap;
    if Envir = nil then begin
      SysMsg('No Map available, please wait and try again.', c_Red, t_Hint);
      m_DuelCreat.SysMsg('No Map available, please wait and try again.', c_Red, t_Hint);
      DuelCancel();
      Exit;
    end;

    if m_StartDuelCreat = Self then begin
      g_DuelEngine.AddDuel(0, m_sCharName, m_DuelCreat.m_sCharName, m_nDuelGolds, m_DuelCreat.m_nDuelGolds);
    end else begin
      g_DuelEngine.AddDuel(0, m_DuelCreat.m_sCharName, m_sCharName, m_DuelCreat.m_nDuelGolds, m_nDuelGolds);
    end;

    for I := 0 to m_DuelItemList.Count - 1 do begin
      UserItem := m_DuelItemList.Items[I];
      if m_StartDuelCreat = Self then begin
        g_DuelEngine.AddItem(m_sCharName, m_DuelCreat.m_sCharName, UserItem);
      end else begin
        g_DuelEngine.AddItem(m_DuelCreat.m_sCharName, m_sCharName, UserItem);
      end;
      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if StdItem <> nil then begin
        if not IsCheapStuff(StdItem.StdMode) then begin
          if StdItem.NeedIdentify = 1 then
            AddGameDataLog('34' + #9 +
              m_DuelCreat.m_sMapName + #9 +
              IntToStr(m_DuelCreat.m_nCurrX) + #9 +
              IntToStr(m_DuelCreat.m_nCurrY) + #9 +
              m_DuelCreat.m_sCharName + #9 +
                //UserEngine.GetStdItemName(UserItem.wIndex) + #9 +
              StdItem.Name + #9 +
              IntToStr(UserItem.MakeIndex) + #9 +
              '1' + #9 +
              m_sCharName);
        end;
      end;
      Dispose(UserItem);
    end;
      //MainOutMessage('TPlayObject.ClientDuelEnd3');
    for I := 0 to m_DuelCreat.m_DuelItemList.Count - 1 do begin
      UserItem := m_DuelCreat.m_DuelItemList.Items[I];
      if m_StartDuelCreat = Self then begin
        g_DuelEngine.AddItem(m_sCharName, m_DuelCreat.m_sCharName, UserItem);
      end else begin
        g_DuelEngine.AddItem(m_DuelCreat.m_sCharName, m_sCharName, UserItem);
      end;
      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if StdItem <> nil then begin
        if not IsCheapStuff(StdItem.StdMode) then begin
          if StdItem.NeedIdentify = 1 then
            AddGameDataLog('34' + #9 +
              m_sMapName + #9 +
              IntToStr(m_nCurrX) + #9 +
              IntToStr(m_nCurrY) + #9 +
              m_sCharName + #9 +
                //UserEngine.GetStdItemName(UserItem.wIndex) + #9 +
              StdItem.Name + #9 +
              IntToStr(UserItem.MakeIndex) + #9 +
              '1' + #9 +
              m_DuelCreat.m_sCharName);
        end;
      end;
      Dispose(UserItem);
    end;

      {if m_DuelCreat.m_nDuelGolds > 0 then begin
        //Inc(m_nGold, m_DuelCreat.m_nDuelGolds);
        //GoldChanged();
        //004DE36E
        if g_boGameLogGold then
          AddGameDataLog('8' + #9 +
            m_DuelCreat.m_sMapName + #9 +
            IntToStr(m_DuelCreat.m_nCurrX) + #9 +
            IntToStr(m_DuelCreat.m_nCurrY) + #9 +
            m_DuelCreat.m_sCharName + #9 +
            sSTRING_GOLDNAME + #9 +
            IntToStr(m_DuelCreat.m_nGold) + #9 +
            '1' + #9 +
            m_sCharName);
      end;  }

    PlayObject := TPlayObject(m_DuelCreat);
    PlayObject.SendDefMessage(SM_DUELSUCCESS, 0, 0, 0, 0, '');
    PlayObject.SysMsg(g_sDuelSuccessMsg {'挑战成功...'}, c_Green, t_Hint);
    PlayObject.m_StartDuelCreat := nil;
    PlayObject.m_DuelCreat := nil;
    PlayObject.m_boDueling := False;
    PlayObject.m_DuelItemList.Clear;

    PlayObject.m_nDuelGolds := 0;
    PlayObject.m_boDuelOK := False; //Jacky 增加

    SendDefMessage(SM_DUELSUCCESS, 0, 0, 0, 0, '');
    SysMsg(g_sDuelSuccessMsg {'挑战成功...'}, c_Green, t_Hint);
    m_DuelCreat := nil;
    m_boDueling := False;
    m_DuelItemList.Clear;
    m_nDuelGolds := 0;
    m_boDuelOK := False;
    m_StartDuelCreat := nil;

//进入决斗场地
    m_DuelTargetCret := PlayObject;
    PlayObject.m_DuelTargetCret := Self;
    m_boStartDuel := True;
    PlayObject.m_boStartDuel := True;
      //Envir.m_boDueling := True;

    if g_FunctionNPC <> nil then begin
      m_nScriptGotoCount := 0;
      PlayObject.m_nScriptGotoCount := 0;
      g_FunctionNPC.GotoLable(Self, '@GotoDuelMap', False);
      g_FunctionNPC.GotoLable(PlayObject, '@GotoDuelMap', False);
    end;
  end else begin //004DE439
    SysMsg(g_sYouDuelOKMsg {'你已经确认挑战了'}, c_Green, t_Hint);
    m_DuelCreat.SysMsg(g_sPoseDuelOKMsg {'对方已经确认挑战了'}, c_Green, t_Hint);
  end;
  ClearCopyItems(); //清除复制装备
end;

procedure TPlayObject.ClientStartStore(sMsg: string; nItemCount: Integer);
var
  I: Integer;
  sData: string;
  ItemIndex: TItemIndex;
  UserItem: pTUserItem;
  sUserItemName: string;
  StoreServerItem: pTStoreServerItem;
  n1C, nCount: Integer;

begin
  if m_boStore or m_boDeath or m_boGhost then Exit;

  if m_NewStatus <> sNone then begin
    SendDefMessage(SM_SENDSTARTSTORE_FAIL, 0, 0, 0, 0, ''); //摆摊失败
    Exit;
  end;

  if not m_PEnvir.m_boOpenStore then begin
    SendDefMessage(SM_SENDSTARTSTORE_FAIL, 0, 0, 0, 0, ''); //摆摊失败
    SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, 'You cannot open a Stall on this Map.');
    Exit;
  end;

  if g_Config.boSafeCanStore then begin
    if not InSafeZone then begin
      SendDefMessage(SM_SENDSTARTSTORE_FAIL, 0, 0, 0, 0, ''); //摆摊失败
      SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, 'You cannot Store items on this Map.');
      Exit;
    end;
  end;
  if g_Config.boOpenStoreCheckLevel then begin
    if m_WAbil.Level < g_Config.nOpenStoreCheckLevel then begin
      SendDefMessage(SM_SENDSTARTSTORE_FAIL, 0, 0, 0, 0, ''); //摆摊失败
      SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, 'You must be level ' + IntToStr(g_Config.nOpenStoreCheckLevel) + ' to open Store.');

      Exit;
    end;
  end;

  GetBackStoreItems();

  n1C := 0;
  nCount := 0;
  while True do begin
    if (sMsg = '') then Break;
    sMsg := GetValidStr3(sMsg, sData, ['/']);
    if sData <> '' then begin
      DecodeBuffer(sData, @ItemIndex, SizeOf(TItemIndex));
      if ItemIndex.ItemName = '' then Continue;
      for I := 0 to m_ItemList.Count - 1 do begin
        UserItem := m_ItemList.Items[I];
        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName = '' then
          sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

        if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
          sUserItemName := '( )' + sUserItemName;
        end;

        if (CompareText(ItemIndex.ItemName, sUserItemName) = 0) and (ItemIndex.MakeIndex = UserItem.MakeIndex) then begin
          n1C := -1;
          if Assigned(PlugInEngine.CheckCanDealItem) then begin //禁止交易物品
            if not PlugInEngine.CheckCanDealItem(Self, PChar(UserEngine.GetStdItemName(UserItem.wIndex)), True) then begin
              SysMsg(ItemIndex.ItemName + ' Prohibited transaction!', c_Red, t_Hint);
              Break;
            end;
          end;
          if g_Config.boBindItemNoDeal and (CheckItemBindUse(UserItem, True) = 2) then Break; //绑定物品禁止交易
          if ItemIndex.Price <= 0 then begin
            n1C := -1;
            break;
          end;

          New(StoreServerItem);
          StoreServerItem.btSellType := ItemIndex.btSellType;
          StoreServerItem.UserItem := UserItem^;
          StoreServerItem.Price := ItemIndex.Price;
          m_StoreItemList.Add(StoreServerItem);
          n1C := 0;
          Inc(nCount);
          Break;
        end;
      end;
      if n1C < 0 then Break;
    end else Break;
  end;

  if (n1C < 0) or (nCount <> nItemCount) then begin
    GetBackStoreItems();
    SendDefMessage(SM_SENDSTARTSTORE_FAIL, 0, 0, 0, 0, ''); //摆摊失败
  end else begin
    m_boStore := True;
    if m_TargetCret <> nil then begin
      if m_TargetCret.m_TargetCret = Self then m_TargetCret.DelTargetCreat;
      m_TargetCret := nil;
    end;
    SendDefMessage(SM_SENDSTARTSTORE_OK, 0, 0, 0, 0, ''); //摆摊成功
    SendRefMsg(RM_SENDSTORE, m_btDirection, m_nCurrX, m_nCurrY, Integer(m_boStore), '');
  end;
end;

procedure TPlayObject.ClientQueryUserStoreState(PlayObject: TActorObject; nX, nY: Integer);
  function FindBagItem(nMakeIndex: Integer): Integer;
  var
    I: Integer;
    UserItem: pTUserItem;
  begin
    Result := -1;
    for I := 0 to PlayObject.m_ItemList.Count - 1 do begin
      UserItem := PlayObject.m_ItemList.Items[I];
      if UserItem.MakeIndex = nMakeIndex then begin
        Result := I;
        Break;
      end;
    end;
  end;
var
  I: Integer;
  UserState: TUserStoreStateInfo;
  StdItem: pTStdItem;
  StdItem24: TStdItem;
  sUserItemName: string;
  StoreServerItem: pTStoreServerItem;
  UserItem: TUserItem;
  StoreItem: TStoreItem;
begin
  if not TPlayObject(PlayObject).m_boStore or m_boDeath or m_boGhost then Exit;

  if (not CretInNearXY(PlayObject, nX, nY)) or (PlayObject.m_btRaceServer <> RC_PLAYOBJECT) then Exit;

  FillChar(UserState, SizeOf(TUserStoreStateInfo), #0);
  UserState.UserName := PlayObject.m_sCharName + ' Stall';
  UserState.NAMECOLOR := GetCharColor(PlayObject);
  UserState.RecogId := Integer(PlayObject);

  for I := 0 to PlayObject.m_StoreItemList.Count - 1 do begin
    StoreServerItem := pTStoreServerItem(PlayObject.m_StoreItemList.Items[I]);
    UserItem := StoreServerItem.UserItem;
    if (UserItem.wIndex > 0) and (FindBagItem(UserItem.MakeIndex) >= 0) then begin
      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if StdItem = nil then Continue;
      StdItem24 := StdItem^;
      ItemUnit.GetItemAddValue(@UserItem, StdItem24);
      Move(StdItem24, StoreItem.Item.s, SizeOf(TStdItem));

        //取自定义物品名称
      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName <> '' then
        StoreItem.Item.s.Name := sUserItemName;

      if (TPlayObject(PlayObject).CheckItemBindUse(@UserItem, False) = 2) then begin //绑定物品
        StoreItem.Item.s.Name := '( )' + StoreItem.Item.s.Name;
      end;

      StoreItem.Item.MakeIndex := UserItem.MakeIndex;
      StoreItem.Item.Dura := UserItem.Dura;
      StoreItem.Item.DuraMax := UserItem.DuraMax;

      StoreItem.btSellType := StoreServerItem.btSellType;

      StoreItem.Item.s.AddValue := UserItem.AddValue;
      StoreItem.Item.s.AddPoint := UserItem.AddPoint;
      StoreItem.Item.s.MaxDate := UserItem.MaxDate;


      StoreItem.Item.s.Price := StoreServerItem.Price;
      UserState.UseItems[I] := StoreItem;
    end;
  end;

  m_DefMsg := MakeDefaultMsg(SM_USERSTOREITEMS, 0, 0, 0, 0);
  SendSocket(@m_DefMsg, EncodeBuffer(@UserState, SizeOf(TUserStoreStateInfo)));
end;

procedure TPlayObject.ClientBuySroreItem(PlayObject: TActorObject; sMsg: string);
  function FindBagItem(nMakeIndex: Integer): Integer;
  var
    I: Integer;
    UserItem: pTUserItem;
  begin
    Result := -1;
    for I := 0 to PlayObject.m_ItemList.Count - 1 do begin
      UserItem := PlayObject.m_ItemList.Items[I];
      if UserItem.MakeIndex = nMakeIndex then begin
        Result := I;
        Break;
      end;
    end;
  end;
var
  I, nIndex: Integer;
  sItemName: string;
  sMakeIndex: string;
  nMakeIndex: Integer;
  sUserItemName: string;
  StdItem: pTStdItem;
  StdItem24: TStdItem;
  StoreServerItem: pTStoreServerItem;
  UserItem: TUserItem;
  Item: pTUserItem;
  ClientItem: TClientItem;
  sSENDTEST: string;
  n1C: Integer;
  btSellType: Integer;
begin
  if Self = PlayObject then Exit;
  //if (not CretInNearXY(PlayObject, nX, nY))
  if UserEngine.GetPlayObject(PlayObject) <> PlayObject then Exit;
  if (PlayObject.m_btRaceServer <> RC_PLAYOBJECT) or (not TPlayObject(PlayObject).m_boStore) then begin
    m_DefMsg := MakeDefaultMsg(SM_SENDBUYSTOREITEM_FAIL, -4, 0, 0, 0);
    SendSocket(@m_DefMsg, '');
    Exit;
  end;
  Item := nil;
  n1C := -2;
  sMsg := GetValidStr3(sMsg, sItemName, ['/']);
  sMsg := GetValidStr3(sMsg, sMakeIndex, ['/']);
  nMakeIndex := Str_ToInt(sMakeIndex, -1);
  nIndex := FindBagItem(nMakeIndex);
  if (sItemName <> '') and (nMakeIndex > 0) and (nIndex >= 0) then begin
    Item := PlayObject.m_ItemList.Items[nIndex];
    for I := 0 to PlayObject.m_StoreItemList.Count - 1 do begin
      StoreServerItem := pTStoreServerItem(PlayObject.m_StoreItemList.Items[I]);
      UserItem := StoreServerItem.UserItem;
      if (UserItem.wIndex > 0) and (StoreServerItem.Price > 0) then begin
        StdItem := UserEngine.GetStdItem(UserItem.wIndex);
        if StdItem = nil then Continue;
        //取自定义物品名称

        StdItem24 := StdItem^;
        ItemUnit.GetItemAddValue(@UserItem, StdItem24);
        Move(StdItem24, ClientItem.s, SizeOf(TStdItem));

        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName <> '' then
          ClientItem.s.Name := sUserItemName;

        if (TPlayObject(PlayObject).CheckItemBindUse(@UserItem, False) = 2) then begin //绑定物品
          ClientItem.s.Name := '(绑)' + ClientItem.s.Name;
        end;

        ClientItem.MakeIndex := UserItem.MakeIndex;
        ClientItem.Dura := UserItem.Dura;
        ClientItem.DuraMax := UserItem.DuraMax;

        ClientItem.s.AddValue := UserItem.AddValue;
        ClientItem.s.AddPoint := UserItem.AddPoint;
        ClientItem.s.MaxDate := UserItem.MaxDate;

        if (CompareText(ClientItem.s.Name, sItemName) = 0) and (nMakeIndex = UserItem.MakeIndex) then begin
          btSellType := StoreServerItem.btSellType;
          case StoreServerItem.btSellType of
            0: begin //金币购买
                if m_nGold >= StoreServerItem.Price then begin
                  if IsEnoughBag then begin
                    DecGold(StoreServerItem.Price);
                    TPlayObject(PlayObject).IncGold(StoreServerItem.Price);
                    GoldChanged;
                    TPlayObject(PlayObject).GoldChanged;

                    if StdItem.NeedIdentify = 1 then begin
                      AddGameDataLog('35' + #9 +
                        m_sMapName + #9 +
                        IntToStr(m_nCurrX) + #9 +
                        IntToStr(m_nCurrY) + #9 +
                        m_sCharName + #9 +
              //UserEngine.GetStdItemName(m_UseItems[I].wIndex) + #9 +
                        StdItem.Name + #9 +
                        IntToStr(UserItem.MakeIndex) + #9 +
                        BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
                        PlayObject.m_sCharName);
                    end;

                    if g_boGameLogGold then begin
                      AddGameDataLog('13' + #9 +
                        m_sMapName + #9 +
                        IntToStr(m_nCurrX) + #9 +
                        IntToStr(m_nCurrY) + #9 +
                        m_sCharName + #9 +
                        sSTRING_GOLDNAME + #9 +
                        IntToStr(StoreServerItem.Price) + #9 +
                        '1' + #9 +
                        PlayObject.m_sCharName);

                      AddGameDataLog('14' + #9 +
                        PlayObject.m_sMapName + #9 +
                        IntToStr(PlayObject.m_nCurrX) + #9 +
                        IntToStr(PlayObject.m_nCurrY) + #9 +
                        PlayObject.m_sCharName + #9 +
                        sSTRING_GOLDNAME + #9 +
                        IntToStr(StoreServerItem.Price) + #9 +
                        '1' + #9 +
                        m_sCharName);
                    end;

                    m_ItemList.Add(Item);
                    SendAddItem(Item);
                    m_DefMsg := MakeDefaultMsg(SM_DELSTOREITEM, 0, 0, 0, 0);
                    sSENDTEST := EncodeBuffer(@ClientItem, SizeOf(TClientItem));
                    SendSocket(@m_DefMsg, sSENDTEST);
                    TPlayObject(PlayObject).SendSocket(@m_DefMsg, sSENDTEST);
                    TPlayObject(PlayObject).SysMsg(m_sCharName + ' buy your ' + sItemName, c_Red, t_Hint);
                    PlayObject.m_ItemList.Delete(nIndex);
                    PlayObject.m_StoreItemList.Delete(I);
                    Dispose(StoreServerItem);
                    n1C := 0;
                    Break;
                  end else n1C := -3;
                end else n1C := -1;
              end;
            1: begin //元宝购买
                if m_nGameGold >= StoreServerItem.Price then begin
                  if IsEnoughBag then begin
                    DecGameGold(StoreServerItem.Price);
                    TPlayObject(PlayObject).IncGameGold(StoreServerItem.Price);
                    GameGoldChanged;
                    TPlayObject(PlayObject).GameGoldChanged;

                    m_ItemList.Add(Item);
                    SendAddItem(Item);

                    m_DefMsg := MakeDefaultMsg(SM_DELSTOREITEM, 0, 0, 0, 0);
                    sSENDTEST := EncodeBuffer(@ClientItem, SizeOf(TClientItem));
                    SendSocket(@m_DefMsg, sSENDTEST);
                    TPlayObject(PlayObject).SendSocket(@m_DefMsg, sSENDTEST);
                    TPlayObject(PlayObject).SysMsg(m_sCharName + ' buy your ' + sItemName, c_Red, t_Hint);

                    if StdItem.NeedIdentify = 1 then begin
                      AddGameDataLog('35' + #9 +
                        m_sMapName + #9 +
                        IntToStr(m_nCurrX) + #9 +
                        IntToStr(m_nCurrY) + #9 +
                        m_sCharName + #9 +
              //UserEngine.GetStdItemName(m_UseItems[I].wIndex) + #9 +
                        StdItem.Name + #9 +
                        IntToStr(UserItem.MakeIndex) + #9 +
                        BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
                        PlayObject.m_sCharName);
                    end;

                    if g_boGameLogGameGold then begin
                      AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEGOLD,
                        m_sMapName,
                          m_nCurrX,
                          m_nCurrY,
                          m_sCharName,
                          g_Config.sGameGoldName,
                          StoreServerItem.Price,
                          '-',
                          m_sCharName]));

                      AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEGOLD,
                        PlayObject.m_sMapName,
                          PlayObject.m_nCurrX,
                          PlayObject.m_nCurrY,
                          PlayObject.m_sCharName,
                          g_Config.sGameGoldName,
                          StoreServerItem.Price,
                          '+',
                          m_sCharName]));
                    end;
                    PlayObject.m_ItemList.Delete(nIndex);
                    PlayObject.m_StoreItemList.Delete(I);
                    Dispose(StoreServerItem);
                    n1C := 0;
                    Break;
                  end else n1C := -3;
                end else n1C := -1;
              end;
            2: begin //声望购买
                if m_btCreditPoint >= StoreServerItem.Price then begin
                  if IsEnoughBag then begin
                    Dec(m_btCreditPoint, StoreServerItem.Price);
                    Inc(TPlayObject(PlayObject).m_btCreditPoint, StoreServerItem.Price);

                    m_ItemList.Add(Item);
                    SendAddItem(Item);

                    m_DefMsg := MakeDefaultMsg(SM_DELSTOREITEM, 0, 0, 0, 0);
                    sSENDTEST := EncodeBuffer(@ClientItem, SizeOf(TClientItem));
                    SendSocket(@m_DefMsg, sSENDTEST);
                    TPlayObject(PlayObject).SendSocket(@m_DefMsg, sSENDTEST);
                    TPlayObject(PlayObject).SysMsg(m_sCharName + ' Buy your' + sItemName, c_Red, t_Hint);

                    if StdItem.NeedIdentify = 1 then begin
                      AddGameDataLog('35' + #9 +
                        m_sMapName + #9 +
                        IntToStr(m_nCurrX) + #9 +
                        IntToStr(m_nCurrY) + #9 +
                        m_sCharName + #9 +
              //UserEngine.GetStdItemName(m_UseItems[I].wIndex) + #9 +
                        StdItem.Name + #9 +
                        IntToStr(UserItem.MakeIndex) + #9 +
                        BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
                        PlayObject.m_sCharName);
                    end;
                    PlayObject.m_ItemList.Delete(nIndex);
                    PlayObject.m_StoreItemList.Delete(I);
                    Dispose(StoreServerItem);
                    n1C := 0;
                    Break;
                  end else n1C := -3;
                end else n1C := -1;
              end;
            3: begin //能量购买
                if m_nGamePoint >= StoreServerItem.Price then begin
                  if IsEnoughBag then begin
                    DecGamePoint(StoreServerItem.Price);
                    TPlayObject(PlayObject).IncGamePoint(StoreServerItem.Price);
                    GameGoldChanged;
                    TPlayObject(PlayObject).GameGoldChanged;

                    m_ItemList.Add(Item);
                    SendAddItem(Item);

                    m_DefMsg := MakeDefaultMsg(SM_DELSTOREITEM, 0, 0, 0, 0);
                    sSENDTEST := EncodeBuffer(@ClientItem, SizeOf(TClientItem));
                    SendSocket(@m_DefMsg, sSENDTEST);
                    TPlayObject(PlayObject).SendSocket(@m_DefMsg, sSENDTEST);
                    TPlayObject(PlayObject).SysMsg(m_sCharName + ' Buy your ' + sItemName, c_Red, t_Hint);

                    if StdItem.NeedIdentify = 1 then begin
                      AddGameDataLog('35' + #9 +
                        m_sMapName + #9 +
                        IntToStr(m_nCurrX) + #9 +
                        IntToStr(m_nCurrY) + #9 +
                        m_sCharName + #9 +
              //UserEngine.GetStdItemName(m_UseItems[I].wIndex) + #9 +
                        StdItem.Name + #9 +
                        IntToStr(UserItem.MakeIndex) + #9 +
                        BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
                        PlayObject.m_sCharName);
                    end;

                    if g_boGameLogGamePoint then begin
                      AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEPOINT,
                        m_sMapName,
                          m_nCurrX,
                          m_nCurrY,
                          m_sCharName,
                          g_Config.sGamePointName,
                          StoreServerItem.Price,
                          '-',
                          m_sCharName]));

                      AddGameDataLog(Format(g_sGameLogMsg1, [LOG_GAMEPOINT,
                        PlayObject.m_sMapName,
                          PlayObject.m_nCurrX,
                          PlayObject.m_nCurrY,
                          PlayObject.m_sCharName,
                          g_Config.sGamePointName,
                          StoreServerItem.Price,
                          '+',
                          m_sCharName]));
                    end;
                    PlayObject.m_ItemList.Delete(nIndex);
                    PlayObject.m_StoreItemList.Delete(I);
                    Dispose(StoreServerItem);
                    n1C := 0;
                    Break;
                  end else n1C := -3;
                end else n1C := -1;
              end;
          end;
        end;
      end;
    end;
  end;
  if n1C = 0 then begin
    m_DefMsg := MakeDefaultMsg(SM_SENDBUYSTOREITEM_OK, 0, 0, btSellType, 0);
    SendSocket(@m_DefMsg, '');
  end else begin
    m_DefMsg := MakeDefaultMsg(SM_SENDBUYSTOREITEM_FAIL, n1C, 0, btSellType, 0);
    SendSocket(@m_DefMsg, '');
  end;
end;

procedure TPlayObject.ClientCancelStore;
begin
  StoreCancel();
end;

procedure TPlayObject.ClientStartSerieSpell(Target: TActorObject; X, Y: Integer); //开始连击
begin
  if m_boSerieMagicTime then begin
    SendDefMessage(SM_STARTSERIESPELL_FAIL, 0, 0, 0, 0, '');
  end else begin
    if GetTickCount - m_dwSerieMagicTimeTick > g_Config.nSerieMagicTime * 1000 then begin
      if CretInNearXY(Target, X, Y) then begin
        m_btDirection := GetNextDirection(m_nCurrX, m_nCurrY, Target.m_nCurrX, Target.m_nCurrY);
        m_dwSerieMagicTimeTick := GetTickCount;
        m_boSerieMagicTime := True;
        SendDefMessage(SM_STARTSERIESPELL_OK, 0, 0, 0, 0, '');
        SysMsg(g_sCanUseSerieMagicMsg, c_Green, t_Hint);
      end else SendDefMessage(SM_STARTSERIESPELL_FAIL, 0, 0, 0, 0, '');
    end else begin
      SendDefMessage(SM_STARTSERIESPELL_FAIL, 0, 0, 0, 0, '');
      SysMsg(Format(g_sUseSerieMagicTimeMsg, [(g_Config.nSerieMagicTime * 1000 - (GetTickCount - m_dwSerieMagicTimeTick)) div 1000]), c_Green, t_Hint);
    end;
  end;
end;

procedure TPlayObject.ClientStopSerieSpell(); //停止连击
begin
  m_boSerieMagicTime := False; //是否在连击
  m_dwSerieMagicTimeTick := GetTickCount;
end;

procedure TPlayObject.ClientGetMinMap;
var
  nMinMap: Integer;
begin
  nMinMap := m_PEnvir.nMinMap;
  if nMinMap > 0 then begin
    SendDefMessage(SM_READMINIMAP_OK, 0, nMinMap, 0, 0, '');
  end else begin
    SendDefMessage(SM_READMINIMAP_FAIL, 0, 0, 0, 0, '');
 end;
    if m_PEnvir.m_boNOMINIMAP then begin
    SendDefMessage(SM_READMINIMAP_FAIL, 0, 0, 0, 0, '');
end;
end;

procedure TPlayObject.ClientMakeDrugItem(NPC: TObject; nItemName: string);
var
  Merchant: TMerchant;
begin
  Merchant := UserEngine.FindMerchant(NPC);
  if (Merchant = nil) or (not Merchant.m_boMakeDrug) then Exit;
  if ((Merchant.m_PEnvir = m_PEnvir) and
    (abs(Merchant.m_nCurrX - m_nCurrX) < 15) and
    (abs(Merchant.m_nCurrY - m_nCurrY) < 15)) then
    Merchant.ClientMakeDrugItem(Self, nItemName);
end;

procedure TPlayObject.ClientOpenGuildDlg;
var
  I: Integer;
  SC: string;
begin
  if m_MyGuild <> nil then begin
    SC := TGUild(m_MyGuild).sGuildName + #13 + ' ' + #13;
    if m_nGuildRankNo = 1 then begin
      SC := SC + '1' + #13;
    end else begin
      SC := SC + '0' + #13;
    end;
    SC := SC + '<Notice>' + #13;
    for I := 0 to TGUild(m_MyGuild).NoticeList.Count - 1 do begin
      if Length(SC) > 5000 then Break;
      SC := SC + TGUild(m_MyGuild).NoticeList.Strings[I] + #13;
    end; // for
    SC := SC + '<KillGuilds>' + #13;
    for I := 0 to TGUild(m_MyGuild).GuildWarList.Count - 1 do begin
      if Length(SC) > 5000 then Break;
      SC := SC + TGUild(m_MyGuild).GuildWarList.Strings[I] + #13;
    end; // for
    SC := SC + '<AllyGuilds>' + #13;
    for I := 0 to TGUild(m_MyGuild).GuildAllList.Count - 1 do begin
      if Length(SC) > 5000 then Break;
      SC := SC + TGUild(m_MyGuild).GuildAllList.Strings[I] + #13;
    end; // for
    m_DefMsg := MakeDefaultMsg(SM_OPENGUILDDLG, 0, 0, 0, 1);
    SendSocket(@m_DefMsg, EncodeString(SC));
  end else begin
    SendDefMessage(SM_OPENGUILDDLG_FAIL, 0, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.ClientGuildHome;
begin
  ClientOpenGuildDlg();
end;

procedure TPlayObject.ClientGuildMemberList;
var
  GuildRank: pTGuildRank;
  I, II: Integer;
  sSENDMSG: string;
begin
  if m_MyGuild = nil then Exit;
  for I := 0 to TGUild(m_MyGuild).m_RankList.Count - 1 do begin
    GuildRank := TGUild(m_MyGuild).m_RankList.Items[I];
    if GuildRank = nil then Continue;
    sSENDMSG := sSENDMSG + '#' + IntToStr(GuildRank.nRankNo) + '/*' + GuildRank.sRankName + '/';
    for II := 0 to GuildRank.MemberList.Count - 1 do begin
      if Length(sSENDMSG) > 5000 then Break;
      sSENDMSG := sSENDMSG + GuildRank.MemberList.Strings[II] + '/';
    end;
  end;
  m_DefMsg := MakeDefaultMsg(SM_SENDGUILDMEMBERLIST, 0, 0, 0, 1);
  SendSocket(@m_DefMsg, EncodeString(sSENDMSG));
end;

procedure TPlayObject.ClientGuildAddMember(sHumName: string);
var
  nC: Integer;
  PlayObject: TPlayObject;
begin
  nC := 1; //'你没有权利使用这个命令。'
  if IsGuildMaster then begin
    PlayObject := UserEngine.GetPlayObject(sHumName);
    if (PlayObject <> nil) { and (not PlayObject.m_boNotOnlineAddExp)} then begin //2006-10-30增加检测不是离线挂机人物
      if (PlayObject.GetPoseCreate = Self) and (PlayObject.GetPoseCreate <> nil) then begin
        if PlayObject.m_boAllowGuild then begin
          if not TGUild(m_MyGuild).IsMember(sHumName) then begin
            if (PlayObject.m_MyGuild = nil) then begin
              if not TGUild(m_MyGuild).IsFull then begin
                TGUild(m_MyGuild).AddMember(PlayObject);
                //UserEngine.SendServerGroupMsg(SS_207, nServerIndex, TGUild(m_MyGuild).sGuildName);
                PlayObject.m_MyGuild := m_MyGuild;
                PlayObject.m_sGuildRankName := TGUild(m_MyGuild).GetRankName(PlayObject, PlayObject.m_nGuildRankNo);
                PlayObject.RefShowName();
                PlayObject.SysMsg('You have joined the Guild: ' + TGUild(m_MyGuild).sGuildName + ' Your Title is: ' + PlayObject.m_sGuildRankName, c_Green, t_Hint);
                nC := 0;
              end else begin
                nC := 6;
                //SysMsg('你的行会成员数最高限制为：' + IntToStr(TGUild(m_MyGuild).m_nMemberMaxLimit), c_Red, t_Hint);
                SendMsg(g_ManageNPC, RM_MENU_OK, 0, Integer(Self), 0, 0, 'Your Guild is limited to ' + IntToStr(TGUild(m_MyGuild).m_nMemberMaxLimit) + ' members');
              end;
            end else nC := 4; //'对方已经加入其他行会。'
          end else nC := 3; //004DEEF4 '对方已经加入我们的行会。'
        end else begin //004DEEFD
          nC := 5; //'对方不允许加入行会。'
          PlayObject.SysMsg('You must allow Guild joining [Command is @' + g_GameCommand.LETGUILD.sCmd + ']', c_Red, t_Hint);
        end;
      end else nC := 2; //004DEF15 '想加入进来的成员应该来面对掌门人。'
    end else nC := 2;
  end;
  if nC = 0 then begin
    SendDefMessage(SM_GUILDADDMEMBER_OK, 0, 0, 0, 0, '');
  end else begin
    SendDefMessage(SM_GUILDADDMEMBER_FAIL, nC, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.ClientGuildDelMember(sHumName: string);
var
  nC: Integer;
  s14: string;
  PlayObject: TPlayObject;
begin
  nC := 1;
  if IsGuildMaster then begin
    if TGUild(m_MyGuild).IsMember(sHumName) then begin
      if m_sCharName <> sHumName then begin
        if TGUild(m_MyGuild).DelMember(sHumName) then begin
          PlayObject := UserEngine.GetPlayObject(sHumName);
          if PlayObject <> nil then begin
            PlayObject.m_MyGuild := nil;
            PlayObject.RefRankInfo(0, '');
            PlayObject.RefShowName(); //10/31
          end;
          //UserEngine.SendServerGroupMsg(SS_207, nServerIndex, TGUild(m_MyGuild).sGuildName);
          nC := 0;
        end else nC := 4;
      end else begin
        nC := 3;
        s14 := TGUild(m_MyGuild).sGuildName;
        if TGUild(m_MyGuild).CancelGuld(sHumName) then begin
          g_GuildManager.DELGUILD(s14);
          //UserEngine.SendServerGroupMsg(SS_206, nServerIndex, s14);
          m_MyGuild := nil;
          RefRankInfo(0, '');
          RefShowName(); //10/31
          SysMsg('The guild ' + s14 + ' has been deleted.', c_Red, t_Hint);
          nC := 0;
        end
      end;
    end else nC := 2;
  end;
  if nC = 0 then begin
    SendDefMessage(SM_GUILDDELMEMBER_OK, 0, 0, 0, 0, '');
  end else begin
    SendDefMessage(SM_GUILDDELMEMBER_FAIL, nC, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.ClientGuildUpdateNotice(sNotict: string);
var
  SC: string;
begin
  if (m_MyGuild = nil) or (m_nGuildRankNo <> 1) then Exit;
  TGUild(m_MyGuild).NoticeList.Clear;
  while (sNotict <> '') do begin
    sNotict := GetValidStr3(sNotict, SC, [#$D]);
    TGUild(m_MyGuild).NoticeList.Add(SC);
  end; // while
  TGUild(m_MyGuild).SaveGuildInfoFile();
  //UserEngine.SendServerGroupMsg(SS_207, nServerIndex, TGUild(m_MyGuild).sGuildName);
  ClientOpenGuildDlg();
end;

procedure TPlayObject.ClientGuildUpdateRankInfo(sRankInfo: string);
var
  nC: Integer;
begin
  if (m_MyGuild = nil) or (m_nGuildRankNo <> 1) then Exit;
  nC := TGUild(m_MyGuild).UpdateRank(sRankInfo);
  if nC = 0 then begin
    //UserEngine.SendServerGroupMsg(SS_207, nServerIndex, TGUild(m_MyGuild).sGuildName);
    ClientGuildMemberList();
  end else begin
    if nC <= -2 then begin
      SendDefMessage(SM_GUILDRANKUPDATE_FAIL, nC, 0, 0, 0, '');
    end;
  end;
end;

procedure TPlayObject.ClientGuildAlly;
var
  n8: Integer;
  nCode: Integer;
  BaseObjectC: TActorObject;
resourcestring
  sExceptionMsg = '[Exception] TPlayObject::ClientGuildAlly %d';
begin
  try
    n8 := -1;
    nCode := 1;
    BaseObjectC := GetPoseCreate();
    nCode := 2;
    if (BaseObjectC <> nil) and
      (BaseObjectC.m_MyGuild <> nil) and
      (TGUild(m_MyGuild) <> nil) and
      (BaseObjectC.m_btRaceServer = RC_PLAYOBJECT) and
      (BaseObjectC.GetPoseCreate = Self) then begin
      if (TGUild(BaseObjectC.m_MyGuild) <> nil) and (TGUild(BaseObjectC.m_MyGuild).m_boEnableAuthAlly) then begin
        nCode := 3;
        if BaseObjectC.IsGuildMaster and IsGuildMaster then begin
          nCode := 4;
          if (TGUild(m_MyGuild) <> nil) and TGUild(m_MyGuild).IsNotWarGuild(TGUild(BaseObjectC.m_MyGuild)) and
            TGUild(BaseObjectC.m_MyGuild).IsNotWarGuild(TGUild(m_MyGuild)) then begin
            nCode := 6;
            TGUild(m_MyGuild).AllyGuild(TGUild(BaseObjectC.m_MyGuild));
            nCode := 7;
            TGUild(BaseObjectC.m_MyGuild).AllyGuild(TGUild(m_MyGuild));
            nCode := 8;
            TGUild(m_MyGuild).SendGuildMsg(TGUild(BaseObjectC.m_MyGuild).sGuildName + 'Guilds and your guild has been a successful alliance.');
            nCode := 9;
            TGUild(BaseObjectC.m_MyGuild).SendGuildMsg(TGUild(m_MyGuild).sGuildName + 'Guilds and your guild has been a successful alliance.');
            nCode := 10;
            TGUild(m_MyGuild).RefMemberName;
            nCode := 11;
            TGUild(BaseObjectC.m_MyGuild).RefMemberName;
            nCode := 12;
            //UserEngine.SendServerGroupMsg(SS_207, nServerIndex, TGUild(m_MyGuild).sGuildName);
            nCode := 13;
            //UserEngine.SendServerGroupMsg(SS_207, nServerIndex, TGUild(BaseObjectC.m_MyGuild).sGuildName);
            nCode := 14;
            n8 := 0;
          end else n8 := -2;
        end else n8 := -3;
      end else n8 := -4;
    end;
    if n8 = 0 then begin
      SendDefMessage(SM_GUILDMAKEALLY_OK, 0, 0, 0, 0, '');
    end else begin
      SendDefMessage(SM_GUILDMAKEALLY_FAIL, n8, 0, 0, 0, '');
    end;
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg, [nCode]));
      MainOutMessage(E.Message);
    end;
  end;
end;

procedure TPlayObject.ClientGuildBreakAlly(sGuildName: string);
var
  n10: Integer;
  Guild: TGUild;
begin
  n10 := -1;
  if not IsGuildMaster() then Exit;
  Guild := g_GuildManager.FindGuild(sGuildName);
  if (Guild <> nil) and (m_MyGuild <> nil) then begin
    if TGUild(m_MyGuild).IsAllyGuild(Guild) then begin
      TGUild(m_MyGuild).DelAllyGuild(Guild);
      Guild.DelAllyGuild(TGUild(m_MyGuild));
      TGUild(m_MyGuild).SendGuildMsg(Guild.sGuildName + ' Line with your line will lift guild success!');
      Guild.SendGuildMsg(TGUild(m_MyGuild).sGuildName + ' Has withdrawn from this group..');
      TGUild(m_MyGuild).RefMemberName();
      Guild.RefMemberName();
      //UserEngine.SendServerGroupMsg(SS_207, nServerIndex, TGUild(m_MyGuild).sGuildName);
      //UserEngine.SendServerGroupMsg(SS_207, nServerIndex, Guild.sGuildName);
      n10 := 0;
    end else n10 := -2;
  end else n10 := -3;
  if n10 = 0 then begin
    SendDefMessage(SM_GUILDBREAKALLY_OK, 0, 0, 0, 0, '');
  end else begin
    SendDefMessage(SM_GUILDMAKEALLY_FAIL, n10, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.RecalcAdjusBonus();
  procedure AdjustAb(Abil: Word; Val: Word; var lov, hiv: Word);
  var
    Lo, Hi: Byte;
    I: Integer;
  begin
    Lo := LoByte(Abil);
    Hi := HiByte(Abil);
    lov := 0; hiv := 0;
    for I := 1 to Val do begin
      if Lo + 1 < Hi then begin
        Inc(Lo);
        Inc(lov);
      end else begin
        Inc(Hi);
        Inc(hiv);
      end;
    end;
  end;
var
  BonusTick: pTNakedAbility;
  NakedAbil: pTNakedAbility;
  adc, amc, asc, aac, amac: Integer;
  ldc, lmc, lsc, lac, lmac, hdc, hmc, hsc, hac, hmac: Word;
begin
  BonusTick := nil;
  NakedAbil := nil;
  case m_btJob of
    0: begin
        BonusTick := @g_Config.BonusAbilofWarr;
        NakedAbil := @g_Config.NakedAbilofWarr;
      end;
    1: begin
        BonusTick := @g_Config.BonusAbilofWizard;
        NakedAbil := @g_Config.NakedAbilofWizard;
      end;
    2: begin
        BonusTick := @g_Config.BonusAbilofTaos;
        NakedAbil := @g_Config.NakedAbilofTaos;
      end;
  end;

  if BonusTick.HP = 0 then BonusTick.HP := 1;
  if BonusTick.MP = 0 then BonusTick.MP := 1;

  if BonusTick.DC = 0 then BonusTick.DC := 1;
  if BonusTick.MC = 0 then BonusTick.MC := 1;
  if BonusTick.SC = 0 then BonusTick.SC := 1;
  if BonusTick.AC = 0 then BonusTick.AC := 1;
  if BonusTick.MAC = 0 then BonusTick.MAC := 1;

  adc := m_BonusAbil.DC div BonusTick.DC;
  amc := m_BonusAbil.MC div BonusTick.MC;
  asc := m_BonusAbil.SC div BonusTick.SC;
  aac := m_BonusAbil.AC div BonusTick.AC;
  amac := m_BonusAbil.MAC div BonusTick.MAC;

  AdjustAb(NakedAbil.DC, adc, ldc, hdc);
  AdjustAb(NakedAbil.MC, amc, lmc, hmc);
  AdjustAb(NakedAbil.SC, asc, lsc, hsc);
  AdjustAb(NakedAbil.AC, aac, lac, hac);
  AdjustAb(NakedAbil.MAC, amac, lmac, hmac);
  //lac  := 0;  hac := aac;
  //lmac := 0;  hmac := amac;

  m_WAbil.DC := MakeLong(_MIN(MAXHUMPOWER, LoWord(m_WAbil.DC) + ldc), _MIN(MAXHUMPOWER, HiWord(m_WAbil.DC) + hdc));
  m_WAbil.MC := MakeLong(_MIN(MAXHUMPOWER, LoWord(m_WAbil.MC) + lmc), _MIN(MAXHUMPOWER, HiWord(m_WAbil.MC) + hmc));
  m_WAbil.SC := MakeLong(_MIN(MAXHUMPOWER, LoWord(m_WAbil.SC) + lsc), _MIN(MAXHUMPOWER, HiWord(m_WAbil.SC) + hsc));
  m_WAbil.AC := MakeLong(_MIN(MAXHUMPOWER, LoWord(m_WAbil.AC) + lac), _MIN(MAXHUMPOWER, HiWord(m_WAbil.AC) + hac));
  m_WAbil.MAC := MakeLong(_MIN(MAXHUMPOWER, LoWord(m_WAbil.MAC) + lmac), _MIN(MAXHUMPOWER, HiWord(m_WAbil.MAC) + hmac));

  m_WAbil.MaxHP := MinLong(g_Config.nMaxLevel, m_WAbil.MaxHP + m_BonusAbil.HP div BonusTick.HP);
  m_WAbil.MaxMP := MinLong(g_Config.nMaxLevel, m_WAbil.MaxMP + m_BonusAbil.MP div BonusTick.MP);

  //      m_btSpeedPoint:=m_btSpeedPoint + m_BonusAbil.Speed div BonusTick.Speed;
  //      m_btHitPoint:=m_btHitPoint + m_BonusAbil.Hit div BonusTick.Hit;
end;

procedure TPlayObject.ClientAdjustBonus(nPoint: Integer; sMsg: string);
var
  BonusAbil: TNakedAbility;
  nTotleUsePoint: Integer;
  //m_nTotleUsePoint: Integer;
begin
  FillChar(BonusAbil, SizeOf(TNakedAbility), #0);
  DecodeBuffer(sMsg, @BonusAbil, SizeOf(TNakedAbility));
  nTotleUsePoint := BonusAbil.DC +
    BonusAbil.MC +
    BonusAbil.SC +
    BonusAbil.AC +
    BonusAbil.MAC +
    BonusAbil.HP +
    BonusAbil.MP +
    BonusAbil.Hit +
    BonusAbil.Speed +
    BonusAbil.X2;

  if (nPoint + nTotleUsePoint = m_nBonusPoint) and (m_nBonusPoint >= 0) and (nPoint >= 0) and (m_nBonusPoint >= nPoint) then begin
    if nTotleUsePoint > 0 then begin
      if m_BonusAbil.DC + BonusAbil.DC > High(Word) then begin
        SysMsg('Cannot increase stats further!', c_Red, t_Hint);
        Exit;
      end;
      if m_BonusAbil.MC + BonusAbil.MC > High(Word) then begin
        SysMsg('Cannot increase stats further!', c_Red, t_Hint);
        Exit;
      end;
      if m_BonusAbil.SC + BonusAbil.SC > High(Word) then begin
        SysMsg('Cannot increase stats further!', c_Red, t_Hint);
        Exit;
      end;
      if m_BonusAbil.AC + BonusAbil.AC > High(Word) then begin
        SysMsg('Cannot increase stats further!', c_Red, t_Hint);
        Exit;
      end;
      if m_BonusAbil.MAC + BonusAbil.MAC > High(Word) then begin
        SysMsg('Cannot increase stats further!', c_Red, t_Hint);
        Exit;
      end;
      if m_BonusAbil.Hit + BonusAbil.Hit > High(Word) then begin
        SysMsg('Cannot increase stats further!', c_Red, t_Hint);
        Exit;
      end;
      if m_BonusAbil.Speed + BonusAbil.Speed > High(Word) then begin
        SysMsg('Cannot increase stats further!', c_Red, t_Hint);
        Exit;
      end;

      if m_BonusAbil.HP + BonusAbil.HP > High(Word) then begin
        SysMsg('Cannot increase stats further!', c_Red, t_Hint);
        Exit;
      end;

      if m_BonusAbil.MP + BonusAbil.MP > High(Word) then begin
        SysMsg('Cannot increase stats further!', c_Red, t_Hint);
        Exit;
      end;

      m_nBonusPoint := nPoint;
      Inc(m_BonusAbil.DC, BonusAbil.DC);
      Inc(m_BonusAbil.MC, BonusAbil.MC);
      Inc(m_BonusAbil.SC, BonusAbil.SC);
      Inc(m_BonusAbil.AC, BonusAbil.AC);
      Inc(m_BonusAbil.MAC, BonusAbil.MAC);
      Inc(m_BonusAbil.HP, BonusAbil.HP);
      Inc(m_BonusAbil.MP, BonusAbil.MP);
      Inc(m_BonusAbil.Hit, BonusAbil.Hit);
      Inc(m_BonusAbil.Speed, BonusAbil.Speed);
      Inc(m_BonusAbil.X2, BonusAbil.X2);
      RecalcAbilitys();
      SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
      SendMsg(Self, RM_SUBABILITY, 0, 0, 0, 0, '');
    end;
  end else begin
    SysMsg('Error adjusting stats.', c_Red, t_Hint);
  end;
end;

function TPlayObject.GetMyStatus: Integer;
begin
  Result := m_nHungerStatus div 1000;
  if Result > 4 then Result := 4;
end;

procedure TPlayObject.SendAdjustBonus;
var
  sSENDMSG: string;
  //NakedAbil:TNakedAbility;
begin
  if m_boNotOnlineAddExp or m_boAI then Exit;
  m_DefMsg := MakeDefaultMsg(SM_ADJUST_BONUS, m_nBonusPoint, 0, 0, 0);
  sSENDMSG := '';
  //NakedAbil:=m_BonusAbil;
  //FillChar(NakedAbil,SizeOf(TNakedAbility),#0);
  case m_btJob of //
    0: sSENDMSG := EncodeBuffer(@g_Config.BonusAbilofWarr, SizeOf(TNakedAbility)) + '/' +
      EncodeBuffer(@m_BonusAbil, SizeOf(TNakedAbility)) + '/' +
        EncodeBuffer(@g_Config.NakedAbilofWarr, SizeOf(TNakedAbility));
    1: sSENDMSG := EncodeBuffer(@g_Config.BonusAbilofWizard, SizeOf(TNakedAbility)) + '/' +
      EncodeBuffer(@m_BonusAbil, SizeOf(TNakedAbility)) + '/' +
        EncodeBuffer(@g_Config.NakedAbilofWizard, SizeOf(TNakedAbility));
    2: sSENDMSG := EncodeBuffer(@g_Config.BonusAbilofTaos, SizeOf(TNakedAbility)) + '/' +
      EncodeBuffer(@m_BonusAbil, SizeOf(TNakedAbility)) + '/' +
        EncodeBuffer(@g_Config.NakedAbilofTaos, SizeOf(TNakedAbility));
  end; // case
  SendSocket(@m_DefMsg, sSENDMSG);
end;

function TActorObject.GetAttackDir(BaseObject: TActorObject; nRange: Integer; var btDir: Byte): Boolean;
var
  nX, nY: Integer;
begin
  Result := False;
  btDir := GetNextDirection(m_nCurrX, m_nCurrY, BaseObject.m_nCurrX, BaseObject.m_nCurrY);
  if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, btDir, nRange, nX, nY) then begin
    Result := BaseObject = TActorObject(m_PEnvir.GetMovingObject(nX, nY, BaseObject, True));
  end;
end;

function TActorObject.GetAttackDir(BaseObject: TActorObject; var btDir: Byte): Boolean;
begin
  Result := False;
  if (m_nCurrX - 1 <= BaseObject.m_nCurrX) and
    (m_nCurrX + 1 >= BaseObject.m_nCurrX) and
    (m_nCurrY - 1 <= BaseObject.m_nCurrY) and
    (m_nCurrY + 1 >= BaseObject.m_nCurrY) and
    ((m_nCurrX <> BaseObject.m_nCurrX) or
    (m_nCurrY <> BaseObject.m_nCurrY)) then begin
    Result := True;
    if ((m_nCurrX - 1) = BaseObject.m_nCurrX) and (m_nCurrY = BaseObject.m_nCurrY) then begin
      btDir := DR_LEFT;
      Exit;
    end;
    if ((m_nCurrX + 1) = BaseObject.m_nCurrX) and (m_nCurrY = BaseObject.m_nCurrY) then begin
      btDir := DR_RIGHT;
      Exit;
    end;
    if (m_nCurrX = BaseObject.m_nCurrX) and ((m_nCurrY - 1) = BaseObject.m_nCurrY) then begin
      btDir := DR_UP;
      Exit;
    end;
    if (m_nCurrX = BaseObject.m_nCurrX) and ((m_nCurrY + 1) = BaseObject.m_nCurrY) then begin
      btDir := DR_DOWN;
      Exit;
    end;
    if ((m_nCurrX - 1) = BaseObject.m_nCurrX) and ((m_nCurrY - 1) = BaseObject.m_nCurrY) then begin
      btDir := DR_UPLEFT;
      Exit;
    end;
    if ((m_nCurrX + 1) = BaseObject.m_nCurrX) and ((m_nCurrY - 1) = BaseObject.m_nCurrY) then begin
      btDir := DR_UPRIGHT;
      Exit;
    end;
    if ((m_nCurrX - 1) = BaseObject.m_nCurrX) and ((m_nCurrY + 1) = BaseObject.m_nCurrY) then begin
      btDir := DR_DOWNLEFT;
      Exit;
    end;
    if ((m_nCurrX + 1) = BaseObject.m_nCurrX) and ((m_nCurrY + 1) = BaseObject.m_nCurrY) then begin
      btDir := DR_DOWNRIGHT;
      Exit;
    end;
    btDir := 0;
  end;
end;

function TActorObject.TargetInSpitRange(BaseObject: TActorObject; var btDir: Byte): Boolean; //004C3E68
var
  n14, n18: Integer;
begin
  Result := False;
  if (abs(BaseObject.m_nCurrX - m_nCurrX) <= 2) and (abs(BaseObject.m_nCurrY - m_nCurrY) <= 2) then begin
    n14 := BaseObject.m_nCurrX - m_nCurrX;
    n18 := BaseObject.m_nCurrY - m_nCurrY;
    if (abs(n14) <= 1) and (abs(n18) <= 1) then begin
      GetAttackDir(BaseObject, btDir);
      Result := True;
      Exit;
    end;
    Inc(n14, 2);
    Inc(n18, 2);
    if ((n14 >= 0) and (n14 <= 4)) and ((n18 >= 0) and (n18 <= 4)) then begin
      btDir := GetNextDirection(m_nCurrX, m_nCurrY, BaseObject.m_nCurrX, BaseObject.m_nCurrY);
      if g_Config.SpitMap[btDir, n18, n14] = 1 then
        Result := True;
    end;
  end;
end;

function TActorObject.RecalcBagWeight: Integer;
var
  I: Integer;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
begin
  Result := 0;
  for I := 0 to m_ItemList.Count - 1 do begin
    UserItem := m_ItemList.Items[I];
    StdItem := UserEngine.GetStdItem(UserItem.wIndex);
    if StdItem <> nil then begin
      Inc(Result, StdItem.Weight);
    end;
  end;
end;

procedure TActorObject.RecalcHitSpeed;
var
  I: Integer;
  UserMagic: pTUserMagic;
  BonusTick: pTNakedAbility;
begin
  BonusTick := nil;
  case m_btJob of
    0: BonusTick := @g_Config.BonusAbilofWarr;
    1: BonusTick := @g_Config.BonusAbilofWizard;
    2: BonusTick := @g_Config.BonusAbilofTaos;
  end;
  m_btHitPoint := DEFHIT + m_BonusAbil.Hit div BonusTick.Hit;
  if BonusTick.Speed = 0 then BonusTick.Speed := 1;
  case m_btJob of
    TAOS: m_btSpeedPoint := DEFSPEED + m_BonusAbil.Speed div BonusTick.Speed + 3;
  else m_btSpeedPoint := DEFSPEED + m_BonusAbil.Speed div BonusTick.Speed;
  end;

  m_nHitPlus := 0;
  m_nHitDouble := 0;

  m_MagicOneSwordSkill := nil;
  m_MagicPowerHitSkill := nil;
  m_MagicErgumSkill := nil;
  m_MagicBanwolSkill := nil;
  m_MagicFireSwordSkill := nil;
  m_MagicCrsSkill := nil;
  m_Magic41Skill := nil;
  m_Magic42Skill := nil;
  m_Magic43Skill := nil;
  m_Magic58Skill := nil;
  m_Magic60Skill := nil;
  m_MagicZRJFSkill := nil;
  m_Magic31Skill := nil;

  m_Magic100Skill := nil;
  m_Magic101Skill := nil;
  m_Magic102Skill := nil;
  m_Magic103Skill := nil;

  for I := 0 to m_MagicList.Count - 1 do begin
    UserMagic := m_MagicList.Items[I];
    case UserMagic.wMagIdx of
      SKILL_FENCING: begin // Fencing - ID: 3
          m_MagicOneSwordSkill := UserMagic;
          if UserMagic.btLevel > 0 then begin
            m_btHitPoint := m_btHitPoint + Round(9 / 3 * UserMagic.btLevel);
          end;
        end;
      SKILL_SPIRITSWORD: begin // Spirit Sword - ID: 4
          m_MagicOneSwordSkill := UserMagic;
          if UserMagic.btLevel > 0 then begin
            m_btHitPoint := m_btHitPoint + Round(8 / 3 * UserMagic.btLevel);
          end;
        end;
      SKILL_SLAYING: begin // Slaying - ID: 7
          m_MagicPowerHitSkill := UserMagic;
          if UserMagic.btLevel > 0 then begin
            m_btHitPoint := m_btHitPoint + Round(3 / 3 * UserMagic.btLevel);
          end;
          m_nHitPlus := DEFHIT + UserMagic.btLevel;
          m_btAttackSkillCount := 7 - UserMagic.btLevel;
          m_btAttackSkillPointCount := Random(m_btAttackSkillCount);
        end;
      SKILL_THRUSTING: begin // Thrusting - ID: 12
          m_MagicErgumSkill := UserMagic;
        end;
      SKILL_BANWOL: begin //半月弯刀
          m_MagicBanwolSkill := UserMagic;
        end;
      SKILL_FIRESWORD: begin //烈火剑法
          m_MagicFireSwordSkill := UserMagic;
          m_nHitDouble := 4 + UserMagic.btLevel * 4;
        end;

      SKILL_SHIELD: begin //魔法盾
          m_Magic31Skill := UserMagic;
        end;
      SKILL_CROSSHALFMOON: begin // CrossHalfMoon - ID: 34
          m_MagicCrsSkill := UserMagic;
        end;
      SKILL_LIONROAR: begin
          m_Magic41Skill := UserMagic;
        end;
      SKILL_TWINDRAKEBLADE: begin
          m_Magic42Skill := UserMagic;
        end;
      SKILL_43: begin
          m_Magic43Skill := UserMagic;
          m_nHitDouble := 4 + UserMagic.btLevel * 4;
        end;
      SKILL_58: begin
          m_Magic58Skill := UserMagic;
          m_nHitDouble := 4 + UserMagic.btLevel * 4;
        end;
      SKILL_60: begin
          m_Magic60Skill := UserMagic;
        end;
      SKILL_77: begin
          m_MagicZRJFSkill := UserMagic;
          m_nHitDouble := 4 + UserMagic.btLevel * 4;
        end;
      SKILL_100: m_Magic100Skill := UserMagic;
      SKILL_101: m_Magic101Skill := UserMagic;
      SKILL_102: m_Magic102Skill := UserMagic;
      SKILL_103: m_Magic103Skill := UserMagic;
    end;
  end;
end;

procedure TActorObject.AddItemSkill(nIndex: Integer);
var
  Magic: pTMagic;
  UserMagic: pTUserMagic;
begin
  Magic := nil;
  if m_btRaceServer <> RC_HEROOBJECT then begin
    case nIndex of
      1: Magic := UserEngine.FindMagic(g_Config.sFireBallSkill);
      2: Magic := UserEngine.FindMagic(g_Config.sHealSkill);
    end;
    if Magic <> nil then begin
      if not IsTrainingSkill(Magic.wMagicId) then begin
        New(UserMagic);
        UserMagic.MagicInfo := Magic;
        UserMagic.wMagIdx := Magic.wMagicId;
        UserMagic.btKey := 0;
        UserMagic.btLevel := 1;
        UserMagic.nTranPoint := 0;
        m_MagicList.Add(UserMagic);
        if m_btRaceServer = RC_PLAYOBJECT then begin
          TPlayObject(Self).SendAddMagic(UserMagic);
        end;
      end;
    end;
  end;
end;

function TActorObject.AddToMap: Boolean;
begin
  Result := m_PEnvir.AddToMap(m_nCurrX, m_nCurrY, Self) = Self;
  if not m_boFixedHideMode then
    SendRefMsg(RM_TURN, m_btDirection, m_nCurrX, m_nCurrY, 0, '');
end;

procedure TActorObject.AttackDir(TargeTActorObject: TActorObject; wHitMode: Word;
  nDir: Integer);
  function GetMagicSpell(UserMagic: pTUserMagic): Integer;
  begin
    Result := Round(UserMagic.MagicInfo.wSpell / (UserMagic.MagicInfo.btTrainLv + 1) * (UserMagic.btLevel + 1));
  end;
  //武器升级设置
  function CheckWeaponUpgradeStatus(UserItem: pTUserItem): Boolean; //004C27C0
  begin
    //if (UserItem.btValue[0] + UserItem.btValue[1] + UserItem.btValue[2]) < 20 then begin
    Result := True;
    if (UserItem.btValue[0] + UserItem.btValue[1] + UserItem.btValue[2]) < g_Config.nUpgradeWeaponMaxPoint then begin
      case UserItem.btValue[10] of
        1: begin
            if not g_Config.boDeleteUpgradeFailWeapon then UserItem.wIndex := 0;
            Result := False;
          end;
        10..13: UserItem.btValue[0] := UserItem.btValue[0] + UserItem.btValue[10] - 9;
        20..23: UserItem.btValue[1] := UserItem.btValue[1] + UserItem.btValue[10] - 19;
        30..33: UserItem.btValue[2] := UserItem.btValue[2] + UserItem.btValue[10] - 29;
      end;
    end else begin
      Result := False;
      if not g_Config.boDeleteUpgradeFailWeapon then UserItem.wIndex := 0;
    end;
    UserItem.btValue[10] := 0;
  end;
  procedure CheckWeaponUpgrade();
  var
    UseItems: TUserItem;
    PlayObject: TPlayObject;
    StdItem: pTStdItem;
  begin
    if m_UseItems[U_WEAPON].btValue[10] > 0 then begin
      UseItems := m_UseItems[U_WEAPON];
      if not CheckWeaponUpgradeStatus(@m_UseItems[U_WEAPON]) then begin
        SysMsg(sTheWeaponRefineFail, c_Red, t_Hint);
        if m_UseItems[U_WEAPON].wIndex = 0 then begin
          SysMsg(g_sTheWeaponBroke, c_Red, t_Hint);
          PlayObject := TPlayObject(Self);
          PlayObject.SendDelItems(@UseItems);
        //PlayObject.StatusChanged;
          SendRefMsg(RM_BREAKWEAPON, 0, 0, 0, 0, '');
        end else begin
          PlayObject := TPlayObject(Self);
          PlayObject.SendUpdateItem(@m_UseItems[U_WEAPON]);
        end;
        StdItem := UserEngine.GetStdItem(UseItems.wIndex);
        //004C29E0
        if StdItem.NeedIdentify = 1 then
          AddGameDataLog('21' + #9 +
            m_sMapName + #9 +
            IntToStr(m_nCurrX) + #9 +
            IntToStr(m_nCurrY) + #9 +
            m_sCharName + #9 +
            //UserEngine.GetStdItemName(UseItems.wIndex) + #9 +
            StdItem.Name + #9 +
            IntToStr(UseItems.MakeIndex) + #9 +
            '1' + #9 +
            '0');
        FeatureChanged();
        RefShowName;
      end else begin
        SysMsg(sTheWeaponRefineSuccessfull, c_Red, t_Hint);
        PlayObject := TPlayObject(Self);
        PlayObject.SendUpdateItem(@m_UseItems[U_WEAPON]);
        StdItem := UserEngine.GetStdItem(UseItems.wIndex);
        //004C2B14
        if StdItem.NeedIdentify = 1 then
          AddGameDataLog('20' + #9 +
            m_sMapName + #9 +
            IntToStr(m_nCurrX) + #9 +
            IntToStr(m_nCurrY) + #9 +
            m_sCharName + #9 +
            //UserEngine.GetStdItemName(UseItems.wIndex) + #9 +
            StdItem.Name + #9 +
            IntToStr(UseItems.MakeIndex) + #9 +
            '1' + #9 +
            '0');
        RecalcAbilitys();
        SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
        SendMsg(Self, RM_SUBABILITY, 0, 0, 0, 0, '');
      end;
    end;
  end;
var
  AttackTarget: TActorObject;
  boPowerHit: Boolean;
  boFireHit: Boolean;
  boCrsHit: Boolean;
  bo41: Boolean;
  bo42: Boolean;
  bo43: Boolean;
  bo58: Boolean;
  bo60: Boolean;
  boZRJFHitSkill: Boolean;
  wIdent: Word;
  nCheckCode: Integer;
//  UserMagic: pTUserMagic;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::AttackDir Code: %d';
begin
  nCheckCode := 0;
  try
    if (wHitMode = 5) and (m_MagicBanwolSkill <> nil) then begin //半月
      if m_WAbil.MP > 0 then begin
        nCheckCode := 1;
        DamageSpell(m_MagicBanwolSkill.MagicInfo.btDefSpell + GetMagicSpell(m_MagicBanwolSkill));
        nCheckCode := 2;
        HealthSpellChanged();
        nCheckCode := 3;
      end else wHitMode := RM_HIT;
      end;

          if (wHitMode = 8) and (m_MagicCrsSkill <> nil) then begin
    if m_WAbil.MP > 0 then begin
      DamageSpell(m_MagicCrsSkill.MagicInfo.btDefSpell + GetMagicSpell(m_MagicCrsSkill));
      HealthSpellChanged();
    end else wHitMode:=RM_HIT;
  end;


    nCheckCode := 4;
    m_btDirection := nDir;
    if TargeTActorObject = nil then begin
      nCheckCode := 41;
      AttackTarget := GetPoseCreate();
    end else AttackTarget := TargeTActorObject;
    if (AttackTarget <> nil) and (m_UseItems[U_WEAPON].wIndex > 0) then begin
      nCheckCode := 42;
      if m_btRaceServer = RC_PLAYOBJECT then
        CheckWeaponUpgrade();
    end;
    nCheckCode := 5;
    boPowerHit := m_boPowerHit;
    boFireHit := m_boFireHitSkill;
    boCrsHit := m_boCrsHitkill;
    bo41 := m_bo41kill;
    bo42 := m_bo42kill;
    bo43 := m_boCIDHitSkill;
    bo58 := m_boKTZHitSkill;
    bo60 := m_bo60kill;
    boZRJFHitSkill := m_boZRJFHitSkill;

    m_bo60kill := False;
    if _Attack(wHitMode, AttackTarget) then begin
      nCheckCode := 6;
      SetTargetCreat(AttackTarget);
      nCheckCode := 7;
    end;
    wIdent := RM_HIT;
    if (m_btRaceServer = RC_PLAYOBJECT) or (m_btRaceServer = RC_HEROOBJECT) or (m_btRaceServer = RC_PLAYMOSTER) then begin
      case wHitMode of
        0: wIdent := RM_HIT;
        1: wIdent := RM_HEAVYHIT;
        2: wIdent := RM_BIGHIT;
        3: if boPowerHit then wIdent := RM_SPELL2;
        4: if m_MagicErgumSkill <> nil then wIdent := RM_LONGHIT;
        5: if m_MagicBanwolSkill <> nil then wIdent := RM_WIDEHIT;
        7: if boFireHit then wIdent := RM_FIREHIT;
        8: if m_MagicCrsSkill <> nil then wIdent := RM_CRSHIT;
        9: if m_Magic42Skill <> nil then wIdent := RM_41;
        10: if bo42 then wIdent := RM_42;
        11: if bo43 then wIdent := RM_43;
        12: if bo58 then wIdent := RM_44;
        13: wIdent := RM_60;
        14: wIdent := RM_61;
        15: wIdent := RM_62;

        16: if boZRJFHitSkill then wIdent := RM_ZRJF;

        100: if m_Magic100Skill <> nil then wIdent := RM_100HIT;
        101: if m_Magic101Skill <> nil then wIdent := RM_101HIT;
        102: if m_Magic102Skill <> nil then wIdent := RM_102HIT;
        103: if m_Magic103Skill <> nil then wIdent := RM_103HIT;
      end;
    end;

    if (wHitMode in [13..15]) and (m_btRaceServer = RC_PLAYOBJECT) then begin //合击技能
      case wHitMode of
        13: m_DefMsg := MakeDefaultMsg(SM_60HIT, Integer(Self), m_nCurrX, m_nCurrY, m_btDirection);
        14: m_DefMsg := MakeDefaultMsg(SM_61HIT, Integer(Self), m_nCurrX, m_nCurrY, m_btDirection);
        15: m_DefMsg := MakeDefaultMsg(SM_62HIT, Integer(Self), m_nCurrX, m_nCurrY, m_btDirection);
      end;
      TPlayObject(Self).SendSocket(@m_DefMsg, '');
    end;

    if (m_btRaceServer = RC_HEROOBJECT) and (wIdent = RM_FIREHIT) and (m_MagicFireSwordSkill <> nil) then begin //4级烈火
      if m_MagicFireSwordSkill.btLevel >= 4 then wIdent := RM_SUPERFIREHIT;
    end;

    nCheckCode := 8;
    SendAttackMsg(wIdent, m_btDirection, m_nCurrX, m_nCurrY);
    nCheckCode := 9;

    if (m_btRaceServer = RC_PLAYOBJECT) then begin
      //if m_TargetCret <> nil then
      //MainOutMessage('AttackJump:'+m_TargetCret.m_sCharName);
      TPlayObject(Self).AttackJump;
    end;
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg, [nCheckCode]));
      MainOutMessage(E.Message);
    end;
  end;
end;

procedure TActorObject.CheckPKStatus;
begin
  if m_boPKFlag and ((GetTickCount - m_dwPKTick) > g_Config.dwPKFlagTime {60 * 1000}) then begin
    m_boPKFlag := False;
    RefNameColor();
  end;
end;

procedure TActorObject.DamageSpell(nSpellPoint: Integer);
begin
  if nSpellPoint > 0 then begin
    if (m_WAbil.MP - nSpellPoint) > 0 then
      Dec(m_WAbil.MP, nSpellPoint)
    else m_WAbil.MP := 0;
  end else begin
    if (m_WAbil.MP - nSpellPoint) < m_WAbil.MaxMP then
      Dec(m_WAbil.MP, nSpellPoint)
    else m_WAbil.MP := m_WAbil.MaxMP;
  end;
end;

procedure TActorObject.DecPKPoint(nPoint: Integer);
var
  nC: Integer;
begin
  nC := PKLevel();
  Dec(m_nPkPoint, nPoint);
  if m_nPkPoint < 0 then m_nPkPoint := 0;
  if (PKLevel <> nC) and (nC > 0) and (nC <= 2) then begin
    RefNameColor();
  end;
end;

procedure TActorObject.DelItemSkill(nIndex: Integer);
  procedure DELETESKILL(sSkillName: string);
  var
    I: Integer;
    UserMagic: pTUserMagic;
    PlayObject: TPlayObject;
  begin
    for I := 0 to m_MagicList.Count - 1 do begin
      UserMagic := m_MagicList.Items[I];
      if UserMagic.MagicInfo.sMagicName = sSkillName then begin
        PlayObject := TPlayObject(Self);
        PlayObject.SendDelMagic(UserMagic);
        Dispose(UserMagic);
        m_MagicList.Delete(I);
        Break;
      end;
    end;
  end;
begin
  if m_btRaceServer <> RC_PLAYOBJECT then Exit;
  case nIndex of
    1: if m_btJob <> 1 then DELETESKILL(g_Config.sFireBallSkill);
    2: if m_btJob <> 2 then DELETESKILL(g_Config.sHealSkill)
  end;
end;

procedure TPlayObject.DelMember(PlayObject: TPlayObject);
var
  I: Integer;
begin
  if m_GroupOwner <> PlayObject then begin
    for I := m_GroupMembers.Count - 1 downto 0 do begin
      if m_GroupMembers.Count <= 0 then Break;
      if (m_GroupMembers.Objects[I] <> nil) and (m_GroupMembers.Objects[I] = PlayObject) then begin
        PlayObject.LeaveGroup();
        m_GroupMembers.Delete(I);
        Break;
      end;
    end;
  end else begin
    for I := m_GroupMembers.Count - 1 downto 0 do begin
      if m_GroupMembers.Count <= 0 then Break;
      if TPlayObject(m_GroupMembers.Objects[I]) <> nil then begin
        TPlayObject(m_GroupMembers.Objects[I]).LeaveGroup;
        m_GroupMembers.Delete(I);
      end;
    end;
  end;
  if not CancelGroup then begin
    SendDefMessage(SM_GROUPCANCEL, 0, 0, 0, 0, '');
  end else SendGroupMembers();
end;

procedure TActorObject.DoDamageWeapon(nWeaponDamage: Integer);
var
  nDura, nDuraPoint: Integer;
  PlayObject: TPlayObject;
  StdItem: pTStdItem;
begin
  if m_UseItems[U_WEAPON].wIndex <= 0 then Exit;
  nDura := m_UseItems[U_WEAPON].Dura;
  nDuraPoint := Round(nDura / 1.03);
  Dec(nDura, nWeaponDamage);
  if nDura <= 0 then begin
    nDura := 0;
    m_UseItems[U_WEAPON].Dura := nDura;
    if m_btRaceServer = RC_PLAYOBJECT then begin
      PlayObject := TPlayObject(Self);
      PlayObject.SendDelItems(@m_UseItems[U_WEAPON]);
      StdItem := UserEngine.GetStdItem(m_UseItems[U_WEAPON].wIndex);
            //004C195A
      if StdItem.NeedIdentify = 1 then
        AddGameDataLog('3' + #9 +
          m_sMapName + #9 +
          IntToStr(m_nCurrX) + #9 +
          IntToStr(m_nCurrY) + #9 +
          m_sCharName + #9 +
                        //UserEngine.GetStdItemName(m_UseItems[U_WEAPON].wIndex) + #9 +
          StdItem.Name + #9 +
          IntToStr(m_UseItems[U_WEAPON].MakeIndex) + #9 +
          BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
          '0');
    end;
    m_UseItems[U_WEAPON].wIndex := 0;
    //SendMsg(Self, RM_DURACHANGE, U_WEAPON, nDura, m_UseItems[U_WEAPON].DuraMax, 0, '');
  end else begin
    m_UseItems[U_WEAPON].Dura := nDura;
  end;
  if (nDura / 1.03) <> nDuraPoint then begin
    SendMsg(Self, RM_DURACHANGE, U_WEAPON, m_UseItems[U_WEAPON].Dura, m_UseItems[U_WEAPON].DuraMax, 0, '');
  end;
end;

procedure TActorObject.GetAccessory(Item: TUserItem; StdItem: pTStdItem; var AddAbility: TAddAbility);
var
  I: Integer;
  nATOM: Integer;
  //StdItem: pTStdItem;
  StdItemA: TStdItem;
begin
  {StdItem := UserEngine.GetStdItem(Item.wIndex);
  if StdItem = nil then Exit; }
  StdItemA := StdItem^;
  ItemUnit.GetItemAddValue(@Item, StdItemA);

  for I := Low(AddAbility.ATOM_DC) to High(AddAbility.ATOM_DC) do begin
    nATOM := ItemUnit._GetItemAddValue(@Item, I);
    Inc(AddAbility.ATOM_DC[I], LoByte(LoWord(nATOM)));
    Inc(AddAbility.ATOM_MC[I], HiByte(LoWord(nATOM)));
    Inc(AddAbility.ATOM_MAC[I], HiWord(nATOM));
  end;

  case StdItem.StdMode of
    5, 6: begin
        Inc(AddAbility.wHitPoint, HiWord(StdItemA.AC)); //准确
        if HiWord(StdItemA.MAC) > 10 then begin
          Inc(AddAbility.nHitSpeed, HiWord(StdItemA.MAC) - 10); //攻击速度
        end else begin
          Dec(AddAbility.nHitSpeed, HiWord(StdItemA.MAC));
        end;
        Inc(AddAbility.btLuck, LoWord(StdItemA.AC));
        Inc(AddAbility.btUnLuck, LoWord(StdItemA.MAC));
      end;
    19: begin
        Inc(AddAbility.wAntiMagic, HiWord(StdItemA.AC));
        Inc(AddAbility.btUnLuck, LoWord(StdItemA.MAC));
        Inc(AddAbility.btLuck, HiWord(StdItemA.MAC));
      end;
    53: begin //新加物品属性
        if not g_Config.boAddUserItemNewValue then begin
          Inc(AddAbility.wAntiMagic, HiWord(StdItemA.AC));
          Inc(AddAbility.btUnLuck, LoWord(StdItemA.MAC)); //诅咒
          Inc(AddAbility.btLuck, HiWord(StdItemA.MAC)); //幸运
        end else begin
          AddAbility.wAC := MakeLong(LoWord(AddAbility.wAC) + LoWord(StdItemA.AC), HiWord(AddAbility.wAC) + HiWord(StdItemA.AC));
          AddAbility.wMAC := MakeLong(LoWord(AddAbility.wMAC) + LoWord(StdItemA.MAC), HiWord(AddAbility.wMAC) + HiWord(StdItemA.MAC));
        end;
      end;
    20, 24: begin
        Inc(AddAbility.wHitPoint, HiWord(StdItemA.AC)); //准确
        Inc(AddAbility.wSpeedPoint, HiWord(StdItemA.MAC)); //敏捷度
      end;
    52: begin //原本与 20,24 一个属性，现在分开单独进行设置
        if not g_Config.boAddUserItemNewValue then begin
          Inc(AddAbility.wHitPoint, HiWord(StdItemA.AC));
          Inc(AddAbility.wSpeedPoint, HiWord(StdItemA.MAC));
        end else begin
          AddAbility.wAC := MakeLong(LoWord(AddAbility.wAC) + LoWord(StdItemA.AC), HiWord(AddAbility.wAC) + HiWord(StdItemA.AC));
          AddAbility.wMAC := MakeLong(LoWord(AddAbility.wMAC) + LoWord(StdItemA.MAC), HiWord(AddAbility.wMAC) + HiWord(StdItemA.MAC));
        end;
      end;
    21: begin
        Inc(AddAbility.wHealthRecover, HiWord(StdItemA.AC));
        Inc(AddAbility.wSpellRecover, HiWord(StdItemA.MAC));
        Inc(AddAbility.nHitSpeed, LoWord(StdItemA.AC)); //敏捷度
        Dec(AddAbility.nHitSpeed, LoWord(StdItemA.MAC));
      end;
    54: begin
        if not g_Config.boAddUserItemNewValue then begin
          Inc(AddAbility.wHealthRecover, HiWord(StdItemA.AC));
          Inc(AddAbility.wSpellRecover, HiWord(StdItemA.MAC));
          Inc(AddAbility.nHitSpeed, LoWord(StdItemA.AC)); //敏捷度
          Dec(AddAbility.nHitSpeed, LoWord(StdItemA.MAC));
        end else begin
          AddAbility.wAC := MakeLong(LoWord(AddAbility.wAC) + LoWord(StdItemA.AC), HiWord(AddAbility.wAC) + HiWord(StdItemA.AC));
          AddAbility.wMAC := MakeLong(LoWord(AddAbility.wMAC) + LoWord(StdItemA.MAC), HiWord(AddAbility.wMAC) + HiWord(StdItemA.MAC));
        end;
      end;
    23: begin
        Inc(AddAbility.wAntiPoison, HiWord(StdItemA.AC));
        Inc(AddAbility.wPoisonRecover, HiWord(StdItemA.MAC));
        Inc(AddAbility.nHitSpeed, LoWord(StdItemA.AC)); //敏捷度
        Dec(AddAbility.nHitSpeed, LoWord(StdItemA.MAC));
      end;
  else begin
      AddAbility.wAC := MakeLong(LoWord(AddAbility.wAC) + LoWord(StdItemA.AC), HiWord(AddAbility.wAC) + HiWord(StdItemA.AC));
      AddAbility.wMAC := MakeLong(LoWord(AddAbility.wMAC) + LoWord(StdItemA.MAC), HiWord(AddAbility.wMAC) + HiWord(StdItemA.MAC));
    end;
  end;
  AddAbility.wDC := MakeLong(LoWord(AddAbility.wDC) + LoWord(StdItemA.DC), HiWord(AddAbility.wDC) + HiWord(StdItemA.DC));
  AddAbility.wMC := MakeLong(LoWord(AddAbility.wMC) + LoWord(StdItemA.MC), HiWord(AddAbility.wMC) + HiWord(StdItemA.MC));
  AddAbility.wSC := MakeLong(LoWord(AddAbility.wSC) + LoWord(StdItemA.SC), HiWord(AddAbility.wSC) + HiWord(StdItemA.SC));
end;

function TActorObject.GetCharColor(BaseObject: TActorObject): Byte;
var
  n10: Integer;
  nCheckCode: Integer;
  Castle: TUserCastle;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::GetCharColor CheckCode=%d';
begin
  try
    nCheckCode := 0;
    Result := BaseObject.GetNamecolor();
    nCheckCode := 1;
    if BaseObject.m_btRaceServer = RC_PLAYOBJECT then begin
      if BaseObject.PKLevel < 2 then begin
        if BaseObject.m_boPKFlag and (not BaseObject.m_PEnvir.m_boFIGHTPK) then Result := g_Config.btPKFlagNameColor; //$2F
        nCheckCode := 2;
        n10 := GetGuildRelation(Self, BaseObject);
        nCheckCode := 3;
        case n10 of
          1, 3: Result := g_Config.btAllyAndGuildNameColor; //$B4;
          2: Result := g_Config.btWarGuildNameColor; //$45;
        end;
        if BaseObject.m_PEnvir.m_boFight3Zone then begin
          if m_MyGuild = BaseObject.m_MyGuild then Result := g_Config.btAllyAndGuildNameColor //$B4
          else Result := g_Config.btWarGuildNameColor //$45;
        end;
      end;
      nCheckCode := 4;
      Castle := g_CastleManager.InCastleWarArea(BaseObject);
    //    if  UserCastle.m_boUnderWar and m_boInFreePKArea and BaseObject.m_boInFreePKArea then begin
      if (Castle <> nil) and Castle.m_boUnderWar and m_boInFreePKArea and BaseObject.m_boInFreePKArea then begin
        nCheckCode := 5;
        Result := g_Config.btInFreePKAreaNameColor; //$DD;
        m_boGuildWarArea := True;
        nCheckCode := 6;
        if (m_MyGuild = nil) then Exit;
      //      if UserCastle.IsMasterGuild(TGuild(m_MyGuild)) then begin
        if Castle.IsMasterGuild(TGUild(m_MyGuild)) then begin
          nCheckCode := 7;
          if (m_MyGuild = BaseObject.m_MyGuild) or
            (TGUild(m_MyGuild).IsAllyGuild(TGUild(BaseObject.m_MyGuild))) then begin
            nCheckCode := 8;
            Result := g_Config.btAllyAndGuildNameColor //$B4;
          end else begin //004BF2A8
            nCheckCode := 9;
          //if UserCastle.IsAttackGuild(TGuild(BaseObject.m_MyGuild)) then begin
            if Castle.IsAttackGuild(TGUild(BaseObject.m_MyGuild)) then begin
              nCheckCode := 10;
              Result := g_Config.btWarGuildNameColor //$45;
            end;
          end;
        end else begin //004BF2CE
          nCheckCode := 11;
        //        if UserCastle.IsAttackGuild(TGuild(m_MyGuild)) then begin
          if Castle.IsAttackGuild(TGUild(m_MyGuild)) then begin
            nCheckCode := 12;
            if (m_MyGuild = BaseObject.m_MyGuild) or
              (TGUild(m_MyGuild).IsAllyGuild(TGUild(BaseObject.m_MyGuild))) then begin
              nCheckCode := 13;
              Result := g_Config.btAllyAndGuildNameColor //$B4;
            end else begin
              nCheckCode := 14;
            //            if UserCastle.IsMember(BaseObject) then begin
              if Castle.IsMember(BaseObject) then begin
                nCheckCode := 15;
                Result := g_Config.btWarGuildNameColor //$45;
              end;
            end;
          end;
        end;
      end;
      Exit;
    end;
    nCheckCode := 16;
    if BaseObject.m_btRaceServer = RC_HEROOBJECT then begin
      if BaseObject.PKLevel < 2 then begin
        if BaseObject.m_boPKFlag and (not m_PEnvir.m_boFIGHTPK) then Result := g_Config.btPKFlagNameColor; //$2F
      end;
    end else begin
      nCheckCode := 17;
      if (BaseObject.m_btSlaveExpLevel < SLAVEMAXLEVEL) then begin
        nCheckCode := 18;
        if BaseObject.m_nChangeColorType < 0 then begin
          Result := g_Config.SlaveColor[BaseObject.m_btSlaveExpLevel];
        end else begin
          nCheckCode := 19;
          if BaseObject.m_nChangeColorType < 256 then begin
            Result := BaseObject.m_nChangeColorType;
          end;
        end;
      end;
      if BaseObject.m_boCrazyMode then Result := $F9;
      if BaseObject.m_boHolySeize then Result := $7D;
    end;
  except
    MainOutMessage(Format(sExceptionMsg, [nCheckCode]));
  end;
end;

function TActorObject.GetLevelExp(nLevel: Integer): LongWord;
  function GetBaseExp(): LongWord;
  var
    //nExp:Cardinal;
    nExp: Int64;
  begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      nExp := (nLevel - MAXCHANGELEVEL) * g_Config.nAddExp + g_Config.nBaseExp;
      if nExp < g_Config.dwNeedExps[High(g_Config.dwNeedExps)] then nExp := g_Config.dwNeedExps[High(g_Config.dwNeedExps)];
      if nExp > High(LongWord) then Result := High(LongWord) else Result := LongWord(nExp);
    end else begin
      nExp := (nLevel - MAXCHANGELEVEL) * g_Config.nAddExp + g_Config.nBaseExp;
      if nExp < g_Config.dwNeedExps[High(g_Config.dwNeedExps)] then nExp := g_Config.dwHeroNeedExps[High(g_Config.dwHeroNeedExps)];
      if nExp > High(LongWord) then Result := High(LongWord) else Result := LongWord(nExp);
    end;
  end;
var
    //nExp:Cardinal;
  nExp: Int64;
begin
  if nLevel <= MAXCHANGELEVEL {1000} then begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      Result := g_Config.dwNeedExps[nLevel];
    end else begin
      Result := g_Config.dwHeroNeedExps[nLevel]; //英雄经验
    end;
    Exit;
  end;
  if g_Config.boUseFixExp then begin
    if m_btRaceServer = RC_PLAYOBJECT then begin
      nExp := g_Config.dwNeedExps[High(g_Config.dwNeedExps)] + _MAX(0, (nLevel - MAXCHANGELEVEL)) * 10000000;
      if nExp > High(LongWord) then Result := High(LongWord) else Result := LongWord(nExp);
    end else begin
      nExp := g_Config.dwHeroNeedExps[High(g_Config.dwHeroNeedExps)] + _MAX(0, (nLevel - MAXCHANGELEVEL)) * 10000000;
      if nExp > High(LongWord) then Result := High(LongWord) else Result := LongWord(nExp);
    end;
  end else begin
    Result := GetBaseExp();
  end;
end;

function TActorObject.GetNamecolor(): Byte;
begin
  Result := m_btNameColor;
  if PKLevel = 1 then Result := g_Config.btPKLevel1NameColor; //$FB;
  if PKLevel >= 2 then Result := g_Config.btPKLevel2NameColor; //$F9;
end;

procedure TActorObject.HearMsg(sMsg: string);
begin
  if sMsg <> '' then SendMsg(nil, RM_HEAR, 0, g_Config.btHearMsgFColor, g_Config.btHearMsgBColor, 0, sMsg);
end;

function TActorObject.InSafeArea(): Boolean;
var
  I: Integer;
  SC: string;
  StartPoint: pTStartPoint;
begin
  Result := False;
  if m_PEnvir = nil then Exit;
  Result := m_PEnvir.m_boSAFE;
  if Result then Exit;

  for I := 0 to g_StartPointList.Count - 1 do begin
    SC := g_StartPointList.Strings[I];
    if SC = m_PEnvir.sMapName then begin
      StartPoint := pTStartPoint(g_StartPointList.Objects[I]);
      if (abs(m_nCurrX - StartPoint.nCurrX) <= StartPoint.nRange) and (abs(m_nCurrY - StartPoint.nCurrY) <= StartPoint.nRange) then begin
        Result := True;
        Break;
      end;
    end;
  end;
end;

procedure TActorObject.MonsterRecalcAbilitys;
var
  n8: Integer;
begin
  m_WAbil.DC := MakeLong(LoWord(m_WAbil.DC), HiWord(m_Abil.DC));
  n8 := 0;
  if (m_btRaceServer = 100) or
    (m_btRaceServer = 113) or
    (m_btRaceServer = 114) then begin

    m_WAbil.DC := MakeLong(LoWord(m_WAbil.DC), _MIN(MAXHUMPOWER, Round((m_btSlaveExpLevel * {1.0e-1} 0.1 + {3.0000000000000000001e-1} 0.3) * {3.0} 3.0 * m_btSlaveExpLevel + HiWord(m_WAbil.DC))));
    n8 := n8 + Round((m_btSlaveExpLevel * {1.0e-1} 0.1 + {3.0000000000000000001e-1} 0.3) * m_Abil.MaxHP) * m_btSlaveExpLevel;
    n8 := n8 + m_Abil.MaxHP;
    if m_btSlaveExpLevel > 0 then m_WAbil.MaxHP := n8
    else m_WAbil.MaxHP := m_Abil.MaxHP;
  end else begin //004BEA85
    n8 := m_Abil.MaxHP;
    m_WAbil.DC := MakeLong(LoWord(m_WAbil.DC), _MIN(MAXHUMPOWER, Round(m_btSlaveExpLevel * 2 + HiWord(m_WAbil.DC))));
    n8 := n8 + Round(m_Abil.MaxHP * {1.5000000000000000001e-1} 0.15) * m_btSlaveExpLevel;
    m_WAbil.MaxHP := MinLong(Round(m_Abil.MaxHP + m_btSlaveExpLevel * 60), n8);
    //m_WAbil.MAC:=0; 01/20 取消此行，防止怪物升级后魔防变0
  end;
  //m_btHitPoint:=15; 01/20 取消此行，防止怪物升级后准确率变15
end;

procedure TPlayObject.ShowMapInfo(sMAP, sX, sY: string);
var
  Map: TEnvirnoment;
  nX, nY: Integer;
  MapCellInfo: pTMapCellinfo;
begin
  nX := Str_ToInt(sX, 0);
  nY := Str_ToInt(sY, 0);
  if (sMAP <> '') and (nX >= 0) and (nY >= 0) then begin
    Map := g_MapManager.FindMap(sMAP);
    if Map <> nil then begin
      if Map.GetMapCellInfo(nX, nY, MapCellInfo) then begin
        SysMsg('Flags: ' + IntToStr(MapCellInfo.chFlag), c_Green, t_Hint);
        if MapCellInfo.ObjList <> nil then begin
          SysMsg('Object Number: ' + IntToStr(MapCellInfo.ObjList.Count), c_Green, t_Hint);
        end;
      end else begin
        SysMsg('Failed to get Map information: ' + sMAP, c_Red, t_Hint);
      end;
    end;
  end else begin
    SysMsg('Use the format: ' + g_GameCommand.MAPINFO.sCmd + ' MapName X Y', c_Green, t_Hint);
  end;
end;

procedure TActorObject.SendFirstMsg(BaseObject: TActorObject; wIdent, wParam: Integer;
  lParam1, lParam2, lParam3: Integer; sMsg: string);
var
  SendMessage: pTSendMessage;
begin
  //if (m_btRaceServer = RC_PLAYOBJECT) and TPlayObject(Self).m_boNotOnlineAddExp then Exit;
  try
    EnterCriticalSection(ProcessMsgCriticalSection);
    if not m_boGhost then begin
      New(SendMessage);
      SendMessage.wIdent := wIdent;
      SendMessage.wParam := wParam;
      SendMessage.nParam1 := lParam1;
      SendMessage.nParam2 := lParam2;
      SendMessage.nParam3 := lParam3;
      SendMessage.dwDeliveryTime := 0;
      SendMessage.BaseObject := BaseObject;
      if sMsg <> '' then begin
        try
          GetMem(SendMessage.Buff, Length(sMsg) + 1);
          Move(sMsg[1], SendMessage.Buff^, Length(sMsg) + 1);
        except
          SendMessage.Buff := nil;
        end;
      end else begin
        SendMessage.Buff := nil;
      end;
      m_MsgList.Insert(0, SendMessage);
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
end;

procedure TActorObject.SendMsg(BaseObject: TActorObject; wIdent, wParam: Integer; nParam1, nParam2, nParam3: Integer; sMsg: string);
var
  SendMessage: pTSendMessage;
  boSend: Boolean;
begin
  boSend := False;
  if (m_boNotOnlineAddExp or m_boAI) then begin
    case wIdent of
      RM_MAGSTRUCK,
        RM_MAGSTRUCK_MINE,
        RM_DELAYPUSHED,
        RM_10155,
        RM_POISON,
        RM_TRANSPARENT,
        RM_DOOPENHEALTH,
        RM_MAGHEALING,
        RM_DELAYMAGIC,
        RM_HEAR2,
        RM_SENDDELITEMLIST,
        RM_10401,
        RM_10101,
        RM_STRUCK,
        RM_STRUCK_MAG,

      RM_MAKEGHOST,
        RM_HEROGROUP,
        RM_UNHEROGROUP: boSend := True;
    end;

    if (not boSend) and m_boAI and (m_btRaceServer = RC_PLAYOBJECT) then begin
      case wIdent of
        RM_HEAR,
          RM_WHISPER,
          RM_CRY,
          RM_SYSMESSAGE,
          RM_GROUPMESSAGE,
          RM_SYSMESSAGE2,
          RM_GUILDMESSAGE,
          RM_SYSMESSAGE3,
          RM_MERCHANTSAY,
          RM_MOVEMESSAGE,
          //RM_DELAYMESSAGE,
        RM_CENTERMESSAGE: boSend := True;
      end;
    end;
  end else boSend := True;

  if boSend then begin
    try
      EnterCriticalSection(ProcessMsgCriticalSection);
      if not m_boGhost then begin
        New(SendMessage);
        SendMessage.wIdent := wIdent;
        SendMessage.wParam := wParam;
        SendMessage.nParam1 := nParam1;
        SendMessage.nParam2 := nParam2;
        SendMessage.nParam3 := nParam3;
        SendMessage.dwDeliveryTime := 0;
        SendMessage.BaseObject := BaseObject;
        SendMessage.boLateDelivery := False;
        if sMsg <> '' then begin
          try
            GetMem(SendMessage.Buff, Length(sMsg) + 1);
            Move(sMsg[1], SendMessage.Buff^, Length(sMsg) + 1);
          except
            SendMessage.Buff := nil;
          end;
        end else begin
          SendMessage.Buff := nil;
        end;
        m_MsgList.Add(SendMessage);
      end;
    finally
      LeaveCriticalSection(ProcessMsgCriticalSection);
    end;
  end;
end;

procedure TActorObject.SendDelayMsg(BaseObject: TActorObject; wIdent,
  wParam: Integer; lParam1, lParam2, lParam3: Integer; sMsg: string;
  dwDelay: LongWord);
var
  SendMessage: pTSendMessage;
  boSend: Boolean;
begin
  boSend := False;
  if (m_boNotOnlineAddExp or m_boAI) then begin
    case wIdent of
      RM_MAGSTRUCK,
        RM_MAGSTRUCK_MINE,
        RM_DELAYPUSHED,
        RM_10155,
        RM_POISON,
        RM_TRANSPARENT,
        RM_DOOPENHEALTH,
        RM_MAGHEALING,
        RM_DELAYMAGIC,
        RM_HEAR2,
        RM_SENDDELITEMLIST,
        RM_10401,
        RM_10101,
        RM_STRUCK,
        RM_STRUCK_MAG,

      RM_MAKEGHOST,
        RM_HEROGROUP,
        RM_UNHEROGROUP: boSend := True;
    end;

    if (not boSend) and m_boAI and (m_btRaceServer = RC_PLAYOBJECT) then begin
      case wIdent of
        RM_HEAR,
          RM_WHISPER,
          RM_CRY,
          RM_SYSMESSAGE,
          RM_GROUPMESSAGE,
          RM_SYSMESSAGE2,
          RM_GUILDMESSAGE,
          RM_SYSMESSAGE3,
          RM_MERCHANTSAY,
          RM_MOVEMESSAGE,
          //RM_DELAYMESSAGE,
        RM_CENTERMESSAGE: boSend := True;
      end;
    end;
  end else boSend := True;

  if boSend then begin
    try
      EnterCriticalSection(ProcessMsgCriticalSection);
      if not m_boGhost then begin
        New(SendMessage);
        SendMessage.wIdent := wIdent;
        SendMessage.wParam := wParam;
        SendMessage.nParam1 := lParam1;
        SendMessage.nParam2 := lParam2;
        SendMessage.nParam3 := lParam3;
        SendMessage.dwDeliveryTime := GetTickCount + dwDelay;
        SendMessage.BaseObject := BaseObject;
        SendMessage.boLateDelivery := True;
        if sMsg <> '' then begin
          try
            GetMem(SendMessage.Buff, Length(sMsg) + 1);
            Move(sMsg[1], SendMessage.Buff^, Length(sMsg) + 1);
          except
            SendMessage.Buff := nil;
          end;
        end else begin
          SendMessage.Buff := nil;
        end;
        m_MsgList.Add(SendMessage);
      end;
    finally
      LeaveCriticalSection(ProcessMsgCriticalSection);
    end;
  end;
end;

procedure TActorObject.SendUpdateDelayMsg(BaseObject: TActorObject; wIdent,
  wParam: Integer; lParam1, lParam2, lParam3: Integer; sMsg: string;
  dwDelay: LongWord);
var
  SendMessage: pTSendMessage;
  I: Integer;
begin
  EnterCriticalSection(ProcessMsgCriticalSection);
  try
    I := 0;
    while (True) do begin
      if m_MsgList.Count <= I then Break;
      SendMessage := m_MsgList.Items[I];
      if (SendMessage.wIdent = wIdent) and (SendMessage.nParam1 = lParam1) then begin
        m_MsgList.Delete(I);
        if SendMessage.Buff <> nil then FreeMem(SendMessage.Buff);
        Dispose(SendMessage);
        Continue;
      end;
      Inc(I);
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
  SendDelayMsg(BaseObject, wIdent, wParam, lParam1, lParam2, lParam3, sMsg, dwDelay);
end;

procedure TActorObject.SendUpdateMsg(BaseObject: TActorObject; wIdent, wParam: Integer;
  lParam1, lParam2, lParam3: Integer; sMsg: string);
var
  SendMessage: pTSendMessage;
  I: Integer;
begin
  try
    EnterCriticalSection(ProcessMsgCriticalSection);
    I := 0;
    while (True) do begin
      if m_MsgList.Count <= I then Break;
      SendMessage := m_MsgList.Items[I];
      if SendMessage.wIdent = wIdent then begin
        m_MsgList.Delete(I);
        if SendMessage.Buff <> nil then FreeMem(SendMessage.Buff);
        Dispose(SendMessage);
        Continue;
      end;
      Inc(I);
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
  SendMsg(BaseObject, wIdent, wParam, lParam1, lParam2, lParam3, sMsg);
end;

procedure TActorObject.SendUpdateMsgA(BaseObject: TActorObject; wIdent, wParam: Integer;
  lParam1, lParam2, lParam3: Integer; sMsg: string);
var
  SendMessage: pTSendMessage;
  boFind: Boolean;
  I: Integer;
begin
  boFind := False;
  try
    EnterCriticalSection(ProcessMsgCriticalSection);
    for I := 0 to m_MsgList.Count - 1 do begin
      SendMessage := m_MsgList.Items[I];
      if SendMessage.wIdent = wIdent then begin
        boFind := True;
        break;
      end;
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
  if not boFind then
    SendMsg(BaseObject, wIdent, wParam, lParam1, lParam2, lParam3, sMsg);
end;

procedure TActorObject.SendActionMsg(BaseObject: TActorObject; wIdent, wParam: Integer;
  lParam1, lParam2, lParam3: Integer; sMsg: string);
var
  SendMessage: pTSendMessage;
  I: Integer;
begin
  EnterCriticalSection(ProcessMsgCriticalSection);
  try
    I := 0;
    while (True) do begin
      if m_MsgList.Count <= I then Break;
      SendMessage := m_MsgList.Items[I];
      if (SendMessage.wIdent = CM_TURN) or
        (SendMessage.wIdent = CM_WALK) or
        (SendMessage.wIdent = CM_SITDOWN) or
        (SendMessage.wIdent = CM_HORSERUN) or
        (SendMessage.wIdent = CM_RUN) or
        (SendMessage.wIdent = CM_HIT) or
        (SendMessage.wIdent = CM_HEAVYHIT) or
        (SendMessage.wIdent = CM_BIGHIT) or
        (SendMessage.wIdent = CM_POWERHIT) or
        (SendMessage.wIdent = CM_LONGHIT) or
        (SendMessage.wIdent = CM_WIDEHIT) or
        (SendMessage.wIdent = CM_CRSHIT) or
        (SendMessage.wIdent = CM_PKHIT) or
        (SendMessage.wIdent = CM_KTHIT) or
        (SendMessage.wIdent = CM_60HIT) or
        (SendMessage.wIdent = CM_FIREHIT) then begin
        m_MsgList.Delete(I);
        if SendMessage.Buff <> nil then FreeMem(SendMessage.Buff);
        Dispose(SendMessage);
        Continue;
      end;
      Inc(I);
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
  SendMsg(BaseObject, wIdent, wParam, lParam1, lParam2, lParam3, sMsg);
end;

function TActorObject.MessageCount(): Integer;
begin
  EnterCriticalSection(ProcessMsgCriticalSection);
  try
    Result := m_MsgList.Count;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
end;

function TActorObject.GetMessage(Msg: pTProcessMessage): Boolean;
var
  I: Integer;
  SendMessage: pTSendMessage;
begin
  Result := False;
  EnterCriticalSection(ProcessMsgCriticalSection);
  try
    I := 0;
    Msg.wIdent := 0;
    while m_MsgList.Count > I do begin
      SendMessage := m_MsgList.Items[I];
      if (SendMessage.dwDeliveryTime <> 0) and (GetTickCount < SendMessage.dwDeliveryTime) then begin
        Inc(I);
        Continue;
      end;
      m_MsgList.Delete(I);
      Msg.wIdent := SendMessage.wIdent;
      Msg.wParam := SendMessage.wParam;
      Msg.nParam1 := SendMessage.nParam1;
      Msg.nParam2 := SendMessage.nParam2;
      Msg.nParam3 := SendMessage.nParam3;
      Msg.BaseObject := SendMessage.BaseObject;
      Msg.dwDeliveryTime := SendMessage.dwDeliveryTime;
      Msg.boLateDelivery := SendMessage.boLateDelivery;
      if SendMessage.Buff <> nil then begin
        Msg.sMsg := StrPas(SendMessage.Buff);
        FreeMem(SendMessage.Buff);
      end else begin
        Msg.sMsg := '';
      end;
      Dispose(SendMessage);
      Result := True;
      Break;
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
end;

function TActorObject.GetMapEvents(tEnvir: TEnvirnoment; nX, nY, nRage: Integer; rList: TList): Boolean;
var
  III: Integer;
  x, y: Integer;
  nStartX, nStartY, nEndX, nEndY: Integer;
  MapCellInfo: pTMapCellinfo;
  BaseObject: TBaseObject;
  Event: TEvent;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::GetMapEvents';
begin
  Result := False;
  if (rList = nil) or (tEnvir = nil) then Exit;
  try
    nStartX := nX - nRage;
    nEndX := nX + nRage;
    nStartY := nY - nRage;
    nEndY := nY + nRage;
    for x := nStartX to nEndX do begin
      for y := nStartY to nEndY do begin
        if tEnvir.GetMapCellInfo(x, y, MapCellInfo) and (MapCellInfo.ObjList <> nil) then begin
          for III := 0 to MapCellInfo.ObjList.Count - 1 do begin
            BaseObject := TBaseObject(MapCellInfo.ObjList.Items[III]);
            if BaseObject.m_ObjType = t_Event then begin
              Event := TEvent(BaseObject);
              if (Event <> nil) and (Event.m_boVisible) and (not Event.m_boClosed) then begin
                rList.Add(Event);
              end;
            end;
          end;
        end;
      end;
    end;
  except
    MainOutMessage(sExceptionMsg);
  end;
  Result := True;
end;

function TActorObject.GetMapActorObjects(tEnvir: TEnvirnoment; nX, nY, nRage: Integer; rList: TList): Boolean;
var
  III: Integer;
  x, y: Integer;
  nStartX, nStartY, nEndX, nEndY: Integer;
  MapCellInfo: pTMapCellinfo;
  BaseObject: TBaseObject;
  ActorObject: TActorObject;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::GetMapActorObjects';
begin
  Result := False;
  if (rList = nil) or (tEnvir = nil) then Exit;
  try
    nStartX := nX - nRage;
    nEndX := nX + nRage;
    nStartY := nY - nRage;
    nEndY := nY + nRage;
    for x := nStartX to nEndX do begin
      for y := nStartY to nEndY do begin
        if tEnvir.GetMapCellInfo(x, y, MapCellInfo) and (MapCellInfo.ObjList <> nil) then begin
          for III := 0 to MapCellInfo.ObjList.Count - 1 do begin
            BaseObject := TBaseObject(MapCellInfo.ObjList.Items[III]);
            if BaseObject.m_ObjType = t_Actor then begin
              ActorObject := TActorObject(BaseObject);
              if (ActorObject <> nil) and (not ActorObject.m_boDeath) and (not ActorObject.m_boGhost) and (not ActorObject.m_boFixedHideMode) then begin
                rList.Add(ActorObject);
              end;
            end;
          end;
        end;
      end;
    end;
  except
    MainOutMessage(sExceptionMsg);
  end;
  Result := True;
end;

procedure TActorObject.SendRefMsg(wIdent, wParam: Word; nParam1, nParam2, nParam3: Integer; sMsg: string);
  function AddVisibleHuman(ActorObject: TActorObject): Boolean;
  var
    I: Integer;
  begin
    Result := True;
    for I := 0 to m_VisibleHumanList.Count - 1 do begin
      if TActorObject(m_VisibleHumanList.Items[I]) = ActorObject then begin
        Result := False;
        Exit;
      end;
    end;
    m_VisibleHumanList.Add(ActorObject);
  end;
var
  II, III, nC, nListCount: Integer;
  nCX, nCY, nLX, nLY, nHX, nHY, nNX, nNY: Integer;
  MapCellInfo: pTMapCellinfo;
  MapCellInfoA: pTMapCellinfo;
  BaseObject: TBaseObject;
  ActorObject: TActorObject;
  btType: Byte;
  nErrorCode: Integer;

resourcestring
  sExceptionMsg = '[Exception] TActorObject::SendRefMsg Name = %s';
  sExceptionMsg1 = '[Exception] TActorObject::SendRefMsg 1 Name = %s';
  sExceptionMsg2 = '[Exception] TActorObject::SendRefMsg 2 Name = %s';
begin
  if m_PEnvir = nil then begin
    MainOutMessage(m_sCharName + ' SendRefMsg nil PEnvir ');
    Exit;
  end;
  //if m_boObMode or m_boFixedHideMode then exit;
  //01/21 增加，原来直接不发信息，如果隐身模式则只发送信息给自己
  try
    if (m_boObMode or m_boFixedHideMode) then begin
      SendMsg(Self, wIdent, wParam, nParam1, nParam2, nParam3, sMsg);
      Exit;
    end;
  except
    MainOutMessage(sExceptionMsg + ' Code=0');
  end;

  EnterCriticalSection(ProcessMsgCriticalSection);
  try
    if ((GetTickCount - m_SendRefMsgTick) >= 500) or (m_VisibleHumanList.Count = 0) then begin
      m_SendRefMsgTick := GetTickCount();
      m_VisibleHumanList.Clear;
      nLX := m_nCurrX - g_Config.nSendRefMsgRange {12};
      nHX := m_nCurrX + g_Config.nSendRefMsgRange {12};
      nLY := m_nCurrY - g_Config.nSendRefMsgRange {12};
      nHY := m_nCurrY + g_Config.nSendRefMsgRange {12};
      for nCX := nLX to nHX do begin
        for nCY := nLY to nHY do begin
          if m_PEnvir.GetMapCellInfo(nCX, nCY, MapCellInfo) then begin
            if MapCellInfo.ObjList <> nil then begin
              for II := MapCellInfo.ObjList.Count - 1 downto 0 do begin
                BaseObject := TBaseObject(MapCellInfo.ObjList.Items[II]);
                try
                  if (BaseObject <> nil) and (BaseObject.m_ObjType = t_Actor) then begin
                    if (GetTickCount - BaseObject.m_dwAddTime) >= 60 * 1000 then begin
                      MapCellInfo.ObjList.Delete(II);
                      if MapCellInfo.ObjList.Count <= 0 then begin
                        nErrorCode := 3;
                        FreeAndNil(MapCellInfo.ObjList);
                        Break;
                      end;
                    end else begin

                      ActorObject := TActorObject(BaseObject);
                      if (ActorObject <> nil) and (ActorObject.m_PEnvir = m_PEnvir) and (not ActorObject.m_boGhost) then begin
                        if ActorObject.m_btRaceServer = RC_PLAYOBJECT then begin
                          if AddVisibleHuman(ActorObject) then begin
                            ActorObject.SendMsg(Self, wIdent, wParam, nParam1, nParam2, nParam3, sMsg);
                          end else begin
                            //2008-3-20增加 删除地图中出现重复的人物
                            {if (ActorObject.m_nCurrX <> nCX) or (ActorObject.m_nCurrY <> nCY) then begin
                              MapCellInfo.ObjList.Delete(II);
                              if MapCellInfo.ObjList.Count <= 0 then begin
                                FreeAndNil(MapCellInfo.ObjList);
                                Break;
                              end;
                            end else begin
                              //2008-3-20增加 删除地图中出现重复的人物
                              for nNX := nLX to nCX do begin
                                for nNY := nLY to nCY do begin
                                  if m_PEnvir.GetMapCellInfo(nNX, nNY, MapCellInfoA) then begin
                                    if MapCellInfoA.ObjList <> nil then begin
                                      for III := MapCellInfoA.ObjList.Count - 1 downto 0 do begin
                                        BaseObject := TBaseObject(MapCellInfoA.ObjList.Items[III]);
                                        if (BaseObject <> nil) and (BaseObject = ActorObject) and ((ActorObject.m_nCurrX <> nNX) or (ActorObject.m_nCurrY <> nNY) or (III <> II)) then begin
                                          MapCellInfoA.ObjList.Delete(III);
                                          if MapCellInfoA.ObjList.Count <= 0 then begin
                                            FreeAndNil(MapCellInfoA.ObjList);
                                            Break;
                                          end;
                                        end;
                                      end;
                                    end;
                                  end;
                                end;
                              end;
                            end;}
                          end;
                        end else
                          if ActorObject.m_boWantRefMsg then begin
                          if (wIdent = RM_STRUCK) or (wIdent = RM_HEAR) or (wIdent = RM_DEATH) or (wIdent = RM_CHARSTATUSCHANGED) or (wIdent = RM_RUSH) then begin {增加分身的魔法盾效果}
                            if AddVisibleHuman(ActorObject) then begin
                              ActorObject.SendMsg(Self, wIdent, wParam, nParam1, nParam2, nParam3, sMsg);
                            end else begin
                              //2008-3-20增加 删除地图中出现重复的人物
                             { if (ActorObject.m_nCurrX <> nCX) or (ActorObject.m_nCurrY <> nCY) then begin
                                MapCellInfo.ObjList.Delete(II);
                                if MapCellInfo.ObjList.Count <= 0 then begin
                                  nErrorCode := 3;
                                  FreeAndNil(MapCellInfo.ObjList);
                                  Break;
                                end;
                              end else begin
                                //2008-3-20增加 删除地图中出现重复的人物
                                for nNX := nLX to nCX do begin
                                  for nNY := nLY to nCY do begin
                                    if m_PEnvir.GetMapCellInfo(nNX, nNY, MapCellInfoA) then begin
                                      if MapCellInfoA.ObjList <> nil then begin
                                        for III := MapCellInfoA.ObjList.Count - 1 downto 0 do begin
                                          BaseObject := TBaseObject(MapCellInfoA.ObjList.Items[III]);
                                          if (BaseObject <> nil) and (BaseObject = ActorObject) and ((ActorObject.m_nCurrX <> nNX) or (ActorObject.m_nCurrY <> nNY) or (III <> II)) then begin
                                            MapCellInfoA.ObjList.Delete(III);
                                            if MapCellInfoA.ObjList.Count <= 0 then begin
                                              FreeAndNil(MapCellInfoA.ObjList);
                                              Break;
                                            end;
                                          end;
                                        end;
                                      end;
                                    end;
                                  end;
                                end;
                              end;}
                            end;
                          end;
                        end;
                      end;

                    end;
                  end;
                except
                  on E: Exception do begin
                       { if (MapCellInfo.ObjList <> nil) then begin
                          if MapCellInfo.ObjList.Count <= 0 then begin
                            FreeAndNil(MapCellInfo.ObjList);
                          end;
                        end; }
                    MainOutMessage(Format(sExceptionMsg1, [m_sCharName]));
                    MainOutMessage(E.Message);
                  end;
                end;

              end; //for I := 0 to MapCellInfo.ObjList.Count - 1 do begin
            end; //if MapCellInfo.ObjList <> nil then begin
          end; //if PEnvir.GetMapCellInfo(nC,n10,MapCellInfo) then begin
        end;
      end;
    end else begin
      for nC := 0 to m_VisibleHumanList.Count - 1 do begin
        ActorObject := TActorObject(m_VisibleHumanList.Items[nC]);
        try
          if (ActorObject <> nil) and (not ActorObject.m_boGhost) and
            (ActorObject.m_PEnvir = m_PEnvir) and
            (abs(ActorObject.m_nCurrX - m_nCurrX) < 11) and
            (abs(ActorObject.m_nCurrY - m_nCurrY) < 11) then begin
            if (ActorObject.m_btRaceServer = RC_PLAYOBJECT) then begin
              ActorObject.SendMsg(Self, wIdent, wParam, nParam1, nParam2, nParam3, sMsg);
            end else
              if ActorObject.m_boWantRefMsg then begin
              if (wIdent = RM_STRUCK) or (wIdent = RM_HEAR) or (wIdent = RM_DEATH) or (wIdent = RM_CHARSTATUSCHANGED) or (wIdent = RM_RUSH) then begin
                ActorObject.SendMsg(Self, wIdent, wParam, nParam1, nParam2, nParam3, sMsg);
              end;
            end; //if ActorObject.m_boWantRefMsg then begin
          end; //if (ActorObject <> nil) and (not ActorObject.m_boGhost) and
        except
          on E: Exception do begin
            MainOutMessage(Format(sExceptionMsg2, [m_sCharName]));
            MainOutMessage(E.Message);
          end;
        end;
      end; //for nC:= 0 to m_VisibleHumanList.Count - 1 do begin
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
end;

function TActorObject.UpdateVisibleGay(ActorObject: TActorObject): Boolean;
var
  I: Integer;
  boIsVisible: Boolean;
  VisibleBaseObject: pTVisibleBaseObject;
begin
  Result := False;
  boIsVisible := False;
  if (ActorObject.m_btRaceServer = RC_PLAYOBJECT) or (ActorObject.m_Master <> nil) then
    m_boIsVisibleActive := True; //如果是人物或宝宝则置TRUE

  for I := 0 to m_VisibleActors.Count - 1 do begin
    VisibleBaseObject := m_VisibleActors.Items[I];
    if VisibleBaseObject.BaseObject = ActorObject then begin
      VisibleBaseObject.btVisibleFlag := 1;
      boIsVisible := True;
      Break;
    end;
  end;

  if not boIsVisible then begin
    New(VisibleBaseObject);
    VisibleBaseObject.btVisibleFlag := 2;
    VisibleBaseObject.BaseObject := ActorObject;
    m_VisibleActors.Add(VisibleBaseObject);
    Result := True;
  end;
end;

function TActorObject.UpdateVisibleItemGay(ItemObject: TItemObject): Boolean;
var
  I, nError: Integer;
  boIsVisible: Boolean;
  VisibleMapItem: pTVisibleMapItem;
begin
  Result := False;
  //try
  nError := 0;
  boIsVisible := False;

  for I := 0 to m_VisibleItems.Count - 1 do begin
    nError := 1;
    VisibleMapItem := m_VisibleItems.Items[I];
    nError := 2;
    if VisibleMapItem.BaseObject = ItemObject then begin
      nError := 3;
      VisibleMapItem.btVisibleFlag := 1;
      nError := 4;
      boIsVisible := True;
      nError := 5;
      Break;
    end;
  end;
  nError := 6;
  if not boIsVisible then begin
    New(VisibleMapItem);
    nError := 7;
    VisibleMapItem.btVisibleFlag := 2;
    nError := 8;
    VisibleMapItem.nX := ItemObject.m_nMapX;
    nError := 9;
    VisibleMapItem.nY := ItemObject.m_nMapY;
    nError := 10;
    VisibleMapItem.BaseObject := ItemObject;
    nError := 11;
    VisibleMapItem.sName := ItemObject.m_sName;
    nError := 12;
    VisibleMapItem.wLooks := ItemObject.m_wLooks;
    nError := 13;
    m_VisibleItems.Add(VisibleMapItem);
    nError := 14;
    Result := True;
  end;
 // except
  //  MainOutMessage('UpdateVisibleItemGay:' + m_sCharName + ' Error:' + IntToStr(nError){ + ' ItemObject.m_sName:' + ItemObject.m_sName});
 // end;
end;

function TPlayObject.UpdateVisibleEventGay(BaseObject: TBaseObject): Boolean;
var
  I: Integer;
  boIsVisible: Boolean;
  VisibleMapEvent: pTVisibleMapEvent;
begin
  Result := False;
  boIsVisible := False;
  for I := 0 to m_VisibleEvent.Count - 1 do begin
    VisibleMapEvent := m_VisibleEvent.Items[I];
    if VisibleMapEvent.BaseObject = BaseObject then begin
      VisibleMapEvent.btVisibleFlag := 1;
      boIsVisible := True;
      Break;
    end;
  end;
  if not boIsVisible then begin
    New(VisibleMapEvent);
    VisibleMapEvent.btVisibleFlag := 2;
    VisibleMapEvent.BaseObject := BaseObject;
    VisibleMapEvent.nX := TEvent(BaseObject).m_nX;
    VisibleMapEvent.nY := TEvent(BaseObject).m_nY;
    m_VisibleEvent.Add(VisibleMapEvent);
    Result := True;
  end;
end;

procedure TActorObject.ClearViewRange;
var
  I: Integer;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::ClearViewRange %d';
begin
  try
    m_VisibleHumanList.Clear;
  except
    MainOutMessage(Format(sExceptionMsg, [1]));
  end;

  try
    for I := 0 to m_VisibleActors.Count - 1 do begin
      Dispose(pTVisibleBaseObject(m_VisibleActors.Items[I]));
    end;
  except
    MainOutMessage(Format(sExceptionMsg, [2]));
  end;

  try
    m_VisibleActors.Clear;
  except
    MainOutMessage(Format(sExceptionMsg, [3]));
  end;

  try
    for I := 0 to m_VisibleItems.Count - 1 do begin
      Dispose(pTVisibleMapItem(m_VisibleItems.Items[I]));
    end;
  except
    MainOutMessage(Format(sExceptionMsg, [4]));
  end;

  try
    m_VisibleItems.Clear;
  except
    MainOutMessage(Format(sExceptionMsg, [5]));
  end;
end;

procedure TActorObject.SearchViewRange;
var
  I: Integer;
  nStartX: Integer;
  nEndX: Integer;
  nStartY: Integer;
  nEndY: Integer;
  n18: Integer;
  n1C: Integer;
  nIdx: Integer;
  n24: Integer;
  nCheckCode: Integer;
  MapCellInfo: pTMapCellinfo;
  BaseObject: TBaseObject;
  ItemObject: TItemObject;
  ActorObject: TActorObject;
  VisibleBaseObject: pTVisibleBaseObject;
  VisibleMapItem: pTVisibleMapItem;
resourcestring
  sExceptionMsg1 = '[Exception] TActorObject::SearchViewRange';
  sExceptionMsg2 = '[Exception] TActorObject::SearchViewRange 1-%d %s %s %d %d %d';
  sExceptionMsg3 = '[Exception] TActorObject::SearchViewRange 2-%d %s %s %d %d';
begin
  if m_PEnvir = nil then begin
    MainOutMessage('SearchViewRange nil PEnvir');
    Exit;
  end;

  if m_boNotOnlineAddExp then begin
    ClearViewRange;
    Exit;
  end;

  if (m_btRaceServer = RC_HEROOBJECT) and (m_Master <> nil) and m_Master.m_boNotOnlineAddExp then begin
    ClearViewRange;
    Exit;
  end;

  if (m_btRaceServer = RC_PLAYMOSTER) and (m_Master <> nil) and (Master.m_btRaceServer = RC_PLAYOBJECT) and Master.m_boNotOnlineAddExp then begin
    ClearViewRange;
    Exit;
  end;

  n24 := 0;
  nCheckCode := 0;
  m_boIsVisibleActive := False; //先置为FALSE
  try
    nCheckCode := 1;
    for I := 0 to m_VisibleActors.Count - 1 do begin
      pTVisibleBaseObject(m_VisibleActors.Items[I]).btVisibleFlag := 0;
    end;
    nCheckCode := 2;
    for I := 0 to m_VisibleItems.Count - 1 do begin
      pTVisibleMapItem(m_VisibleItems.Items[I]).btVisibleFlag := 0;
    end;
    nCheckCode := 3;
  except
    MainOutMessage(sExceptionMsg1);
  end;
  nStartX := m_nCurrX - m_nViewRange;
  nEndX := m_nCurrX + m_nViewRange;
  nStartY := m_nCurrY - m_nViewRange;
  nEndY := m_nCurrY + m_nViewRange;
  try
    for n18 := nStartX to nEndX do begin
      for n1C := nStartY to nEndY do begin
        nCheckCode := 4;
        if m_PEnvir.GetMapCellInfo(n18, n1C, MapCellInfo) and (MapCellInfo.ObjList <> nil) then begin
          n24 := 1;
          nIdx := 0;
          nCheckCode := 5;
          while (True) do begin
            if (MapCellInfo.ObjList.Count <= 0) then begin
              nCheckCode := 6;
              FreeAndNil(MapCellInfo.ObjList);
              Break;
            end;
            if MapCellInfo.ObjList.Count <= nIdx then Break;

            BaseObject := TBaseObject(MapCellInfo.ObjList.Items[nIdx]);
            nCheckCode := 66;
            if (BaseObject <> nil) then begin
              if (BaseObject.m_ObjType = t_Actor) then begin
                nCheckCode := 67;
                if (GetTickCount - BaseObject.m_dwAddTime) >= 60 * 1000 then begin
                  nCheckCode := 7;
                  MapCellInfo.ObjList.Delete(nIdx);
                  nCheckCode := 8;
                  if (MapCellInfo.ObjList.Count <= 0) then begin
                    nCheckCode := 9;
                    FreeAndNil(MapCellInfo.ObjList);
                    Break;
                  end;
                  Continue;
                end;
                nCheckCode := 10;
                ActorObject := TActorObject(BaseObject);
                if (not ActorObject.m_boGhost) and (not ActorObject.m_boFixedHideMode) and (not ActorObject.m_boObMode) then begin
                  if (m_btRaceServer < RC_ANIMAL) or
                    (m_Master <> nil) or
                    m_boCrazyMode or
                    m_boWantRefMsg or
                    ((ActorObject.m_Master <> nil) and (abs(ActorObject.m_nCurrX - m_nCurrX) <= 3) and (abs(ActorObject.m_nCurrY - m_nCurrY) <= 3)) or
                    (ActorObject.m_btRaceServer = RC_PLAYOBJECT) then begin
                    nCheckCode := 11;
                    UpdateVisibleGay(ActorObject);
                    nCheckCode := 12;
                  end;
                end;
              end else {if BaseObject.m_ObjType = t_Actor then begin}
                if (BaseObject.m_ObjType = t_Item) and (m_btRaceServer <> RC_MOONOBJECT) and (m_Master <> nil) and (m_nCopyHumanLevel > 0) then begin
                ItemObject := TItemObject(BaseObject); {增加分身人搜索地面物品}
                if not ItemObject.m_boGhost then begin
               { ItemObject.m_PEnvir := nil;
                MapCellInfo.ObjList.Delete(nIdx);
                if (MapCellInfo.ObjList.Count <= 0) then begin
                  FreeAndNil(MapCellInfo.ObjList);
                  Break;
                end;}
                  nCheckCode := 21;
                  UpdateVisibleItemGay(ItemObject);
                  nCheckCode := 22;
                end;
              end;
            end;
                {增加分身人搜索地面物品}
            Inc(nIdx);
          end;
        end;
      end;
    end;
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg2, [n24, m_sCharName, m_sMapName, m_nCurrX, m_nCurrY, nCheckCode]));
      MainOutMessage(E.Message);
      KickException();
    end;
  end;
  nCheckCode := 23;
  n24 := 2;
  try
    n18 := 0;
    while (True) do begin
      if m_VisibleActors.Count <= n18 then Break;
      VisibleBaseObject := m_VisibleActors.Items[n18];
      nCheckCode := 24;
      if VisibleBaseObject.btVisibleFlag = 0 then begin
        nCheckCode := 25;
        m_VisibleActors.Delete(n18);
        nCheckCode := 26;
        Dispose(VisibleBaseObject);
        nCheckCode := 27;
        Continue;
      end;
      Inc(n18);
    end;
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg2, [n24, m_sCharName, m_sMapName, m_nCurrX, m_nCurrY, nCheckCode]));
    {MainOutMessage(m_sCharName + ',' +
                   m_sMapName + ',' +
                   IntToStr(m_nCurrX) + ',' +
                   IntToStr(m_nCurrY) + ',' +
                   ' SearchViewRange 2');}
      KickException();
    end;
  end;
  nCheckCode := 28;
  n24 := 2;
  try
    n18 := 0;
    while (True) do begin
      if m_VisibleItems.Count <= n18 then Break;
      nCheckCode := 29;
      VisibleMapItem := m_VisibleItems.Items[n18];
      nCheckCode := 30;
      if VisibleMapItem.btVisibleFlag = 0 then begin
        nCheckCode := 31;
        m_VisibleItems.Delete(n18);
        nCheckCode := 32;
        Dispose(VisibleMapItem);
        nCheckCode := 33;
        Continue;
      end;
      Inc(n18);
    end;
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg3, [n24, m_sCharName, m_sMapName, m_nCurrX, m_nCurrY, nCheckCode]));
    {MainOutMessage(m_sCharName + ',' +
                   m_sMapName + ',' +
                   IntToStr(m_nCurrX) + ',' +
                   IntToStr(m_nCurrY) + ',' +
                   ' SearchViewRange 2');}
      KickException();
    end;
  end;
end;

function TActorObject.GetFeatureToLong: Integer;
begin
  Result := GetFeature(nil);
end;

function TActorObject.GetFeatureEx(): Word;
begin
  if m_boOnHorse then begin
    Result := MakeWord(m_btHorseType, m_btDressEffType);
  end else begin
    Result := MakeWord(0, m_btDressEffType);
  end;
end;

function TActorObject.GetFeature(BaseObject: TActorObject): Integer;
var
  nDress, nWeapon, nHair, nRaceImg, nAppr, nNewHair: Integer;
  StdItem: pTStdItem;
  bo25: Boolean;
begin
  if Assigned(PlugInEngine.GetFeature) then begin
    Result := PlugInEngine.GetFeature(Self, BaseObject);
  end else begin
    if (m_btRaceServer = RC_PLAYOBJECT) or (m_btRaceServer = RC_PLAYMOSTER) or (m_btRaceServer = RC_HEROOBJECT) then begin
      nDress := 0;
      //衣服
      if m_UseItems[U_DRESS].wIndex > 0 then begin
        StdItem := UserEngine.GetStdItem(m_UseItems[U_DRESS].wIndex);
        if StdItem <> nil then begin
          nDress := StdItem.Shape * 2;
        end;
      end;
      Inc(nDress, m_btGender);
      nWeapon := 0;
      nNewHair := 0;
      //武器
      if m_UseItems[U_WEAPON].wIndex > 0 then begin
        StdItem := UserEngine.GetStdItem(m_UseItems[U_WEAPON].wIndex);
        if StdItem <> nil then begin
          nWeapon := StdItem.Shape * 2;
        end;
      end;

      //头盔
      if m_UseItems[U_HELMET].wIndex > 0 then begin
        StdItem := UserEngine.GetStdItem(m_UseItems[U_HELMET].wIndex);
        if StdItem <> nil then begin
          case StdItem.Shape of
            1: nNewHair := 4;
            2: nNewHair := 5;
          end;
        end;
      end;

      Inc(nWeapon, m_btGender);
      if nNewHair = 0 then begin
        nHair := m_btHair * 2 + m_btGender;
      end else begin
        nHair := nNewHair * 2 + m_btGender;
      end;
      Result := MakeHumanFeature(0, nDress, nWeapon, nHair);
      Exit;
    end;
    bo25 := False;
    if (BaseObject <> nil) and (BaseObject.bo245) then
      bo25 := True;
    if bo25 then begin
      nRaceImg := m_btRaceImg;
      nAppr := m_wAppr;
      case nAppr of
        0: begin
            nRaceImg := 12;
            nAppr := 5;
          end;
        1: begin
            nRaceImg := 11;
            nAppr := 9;
          end;
        160: begin
            nRaceImg := 10;
            nAppr := 0;
          end;
        161: begin
            nRaceImg := 10;
            nAppr := 1;
          end;
        162: begin
            nRaceImg := 11;
            nAppr := 6;
          end;
        163: begin
            nRaceImg := 11;
            nAppr := 3;
          end;
      end;
      if m_btRaceServer in [RC_NPC, RC_ANIMAL] then begin
        Result := MakeMonsterFeature(nRaceImg, TNormNpc(Self).m_nNpcMagicIdx, nAppr);
      end else begin
        Result := MakeMonsterFeature(nRaceImg, m_btMonsterWeapon, nAppr);
      end;
      Exit;
    end;
    if m_btRaceServer in [RC_NPC, RC_ANIMAL] then begin
      Result := MakeMonsterFeature(m_btRaceImg, TNormNpc(Self).m_nNpcMagicIdx, m_wAppr);
    end else begin
      Result := MakeMonsterFeature(m_btRaceImg, m_btMonsterWeapon, m_wAppr);
    end;
  end;
end;

function TActorObject.GetAddStatus(): Integer;
begin
  Result := MakeLong(MakeWord(m_btJob, GetObjectCastle), MakeWord(Integer(m_boStore), 0));
end;

function TActorObject.GetAddStatus2(): Integer;
begin
  Result := MakeLong(MakeWord(Integer(m_boStore), 0), 0);
end;

function TActorObject.GetCharStatus(): Integer;
var
  I: Integer;
  nStatus: Integer;
begin
  nStatus := 0;
  for I := Low(TStatusTime) to High(TStatusTime) do begin
    if m_wStatusTimeArr[I] > 0 then begin
      nStatus := ($80000000 shr I) or nStatus;
    end;
  end;
  Result := (m_nCharStatusEx and $FFFFF) or nStatus;
end;

procedure TActorObject.AbilCopyToWAbil;
begin
  m_WAbil := m_Abil;
end;

procedure TActorObject.AddMapCount;
begin
  if not m_boAddToMaped then begin
    m_boDelFormMaped := False;
    m_boAddToMaped := True;

    if m_PEnvir <> nil then begin
      m_PEnvir.AddObject(Self);
      {if m_btRaceServer = RC_PLAYOBJECT then begin
        MainOutMessage('m_PEnvir.AddObject(Self):'+m_sCharName);
      end;}
    end;
  end;
end;

procedure TActorObject.DelMapCount;
begin
  if not m_boDelFormMaped then begin
    m_boDelFormMaped := True;
    m_boAddToMaped := False;
    if m_PEnvir <> nil then m_PEnvir.DelObject(Self);
  end;
end;

procedure TActorObject.Initialize;
var
  I: Integer;
  UserMagic: pTUserMagic;
begin
  AbilCopyToWAbil();
  //if (m_btRaceServer = RC_PLAYOBJECT) or (m_btRaceServer = RC_PLAYMOSTER) or (m_btRaceServer = RC_HEROOBJECT) then begin
  if m_MagicList.Count > 0 then begin
    for I := 0 to m_MagicList.Count - 1 do begin
      UserMagic := m_MagicList.Items[I];
      if (m_btRaceServer = RC_HEROOBJECT) or (UserMagic.MagicInfo.wMagicId = 31) then begin
        if (UserMagic.MagicInfo.wMagicId in [160,161]) then begin
          if (UserMagic.btLevel >= 5) then begin
            UserMagic.btLevel := 0;
            //MainOutMessage('UserMagic.btLevel >= 5');
          end;
        end else if UserMagic.btLevel >= 4 then begin
          UserMagic.btLevel := 0;
          //MainOutMessage('UserMagic.btLevel >= 4');
        end;
      end else begin
        if UserMagic.btLevel >= 4 then UserMagic.btLevel := 0;
      end;
    end;
  end;
  m_boAddtoMapSuccess := True;
  if m_PEnvir.CanWalk(m_nCurrX, m_nCurrY, True) and m_PEnvir.CanWalkOfEvent(Self, m_nCurrX, m_nCurrY) and AddToMap() then
    m_boAddtoMapSuccess := False;
  m_nCharStatus := GetCharStatus();
  AddBodyLuck(0);
  LoadSayMsg();
  if g_Config.boMonSayMsg then MonsterSayMsg(nil, s_MonGen);
end;
//==============================
//取得怪物说话信息列表

procedure TActorObject.LoadSayMsg();
var
  I: Integer;
begin
  for I := 0 to g_MonSayMsgList.Count - 1 do begin
    if CompareText(g_MonSayMsgList.Strings[I], m_sCharName) = 0 then begin
      m_SayMsgList := TList(g_MonSayMsgList.Objects[I]);
      Break;
    end;
  end;
end;

procedure TActorObject.Disappear();
begin

end;

procedure TActorObject.FeatureChanged;
begin
  SendRefMsg(RM_FEATURECHANGED, GetFeatureEx, GetFeatureToLong, 0, 0, '')
end;

procedure TActorObject.StatusChanged();
var
  nShield: Integer;
begin
  nShield := 0;
  if (m_Magic31Skill <> nil) and (m_Magic31Skill.btLevel >= 4) then nShield := 1;
  SendRefMsg(RM_CHARSTATUSCHANGED, m_nHitSpeed, m_nCharStatus, 0, nShield, '');
end;

procedure TPlayObject.Disappear;
begin
  if m_boReadyRun then DisappearA;
  if m_boTransparent and m_boHideMode then
    m_wStatusTimeArr[STATE_TRANSPARENT {0x70}] := 0;

  if m_GroupOwner <> nil then begin
    m_GroupOwner.DelMember(Self);
  end;
  if m_MyGuild <> nil then begin
    TGUild(m_MyGuild).DelHumanObj(Self);
  end;
  LogonTimcCost();
  inherited;
end;

procedure TActorObject.DisappearA();
begin
  if m_PEnvir.DeleteFromMap(m_nCurrX, m_nCurrY, Self) <> 1 then DelMapCount;
  SendRefMsg(RM_DISAPPEAR, 0, 0, 0, 0, '');
end;

procedure TPlayObject.DisappearB();
begin
  if m_GroupOwner <> nil then begin
    m_GroupOwner.DelMember(Self);
  end;
  if m_MyGuild <> nil then begin
    TGUild(m_MyGuild).DelHumanObj(Self);
  end;
end;

procedure TActorObject.KickException;
var
  PlayObject: TPlayObject;
begin
  if m_btRaceServer = RC_PLAYOBJECT then begin
    m_sMapName := g_Config.sHomeMap;
    m_nCurrX := g_Config.nHomeX;
    m_nCurrY := g_Config.nHomeY;
    PlayObject := TPlayObject(Self);
    PlayObject.m_boEmergencyClose := True;
    PlayObject.m_boNotOnlineAddExp := False;
    PlayObject.m_boPlayOffLine := False; //关闭下线触发
    PlayObject.DisappearB;
    //MainOutMessage(Format('[Exception] TPlayObject::KickException:%s', [m_sCharName]));
  end else begin
    m_boDeath := True;
    m_dwDeathTick := GetTickCount;
    MakeGhost;
    //MainOutMessage(Format('[Exception] TActorObject::KickException:%s', [m_sCharName]));
  end;
end;

function TActorObject.WalkToSpaceLock(nOldX, nOldY, nNewX, nNewY: Integer): Boolean;
var
  I, II, III: Integer;
  MapCellInfo: pTMapCellinfo;
  BaseObject: TBaseObject;
  Event: TEvent;
  nMinX, nMaxX, nMinY, nMaxY: Integer;
  MasterObject: TActorObject;
begin
  Result := False;
  if m_boAdminMode then Exit; //锁定空间管理员不受限制
  if nOldX = nNewX then begin
    if nOldY > nNewY then begin
      nMinY := nNewY;
      nMaxY := nOldY;
    end else begin
      nMinY := nOldY;
      nMaxY := nNewY;
    end;
    for I := nMinY to nMaxY do begin
      if m_PEnvir.GetMapCellInfo(nOldX, I, MapCellInfo) and (MapCellInfo.ObjList <> nil) then begin
        for II := 0 to MapCellInfo.ObjList.Count - 1 do begin
          BaseObject := TBaseObject(MapCellInfo.ObjList.Items[II]);
          if BaseObject.m_ObjType = t_Event then begin
            if TEvent(BaseObject).m_OwnActorObject <> nil then begin
              Event := TEvent(BaseObject);
              if Event.m_boVisible and (not Event.m_boClosed) and (Event.m_nServerEventType = ET_MAGICLOCK) then begin
                if not m_boSpaceLock then begin
                  if InRect(nOldX, nOldY, Event.m_OwnActorObject.m_SpaceRect) then begin
                    m_SpaceOwner := Event.m_OwnActorObject;
                    m_boSpaceLock := True;
                    m_SpaceRect := Event.m_OwnActorObject.m_SpaceRect;
                  end;
                end;

                if (Event.m_OwnActorObject = Self) then
                  SysMsg(Format('Area locked for %d seconds.', [Event.m_dwContinueTime div 1000 - (GetTickCount - Event.m_dwOpenStartTick) div 1000]), c_Red, t_Hint);

                MasterObject := Master;
                if MasterObject = nil then begin
                  if (Event.m_OwnActorObject <> Self) then begin
                    if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT] then begin
                      SysMsg('Blocked by Unknown forces.', c_Red, t_Hint);
                      SysMsg(Format('Area locked for %d seconds.', [Event.m_dwContinueTime div 1000 - (GetTickCount - Event.m_dwOpenStartTick) div 1000]), c_Red, t_Hint);
                    end;
                    Result := True;
                  end;
                  Exit;
                end else begin
                  if (MasterObject.m_SpaceOwner <> Event.m_OwnActorObject) then begin
                    if (MasterObject.m_SpaceOwner = m_SpaceOwner) or (m_SpaceOwner <> Event.m_OwnActorObject) then begin
                      if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT] then begin
                        SysMsg('Blocked by Unknown forces.', c_Red, t_Hint);
                        SysMsg(Format('Area locked for %d seconds.', [Event.m_dwContinueTime div 1000 - (GetTickCount - Event.m_dwOpenStartTick) div 1000]), c_Red, t_Hint);
                      end;
                      Result := True;
                      Exit;
                    end;
                  end;
                end;
              end;
            end;
          end;
        end;
      end;
    end;
  end else begin
    if nOldY = nNewY then begin
      if nOldX > nNewX then begin
        nMinX := nNewX;
        nMaxX := nOldX;
      end else begin
        nMinX := nOldX;
        nMaxX := nNewX;
      end;
      for I := nMinX to nMaxX do begin
        if m_PEnvir.GetMapCellInfo(I, nOldY, MapCellInfo) and (MapCellInfo.ObjList <> nil) then begin
          for II := 0 to MapCellInfo.ObjList.Count - 1 do begin
            BaseObject := TBaseObject(MapCellInfo.ObjList.Items[II]);
            if BaseObject.m_ObjType = t_Event then begin
              if TEvent(BaseObject).m_OwnActorObject <> nil then begin
                Event := TEvent(BaseObject);
                if Event.m_boVisible and (not Event.m_boClosed) and (Event.m_nServerEventType = ET_MAGICLOCK) then begin
                  if not m_boSpaceLock then begin
                    if InRect(nOldX, nOldY, Event.m_OwnActorObject.m_SpaceRect) then begin
                      m_SpaceOwner := Event.m_OwnActorObject;
                      m_boSpaceLock := True;
                      m_SpaceRect := Event.m_OwnActorObject.m_SpaceRect;
                    end;
                  end;

                  if (Event.m_OwnActorObject = Self) then
                    SysMsg(Format('Area locked for %d seconds.', [Event.m_dwContinueTime div 1000 - (GetTickCount - Event.m_dwOpenStartTick) div 1000]), c_Red, t_Hint);

                  MasterObject := Master;
                  if MasterObject = nil then begin
                    if (Event.m_OwnActorObject <> Self) then begin
                      if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT] then begin
                        SysMsg('Blocked by Unknown forces.', c_Red, t_Hint);
                        SysMsg(Format('Area locked for %d seconds.', [Event.m_dwContinueTime div 1000 - (GetTickCount - Event.m_dwOpenStartTick) div 1000]), c_Red, t_Hint);
                      end;
                      Result := True;
                    end;
                    Exit;
                  end else begin
                    if (MasterObject.m_SpaceOwner <> Event.m_OwnActorObject) then begin
                      if (MasterObject.m_SpaceOwner = m_SpaceOwner) or (m_SpaceOwner <> Event.m_OwnActorObject) then begin
                        if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT] then begin
                          SysMsg('Blocked by Unknown forces.', c_Red, t_Hint);
                          SysMsg(Format('Area locked for %d seconds.', [Event.m_dwContinueTime div 1000 - (GetTickCount - Event.m_dwOpenStartTick) div 1000]), c_Red, t_Hint);
                        end;
                        Result := True;
                        Exit;
                      end;
                    end;
                  end;
                end;
              end;
            end;
          end;
        end;
      end;
    end else begin
      if nOldY > nNewY then begin
        nMinY := nNewY;
        nMaxY := nOldY;
      end else begin
        nMinY := nOldY;
        nMaxY := nNewY;
      end;

      if nOldX > nNewX then begin
        nMinX := nNewX;
        nMaxX := nOldX;
      end else begin
        nMinX := nOldX;
        nMaxX := nNewX;
      end;

      for I := nMinX to nMaxX do begin
        for II := nMinY to nMaxY do begin
          if m_PEnvir.GetMapCellInfo(I, II, MapCellInfo) and (MapCellInfo.ObjList <> nil) then begin
            for III := 0 to MapCellInfo.ObjList.Count - 1 do begin
              BaseObject := TBaseObject(MapCellInfo.ObjList.Items[III]);
              if BaseObject.m_ObjType = t_Event then begin
                if TEvent(BaseObject).m_OwnActorObject <> nil then begin
                  Event := TEvent(BaseObject);
                  if Event.m_boVisible and (not Event.m_boClosed) and (Event.m_nServerEventType = ET_MAGICLOCK) then begin
                    if not m_boSpaceLock then begin
                      if InRect(nOldX, nOldY, Event.m_OwnActorObject.m_SpaceRect) then begin
                        m_SpaceOwner := Event.m_OwnActorObject;
                        m_boSpaceLock := True;
                        m_SpaceRect := Event.m_OwnActorObject.m_SpaceRect;
                      end;
                    end;

                    if (Event.m_OwnActorObject = Self) then
                      SysMsg(Format('Area locked for %d seconds.', [Event.m_dwContinueTime div 1000 - (GetTickCount - Event.m_dwOpenStartTick) div 1000]), c_Red, t_Hint);

                    MasterObject := Master;
                    if MasterObject = nil then begin
                      if (Event.m_OwnActorObject <> Self) then begin
                        if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT] then begin
                          SysMsg('Blocked by Unknown forces.', c_Red, t_Hint);
                          SysMsg(Format('Area locked for %d seconds.', [Event.m_dwContinueTime div 1000 - (GetTickCount - Event.m_dwOpenStartTick) div 1000]), c_Red, t_Hint);
                        end;
                        Result := True;
                      end;
                      Exit;
                    end else begin
                      if (MasterObject.m_SpaceOwner <> Event.m_OwnActorObject) then begin
                        if (MasterObject.m_SpaceOwner = m_SpaceOwner) or (m_SpaceOwner <> Event.m_OwnActorObject) then begin
                          if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT] then begin
                            SysMsg('Blocked by Unknown forces.', c_Red, t_Hint);
                            SysMsg(Format('Area locked for %d seconds.', [Event.m_dwContinueTime div 1000 - (GetTickCount - Event.m_dwOpenStartTick) div 1000]), c_Red, t_Hint);
                          end;
                          Result := True;
                          Exit;
                        end;
                      end;
                    end;
                  end;
                end;
              end;
            end;
          end;
        end;
      end;
    end;
  end;
end;

function TActorObject.Walk(nIdent: Integer): Boolean;
var
  I: Integer;
  MapCellInfo: pTMapCellinfo;
  BaseObject: TBaseObject;
  GateObject: TGateObject;
  bo1D: Boolean;
  Event: TEvent;
  MagicEvent: TMapMagicEvent;
  PlayObject: TPlayObject;
  nCheckCode: Integer;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::Walk  CheckCode:%d %s %s %d:%d';
  sExceptionMsg1 = '[Exception] TActorObject::WalkToEvent  CheckCode:%d %s %s %d:%d';
begin
  Result := True;
  nCheckCode := -1;
  if m_PEnvir = nil then begin
    MainOutMessage('Walk nil PEnvir');
    Exit;
  end;
  try
    nCheckCode := 1;
    bo1D := m_PEnvir.GetMapCellInfo(m_nCurrX, m_nCurrY, MapCellInfo);
    GateObject := nil;
    Event := nil;
    MagicEvent := nil;
    nCheckCode := 2;
    if bo1D and (MapCellInfo.ObjList <> nil) then begin
      for I := 0 to MapCellInfo.ObjList.Count - 1 do begin
        BaseObject := TBaseObject(MapCellInfo.ObjList.Items[I]);
        if BaseObject.m_ObjType = t_Gate then begin
          GateObject := TGateObject(BaseObject);
        end;
        if BaseObject.m_ObjType = t_Event then begin
          if (TEvent(BaseObject).m_OwnActorObject <> nil) then begin
            Event := TEvent(BaseObject);
          end else
            if TEvent(BaseObject).m_nEventType = ET_MAPMAGIC then begin
            MagicEvent := TMapMagicEvent(BaseObject);
          end;
        end;
        if BaseObject.m_ObjType = t_MapEvent then begin

        end;
        if BaseObject.m_ObjType = t_Door then begin

        end;
        if BaseObject.m_ObjType = t_Roon then begin

        end;
      end;
    end;
    nCheckCode := 3;
    try
      if (Event <> nil) and Event.m_boVisible and (not Event.m_boClosed) and (Event.m_Envir <> nil) then begin
        nCheckCode := 31;
        if (Event.m_OwnActorObject <> nil) then begin
          nCheckCode := 32;
          if Event.m_OwnActorObject.IsProperTarget(Self) then begin
            nCheckCode := 320;
            SendMsg(Event.m_OwnActorObject, RM_MAGSTRUCK_MINE, 0, Event.m_nDamage, 0, 0, '');
            nCheckCode := 321;
          end;
        end;
      end;

      nCheckCode := 33;
      if (MagicEvent <> nil) and (not MagicEvent.m_boClosed) and (MagicEvent.m_Envir <> nil) then begin
        nCheckCode := 34;
        if MagicEvent.m_btUseType = 1 then Inc(MagicEvent.m_nUseCount);
        if MagicEvent.IsProperTarget(Self) then begin
          nCheckCode := 35;
          MagicEvent.m_dwRunTick := GetTickCount();
          SendMsg(nil, RM_MAGSTRUCK_MINE, 0, MagicEvent.m_nDamage, 0, 0, '');
          MagicEvent.SendRefMsg(RM_10205, 0, MagicEvent.m_nX, MagicEvent.m_nY, MagicEvent.m_nServerEventType - 100, '');
        end;
      end;

      nCheckCode := 36;
    except
      on E: Exception do begin
        Event.m_OwnActorObject := nil;
        MainOutMessage(Format(sExceptionMsg1, [nCheckCode, m_sCharName, m_sMapName, m_nCurrX, m_nCurrY]));
        MainOutMessage(E.Message);
      end;
    end;
    nCheckCode := 4;

    if Result and (GateObject <> nil) then begin
      nCheckCode := 5;
      if (m_btRaceServer = RC_PLAYOBJECT) or ((m_btRaceServer = 122) and (m_Master <> nil) and (m_Master.m_PEnvir <> m_PEnvir) and TCartMonster(Self).m_boEnterAnotherMap) then begin
        nCheckCode := 6;
        {
        if g_Config.boCheck and (m_sCharName = '10000') then MainOutMessage('m_btRaceServer = RC_PLAYOBJECT');
        if g_Config.boCheck and (m_sCharName = '10000') then MainOutMessage('m_PEnvir.ArroundDoorOpened(m_nCurrX, m_nCurrY):' + BooleanToStr(m_PEnvir.ArroundDoorOpened(m_nCurrX, m_nCurrY)));
        if g_Config.boCheck and (m_sCharName = '10000') then MainOutMessage('TEnvirnoment(GateObject.m_DEnvir).m_boNEEDHOLE:' + BooleanToStr(TEnvirnoment(GateObject.m_DEnvir).m_boNEEDHOLE));
        if g_Config.boCheck and (m_sCharName = '10000') then MainOutMessage('g_EventManager.GetEvent:' + BooleanToStr((g_EventManager.GetEvent(m_PEnvir, m_nCurrX, m_nCurrY, ET_DIGOUTZOMBI) <> nil)));
        if g_Config.boCheck and (m_sCharName = '10000') then MainOutMessage(Format('nSMapX%d nSMapY%d nMapX%d nMapY%d', [GateObject.m_nSMapX, GateObject.m_nSMapY, GateObject.m_nMapX, GateObject.m_nMapY]));
        }
        if m_PEnvir.ArroundDoorOpened(m_nCurrX, m_nCurrY) or GateObject.m_boFlag then begin
          nCheckCode := 7;
          if (GateObject.m_DEnvir <> nil) then begin
            nCheckCode := 8;
            if (not TEnvirnoment(GateObject.m_DEnvir).m_boNEEDHOLE) then begin
              nCheckCode := 9;
              if (not EnterAnotherMap(TEnvirnoment(GateObject.m_DEnvir), GateObject.m_nMapX, GateObject.m_nMapY)) then begin
                Result := False;
              end;
              nCheckCode := 10;
            end else begin
              nCheckCode := 11;
              if (g_EventManager.GetEvent(m_PEnvir, m_nCurrX, m_nCurrY, ET_DIGOUTZOMBI) <> nil) then begin
                nCheckCode := 12;
                if (not EnterAnotherMap(TEnvirnoment(GateObject.m_DEnvir), GateObject.m_nMapX, GateObject.m_nMapY)) then begin
                  Result := False;
                end;
                nCheckCode := 13;
              end;
            end;
          end else begin
            DisappearA();
            m_bo316 := True;
            if m_btRaceServer = RC_PLAYOBJECT then begin
              PlayObject := TPlayObject(Self);
              PlayObject.m_nSwitchMapX := GateObject.m_nMapX;
              PlayObject.m_nSwitchMapY := GateObject.m_nMapY;
              PlayObject.m_boSwitchData := True;
              PlayObject.m_boEmergencyClose := True;
              PlayObject.m_boReconnection := True;
              PlayObject.m_boPlayOffLine := False;
              PlayObject.DisappearB();
            end;
            MainOutMessage(Format('[Exception] TActorObject::Walk KickException:%s', [m_sCharName]));
          end;

        end;
      end else Result := False;
    end else begin
      nCheckCode := 14;
      if Result then begin
        nCheckCode := 15;
        SendRefMsg(nIdent, m_btDirection, m_nCurrX, m_nCurrY, 0, '');
        nCheckCode := 16;
        if (m_Master <> nil) and (m_Master.m_PEnvir = m_PEnvir) and (m_btRaceServer = 122) { and ((abs(m_nCurrX - m_Master.m_nCurrX) > 12) or (abs(m_nCurrY - m_Master.m_nCurrY) > 12))} then begin
          m_Master.SendMsg(Self, RM_SENDCARTINFO, 0, Integer(Self), m_nCurrX, m_nCurrY, ''); //镖车信息
        end;
        nCheckCode := 17;
      end;
    end;
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg, [nCheckCode, m_sCharName, m_sMapName, m_nCurrX, m_nCurrY]));
      MainOutMessage(E.Message);
    end;
  end;
end;

function TActorObject.EnterAnotherMap(Envir: TEnvirnoment; nDMapX,
  nDMapY: Integer): Boolean;
var
  I: Integer;
  MapCellInfo: pTMapCellinfo;
  OldEnvir: TEnvirnoment;
  nOldX: Integer;
  nOldY: Integer;
  Castle: TUserCastle;
  ActorObject: TActorObject;
  nError: Integer;
resourcestring
  {sExceptionMsg1 = '[Exception] TActorObject::EnterAnotherMap -> MsgTargetList Clear';
  sExceptionMsg2 = '[Exception] TActorObject::EnterAnotherMap -> VisbleItems Dispose';
  sExceptionMsg3 = '[Exception] TActorObject::EnterAnotherMap -> VisbleItems Clear';
  sExceptionMsg4 = '[Exception] TActorObject::EnterAnotherMap -> VisbleEvents Clear';
  sExceptionMsg5 = '[Exception] TActorObject::EnterAnotherMap -> VisbleActors Dispose';
  sExceptionMsg6 = '[Exception] TActorObject::EnterAnotherMap -> VisbleActors Clear'; }
  sExceptionMsg = '[Exception] TActorObject::EnterAnotherMap Code:%d';
begin
  if Assigned(PlugInEngine.ObjectEnterAnotherMap) then begin
    Result := PlugInEngine.ObjectEnterAnotherMap(Self, Envir, nDMapX, nDMapY);
  end else begin
    Result := False;
    nError := 0;
    try
      if m_Abil.Level < Envir.nRequestLevel then Exit;
      nError := 1;
      if (Envir.QuestNPC <> nil) and (m_btRaceServer = RC_PLAYOBJECT) then TMerchant(Envir.QuestNPC).Click(TPlayObject(Self));
      nError := 2;
      if Envir.nNEEDSETONFlag >= 0 then begin
        nError := 3;
        if GetQuestFalgStatus(Envir.nNEEDSETONFlag) <> Envir.nNeedONOFF then Exit;
        nError := 4;
      end;
      nError := 5;
      if not Envir.GetMapCellInfo(nDMapX, nDMapY, MapCellInfo) then Exit;
      nError := 6;
      Castle := g_CastleManager.IsCastlePalaceEnvir(Envir);
      nError := 7;
      if (Castle <> nil) and (m_btRaceServer = RC_PLAYOBJECT) then begin
        nError := 8;
        if not Castle.CheckInPalace(m_nCurrX, m_nCurrY, Self) then Exit;
        nError := 9;
      end;
      nError := 10;
      {
      if (UserCastle.m_MapPalace = Envir) and (m_btRaceServer = RC_PLAYOBJECT) then begin
        if not UserCastle.CheckInPalace(m_nCurrX,m_nCurrY,Self) then exit;
      end;
      }
      OldEnvir := m_PEnvir;
      nOldX := m_nCurrX;
      nOldY := m_nCurrY;
      nError := 11;
      DisappearA();
      nError := 12;
      ClearViewRange();
      nError := 13;
      if m_btRaceServer = RC_PLAYOBJECT then
        SendMsg(Self, RM_CLEAROBJECTS, 0, 0, 0, 0, '');

      nError := 14;

      m_PEnvir := Envir;
      m_sMapName := Envir.sMapName;
      m_nCurrX := nDMapX;
      m_nCurrY := nDMapY;
      nError := 15;
      if m_btRaceServer = RC_PLAYOBJECT then
        SendMsg(Self, RM_CHANGEMAP, 0, 0, 0, 0, Envir.MapName);
      nError := 16;
      if AddToMap() then begin
        m_dwMapMoveTick := GetTickCount();
        m_bo316 := True;
        nError := 17;
        for I := 0 to m_SlaveList.Count - 1 do begin
          ActorObject := TActorObject(m_SlaveList.Items[I]);
          if (ActorObject.m_btRaceServer = 122) and (ActorObject.m_PEnvir = OldEnvir) and (abs(nOldX - ActorObject.m_nCurrX) <= 12) and (abs(nOldY - ActorObject.m_nCurrY) <= 12) then begin
            TCartMonster(ActorObject).m_nGateX := nOldX;
            TCartMonster(ActorObject).m_nGateY := nOldY;
            TCartMonster(ActorObject).m_boEnterAnotherMap := True;
          end;
        end;
        nError := 18;
        if m_btRaceServer = 122 then begin
          TCartMonster(Self).m_boEnterAnotherMap := False;
        end;
        nError := 19;
        if m_btRaceServer = RC_PLAYOBJECT then begin //复位泡点，及金币，时间
          TPlayObject(Self).m_dwIncGamePointTick := GetTickCount();
          TPlayObject(Self).m_dwIncGameGoldTick := GetTickCount();
          TPlayObject(Self).m_dwAutoGetExpTick := GetTickCount();
          TPlayObject(Self).m_boSayAdvertise := False;
          TPlayObject(Self).m_dwSayAdvertiseTick := GetTickCount;
        end;
        nError := 20;
        if m_PEnvir.m_boFight3Zone and (m_PEnvir.m_boFight3Zone <> OldEnvir.m_boFight3Zone) then
          RefShowName();
        nError := 21;
        Result := True;
      end else begin
        nError := 22;
        m_PEnvir := OldEnvir;
        m_nCurrX := nOldX;
        m_nCurrY := nOldY;
        m_PEnvir.AddToMap(m_nCurrX, m_nCurrY, Self);
        nError := 23;
          //if m_PEnvir.AddToMap(m_nCurrX, m_nCurrY, Self) = nil then MakeGhost;
      end;
      nError := 24;
      AddMapCount;
      nError := 25;
    except
      MainOutMessage(Format(sExceptionMsg, [nError]));
    end;
  end;
end;

procedure TActorObject.TurnTo(nDir: Integer);
begin
  m_btDirection := nDir;
  SendRefMsg(RM_TURN, nDir, m_nCurrX, m_nCurrY, 0, '');
end;

procedure TActorObject.ProcessSayMsg(sMsg: string);
var
  sCharName: string;
begin
  if m_btRaceServer = RC_PLAYOBJECT then Exit; //修改
  sCharName := FilterShowName(m_sCharName);
  SendRefMsg(RM_HEAR, 0, g_Config.btHearMsgFColor, g_Config.btHearMsgBColor, 0, sCharName + ':' + sMsg);
end;

procedure TActorObject.SysMsg(sMsg: string; MsgColor: TMsgColor; MsgType: TMsgType);
begin
  if m_boNotOnlineAddExp or m_boAI then Exit; //修改离线人物不发送
  if g_Config.boShowPreFixMsg then begin
    case MsgType of
      t_Mon: sMsg := g_Config.sMonSayMsgpreFix + sMsg;
      t_Hint: sMsg := g_Config.sHintMsgPreFix + sMsg;
      {
      s_GroupMsg: sMsg:=g_Config.sGroupMsgPreFix + sMsg;
      s_GuildMsg: sMsg:=g_Config.sGuildMsgPreFix + sMsg;
      }
      t_GM: sMsg := g_Config.sGMRedMsgpreFix + sMsg;
      t_System: sMsg := g_Config.sSysMsgPreFix + sMsg;
      t_Notice: sMsg := g_Config.sLineNoticePreFix + sMsg;
      t_Cust: sMsg := g_Config.sCustMsgpreFix + sMsg;
      t_Castle: sMsg := g_Config.sCastleMsgpreFix + sMsg;
    end;
  end;
  {
  case MsgColor of
    c_Green: SendMsg(Self,RM_SYSMESSAGE2,0,0,0,0,sMsg);
    c_Blue: SendMsg(Self,RM_SYSMESSAGE3,0,0,0,0,sMsg);
    else SendMsg(Self,RM_SYSMESSAGE,0,0,0,0,sMsg);
  end;
  }
  case MsgColor of
    c_Green: SendMsg(Self, RM_SYSMESSAGE, 0, g_Config.btGreenMsgFColor, g_Config.btGreenMsgBColor, 0, sMsg);
    c_Blue: SendMsg(Self, RM_SYSMESSAGE, 0, g_Config.btBlueMsgFColor, g_Config.btBlueMsgBColor, 0, sMsg);
  else begin
      if MsgType = t_Cust then begin
        SendMsg(Self, RM_SYSMESSAGE, 0, g_Config.btCustMsgFColor, g_Config.btCustMsgBColor, 0, sMsg);
      end else begin
        SendMsg(Self, RM_SYSMESSAGE, 0, g_Config.btRedMsgFColor, g_Config.btRedMsgBColor, 0, sMsg);
      end;
    end;
  end;
end;

procedure TActorObject.SysMsg(sMsg: string; btFColor, btBColor: Byte; MsgType: TMsgType);
begin
  if m_boNotOnlineAddExp or m_boAI then Exit; //修改离线人物不发送
  if g_Config.boShowPreFixMsg then begin
    case MsgType of
      t_Mon: sMsg := g_Config.sMonSayMsgpreFix + sMsg;
      t_Hint: sMsg := g_Config.sHintMsgPreFix + sMsg;
      t_GM: sMsg := g_Config.sGMRedMsgpreFix + sMsg;
      t_System: sMsg := g_Config.sSysMsgPreFix + sMsg;
      t_Notice: sMsg := g_Config.sLineNoticePreFix + sMsg;
      t_Cust: sMsg := g_Config.sCustMsgpreFix + sMsg;
      t_Castle: sMsg := g_Config.sCastleMsgpreFix + sMsg;
    end;
  end;
  SendMsg(Self, RM_SYSMESSAGE, 0, btFColor, btBColor, 0, sMsg);
end;

procedure TActorObject.MoveMsg(sMsg: string; btFColor, btBColor: Byte; nX, nY: Word; nMoveCount: Integer);
begin
  if m_boNotOnlineAddExp or m_boAI then Exit; //修改离线人物不发送
  SendMsg(Self, RM_MOVEMESSAGE, MakeWord(btFColor, btBColor), nX, nY, nMoveCount, sMsg);
end;

procedure TActorObject.CenterMsg(sMsg: string; btFColor, btBColor: Byte; nTime: Integer);
begin
  if m_boNotOnlineAddExp or m_boAI then Exit; //修改离线人物不发送
  SendMsg(Self, RM_CENTERMESSAGE, MakeWord(btFColor, btBColor), 0, 0, nTime, sMsg);
end;

procedure TActorObject.MonsterSayMsg(AttackBaseObject: TActorObject; MonStatus: TMonStatus);
var
  I: Integer;
  nMsgColor: Integer;
  sMsg: string;
  MonSayMsg: pTMonSayMsg;
  sAttackName: string;
begin
  if m_SayMsgList = nil then Exit;
  if (AttackBaseObject <> nil) then begin
    if (AttackBaseObject.m_btRaceServer <> RC_PLAYOBJECT) and (AttackBaseObject.m_Master = nil) then begin
      Exit;
    end;
    if AttackBaseObject.Master <> nil then sAttackName := AttackBaseObject.Master.m_sCharName
    else sAttackName := AttackBaseObject.m_sCharName;
  end;
  for I := 0 to m_SayMsgList.Count - 1 do begin
    MonSayMsg := m_SayMsgList.Items[I];
    if MonSayMsg = nil then Continue;
    sMsg := AnsiReplaceText(MonSayMsg.sSayMsg, '%s', FilterShowName(m_sCharName));
    sMsg := AnsiReplaceText(sMsg, '%d', sAttackName);
    if (MonSayMsg.State = MonStatus) and (Random(MonSayMsg.nRate) = 0) then begin
      if MonStatus = s_MonGen then begin
        UserEngine.SendBroadCastMsg(sMsg, t_Mon);
        Break;
      end;
      if MonSayMsg.Color = c_White then begin
        ProcessSayMsg(sMsg);
      end else begin
        AttackBaseObject.SysMsg(sMsg, MonSayMsg.Color, t_Mon);
      end;
      Break;
    end;
  end;
end;

procedure TPlayObject.SendGroupText(sMsg: string);
var
  I: Integer;
  PlayObject: TPlayObject;
begin
  sMsg := g_Config.sGroupMsgPreFix + sMsg;
  if m_GroupOwner <> nil then begin
    for I := 0 to m_GroupOwner.m_GroupMembers.Count - 1 do begin
      PlayObject := TPlayObject(m_GroupOwner.m_GroupMembers.Objects[I]);
      if (PlayObject = nil) or PlayObject.m_boNotOnlineAddExp then Continue;
      PlayObject.SendMsg(Self, RM_GROUPMESSAGE, 0, g_Config.btGroupMsgFColor, g_Config.btGroupMsgBColor, 0, sMsg);
    end;
  end;
end;

procedure TPlayObject.SendGroupText(sMsg: string; boSend: Boolean);
var
  I: Integer;
  PlayObject: TPlayObject;
begin
  sMsg := g_Config.sGroupMsgPreFix + sMsg;
  if m_GroupOwner <> nil then begin
    for I := 0 to m_GroupOwner.m_GroupMembers.Count - 1 do begin
      PlayObject := TPlayObject(m_GroupOwner.m_GroupMembers.Objects[I]);
      if (PlayObject = nil) or PlayObject.m_boSayAdvertise then Continue;
      PlayObject.m_boSayAdvertise := boSend;
      PlayObject.m_dwSayAdvertiseTick := GetTickCount;
      PlayObject.SendMsg(Self, RM_GROUPMESSAGE, 0, g_Config.btGroupMsgFColor, g_Config.btGroupMsgBColor, 0, sMsg);
    end;
  end;
end;

procedure TActorObject.MakeGhost();
begin
  m_boGhost := True;
  m_dwGhostTick := GetTickCount();
  DisappearA();
end;

procedure TActorObject.ApplyMeatQuality;
var
  I: Integer;
  StdItem: pTStdItem;
  UserItem: pTUserItem;
begin
  for I := 0 to m_ItemList.Count - 1 do begin
    UserItem := m_ItemList.Items[I];
    StdItem := UserEngine.GetStdItem(UserItem.wIndex);
    if StdItem <> nil then begin
      if StdItem.StdMode = 40 then begin
        UserItem.Dura := m_nMeatQuality;
      end;
    end;
  end;
end;

function TActorObject.TakeBagItems(BaseObject: TActorObject): Boolean;
var
  UserItem: pTUserItem;
  PlayObject: TPlayObject;
begin
  Result := False;
  while (True) do begin
    if (BaseObject.m_ItemList.Count <= 0) then Break;
    UserItem := BaseObject.m_ItemList.Items[0];

    UserEngine._RandomItemLimitDay(UserItem, g_Config.nMonRandomNotLimit);
    if (not AddItemToBag(UserItem)) then break;
    if Self is TPlayObject then begin
      m_sButchItem := UserEngine.GetStdItemName(UserItem.wIndex);
      PlayObject := TPlayObject(Self);
      PlayObject.SendAddItem(UserItem);
      g_FunctionNPC.Click(PlayObject, '@ButchItem');
      Result := True;
    end;
    BaseObject.m_ItemList.Delete(0);
  end;
end;

procedure TActorObject.ScatterBagItems(ItemOfCreat: TActorObject);
var
  I, II, DropWide: Integer;
  UserItem: pTUserItem;
  StdItem: pTStdItem;
  boCanNotDrop: Boolean;
  MonDrop: pTMonDrop;
//  pu: pTUserItem;
//  DelList: TStringList;
//  boDropall: Boolean;
  sCheckItemName: string;
  PlayObject: TPlayObject;
  ActorObject, ActorObject18: TActorObject;
  nC, n10: Integer;
resourcestring
  sExceptionMsg = '[Exception] TActorObject::ScatterBagItems';
begin
  DropWide := 3;

  //if not m_boDropUseItem then Exit;

  PlayObject := nil;
  if (ItemOfCreat <> nil) and (ItemOfCreat.m_btRaceServer = RC_PLAYOBJECT) then
    PlayObject := TPlayObject(ItemOfCreat)
  else begin
    ActorObject18 := nil;
    n10 := 9999;
    for I := 0 to m_VisibleActors.Count - 1 do begin
      ActorObject := TActorObject(pTVisibleBaseObject(m_VisibleActors.Items[I]).BaseObject);
      if (ActorObject.m_btRaceServer = RC_PLAYOBJECT) then begin
        if (not ActorObject.m_boHideMode or m_boCoolEye) then begin
          nC := abs(m_nCurrX - ActorObject.m_nCurrX) + abs(m_nCurrY - ActorObject.m_nCurrY);
          if nC < n10 then begin
            n10 := nC;
            ActorObject18 := ActorObject;
          end;
        end;
      end;
    end;
    if ActorObject18 <> nil then
      PlayObject := TPlayObject(ActorObject18);
  end;

  try
    g_MonDropLimitLIst.Lock;
    try
      for I := m_ItemList.Count - 1 downto 0 do begin
        if m_ItemList.Count <= 0 then Break;
        UserItem := m_ItemList.Items[I];
        StdItem := UserEngine.GetStdItem(UserItem.wIndex);

        boCanNotDrop := False;

        if (StdItem <> nil) and (g_MonDropLimitLIst.Count > 0) then begin
          for II := 0 to g_MonDropLimitLIst.Count - 1 do begin
            if CompareText(StdItem.Name, g_MonDropLimitLIst.Strings[II]) = 0 then begin
              MonDrop := pTMonDrop(g_MonDropLimitLIst.Objects[II]);
              if MonDrop <> nil then begin
                if MonDrop.nDropCount < MonDrop.nCountLimit then begin
                  Inc(MonDrop.nDropCount);
                  g_MonDropLimitLIst.Objects[II] := TObject(MonDrop);
                end else begin
                  Inc(MonDrop.nNoDropCount);
                  boCanNotDrop := True;
                end;
              end;
              Break;
            end;
          end;
        end;

        if Assigned(PlugInEngine.CheckCanDieScatterItem) then begin //死亡必爆
          sCheckItemName := StdItem.Name;
          if PlugInEngine.CheckCanDieScatterItem(Self, PChar(sCheckItemName), False) then begin
            boCanNotDrop := False;
          end;
        end;

        if Assigned(PlugInEngine.CheckNotCanScatterItem) then begin //禁止爆出
          sCheckItemName := StdItem.Name;
          if PlugInEngine.CheckNotCanScatterItem(Self, PChar(sCheckItemName), False) then begin
            Continue;
          end;
        end;

        if boCanNotDrop then Continue;

        UserEngine._RandomItemLimitDay(UserItem, g_Config.nMonRandomNotLimit);

        if DropItemDown(UserItem, DropWide, True, ItemOfCreat, Self) then begin
          if (PlayObject <> nil) and (g_FunctionNPC <> nil) then begin
            PlayObject.m_sScatterItemName := UserEngine.GetStdItemName(UserItem.wIndex);

            PlayObject.m_sScatterItemOwnerName := m_sCharName;
            PlayObject.m_sScatterItemMapName := m_sMapName;
            PlayObject.m_sScatterItemMapDesc := m_PEnvir.sMapDesc;
            PlayObject.m_nScatterItemX := m_nScatterItemX;
            PlayObject.m_nScatterItemY := m_nScatterItemY;
            if IsAllowScatterItem(PlayObject.m_sScatterItemName) then begin
              try
                g_FunctionNPC.GotoLable(PlayObject, '@ScatterBagItems', False);
              except

              end;
            end;
          end;
          m_ItemList.Delete(I);
          Dispose(UserItem);
        end;
      end;
    finally
      g_MonDropLimitLIst.UnLock;
    end;
  except
    MainOutMessage(sExceptionMsg + ' ' + m_sCharName);
  end;
end;

procedure TActorObject.ScatterGolds(GoldOfCreat: TActorObject);
var
  I, nGold: Integer;
begin
  if m_nGold > 0 then begin
    I := 0;
    while (True) do begin
      //      for i:=0 to 18 do begin
      if m_nGold > g_Config.nMonOneDropGoldCount then begin
        nGold := g_Config.nMonOneDropGoldCount;
        m_nGold := m_nGold - g_Config.nMonOneDropGoldCount;
      end else begin
        nGold := m_nGold;
        m_nGold := 0;
      end;
      if nGold > 0 then begin
        if not DropGoldDown(nGold, True, GoldOfCreat, Self) then begin
          m_nGold := m_nGold + nGold;
          Break;
        end;
      end else Break;
      Inc(I);
      if I >= 17 then Break;
    end;
    GoldChanged;
  end;
end;

procedure TActorObject.DropUseItems(BaseObject: TActorObject);
begin

end;

{执行杀怪触发}

function TPlayObject.KillMonsterFunc(): Boolean;
  function GotoKillMonsterFunc(): Boolean;
  begin
    Result := False;
    if g_FunctionNPC <> nil then begin
      m_nScriptGotoCount := 0;
      g_FunctionNPC.GotoLable(Self, '@KillMon', False);
      Result := True;
    end;
  end;
begin
  Result := False;
  if m_ExpHitter <> nil then begin
    if m_ExpHitter.m_btRaceServer >= RC_ANIMAL then begin
      m_sString[0] := m_ExpHitter.m_sCharName;
      m_nInteger[0] := m_ExpHitter.m_Abil.Level;
      Result := GotoKillMonsterFunc;
    end;
  end;
end;

procedure TActorObject.Die;
var
  boPK, guildwarkill: Boolean;
  tStr: string;
  tExp: LongWord;
  I: Integer;
  GroupHuman: TPlayObject;
  QuestNPC: TMerchant;
  tCheck, boCheck: Boolean;
  AttackBaseObject: TActorObject;
  BaseObject: TActorObject;
  MasterObject: TActorObject;
  Castle: TUserCastle;
  nCheckCode: Integer;
  GroupMembers: TStringList;
  n10: Integer;
  boDuel: Boolean;
  StdItem: pTStdItem;
  ExpHitter: TActorObject;
  LastHiter: TActorObject;
  HeroObject: THeroObject;
resourcestring
  sExceptionMsg1 = '[Exception] TActorObject::Die 1';
  sExceptionMsg2 = '[Exception] TActorObject::Die 2';
  sExceptionMsg3 = '[Exception] TActorObject::Die 3 CheckCode:';
begin
  if m_boSuperMan then Exit;
  if m_boSupermanItem then Exit;
  m_boDeath := True;
  m_dwDeathTick := GetTickCount();

  ExpHitter := nil;
  LastHiter := nil;

  if m_Master <> nil then begin
    ExpHitter := m_ExpHitter;
    LastHiter := m_LastHiter;
    m_ExpHitter := nil;
    m_LastHiter := nil;
  end;

  m_nIncSpell := 0;
  m_nIncHealth := 0;
  m_nIncHealing := 0;
  GetDropUseItem;
  try
    if Assigned(PlugInEngine.ObjectDie) then begin
      if PlugInEngine.ObjectDie(Self) then Exit;
    end;

    nCheckCode := 0;
    if (m_btRaceServer <> RC_PLAYOBJECT) and (m_LastHiter <> nil) then begin
      nCheckCode := 1;
      if g_Config.boMonSayMsg then MonsterSayMsg(m_LastHiter, s_Die);
      nCheckCode := 2;
      if m_ExpHitter <> nil then begin
        nCheckCode := 3;
        m_ExpHitter.m_KillTargetCret := Self;
        nCheckCode := 4;
        if m_ExpHitter.m_btRaceServer = RC_PLAYOBJECT then begin
          nCheckCode := 5;
          tExp := m_ExpHitter.CalcGetExp(m_Abil.Level, m_dwFightExp);
          nCheckCode := 6;
          if not g_Config.boVentureServer then begin
            HeroObject := THeroObject(TPlayObject(m_ExpHitter).m_MyHero);
            nCheckCode := 7;
            if (HeroObject <> nil) and (not TPlayObject(m_ExpHitter).m_boWaitHeroDate) and (not HeroObject.m_boDeath) and (not HeroObject.m_boGhost) then begin //给英雄分配经验
              nCheckCode := 8;
              //BaseObject := TPlayObject(m_ExpHitter).m_MyHero;
              nCheckCode := 9;
              HeroObject.GainExp(abs(tExp * g_Config.nHeroKillMonExpRate div 100) + 1);
              nCheckCode := 10;
              TPlayObject(m_ExpHitter).m_nGetMonExp := abs(tExp * (100 - g_Config.nHeroKillMonExpRate) div 100) + 1;
              nCheckCode := 11;
              m_ExpHitter.GainExp(TPlayObject(m_ExpHitter).m_nGetMonExp);
              nCheckCode := 12;
            end else begin
              nCheckCode := 13;
              TPlayObject(m_ExpHitter).m_nGetMonExp := tExp;
              nCheckCode := 14;
              TPlayObject(m_ExpHitter).GainExp(tExp);
              nCheckCode := 15;
            end;
          end;
          nCheckCode := 16;

          //是否执行任务脚本
          if m_PEnvir.IsCheapStuff then begin
            nCheckCode := 17;
            if (TPlayObject(m_ExpHitter).m_GroupOwner <> nil) and (TPlayObject(m_ExpHitter).m_GroupOwner.m_GroupMembers <> nil) then begin
              nCheckCode := 18;
              GroupMembers := TPlayObject(m_ExpHitter).m_GroupOwner.m_GroupMembers;
              nCheckCode := 19;
              for I := 0 to GroupMembers.Count - 1 do begin
                GroupHuman := TPlayObject(GroupMembers.Objects[I]);
                nCheckCode := 20;
                if not GroupHuman.m_boDeath and (m_ExpHitter.m_PEnvir = GroupHuman.m_PEnvir) and (abs(m_ExpHitter.m_nCurrX - GroupHuman.m_nCurrX) <= 6) and (abs(m_ExpHitter.m_nCurrY - GroupHuman.m_nCurrY) <= 6) then begin
                  nCheckCode := 21;
                  if m_ExpHitter = GroupHuman then begin
                    tCheck := False;
                  end else begin
                    tCheck := True;
                  end;
                  nCheckCode := 22;
                  QuestNPC := TMerchant(m_PEnvir.GetQuestNPC(GroupHuman, m_sCharName, '', tCheck));
                  nCheckCode := 23;
                  if QuestNPC <> nil then begin
                    QuestNPC.Click(GroupHuman);
                  end;
                  nCheckCode := 24;
                end;
              end;
            end else begin
              nCheckCode := 25;
              QuestNPC := TMerchant(m_PEnvir.GetQuestNPC(m_ExpHitter, m_sCharName, '', False));
              if QuestNPC <> nil then begin
                QuestNPC.Click(TPlayObject(m_ExpHitter));
              end;
              nCheckCode := 26;
            end;
          end;

    {执行人物杀怪触发}
          nCheckCode := 27;
          if g_FunctionNPC <> nil then begin
            nCheckCode := 28;
            TPlayObject(m_ExpHitter).m_sString[0] := m_sCharName;
            nCheckCode := 29;
            TPlayObject(m_ExpHitter).m_nInteger[0] := m_WAbil.Level;
            nCheckCode := 30;
            TPlayObject(m_ExpHitter).m_nScriptGotoCount := 0;
            nCheckCode := 31;
            g_FunctionNPC.GotoLable(TPlayObject(m_ExpHitter), '@KillMon', False);
            nCheckCode := 32;
          end;
          nCheckCode := 33;
        end else begin //if m_ExpHitter.m_btRaceServer = RC_PLAYOBJECT then begin
          nCheckCode := 34;
          if m_ExpHitter.m_Master <> nil then begin
            nCheckCode := 35;
            m_ExpHitter.Master.m_KillTargetCret := Self;
            nCheckCode := 36;
            if m_ExpHitter.m_btRaceServer = RC_HEROOBJECT then begin //英雄获取经验
              nCheckCode := 37;
              tExp := m_ExpHitter.CalcGetExp(m_Abil.Level, m_dwFightExp);
              nCheckCode := 38;
              if not g_Config.boVentureServer then begin
                nCheckCode := 39;
                m_ExpHitter.GainExp(abs(tExp * g_Config.nHeroKillMonExpRate div 100) + 1);
                nCheckCode := 40;
                TPlayObject(m_ExpHitter.m_Master).m_nGetMonExp := abs(tExp * (100 - g_Config.nHeroKillMonExpRate) div 100) + 1;
                nCheckCode := 41;
                m_ExpHitter.m_Master.GainExp(TPlayObject(m_ExpHitter.m_Master).m_nGetMonExp);
                nCheckCode := 42;
              end;
              nCheckCode := 43;
          //英雄是否执行任务脚本
              if m_PEnvir.IsCheapStuff then begin
                nCheckCode := 44;
                if (TPlayObject(m_ExpHitter.m_Master).m_GroupOwner <> nil) and (TPlayObject(m_ExpHitter.m_Master).m_GroupOwner.m_GroupMembers <> nil) then begin
                  nCheckCode := 45;
                  GroupMembers := TPlayObject(m_ExpHitter.m_Master).m_GroupOwner.m_GroupMembers;
                  nCheckCode := 46;
                  for I := 0 to GroupMembers.Count - 1 do begin
                    GroupHuman := TPlayObject(GroupMembers.Objects[I]);
                    nCheckCode := 47;
                    if not GroupHuman.m_boDeath and (m_ExpHitter.m_PEnvir = GroupHuman.m_PEnvir) and (abs(m_ExpHitter.m_nCurrX - GroupHuman.m_nCurrX) <= 6) and (abs(m_ExpHitter.m_nCurrY - GroupHuman.m_nCurrY) <= 6) then begin
                      if m_ExpHitter = GroupHuman then begin
                        tCheck := False;
                      end else begin
                        tCheck := True;
                      end;
                      nCheckCode := 48;
                      QuestNPC := TMerchant(m_PEnvir.GetQuestNPC(GroupHuman, m_sCharName, '', tCheck));
                      if QuestNPC <> nil then begin
                        QuestNPC.Click(GroupHuman);
                      end;
                      nCheckCode := 49;
                    end;
                  end;
                end else begin
                  nCheckCode := 50;
                  QuestNPC := TMerchant(m_PEnvir.GetQuestNPC(TPlayObject(m_ExpHitter.m_Master), m_sCharName, '', False));
                  if QuestNPC <> nil then begin
                    QuestNPC.Click(TPlayObject(m_ExpHitter.m_Master));
                  end;
                  nCheckCode := 51;
                end;
              end;
            end else begin //if m_ExpHitter.m_btRaceServer = RC_HEROOBJECT then begin
              nCheckCode := 52;
              m_ExpHitter.GainSlaveExp(m_Abil.Level);
              nCheckCode := 53;
              MasterObject := m_ExpHitter.Master;
              nCheckCode := 54;
              if (MasterObject <> nil) and (MasterObject.m_btRaceServer = RC_PLAYOBJECT) then begin //是人物召唤的宝宝才给主人加经验
                nCheckCode := 55;
                tExp := MasterObject.CalcGetExp(m_Abil.Level, m_dwFightExp);
                nCheckCode := 56;
                if not g_Config.boVentureServer then begin
                  nCheckCode := 57;
                  BaseObject := TPlayObject(MasterObject).m_MyHero;
                  nCheckCode := 58;
                  if (BaseObject <> nil) and (not BaseObject.m_boDeath) then begin //给英雄分配经验
                    nCheckCode := 59;
                    BaseObject.GainExp(abs(tExp * g_Config.nHeroKillMonExpRate div 100) + 1);
                    nCheckCode := 60;
                    TPlayObject(MasterObject).m_nGetMonExp := abs(tExp * (100 - g_Config.nHeroKillMonExpRate) div 100) + 1;
                    nCheckCode := 61;
                    MasterObject.GainExp(TPlayObject(MasterObject).m_nGetMonExp);
                    nCheckCode := 62;
                  end else begin
                    TPlayObject(MasterObject).m_nGetMonExp := tExp;
                    nCheckCode := 63;
                    MasterObject.GainExp(tExp);
                    nCheckCode := 64;
                  end;
                end;
              end;
            end;
    {执行宝宝杀怪触发}
            nCheckCode := 65;
            if g_FunctionNPC <> nil then begin
              nCheckCode := 66;
              MasterObject := m_ExpHitter.Master;
              nCheckCode := 67;
              if (MasterObject <> nil) and (MasterObject.m_btRaceServer = RC_PLAYOBJECT) then begin
                nCheckCode := 68;
                TPlayObject(MasterObject).m_sString[0] := m_sCharName;
                nCheckCode := 69;
                TPlayObject(MasterObject).m_nInteger[0] := m_WAbil.Level;
                nCheckCode := 70;
                TPlayObject(MasterObject).m_nScriptGotoCount := 0;
                nCheckCode := 71;
                g_FunctionNPC.GotoLable(TPlayObject(MasterObject), '@KillMon', False);
                nCheckCode := 72;
              end;
            end;
          end;
        end;
      end else begin //if m_ExpHitter <> nil then begin
        nCheckCode := 73;
        if m_LastHiter.m_btRaceServer = RC_PLAYOBJECT then begin
          nCheckCode := 74;
          tExp := m_LastHiter.CalcGetExp(m_Abil.Level, m_dwFightExp);
          nCheckCode := 75;
          if not g_Config.boVentureServer then begin
            nCheckCode := 76;
            if TPlayObject(m_LastHiter).m_MyHero <> nil then begin //给英雄分配经验
              nCheckCode := 77;
              BaseObject := TPlayObject(m_LastHiter).m_MyHero;
              nCheckCode := 78;
              BaseObject.GainExp(abs(tExp * g_Config.nHeroKillMonExpRate div 100) + 1);
              nCheckCode := 79;
              TPlayObject(m_LastHiter).m_nGetMonExp := abs(tExp * (100 - g_Config.nHeroKillMonExpRate) div 100) + 1;
              nCheckCode := 80;
              m_LastHiter.GainExp(TPlayObject(m_LastHiter).m_nGetMonExp);
              nCheckCode := 81;
            end else begin
              nCheckCode := 82;
              TPlayObject(m_LastHiter).m_nGetMonExp := tExp;
              nCheckCode := 83;
              m_LastHiter.GainExp(tExp);
              nCheckCode := 84;
            end;
          end;
          if g_FunctionNPC <> nil then begin
            nCheckCode := 85;
            if (m_LastHiter <> nil) then begin
              nCheckCode := 86;
              TPlayObject(m_LastHiter).m_sString[0] := m_sCharName;
              nCheckCode := 87;
              TPlayObject(m_LastHiter).m_nInteger[0] := m_WAbil.Level;
              nCheckCode := 88;
              TPlayObject(m_LastHiter).m_nScriptGotoCount := 0;
              nCheckCode := 89;
              g_FunctionNPC.GotoLable(TPlayObject(m_LastHiter), '@KillMon', False);
              nCheckCode := 90;
            end;
          end;
        end;
      end;
    end;
    nCheckCode := 91;
    if (g_Config.boMonSayMsg) and (m_btRaceServer = RC_PLAYOBJECT) and (m_LastHiter <> nil) then begin
      nCheckCode := 92;
      m_LastHiter.MonsterSayMsg(Self, s_KillHuman);
      nCheckCode := 93;
    end;

{-----------------------------------杀宝宝触发---------------------------------}
    nCheckCode := 94;
    if (m_btRaceServer <> RC_PLAYOBJECT) and (LastHiter <> nil) then begin
      if ExpHitter <> nil then begin
        nCheckCode := 95;
        ExpHitter.m_KillTargetCret := Self;
        nCheckCode := 96;
        if ExpHitter.m_btRaceServer = RC_PLAYOBJECT then begin
          nCheckCode := 97;
    {执行人物杀怪触发}
          if g_FunctionNPC <> nil then begin
            nCheckCode := 98;
            TPlayObject(ExpHitter).m_sString[0] := m_sCharName;
            nCheckCode := 99;
            TPlayObject(ExpHitter).m_nInteger[0] := m_WAbil.Level;
            nCheckCode := 100;
            TPlayObject(ExpHitter).m_nScriptGotoCount := 0;
            nCheckCode := 101;
            g_FunctionNPC.GotoLable(TPlayObject(ExpHitter), '@KillMon', False);
            nCheckCode := 102;
          end;
        end else begin //if ExpHitter.m_btRaceServer = RC_PLAYOBJECT then begin
          nCheckCode := 103;
          if ExpHitter.m_Master <> nil then begin
    {执行宝宝杀怪触发}
            nCheckCode := 104;
            if g_FunctionNPC <> nil then begin
              nCheckCode := 105;
              MasterObject := ExpHitter.Master;
              nCheckCode := 106;
              if (MasterObject <> nil) and (MasterObject.m_btRaceServer = RC_PLAYOBJECT) then begin
                nCheckCode := 107;
                TPlayObject(MasterObject).m_sString[0] := m_sCharName;
                nCheckCode := 108;
                TPlayObject(MasterObject).m_nInteger[0] := m_WAbil.Level;
                nCheckCode := 109;
                TPlayObject(MasterObject).m_nScriptGotoCount := 0;
                nCheckCode := 110;
                g_FunctionNPC.GotoLable(TPlayObject(MasterObject), '@KillMon', False);
                nCheckCode := 111;
              end;
            end;
          end;
        end;
      end else begin //if ExpHitter <> nil then begin
        nCheckCode := 112;
        if LastHiter.m_btRaceServer = RC_PLAYOBJECT then begin
          nCheckCode := 113;
          if g_FunctionNPC <> nil then begin
            nCheckCode := 114;
            if (LastHiter <> nil) then begin
              nCheckCode := 115;
              TPlayObject(LastHiter).m_sString[0] := m_sCharName;
              nCheckCode := 116;
              TPlayObject(LastHiter).m_nInteger[0] := m_WAbil.Level;
              nCheckCode := 117;
              TPlayObject(LastHiter).m_nScriptGotoCount := 0;
              nCheckCode := 118;
              g_FunctionNPC.GotoLable(TPlayObject(LastHiter), '@KillMon', False);
              nCheckCode := 119;
            end;
          end;
        end;
      end;
    end;

    m_Master := nil;
  except
    on E: Exception do begin
      MainOutMessage(sExceptionMsg1 + ' ' + m_sCharName + ' CheckCode ' + IntToStr(nCheckCode));
      MainOutMessage(E.Message);
    end;
  end;

  {执行杀人和死亡触发}
  nCheckCode := 120;
  if (m_LastHiter <> nil) and (m_btRaceServer = RC_PLAYOBJECT) then TPlayObject(Self).DieGotoLable();
  nCheckCode := 121;
  //人物死亡后,英雄退出
  //if (m_btRaceServer = RC_PLAYOBJECT) and (TPlayObject(Self).m_MyHero <> nil) then THeroObject(TPlayObject(Self).m_MyHero).LogOut();
  //if m_PEnvir.m_boFIGHTPK then
    //m_boPKFlag := True;

  try
    boPK := False;
    if (not g_Config.boVentureServer) and (not m_PEnvir.m_boFightZone) and (not m_PEnvir.m_boFight3Zone) then begin
      if (m_btRaceServer = RC_PLAYOBJECT) and (m_LastHiter <> nil) and (PKLevel < 2) then begin
        nCheckCode := 122;
        //            if (m_LastHiter.m_btRaceServer = RC_PLAYOBJECT) then
        if (m_LastHiter.m_btRaceServer = RC_PLAYOBJECT) or (m_LastHiter.m_btRaceServer = RC_NPC) then begin
          {修改日期2004/07/21，允许NPC杀死人物}
          boPK := True;
        end;
        nCheckCode := 123;
        if m_LastHiter.m_Master <> nil then
          if m_LastHiter.Master.m_btRaceServer = RC_PLAYOBJECT then begin
            m_LastHiter := m_LastHiter.Master;
            boPK := True;
          end;
      end;
      nCheckCode := 124;
    end;

   { if m_PEnvir.m_boFIGHTPK then
      boPK := True; }
    nCheckCode := 125;
    boDuel := False;
    if (m_btRaceServer = RC_PLAYOBJECT) then begin
      if m_DuelTargetCret <> nil then begin //决斗失败
        if (m_DuelTargetCret.m_PEnvir = m_PEnvir) and
          (m_DuelTargetCret.m_DuelTargetCret = Self) and
          m_PEnvir.m_boDueling and m_boStartDuel and
          (m_DuelTargetCret.m_boStartDuel) then begin
          nCheckCode := 126;
          boDuel := True;
          g_DuelEngine.Defeat(m_sCharName, m_DuelTargetCret.m_sCharName);
          nCheckCode := 127;
          m_PEnvir.m_boClearDuel := False;
          m_PEnvir.m_boDueling := False;
          m_boStartDuel := False;
          m_PEnvir.m_dwDuelTick := GetTickCount;
          m_DuelTargetCret.m_boStartDuel := False;
          nCheckCode := 128;
          //m_boPKFlag := False; //m_boPKFlag =True 属于正当防卫
          //m_DuelTargetCret.m_boPKFlag := True; //m_boPKFlag = True 属于正当防卫
          if g_FunctionNPC <> nil then begin
            TPlayObject(m_DuelTargetCret).m_nScriptGotoCount := 0;
            TPlayObject(Self).m_nScriptGotoCount := 0;
            nCheckCode := 129;
            g_FunctionNPC.GotoLable(TPlayObject(m_DuelTargetCret), '@Victory', False);
            nCheckCode := 130;
            g_FunctionNPC.GotoLable(TPlayObject(Self), '@Defeat', False);
            nCheckCode := 131;
          end;
          m_DuelTargetCret.m_DuelTargetCret := nil;
          m_DuelTargetCret := nil;
        end;
      end;
    end;
    nCheckCode := 132;

   { if boPK or boDuel or m_PEnvir.m_boFIGHTPK then
      m_boPKFlag := True; }

    {if (m_LastHiter <> nil) and (boPK or boDuel) then begin
      m_LastHiter.m_boPKFlag := True; //m_boPKFlag = True 属于正当防卫
      m_boPKFlag := True;
    end; }
   { if (m_LastHiter <> nil) and boPK and (m_LastHiter.m_PEnvir.m_boFIGHTPK or boDuel) then begin //2007-01-30增加 PK不红名
      m_LastHiter.m_boPKFlag := True; //m_boPKFlag = True 属于正当防卫
      m_boPKFlag := True;
    end; }

    if boPK and (m_LastHiter <> nil) then begin
      guildwarkill := False;
      nCheckCode := 133;
      if (m_MyGuild <> nil) and (m_LastHiter.m_MyGuild <> nil) then begin
        if GetGuildRelation(Self, m_LastHiter) = 2 then
          guildwarkill := True;
      end;
      nCheckCode := 134;
      Castle := g_CastleManager.InCastleWarArea(Self);
      if ((Castle <> nil) and Castle.m_boUnderWar) or (m_boInFreePKArea) then
        guildwarkill := True;
      nCheckCode := 135;
      //=================================================================
      //
      if (not guildwarkill) { and (not boDuel)} then begin
        nCheckCode := 136;
        if (g_Config.boKillHumanWinLevel or g_Config.boKillHumanWinExp or m_PEnvir.m_boPKWINLEVEL or m_PEnvir.m_boFIGHTPK or m_PEnvir.m_boPKWINEXP) and (m_LastHiter.m_btRaceServer = RC_PLAYOBJECT) then begin
          nCheckCode := 137;
          TPlayObject(Self).PKDie(TPlayObject(m_LastHiter));
          nCheckCode := 138;
        end else begin
          if not m_LastHiter.IsGoodKilling(Self) then begin
            nCheckCode := 139;
            if not m_LastHiter.m_boIsUnknowActor then begin
              m_LastHiter.IncPkPoint(g_Config.nKillHumanAddPKPoint {100});
              m_LastHiter.SysMsg(g_sYouMurderedMsg {'你犯了谋杀罪！！！'}, c_Red, t_Hint);
            end;
            nCheckCode := 140;
            if not m_LastHiter.m_boIsUnknowActor then begin
              SysMsg(Format(g_sYouKilledByMsg, [m_LastHiter.m_sCharName]), c_Red, t_Hint);
            end else begin
              SysMsg(Format(g_sYouKilledByMsg, [g_Config.sUnKnowName]), c_Red, t_Hint);
            end;
            nCheckCode := 141;
            m_LastHiter.AddBodyLuck(-g_Config.nKillHumanDecLuckPoint {500});
            if PKLevel < 1 then
              if Random(5) = 0 then
                m_LastHiter.MakeWeaponUnlock;
          end else
            //if not m_PEnvir.m_boFIGHTPK then
            m_LastHiter.SysMsg(g_sYouProtectedByLawOfDefense {'[你受到正当规则保护。]'}, c_Green, t_Hint);
        end;
        nCheckCode := 142;
        //检查攻击人是否用了着经验或等级装备
        if m_LastHiter.m_btRaceServer = RC_PLAYOBJECT then begin
          nCheckCode := 143;
          if m_LastHiter.m_dwPKDieLostExp > 0 then begin
            if m_Abil.Exp >= m_LastHiter.m_dwPKDieLostExp then begin
              Dec(m_Abil.Exp, m_LastHiter.m_dwPKDieLostExp);
            end else m_Abil.Exp := 0;
          end;
          nCheckCode := 144;
          if m_LastHiter.m_nPKDieLostLevel > 0 then begin
            if m_Abil.Level >= m_LastHiter.m_nPKDieLostLevel then begin
              Dec(m_Abil.Level, m_LastHiter.m_nPKDieLostLevel);
            end else m_Abil.Level := 0;
          end;
          nCheckCode := 145;
        end;
      end;
      //=================================================================
    end;
  except
    MainOutMessage(sExceptionMsg2 + ' ' + m_sCharName + ' CheckCode ' + IntToStr(nCheckCode));
  end;

  try
    if (not m_PEnvir.m_boFightZone) and
      (not m_PEnvir.m_boFight3Zone) and
      (not m_boAnimal) then begin
      AttackBaseObject := m_ExpHitter;
      if (m_ExpHitter <> nil) and (m_ExpHitter.m_Master <> nil) then begin
        AttackBaseObject := m_ExpHitter.Master; //m_ExpHitter.m_Master;
      end;
      nCheckCode := 1000;
      if m_btRaceServer = RC_PLAYMOSTER then begin //人型怪死亡
        if (not m_boNoItem) and (m_nCopyHumanLevel = 0) and (m_Master = nil) then begin {修改日期2004/07/21，增加此行，允许设置 m_boNoItem 后人物死亡不掉物品}
          nCheckCode := 1004;
          DropUseItems(AttackBaseObject);
          nCheckCode := 1005;
        end;
      end else
        if m_btRaceServer <> RC_PLAYOBJECT then begin
        nCheckCode := 1006;
        {if AttackBaseObject <> nil then
          DropUseItems(AttackBaseObject); }
        nCheckCode := 1007;
        if (m_Master = nil) and (m_nCopyHumanLevel = 0) and (not m_boNoItem) and (AttackBaseObject <> nil) then
          ScatterBagItems(AttackBaseObject);
        nCheckCode := 1008;
        if (m_btRaceServer >= RC_ANIMAL) and (m_nCopyHumanLevel = 0) and (m_nCopyHumanLevel = 0) and (m_Master = nil) and (not m_boNoItem) then begin
          if (AttackBaseObject <> nil) and (AttackBaseObject.m_btRaceServer = RC_PLAYOBJECT) and (g_Config.boDropGoldToPlayBag) then begin
            if TPlayObject(AttackBaseObject).IncGold(m_nGold) then begin
              nCheckCode := 1009;
              TPlayObject(AttackBaseObject).GoldChanged();
              nCheckCode := 1010;
            end else begin
              ScatterGolds(AttackBaseObject);
              nCheckCode := 1011;
            end;
          end else begin
            ScatterGolds(AttackBaseObject);
            nCheckCode := 1012;
          end;
        end;
      end else begin
        if not m_boNoItem then begin {修改日期2004/07/21，增加此行，允许设置 m_boNoItem 后人物死亡不掉物品}
          if AttackBaseObject <> nil then begin
            if (g_Config.boKillByHumanDropUseItem and (AttackBaseObject.m_btRaceServer = RC_PLAYOBJECT)) or (g_Config.boKillByMonstDropUseItem and (AttackBaseObject.m_btRaceServer <> RC_PLAYOBJECT)) then begin
              nCheckCode := 1013;
              DropUseItems(nil);
              nCheckCode := 1014;
            end;
          end else begin
            nCheckCode := 1015;
            DropUseItems(nil);
            nCheckCode := 1016;
          end;
          nCheckCode := 1017;
          if g_Config.boDieScatterBag then ScatterBagItems(nil);
          if g_Config.boDieDropGold then ScatterGolds(nil);
          nCheckCode := 1018;
        end;
        AddBodyLuck(-(50 - (50 - m_Abil.Level * 5)));
        nCheckCode := 1019;
      end;
    end;
    if m_PEnvir.m_boFight3Zone then begin
      Inc(m_nFightZoneDieCount);
      if m_MyGuild <> nil then begin
        nCheckCode := 1020;
        TGUild(m_MyGuild).TeamFightWhoDead(m_sCharName);
        nCheckCode := 1021;
      end;
      if (m_LastHiter <> nil) then begin
        if (m_LastHiter.m_MyGuild <> nil) and (m_MyGuild <> nil) then begin
          TGUild(m_LastHiter.m_MyGuild).TeamFightWhoWinPoint(m_LastHiter.m_sCharName, 100);
          tStr := TGUild(m_LastHiter.m_MyGuild).sGuildName + ':' +
            IntToStr(TGUild(m_LastHiter.m_MyGuild).nContestPoint) + '  ' +
            TGUild(m_MyGuild).sGuildName + ':' +
            IntToStr(TGUild(m_MyGuild).nContestPoint);
          UserEngine.CryCry(RM_CRY, m_PEnvir, m_nCurrX, m_nCurrY, 1000, g_Config.btCryMsgFColor, g_Config.btCryMsgBColor, '- ' + tStr);
        end;
      end;
    end;

    if m_btRaceServer = RC_PLAYOBJECT then begin
      m_btLastOutStatus := 1; //2006-01-12 增加人物最后退出状态 1为死亡退出
      //Jacky 2004/09/05
      //人物死亡立即退组，以防止组队刷经验
      if TPlayObject(Self).m_GroupOwner <> nil then TPlayObject(Self).m_GroupOwner.DelMember(TPlayObject(Self));

      if m_LastHiter <> nil then begin
        if m_LastHiter.m_btRaceServer = RC_PLAYOBJECT then tStr := m_LastHiter.m_sCharName
        else tStr := '#' + m_LastHiter.m_sCharName;
      end else tStr := '####';
      AddGameDataLog('19' + #9 +
        m_sMapName + #9 +
        IntToStr(m_nCurrX) + #9 +
        IntToStr(m_nCurrY) + #9 +
        m_sCharName + #9 +
        'FZ-' + BoolToIntStr(m_PEnvir.m_boFightZone) +
        '_F3-' + BoolToIntStr(m_PEnvir.m_boFight3Zone) + #9 +
        '0' + #9 +
        '1' + #9 +
        tStr);
    end;
    //减少地图上怪物计数
    SendRefMsg(RM_DEATH, m_btDirection, m_nCurrX, m_nCurrY, 1, '');
  except
    MainOutMessage(sExceptionMsg3 + ' ' + m_sCharName + ' CheckCode ' + IntToStr(nCheckCode));
  end;
end;

procedure TPlayObject.PKDie(PlayObject: TPlayObject);
var
  nWinLevel, nLostLevel, nWinExp, nLostExp: Integer;
  boWinLEvel, boLostLevel, boWinExp, boLostExp: Boolean;
begin
  nWinLevel := g_Config.nKillHumanWinLevel;
  nLostLevel := g_Config.nKilledLostLevel;
  nWinExp := g_Config.nKillHumanWinExp;
  nLostExp := g_Config.nKillHumanLostExp;

  boWinLEvel := g_Config.boKillHumanWinLevel;
  boLostLevel := g_Config.boKilledLostLevel;
  boWinExp := g_Config.boKillHumanWinExp;
  boLostExp := g_Config.boKilledLostExp;

  if m_PEnvir.m_boPKWINLEVEL then begin
    boWinLEvel := True;
    nWinLevel := m_PEnvir.m_nPKWINLEVEL;
  end;
  if m_PEnvir.m_boPKLOSTLEVEL then begin
    boLostLevel := True;
    nLostLevel := m_PEnvir.m_nPKLOSTLEVEL;
  end;
  if m_PEnvir.m_boPKWINEXP then begin
    boWinExp := True;
    nWinExp := m_PEnvir.m_nPKWINEXP;
  end;
  if m_PEnvir.m_boPKLOSTEXP then begin
    boLostExp := True;
    nLostExp := m_PEnvir.m_nPKLOSTEXP;
  end;

  if PlayObject.m_Abil.Level - m_Abil.Level > g_Config.nHumanLevelDiffer then begin
    if not PlayObject.IsGoodKilling(Self) then begin
      if not PlayObject.m_boIsUnknowActor then begin
        PlayObject.IncPkPoint(g_Config.nKillHumanAddPKPoint {100});
        PlayObject.SysMsg(g_sYouMurderedMsg {'你犯了谋杀罪！！！'}, c_Red, t_Hint);
      end;
      if not m_LastHiter.m_boIsUnknowActor then begin
        SysMsg(Format(g_sYouKilledByMsg, [m_LastHiter.m_sCharName]), c_Red, t_Hint);
      end else begin
        SysMsg(Format(g_sYouKilledByMsg, [g_Config.sUnKnowName]), c_Red, t_Hint);
      end;
      PlayObject.AddBodyLuck(-g_Config.nKillHumanDecLuckPoint {500});
      if PKLevel < 1 then
        if Random(5) = 0 then
          PlayObject.MakeWeaponUnlock;
    end else begin
      //if not m_PEnvir.m_boFIGHTPK then
      PlayObject.SysMsg(g_sYouProtectedByLawOfDefense {'[你受到正当规则保护。]'}, c_Green, t_Hint);
    end;
    Exit;
  end;
  if boWinLEvel then begin
    //Inc(PlayObject.m_Abil.Level,nWinLevel);

    if PlayObject.m_Abil.Level + nWinLevel <= g_Config.nMaxLevel then begin
      Inc(PlayObject.m_Abil.Level, nWinLevel);
    end else begin
      PlayObject.m_Abil.Level := g_Config.nMaxLevel;
    end;

    PlayObject.HasLevelUp(PlayObject.m_Abil.Level - nWinLevel);

    if boLostLevel then begin
      if PKLevel >= 2 then begin
        if m_Abil.Level >= nLostLevel * 2 then
          Dec(m_Abil.Level, nLostLevel * 2);
      end else begin
        if m_Abil.Level >= nLostLevel then
          Dec(m_Abil.Level, nLostLevel);
      end;
      {m_Abil.MaxExp := GetLevelExp(m_Abil.Level);
    if m_Abil.Exp > m_Abil.MaxExp then
      m_Abil.Exp := m_Abil.MaxExp;  }
    end;
  end;

  if boWinExp then begin
    PlayObject.WinExp(nWinExp);
    if boLostExp then begin
      if m_Abil.Exp >= LongWord(nLostExp) then begin
        if m_Abil.Exp >= LongWord(nLostExp) then begin
          Dec(m_Abil.Exp, LongWord(nLostExp));
        end else begin
          m_Abil.Exp := 0;
        end;
      end else begin
        if m_Abil.Level >= 1 then begin
          Dec(m_Abil.Level);
          Inc(m_Abil.Exp, GetLevelExp(m_Abil.Level));
          if m_Abil.Exp >= LongWord(nLostExp) then begin
            Dec(m_Abil.Exp, LongWord(nLostExp));
          end else begin
            m_Abil.Exp := 0;
          end;
        end else begin
          m_Abil.Level := 0;
          m_Abil.Exp := 0;
        end;
        //HasLevelUp(m_Abil.Level + 1);
      end;
    end;
  end;
end;

procedure TActorObject.ReAlive;
begin
  m_boDeath := False;
  SendRefMsg(RM_ALIVE, m_btDirection, m_nCurrX, m_nCurrY, 0, '');
end;

procedure TActorObject.SetLastHiter(BaseObject: TActorObject);
begin
  m_LastHiter := BaseObject;
  m_LastHiterTick := GetTickCount();
  if m_ExpHitter = nil then begin
    m_ExpHitter := BaseObject;
    m_ExpHitterTick := GetTickCount();
  end else begin
    if m_ExpHitter = BaseObject then
      m_ExpHitterTick := GetTickCount();
  end;
end;

procedure TActorObject.SetPKFlag(BaseObject: TActorObject);
begin
  if (PKLevel < 2) and
    (BaseObject.PKLevel < 2) and
    (not m_PEnvir.m_boFightZone) and
    (not m_PEnvir.m_boFight3Zone) and
    (not m_PEnvir.m_boFIGHTPK) and //允许PK地图
    (not m_PEnvir.m_boDuel) and //挑战地图
    (not m_boPKFlag) then begin

    BaseObject.m_dwPKTick := GetTickCount();
    if not BaseObject.m_boPKFlag then begin
      BaseObject.m_boPKFlag := True;
      BaseObject.RefNameColor();
    end;
  end;
end;

function TActorObject.IsGoodKilling(Cert: TActorObject): Boolean;
begin
  Result := Cert.m_boPKFlag or Cert.m_PEnvir.m_boFIGHTPK or Cert.m_PEnvir.m_boDuel;
end;

function TActorObject.GetMaster(): TActorObject;
var
  MasterObject: TActorObject;
begin
  if m_btRaceServer <> RC_PLAYOBJECT then begin
    MasterObject := m_Master;
    if MasterObject <> nil then begin
      while True do begin
        if MasterObject.m_Master <> nil then begin
          MasterObject := MasterObject.m_Master;
        end else Break;
      end;
    end;
    Result := MasterObject;
  end else Result := nil;
end;

function TActorObject.IsProtectTarget(BaseObject: TActorObject): Boolean;
begin
  Result := True;
  if BaseObject = nil then Exit;

  if (m_PEnvir.m_boDuel) and (m_PEnvir.m_boDueling) then begin
    if m_boStartDuel and BaseObject.m_boStartDuel and (m_DuelTargetCret = BaseObject) and
      (BaseObject.m_DuelTargetCret = Self) then Exit;
  end;

  if (InSafeZone) or (BaseObject.InSafeZone) then Result := False;
  if not BaseObject.m_boInFreePKArea then begin
    //新人保护
    if g_Config.boPKLevelProtect then begin
      if (m_Abil.Level > g_Config.nPKProtectLevel) then begin //如果大于指定等级
        if not BaseObject.m_boPKFlag and (BaseObject.m_Abil.Level <= g_Config.nPKProtectLevel) and (BaseObject.PKLevel < 2) then begin
          //被攻击的人物小指定等级没有红名，则不可以攻击。
          Result := False;
          Exit;
        end;
      end;
      if (m_Abil.Level <= g_Config.nPKProtectLevel) then begin //如果小于指定等级
        if not BaseObject.m_boPKFlag and (BaseObject.m_Abil.Level > g_Config.nPKProtectLevel) and (BaseObject.PKLevel < 2) then begin
          Result := False;
          Exit;
        end;
      end;
    end;

    {
    //大于指定级别的红名人物不可以杀指定级别未红名的人物。
    if (PKLevel >= 2) and (m_Abil.Level > 10) then begin
      if (BaseObject.m_Abil.Level <= 10) and (BaseObject.PKLevel < 2) then begin
        Result:=False;
        exit;
      end;
    end;

    //小于指定级别的非红名人物不可以杀指定级别红名人物。
    if (m_Abil.Level <= 10) and (PKLevel < 2) then begin
      if (BaseObject.PKLevel >= 2) and (BaseObject.m_Abil.Level > 10) then begin
        Result:=False;
        exit;
      end;
    end;
    }
    //大于指定级别的红名人物不可以杀指定级别未红名的人物。
    if (PKLevel >= 2) and (m_Abil.Level > g_Config.nRedPKProtectLevel) then begin
      if (BaseObject.m_Abil.Level <= g_Config.nRedPKProtectLevel) and (BaseObject.PKLevel < 2) then begin
        Result := False;
        Exit;
      end;
    end;

    //小于指定级别的非红名人物不可以杀指定级别红名人物。
    if (m_Abil.Level <= g_Config.nRedPKProtectLevel) and (PKLevel < 2) then begin
      if (BaseObject.PKLevel >= 2) and (BaseObject.m_Abil.Level > g_Config.nRedPKProtectLevel) then begin
        Result := False;
        Exit;
      end;
    end;

    if (GetTickCount - m_dwMapMoveTick < 3000) or (GetTickCount - BaseObject.m_dwMapMoveTick < 3000) then
      Result := False;
  end;

  if g_Config.boOpenStoreGMMode and BaseObject.m_boStore then Result := False;
end;


function TActorObject.IsAttackTarget(BaseObject: TActorObject): Boolean;
  function sub_4C88E4(): Boolean;
  begin
    Result := True;
  end;
var
  I: Integer;
  MasterSelfObject: TActorObject;
  MasterBaseObject: TActorObject;
  MasterTargetCret: TActorObject;
  Castle: TUserCastle;
  nCode: Integer;
begin
  try
    nCode := 0;
    Result := False;
    if (BaseObject = nil) or (BaseObject = Self) or (m_PEnvir = nil) then Exit;
    nCode := 1;
    if BaseObject.m_boFixedHideMode then Exit;
    nCode := 2;
    if m_btRaceServer >= RC_ANIMAL {50} then begin
      nCode := 3;
      MasterSelfObject := Master;
      nCode := 4;
      MasterBaseObject := BaseObject.Master;
      nCode := 5;
      if BaseObject.m_TargetCret <> nil then begin
        nCode := 6;
        MasterTargetCret := BaseObject.m_TargetCret.Master;
        nCode := 7;
      end else begin
        MasterTargetCret := nil;
      end;
      nCode := 8;
      if (m_Master <> nil) and (MasterSelfObject <> nil) and (MasterSelfObject.m_btRaceServer = RC_PLAYOBJECT) then begin
        if (MasterSelfObject.m_LastHiter = BaseObject) or
          (MasterSelfObject.m_ExpHitter = BaseObject) or
          (MasterSelfObject.m_TargetCret = BaseObject) then Result := True;
        nCode := 9;
        if BaseObject.m_TargetCret <> nil then begin
          if (BaseObject.m_TargetCret = MasterSelfObject) or
            (MasterTargetCret = MasterSelfObject) and
            (BaseObject.m_btRaceServer <> RC_PLAYOBJECT) then
            Result := True;

          if (BaseObject.m_TargetCret = Self) or
            (BaseObject.m_TargetCret = MasterSelfObject) or
            (MasterTargetCret = MasterSelfObject)
            then
            Result := True;
        end;
        nCode := 10;

        if (BaseObject.m_TargetCret = Self) and (BaseObject.m_btRaceServer >= RC_ANIMAL) then
          Result := True;
        nCode := 11;
        if (BaseObject.m_btRaceServer <> RC_GUARD) and (BaseObject.m_btRaceServer <> RC_ARCHERGUARD) and (BaseObject.m_btRaceServer <> 51) and (BaseObject.m_btRaceServer <> 52) then begin
          if (BaseObject.m_btRaceServer >= RC_ANIMAL) then Result := True;
        end;


        nCode := 12;
        if (MasterBaseObject <> nil) and (MasterBaseObject <> Self) then begin
          if (MasterBaseObject = MasterSelfObject.m_LastHiter) or (MasterBaseObject = MasterSelfObject.m_TargetCret) then Result := True;
          if MasterBaseObject.m_btRaceServer >= RC_ANIMAL then Result := True;
        end;
        nCode := 13;

        if (BaseObject.m_btRaceServer >= RC_ANIMAL) and (MasterBaseObject <> nil) then begin
          if MasterBaseObject.m_btRaceServer = RC_PLAYOBJECT then begin
            if (MasterSelfObject.m_LastHiter = MasterBaseObject) or
              (MasterSelfObject.m_ExpHitter = MasterBaseObject) or
              (MasterSelfObject.m_TargetCret = MasterBaseObject) then Result := True else Result := False;
            nCode := 14;
            if (MasterBaseObject <> nil) and (MasterBaseObject.m_TargetCret <> nil) then begin
              if (MasterBaseObject.m_TargetCret = MasterSelfObject) or
                (MasterTargetCret = MasterSelfObject) then Result := True else Result := False;
            end;
            nCode := 15;
          end else Result := True;
        end;
        nCode := 16;

        if (MasterSelfObject <> nil) and (MasterSelfObject.m_btRaceServer = RC_PLAYOBJECT) and (MasterBaseObject <> nil) and (MasterBaseObject.m_btRaceServer = RC_PLAYOBJECT) then begin
          if MasterSelfObject = MasterBaseObject then Result := False else
          begin

          //Result := MasterSelfObject.IsAttackTarget(MasterBaseObject);
          end;
        end;
        nCode := 17;

        if m_btRaceServer = RC_HEROOBJECT then begin
          if THeroObject(Self).m_boTarget and (m_TargetCret = BaseObject) then Result := True;
        end;
        nCode := 18;
        if Result and (MasterSelfObject <> nil) and (BaseObject.m_btRaceServer in [110, 111]) then begin //2007-09-22增加宝宝不打沙城墙
          if (MasterSelfObject.m_MyGuild = nil) and (MasterSelfObject.m_TargetCret <> BaseObject) then Result := False; //主人没有行会
          if (MasterSelfObject.m_MyGuild <> nil) and Result then begin
            Castle := g_CastleManager.IsCastleMember(MasterSelfObject);
            if Castle <> nil then Result := False; //主人是沙行会
            if (MasterSelfObject.m_MyGuild <> nil) and Result then begin
              Castle := g_CastleManager.InCastleWarArea(m_PEnvir, m_nCurrX, m_nCurrY);
              if (Castle <> nil) and Castle.m_boStartWar and Castle.IsAttackGuild(TGUild(MasterSelfObject.m_MyGuild)) then //主人是攻城方 正在攻城
                Result := True else if MasterSelfObject.m_TargetCret <> BaseObject then Result := False;
            end;
          end;
        end;
        nCode := 19;
      {
      if (BaseObject.m_btRaceServer = RC_PLAYOBJECT) and Result then begin
        Result := MasterSelfObject.IsProperTarget(BaseObject);
      end;

      if (MasterBaseObject <> nil) and (MasterBaseObject.m_btRaceServer = RC_PLAYOBJECT) and Result then begin
        Result := MasterSelfObject.IsProperTarget(MasterBaseObject);
      end;
      }

        if (MasterBaseObject <> nil) and (MasterBaseObject = MasterSelfObject) then Result := False;
        nCode := 20;
        if BaseObject.m_boHolySeize then Result := False;

        nCode := 21;

        if m_btRaceServer = RC_HEROOBJECT then begin
        //if (MasterBaseObject <> nil) then Result := m_Master.IsAttackTarget(MasterBaseObject);

          if THeroObject(Self).m_boTarget and (m_TargetCret = BaseObject) then Result := True;
        end;
        nCode := 22;
        if BaseObject.m_btRaceServer = RC_HEROOBJECT then begin
          if THeroObject(BaseObject).m_boTarget and (BaseObject.m_TargetCret = Self) then Result := True;
        end;

        nCode := 23;

        if m_PEnvir.m_boDueling then begin //非比赛人员禁止攻击
          if (MasterSelfObject <> nil) and (MasterSelfObject.m_btRaceServer = RC_PLAYOBJECT) and MasterSelfObject.m_boStartDuel then begin
            if (MasterBaseObject <> nil) and
              ((MasterBaseObject.m_btRaceServer <> RC_PLAYOBJECT) or
              (MasterBaseObject.m_btRaceServer = RC_PLAYOBJECT) and MasterBaseObject.m_boStartDuel) then Result := True;
            if (MasterBaseObject <> nil) and (MasterBaseObject.m_btRaceServer = RC_PLAYOBJECT) and
              (not MasterBaseObject.m_boStartDuel) then Result := False;
          //if (MasterBaseObject = nil) then Result := True;
          end;
          if (BaseObject.m_btRaceServer = RC_PLAYOBJECT) and (not BaseObject.m_boStartDuel) then Result := False;
          if (MasterBaseObject <> nil) and (MasterBaseObject.m_btRaceServer = RC_PLAYOBJECT) and (not MasterBaseObject.m_boStartDuel) then Result := False;
        end;
        nCode := 24;
        if m_btRaceServer = RC_HEROOBJECT then begin
          if THeroObject(Self).m_btStatus = 2 then Result := False;
        end else begin
          if m_Master.m_boSlaveRelax then Result := False;
        end;
        nCode := 25;
        if BaseObject.m_btRaceServer = RC_PLAYOBJECT then begin {检测目标是人物}
          nCode := 26;
          if BaseObject = MasterSelfObject then Result := False;
          nCode := 27;
        //if (m_Master.InSafeZone) or (BaseObject.InSafeZone) then begin
          if BaseObject.InSafeZone then Result := False;
          nCode := 28;
          if g_Config.boOpenStoreGMMode and BaseObject.m_boStore then begin
            Result := False; //摆摊人物无敌
            nCode := 29;
          //MainOutMessage('g_Config.boOpenStoreGMMode and BaseObject.m_boStor');
          end;
          nCode := 30;
        end;
        nCode := 31;
        if (MasterBaseObject <> nil) and (MasterBaseObject.m_btRaceServer = RC_PLAYOBJECT) then begin
          nCode := 32;
          if BaseObject.InSafeZone then Result := False;
          nCode := 33;
        end;
        nCode := 34;
        BreakCrazyMode();
        nCode := 35;
      end else begin
        if MasterBaseObject <> nil then begin
          if MasterBaseObject.m_btRaceServer = RC_PLAYOBJECT then Result := True;
        end else begin
        //if BaseObject.m_btRaceServer = RC_PLAYOBJECT then Result := True;
          if BaseObject.m_btRaceServer = RC_PLAYOBJECT then begin
            if g_Config.boOpenStoreGMMode and BaseObject.m_boStore then begin
              Result := False; //摆摊人物无敌
          //MainOutMessage('g_Config.boOpenStoreGMMode and BaseObject.m_boStor');
            end else Result := True;
          end;
        end;
        if (m_btRaceServer > RC_PEACENPC {15}) and (m_btRaceServer < RC_ANIMAL {50}) then
          Result := True;
      end;
      if m_boCrazyMode then Result := True;
      nCode := 36;
    end else begin
      nCode := 37;
      if m_btRaceServer = RC_PLAYOBJECT then begin
        nCode := 38;
        if (m_PEnvir.m_boDuel) and (m_PEnvir.m_boDueling) then begin
          if BaseObject.m_btRaceServer = RC_PLAYOBJECT then begin
            if m_boStartDuel and BaseObject.m_boStartDuel and (m_DuelTargetCret = BaseObject) and
              (BaseObject.m_DuelTargetCret = Self) then Result := True else Result := False;
          end else begin
            if (BaseObject.m_btRaceServer < RC_NPC {10}) or (BaseObject.m_btRaceServer > RC_PEACENPC {15}) then
              Result := True;
            if BaseObject.m_btRaceServer >= RC_ANIMAL then
              Result := True;
          end;
          nCode := 39;
        end else begin
          case m_btAttatckMode of
            HAM_ALL {0}: begin
                nCode := 40;
                if (BaseObject.m_btRaceServer < RC_NPC {10}) or (BaseObject.m_btRaceServer > RC_PEACENPC {15}) then
                  Result := True;
                if g_Config.boNonPKServer then
                  Result := sub_4C88E4();
              end;
            HAM_PEACE {1}: begin
                nCode := 41;
                if BaseObject.m_btRaceServer >= RC_ANIMAL then
                  Result := True;
              end;
            HAM_DEAR: begin
                nCode := 42;
                if BaseObject <> TPlayObject(Self).m_DearHuman then begin
                  Result := True;
                end;
              end;
            HAM_MASTER: begin
                nCode := 43;
                if BaseObject.m_btRaceServer = RC_PLAYOBJECT then begin
                  Result := True;
                  if TPlayObject(Self).m_boMaster then begin
                    for I := 0 to TPlayObject(Self).m_MasterList.Count - 1 do begin
                      if TPlayObject(Self).m_MasterList.Items[I] = BaseObject then begin
                        Result := False;
                        Break;
                      end;
                    end;
                  end;
                  if TPlayObject(BaseObject).m_boMaster then begin
                    for I := 0 to TPlayObject(BaseObject).m_MasterList.Count - 1 do begin
                      if TPlayObject(BaseObject).m_MasterList.Items[I] = Self then begin
                        Result := False;
                        Break;
                      end;
                    end;
                  end;
                end else Result := True;
              end;
            HAM_GROUP {2}: begin
                nCode := 44;
                if (BaseObject.m_btRaceServer < RC_NPC) or (BaseObject.m_btRaceServer > RC_PEACENPC) then
                  Result := True;
                if BaseObject.m_btRaceServer = RC_PLAYOBJECT then
                  if TPlayObject(Self).IsGroupMember(BaseObject) then
                    Result := False;
                if g_Config.boNonPKServer then
                  Result := sub_4C88E4();
              end;
            HAM_GUILD {3}: begin
                nCode := 45;
                if (BaseObject.m_btRaceServer < RC_NPC) or (BaseObject.m_btRaceServer > RC_PEACENPC) then
                  Result := True;
                if BaseObject.m_btRaceServer = RC_PLAYOBJECT then
                  if m_MyGuild <> nil then begin
                    if TGUild(m_MyGuild).IsMember(BaseObject.m_sCharName) then
                      Result := False;
                    if m_boGuildWarArea and (BaseObject.m_MyGuild <> nil) then begin
                      if TGUild(m_MyGuild).IsAllyGuild(TGUild(BaseObject.m_MyGuild)) then
                        Result := False;
                    end;
                  end;
                if g_Config.boNonPKServer then
                  Result := sub_4C88E4();
              end;
            HAM_PKATTACK {4}: begin
                nCode := 46;
                if (BaseObject.m_btRaceServer < RC_NPC) or (BaseObject.m_btRaceServer > RC_PEACENPC) then
                  Result := True;
                if BaseObject.m_btRaceServer = RC_PLAYOBJECT then
                  if PKLevel >= 2 then begin
                    if BaseObject.PKLevel < 2 then
                      Result := True
                    else Result := False;
                  end else begin
                    if BaseObject.PKLevel >= 2 then
                      Result := True
                    else Result := False;
                  end;
                if g_Config.boNonPKServer then
                  Result := sub_4C88E4();
              end;
          end;
        end;
      end else Result := True;
    end;
    if BaseObject.m_boAdminMode or BaseObject.m_boStoneMode then
      Result := False;
  except
    if nCode = 6 then
      BaseObject.m_TargetCret := nil;
    MainOutMessage('TActorObject::IsAttackTarget: ' + m_sCharName + ' Code ' + IntToStr(nCode));
  end;
end;

function TActorObject.IsProperTarget(BaseObject: TActorObject): Boolean;
begin
  Result := IsAttackTarget(BaseObject);
  if Result then begin
    if (m_btRaceServer = RC_PLAYOBJECT) and (BaseObject.m_btRaceServer = RC_PLAYOBJECT) then begin
      Result := IsProtectTarget(BaseObject); {检测人物是否符合攻击要求}
    end;
  end;
  if (BaseObject <> nil) and
    (m_btRaceServer = RC_PLAYOBJECT) and
    (BaseObject.m_Master <> nil) and
    (BaseObject.m_btRaceServer <> RC_PLAYOBJECT) then begin
    if BaseObject.Master = Self then begin
      if (m_btAttatckMode <> HAM_ALL) or (BaseObject = TPlayObject(Self).m_MyHero) then Result := False; {检测是否是自己的宝宝}
    end else begin
      if not BaseObject.m_boFixedHideMode then
        Result := IsAttackTarget(BaseObject.Master);
      if InSafeZone or BaseObject.InSafeZone then Result := False; {检测是否是在安全区}
      //if (m_btRaceServer = RC_HEROOBJECT) and (InSafeZone or BaseObject.InSafeZone)and (BaseObject.m_Master <> nil) then Result := False;
    end;
  end;
  if Result and (BaseObject <> nil) then Result := InLockRect(BaseObject);
end;

//擒龙手目标检测

function TActorObject.IsProperTargetSKILL_55(nLevel: Integer; BaseObject: TActorObject): Boolean;
begin
  Result := False;
  if (BaseObject = nil) or (BaseObject = Self) then Exit;
  if (BaseObject.m_btRaceServer < RC_NPC) or (BaseObject.m_btRaceServer > RC_PEACENPC) then begin
    if (BaseObject.m_btRaceServer <> RC_GUARD) and
      (BaseObject.m_btRaceServer <> RC_ARCHERGUARD) and
      (BaseObject.m_btRaceServer <> 110) and
      (BaseObject.m_btRaceServer <> 111) and
      (nLevel >= BaseObject.m_Abil.Level) and
      (not BaseObject.m_boGhost) and
      (not BaseObject.m_boDeath) then begin

      if BaseObject.m_boSpaceLock and BaseObject.InRect(BaseObject.m_nCurrX, BaseObject.m_nCurrY, BaseObject.m_SpaceRect) then Exit;
      if m_boSpaceLock and BaseObject.m_boSpaceLock and (m_SpaceOwner <> BaseObject.m_SpaceOwner) then Exit;

      if ((not g_Config.boPullPlayObject) or (nLevel < BaseObject.m_Abil.Level)) and (BaseObject.m_btRaceServer = RC_PLAYOBJECT) then Exit;
      if g_Config.boPullCrossInSafeZone and (BaseObject.m_btRaceServer = RC_PLAYOBJECT) and BaseObject.InSafeZone then Exit;
      if g_Config.boPullCrossInSafeZone and (BaseObject.m_Master <> nil) and BaseObject.InSafeZone then Exit; //不允许抓安全区的宝宝
      if g_Config.boPullCrossInSafeZone and InSafeZone then Exit;
      Result := True;
    end;
  end;
end;

//骷髅咒

function TActorObject.IsProperTargetSKILL_54(BaseObject: TActorObject): Boolean;
begin
  Result := False;
  if (BaseObject = nil) or (BaseObject = Self) then Exit;
  if BaseObject.m_boDeath and (BaseObject.m_btRaceServer = RC_PLAYOBJECT) then begin
    if (BaseObject.m_btRaceServer < RC_NPC) or (BaseObject.m_btRaceServer > RC_PEACENPC) then begin
      if (BaseObject.m_btRaceServer <> RC_GUARD) and
        (BaseObject.m_btRaceServer <> RC_ARCHERGUARD) and
        (BaseObject.m_btRaceServer <> 110) and
        (BaseObject.m_btRaceServer <> 111) then Result := True;
    end;
  end;
end;

//复活术

function TActorObject.IsProperTargetSKILL_57(BaseObject: TActorObject): Boolean;
begin
  Result := False;
  if (BaseObject = nil) or (BaseObject = Self) then Exit;
  if BaseObject.m_boDeath and (BaseObject.m_btRaceServer = RC_PLAYOBJECT) then begin
    if TPlayObject(BaseObject).m_boAllowReAlive then Result := True
    else SysMsg('Target does not allow Resurrection.', c_Green, t_Hint);
  end;
end;

function TPlayObject.AttackJump: Boolean;
begin
  Result := False;
  if (m_btRaceServer = RC_PLAYOBJECT) and (g_FunctionNPC <> nil) then begin
    g_FunctionNPC.GotoLable(TPlayObject(Self), '@Attack', False);
    Result := True;
  end;
end;

function TPlayObject.UseMagicJump(MagicID: Word): Boolean;
begin
  Result := False;
  if (m_btRaceServer = RC_PLAYOBJECT) and (g_FunctionNPC <> nil) then begin
      //TPlayObject(PlayObject).m_nScriptGotoCount := 0;
    g_FunctionNPC.GotoLable(TPlayObject(Self), '@Magic' + IntToStr(MagicID), False);

    Result := True;
  end;
end;

//触发函数

function TPlayObject.DieGotoLable(): Boolean;
begin
  Result := False;
  if (m_LastHiter <> nil) and (m_btRaceServer = RC_PLAYOBJECT) then begin
    if m_LastHiter.m_Master <> nil then begin
      if m_LastHiter.m_Master.m_btRaceServer = RC_PLAYOBJECT then begin
        m_sString[0] := TPlayObject(m_LastHiter.m_Master).m_sCharName;
        m_nInteger[0] := TPlayObject(m_LastHiter.m_Master).m_WAbil.Level;
        TPlayObject(m_LastHiter.m_Master).m_sString[0] := m_sCharName;
        TPlayObject(m_LastHiter.m_Master).m_nInteger[0] := m_WAbil.Level;
        TPlayObject(m_LastHiter.m_Master).m_nScriptGotoCount := 0;
        TPlayObject(m_LastHiter.m_Master).KillPlayFunc;
      end;
    end else
      if m_LastHiter.m_btRaceServer = RC_PLAYOBJECT then begin
      m_sString[0] := TPlayObject(m_LastHiter).m_sCharName;
      m_nInteger[0] := TPlayObject(m_LastHiter).m_WAbil.Level;
      TPlayObject(m_LastHiter).m_sString[0] := m_sCharName;
      TPlayObject(m_LastHiter).m_nInteger[0] := m_WAbil.Level;
      TPlayObject(m_LastHiter).m_nScriptGotoCount := 0;
      TPlayObject(m_LastHiter).KillPlayFunc;
    end;
    if m_LastHiter.m_btRaceServer <> RC_PLAYOBJECT then begin
      m_sString[0] := m_LastHiter.m_sCharName;
      m_nInteger[0] := m_LastHiter.m_WAbil.Level;
    end;
    Result := DieFunc(); //死亡触发
  end;
end;

procedure TActorObject.WeightChanged;
begin
  m_WAbil.Weight := RecalcBagWeight();
  SendUpdateMsg(Self, RM_WEIGHTCHANGED, 0, 0, 0, 0, '');
end;

function TActorObject.GetRandomHomePoint(var nX, nY: Integer): Boolean;
var
  I: Integer;
  nC, n10, nSafeZoneSize: Integer;
//  nSafeX, nSafeY: Integer;
  StartPoint: pTStartPoint;
begin
  if not g_Config.boGetRandomHomePoint then begin
    nX := m_nHomeX;
    nY := m_nHomeY;
    Result := True;
    Exit;
  end;
  Result := False;
  nSafeZoneSize := 0;
  //nSafeZoneSize := g_Config.nSafeZoneSize;
  if (m_sHomeMap = '') or ((m_nHomeX <= 0) and (m_nHomeY <= 0)) then begin
    n10 := 9999;
    for I := 0 to g_StartPointList.Count - 1 do begin
      if CompareText(g_StartPointList.Strings[I], m_PEnvir.MapName) = 0 then begin
        StartPoint := pTStartPoint(g_StartPointList.Objects[I]);
        nC := abs(m_nCurrX - StartPoint.nCurrX) + abs(m_nCurrY - StartPoint.nCurrY);
        if nC < n10 then begin
          n10 := nC;
          m_sHomeMap := g_StartPointList.Strings[I];
          m_nHomeX := StartPoint.nCurrX;
          m_nHomeY := StartPoint.nCurrY;
          nSafeZoneSize := StartPoint.nRange;
        end;
      end;
    end;
  end else begin
    for I := 0 to g_StartPointList.Count - 1 do begin
      if CompareText(g_StartPointList.Strings[I], m_sHomeMap) = 0 then begin
        StartPoint := pTStartPoint(g_StartPointList.Objects[I]);
        nSafeZoneSize := StartPoint.nRange;
        break;
      end;
    end;
  end;

  nX := m_nHomeX - nSafeZoneSize + Random(nSafeZoneSize * 2);
  nY := m_nHomeY - nSafeZoneSize + Random(nSafeZoneSize * 2);
  Result := True;
end;

function TActorObject.InSafeZone: Boolean;
var
  I: Integer;
  sMapName: string;
  StartPoint: pTStartPoint;
begin
  if m_PEnvir = nil then begin //修正机器人刷火墙的错误 叶随风飘
    Result := True;
    Exit;
  end;
  Result := m_PEnvir.m_boSAFE;
  if Result then Exit;
  if (m_PEnvir.sMapName <> g_Config.sRedHomeMap) or
    (abs(m_nCurrX - g_Config.nRedHomeX) > g_Config.nSafeZoneSize) or
    (abs(m_nCurrY - g_Config.nRedHomeY) > g_Config.nSafeZoneSize) then begin
    Result := False;
  end else begin
    Result := True;
  end;
  if Result then Exit;
  for I := 0 to g_StartPointList.Count - 1 do begin
    sMapName := g_StartPointList.Strings[I];
    if (sMapName = m_PEnvir.sMapName) then begin
      StartPoint := pTStartPoint(g_StartPointList.Objects[I]);
      if (abs(m_nCurrX - StartPoint.nCurrX) <= StartPoint.nRange) and
        (abs(m_nCurrY - StartPoint.nCurrY) <= StartPoint.nRange) then begin
        Result := True;
        Break;
      end;
    end;
  end;
end;

function TActorObject.InSafeZone(Envir: TEnvirnoment; nX,
  nY: Integer): Boolean;
var
  I: Integer;
  sMapName: string;
  StartPoint: pTStartPoint;
begin
  if m_PEnvir = nil then begin //修正机器人刷火墙的错误 叶随风飘
    Result := True;
    Exit;
  end;
  Result := Envir.m_boSAFE;
  if Result then Exit;
  if (Envir.sMapName <> g_Config.sRedHomeMap) or
    (abs(nX - g_Config.nRedHomeX) > g_Config.nSafeZoneSize) or
    (abs(nY - g_Config.nRedHomeY) > g_Config.nSafeZoneSize) then begin
    Result := False;
  end else begin
    Result := True;
  end;
  if Result then Exit;

  for I := 0 to g_StartPointList.Count - 1 do begin
    sMapName := g_StartPointList.Strings[I];
    if (sMapName = Envir.sMapName) then begin
      StartPoint := pTStartPoint(g_StartPointList.Objects[I]);
      if (abs(nX - StartPoint.nCurrX) <= StartPoint.nRange) and
        (abs(nY - StartPoint.nCurrY) <= StartPoint.nRange) then begin
        Result := True;
        Break;
      end;
    end;
  end;
end;

procedure TActorObject.OpenHolySeizeMode(dwInterval: LongWord);
begin
  m_boHolySeize := True;
  m_dwHolySeizeTick := GetTickCount();
  m_dwHolySeizeInterval := dwInterval;
  RefNameColor();
end;

procedure TActorObject.BreakHolySeizeMode;
begin
  m_boHolySeize := False;
  RefNameColor();
end;

procedure TActorObject.OpenCrazyMode(nTime: Integer);
begin
  m_boCrazyMode := True;
  m_dwCrazyModeTick := GetTickCount();
  m_dwCrazyModeInterval := nTime * 1000;
  RefNameColor();
end;

procedure TActorObject.BreakCrazyMode;
begin
  if m_boCrazyMode then begin
    m_boCrazyMode := False;
    RefNameColor();
  end;
end;

procedure TPlayObject.LeaveGroup;
resourcestring
  sExitGropMsg = '%s Has withdrawn from this group....';
begin
  SendGroupText(Format(sExitGropMsg, [m_sCharName]));
  m_GroupOwner := nil;
  SendMsg(Self, RM_GROUPCANCEL, 0, 0, 0, 0, '');
end;

function TPlayObject.CancelGroup: Boolean;
resourcestring
  sCanceGrop = 'Your group has been dissolved...';
begin
  Result := True;
  if m_GroupMembers.Count <= 1 then begin
    SendGroupText(sCanceGrop);
    m_GroupMembers.Clear;
    m_GroupOwner := nil;
    Result := False;
  end;
end;

procedure TPlayObject.SendGroupMembers;
var
  I: Integer;
  PlayObject: TPlayObject;
  sSENDMSG: string;
begin
  sSENDMSG := '';
  for I := 0 to m_GroupMembers.Count - 1 do begin
    PlayObject := TPlayObject(m_GroupMembers.Objects[I]);
    if PlayObject <> nil then
      sSENDMSG := sSENDMSG + PlayObject.m_sCharName + '/';
  end;
  for I := 0 to m_GroupMembers.Count - 1 do begin
    PlayObject := TPlayObject(m_GroupMembers.Objects[I]);
    if PlayObject <> nil then
      PlayObject.SendDefMessage(SM_GROUPMEMBERS, 0, 0, 0, 0, sSENDMSG);
  end;
end;

function TPlayObject.GetMagicInfo(nMagicID: Integer): pTUserMagic;
var
  I: Integer;
  UserMagic: pTUserMagic;
begin
  Result := nil;
  for I := 0 to m_MagicList.Count - 1 do begin
    UserMagic := m_MagicList.Items[I];
    if UserMagic.MagicInfo.wMagicId = nMagicID then begin
      Result := UserMagic;
      Break;
    end;
  end;
end;

function TPlayObject.GetMagicInfo(sMagicName: string): pTUserMagic;
var
  I: Integer;
  UserMagic: pTUserMagic;
begin
  Result := nil;
  for I := 0 to m_MagicList.Count - 1 do begin
    UserMagic := m_MagicList.Items[I];
    if CompareText(UserMagic.MagicInfo.sMagicName, sMagicName) = 0 then begin
      Result := UserMagic;
      Break;
    end;
  end;
end;

function TPlayObject.DoMotaebo(nDir: Byte; nMagicLevel: Integer): Boolean;
  function CanMotaebo(BaseObject: TActorObject): Boolean;
  var
    nC: Integer;
  begin
    Result := False;
    if (m_Abil.Level > BaseObject.m_Abil.Level) and (not BaseObject.m_boStickMode) then begin
      nC := m_Abil.Level - BaseObject.m_Abil.Level;
      if Random(20) < ((nMagicLevel * 4) + 6 + nC) then begin
        if IsProperTarget(BaseObject) then Result := True;
      end;
    end;
  end;
var
  bo35: Boolean;
  I, n20, n24, n28: Integer;
  PoseCreate: TActorObject;
//  BaseObject_30: TActorObject;
  BaseObject_34: TActorObject;
  nX, nY: Integer;
begin
  Result := False;
  bo35 := True;
  m_btDirection := nDir;
  BaseObject_34 := nil;
  n24 := nMagicLevel + 1;
  n28 := n24;

  PoseCreate := GetPoseCreate();

  if PoseCreate <> nil then begin
    for I := 0 to _MAX(2, nMagicLevel + 1) do begin
      PoseCreate := GetPoseCreate();
      if PoseCreate <> nil then begin
        n28 := 0;
        if not CanMotaebo(PoseCreate) then Break;
        {if nMagicLevel >= 3 then begin
          if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, m_btDirection, 2, nX, nY) then begin
            BaseObject_30 := TActorObject(m_PEnvir.GetMovingObject(nX, nY, True));
            if (BaseObject_30 <> nil) and CanMotaebo(BaseObject_30) then
              BaseObject_30.CharPushed(m_btDirection, 1);
          end;
        end;}
        BaseObject_34 := PoseCreate;
        if PoseCreate.CharPushed(m_btDirection, 1) <> 1 then Break;
        GetFrontPosition(nX, nY);
        if m_PEnvir.CanWalkOfEvent(Self, nX, nY) and (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nX, nY, False) > 0) then begin
          m_nCurrX := nX;
          m_nCurrY := nY;
          SendRefMsg(RM_RUSH, nDir, m_nCurrX, m_nCurrY, 0, '');
          bo35 := False;
          Result := True;
        end;
        Dec(n24);
      end; //004C32D7  if PoseCreate <> nil  then begin
    end; //004C32DD for i:=0 to _MAX(2,nMagicLevel + 1) do begin
  end else begin //004C32E8 if PoseCreate <> nil  then begin
    bo35 := False;
    for I := 0 to _MAX(2, nMagicLevel + 1) do begin
      GetFrontPosition(nX, nY); //sub_004B2790
      if m_PEnvir.CanWalkOfEvent(Self, nX, nY) and (m_PEnvir.MoveToMovingObject(m_nCurrX, m_nCurrY, Self, nX, nY, False) > 0) then begin
        m_nCurrX := nX;
        m_nCurrY := nY;
        SendRefMsg(RM_RUSH, nDir, m_nCurrX, m_nCurrY, 0, '');
        Dec(n28);
      end else begin
        if m_PEnvir.CanWalk(nX, nY, True) and m_PEnvir.CanWalkOfEvent(Self, nX, nY) then n28 := 0
        else begin
          bo35 := True;
          Break;
        end;
      end;
    end;
  end;
  if (BaseObject_34 <> nil) then begin
    if n24 < 0 then n24 := 0;
    n20 := Random((n24 + 1) * 10) + ((n24 + 1) * 10);

    BaseObject_34.m_boNotDefendoof := GetNotDefendoof; //忽视目标防御
    n20 := n20 + GetAddPowerPoint(5, n20);

    n20 := BaseObject_34.GetHitStruckDamage(Self, n20);
    BaseObject_34.StruckDamage(n20);
    BaseObject_34.StartNewShield();
    BaseObject_34.SendRefMsg(RM_STRUCK, n20, BaseObject_34.m_WAbil.HP, BaseObject_34.m_WAbil.MaxHP, Integer(Self), '');
    if BaseObject_34.m_btRaceServer <> RC_PLAYOBJECT then begin
      BaseObject_34.SendMsg(BaseObject_34, RM_STRUCK, n20, BaseObject_34.m_WAbil.HP, BaseObject_34.m_WAbil.MaxHP, Integer(Self), '');
    end;
  end;
  if bo35 then begin
    GetFrontPosition(nX, nY);
    SendRefMsg(RM_RUSHKUNG, m_btDirection, nX, nY, 0, '');
    SysMsg(sMateDoTooweak {冲撞力不够！！！}, c_Red, t_Hint);
  end;
  if n28 > 0 then begin
    if n24 < 0 then n24 := 0;
    n20 := Random(n24 * 10) + ((n24 + 1) * 3);

    m_boNotDefendoof := GetNotDefendoof; //忽视目标防御

    n20 := GetHitStruckDamage(Self, n20);
    StruckDamage(n20);
    StartNewShield();
    SendRefMsg(RM_STRUCK, n20, m_WAbil.HP, m_WAbil.MaxHP, 0, '');
  end;
end;

procedure TActorObject.TrainSkill(var UserMagic: pTUserMagic;
  nTranPoint: Integer);
begin
  if m_boFastTrain then
    nTranPoint := nTranPoint * 3;
  Inc(UserMagic.nTranPoint, nTranPoint);
end;

function TActorObject.CheckMagicLevelup(UserMagic: pTUserMagic): Boolean;
var
  n10: Integer;
  btTrainLv: Byte;
begin
  Result := False;
  btTrainLv := 3;
  if (m_btRaceServer = RC_HEROOBJECT) or (UserMagic.wMagIdx = 31) then begin
    if UserMagic.wMagIdx in [160,161] then begin
      if (UserMagic.btLevel < 5) and (UserMagic.MagicInfo.btTrainLv >= UserMagic.btLevel) then
        n10 := UserMagic.btLevel
      else n10 := 0;
      if n10 >= 4 then n10 := 3;
      btTrainLv := 4;
    end else begin
      if (UserMagic.btLevel < 4) and (UserMagic.MagicInfo.btTrainLv >= UserMagic.btLevel) then
        n10 := UserMagic.btLevel
      else n10 := 0;
    end;
  end else begin
    if (UserMagic.btLevel < 4) and (UserMagic.MagicInfo.btTrainLv >= UserMagic.btLevel) then
      n10 := UserMagic.btLevel
    else n10 := 0;
  end;

  if (btTrainLv > UserMagic.btLevel) and
    (UserMagic.MagicInfo.MaxTrain[n10] <= UserMagic.nTranPoint) then begin
    if (btTrainLv > UserMagic.btLevel) then begin
      Dec(UserMagic.nTranPoint, UserMagic.MagicInfo.MaxTrain[n10]);
      Inc(UserMagic.btLevel);
      if (m_btRaceServer = RC_PLAYOBJECT) or (m_btRaceServer = RC_HEROOBJECT) then begin
        if (UserMagic.wMagIdx = 31) and (UserMagic.btLevel >= 4) then StatusChanged();
        SendUpdateDelayMsg(Self, RM_MAGIC_LVEXP, 0, UserMagic.MagicInfo.wMagicId, UserMagic.btLevel, UserMagic.nTranPoint, '', 800);
      end;
      sub_4C713C(UserMagic);
    end else begin
      UserMagic.nTranPoint := UserMagic.MagicInfo.MaxTrain[n10];
    end;
    Result := True;
  end;
end;

function TPlayObject.DoSpell(UserMagic: pTUserMagic; nTargetX,
  nTargetY: Integer; BaseObject: TActorObject): Boolean;
var
  nSpellPoint: Integer;
begin
  Result := False;
  try
    if not MagicManager.IsWarrSkill(UserMagic.wMagIdx) then begin
      nSpellPoint := GetSpellPoint(UserMagic);
      if nSpellPoint > 0 then begin
        if m_WAbil.MP < nSpellPoint then Exit;
        DamageSpell(nSpellPoint);
        HealthSpellChanged();
      end;
      UseMagicJump(UserMagic.wMagIdx);
      Result := MagicManager.DoSpell(Self, UserMagic, nTargetX, nTargetY, BaseObject);
      AttackJump;
    end;
    //MainOutMessage(Format('TPlayObject.DoSpell MagID:%d X:%d Y:%d', [UserMagic.wMagIdx, nTargetX, nTargetY]));
  except
    on E: Exception do begin
      MainOutMessage(Format('[Exception] TPlayObject.DoSpell MagID:%d X:%d Y:%d', [UserMagic.wMagIdx, nTargetX, nTargetY]));
      MainOutMessage(E.Message);
    end;
  end;
end;

function TPlayObject.PileStones(nX, nY: Integer): Boolean;
var
  Event: TEvent;
  PileEvent: TEvent;
  s1C: string;
begin
  Result := False;
  s1C := '';
  Event := TEvent(m_PEnvir.GetEvent(nX, nY));
  if (Event <> nil) and (Event.m_nEventType = ET_STONEMINE) then begin
    if TStoneMineEvent(Event).m_nMineCount > 0 then begin
      Dec(TStoneMineEvent(Event).m_nMineCount);
      if Random(g_Config.nMakeMineHitRate {4}) = 0 then begin
        PileEvent := TEvent(m_PEnvir.GetEvent(m_nCurrX, m_nCurrY));
        if PileEvent = nil then begin
          PileEvent := TPileStones.Create(m_PEnvir, m_nCurrX, m_nCurrY, ET_PILESTONES, 5 * 60 * 1000);
          g_EventManager.AddEvent(PileEvent);
        end else begin
          if PileEvent.m_nEventType = ET_PILESTONES then
            TPileStones(PileEvent).AddEventParam;
        end;
        if Random(g_Config.nMakeMineRate {12}) = 0 then begin
          MakeMine();
        end;
        s1C := '1';
        DoDamageWeapon(Random(15) + 5);
        Result := True;
      end;
    end else begin
      if (GetTickCount - TStoneMineEvent(Event).m_dwAddStoneMineTick) > 10 * 60 * 1000 then
        TStoneMineEvent(Event).AddStoneMine();
    end;
  end;
  SendRefMsg(RM_HEAVYHIT, m_btDirection, m_nCurrX, m_nCurrY, 0, s1C);
end;

procedure TActorObject.SetTargetCreat(BaseObject: TActorObject);
begin
  m_TargetCret := BaseObject;
  m_dwTargetFocusTick := GetTickCount();
end;

procedure TActorObject.DelTargetCreat();
begin
  m_TargetCret := nil;
end;

function TActorObject._Attack(var wHitMode: Word; AttackTarget: TActorObject): Boolean;
//攻击角色
  function DirectAttack(BaseObject: TActorObject; nSecPwr: Integer): Boolean;
  begin
    Result := False;
    if (m_btRaceServer = RC_PLAYOBJECT) or
      (BaseObject.m_btRaceServer = RC_PLAYOBJECT) or
      not (InSafeZone and BaseObject.InSafeZone) then begin
      if IsProperTarget(BaseObject) then begin
        if Random(BaseObject.m_btSpeedPoint) < m_btHitPoint then begin //敏捷 < 准确度 攻击有效

          BaseObject.m_boNotDefendoof := GetNotDefendoof; //忽视目标防御
          nSecPwr := _GetAtomPower(BaseObject, nSecPwr);

          nSecPwr := nSecPwr + GetAddPowerPoint(5, nSecPwr);
          nSecPwr := BaseObject.GetAddPowerPoint(True, nSecPwr);

          BaseObject.StartNewShield();
          if nSecPwr > 0 then begin
            BaseObject.StruckDamage(nSecPwr);

            BaseObject.SendDelayMsg(TActorObject(RM_STRUCK),
              RM_10101,
              nSecPwr,
              BaseObject.m_WAbil.HP,
              BaseObject.m_WAbil.MaxHP,
              Integer(Self),
              '', 500);
            if BaseObject.m_btRaceServer <> RC_PLAYOBJECT then begin
              BaseObject.SendMsg(BaseObject,
                RM_STRUCK,
                nSecPwr,
                BaseObject.m_WAbil.HP,
                BaseObject.m_WAbil.MaxHP,
                Integer(Self),
                '');
            end;
          end;

          nSecPwr := BaseObject.GetAddPowerPoint(4, nSecPwr); //反射伤害
          if nSecPwr > 0 then begin
            //nSecPwr := nSecPwr + BaseObject.GetAddPowerPoint(5, nSecPwr);
            StruckDamage(nSecPwr);
            SendDelayMsg(TActorObject(RM_STRUCK),
              RM_10101,
              nSecPwr,
              m_WAbil.HP,
              m_WAbil.MaxHP,
              Integer(BaseObject),
              '', 500);
            if m_btRaceServer <> RC_PLAYOBJECT then begin
              SendMsg(Self,
                RM_STRUCK,
                nSecPwr,
                m_WAbil.HP,
                m_WAbil.MaxHP,
                Integer(BaseObject),
                '');
            end;
          end;

          Result := True;
        end;
      end;
    end;
  end;
  //刺杀前面一个位置的攻击
  function SwordLongAttack(nSecPwr: Integer): Boolean;
  var
    nX, nY: Integer;
    BaseObject: TActorObject;
  begin
    Result := False;
    if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, m_btDirection, 2, nX, nY) then begin
      BaseObject := TActorObject(m_PEnvir.GetMovingObject(nX, nY, True));
      if BaseObject <> nil then begin
        nSecPwr := Round(nSecPwr * g_Config.nSwordLongPowerRate / 100);
        if (nSecPwr > 0) and IsProperTarget(BaseObject) then begin
          Result := DirectAttack(BaseObject, nSecPwr);
          SetTargetCreat(BaseObject);
        end;
        Result := True;
      end;
    end;
  end;
  //半月攻击
  function SwordWideAttack(nSecPwr: Integer): Boolean;
  var
    nC, n10: Integer;
    nX, nY: Integer;
    BaseObject: TActorObject;
  begin
    Result := False;
    nC := 0;
    while (True) do begin
      n10 := (m_btDirection + g_Config.WideAttack[nC]) mod 8;
      if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, n10, 1, nX, nY) then begin
        BaseObject := TActorObject(m_PEnvir.GetMovingObject(nX, nY, True));
        if (nSecPwr > 0) and (BaseObject <> nil) and IsProperTarget(BaseObject) then begin
          Result := DirectAttack(BaseObject, nSecPwr);
          SetTargetCreat(BaseObject);
        end;
      end;
      Inc(nC);
      if nC >= 3 then Break;
    end;
  end;
  //抱月弯刀
  function CrsWideAttack(nSecPwr: Integer): Boolean;
  var
    nC, n10: Integer;
    nX, nY: Integer;
    BaseObject: TActorObject;
  begin
    Result := False;
    nC := 0;
    while (True) do begin
      n10 := (m_btDirection + g_Config.CrsAttack[nC]) mod 8;
      if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, n10, 1, nX, nY) then begin
        BaseObject := TActorObject(m_PEnvir.GetMovingObject(nX, nY, True));
        if (nSecPwr > 0) and (BaseObject <> nil) and IsProperTarget(BaseObject) then begin
          Result := DirectAttack(BaseObject, nSecPwr);
          SetTargetCreat(BaseObject);
          //MainOutMessage('CrsWideAttack ' + IntToStr(nSecPwr));
        end;
      end;
      Inc(nC);
      if nC >= 7 then Break;
    end;
  end;

  //破空剑
  function CIDAttack(nSecPwr: Integer): Boolean;
  var
    nX, nY: Integer;
    BaseObject: TActorObject;
    I: Integer;
    BaseObjectList: TList;
  begin
    Result := False;
    nX := m_nCurrX;
    nY := m_nCurrY;
    if m_PEnvir.GetNextPosition(nX, nY, m_btDirection, 2, nX, nY) then begin
      BaseObjectList := TList.Create;
      GetMapActorObjects(m_PEnvir, nX, nY, 2, BaseObjectList);
      for I := 0 to BaseObjectList.Count - 1 do begin
        BaseObject := TActorObject(BaseObjectList.Items[I]);
        if (nSecPwr > 0) and (BaseObject <> nil) and IsProperTarget(BaseObject) then begin
          Result := DirectAttack(BaseObject, nSecPwr);
          SetTargetCreat(BaseObject);
        end;
      end;
      BaseObjectList.Free;
    end;
  end;

  //开天斩
  function KTZAttack(nSecPwr: Integer): Boolean;
  var
    nX, nY: Integer;
    BaseObject: TActorObject;
    I: Integer;
  begin
    Result := False;
    nX := m_nCurrX;
    nY := m_nCurrY;
    for I := 1 to 4 do begin
      if m_PEnvir.GetNextPosition(nX, nY, m_btDirection, 1, nX, nY) then begin
        BaseObject := TActorObject(m_PEnvir.GetMovingObject(nX, nY, True));
        if (nSecPwr > 0) and (BaseObject <> nil) and IsProperTarget(BaseObject) then begin
          Result := DirectAttack(BaseObject, nSecPwr);
          SetTargetCreat(BaseObject);
        end;
      end;
    end;
  end;

{破魂斩}

  function Skill60Attack(nSecPwr: Integer): Boolean;
  var
    I: Integer;
    //nC, n10: Integer;
    nX, nY: Integer;
    nPower: Integer;
    BaseObject: TActorObject;
  begin
    Result := False;
    nX := m_nCurrX;
    nY := m_nCurrY;
    nPower := Round(nSecPwr * (g_Config.nSkill60Rate / 100));
    for I := 1 to 6 do begin
      if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, m_btDirection, I, nX, nY) then begin
        BaseObject := TActorObject(m_PEnvir.GetMovingObject(nX, nY, True));
        if (BaseObject <> nil) and IsProperTarget(BaseObject) then begin
          SetTargetCreat(BaseObject);
          SendRefMsg(RM_10205, 0, BaseObject.m_nCurrX, BaseObject.m_nCurrY, 14, '');
          Result := DirectAttack(BaseObject, nPower);
        end;
      end;
    end;
  end;

  function Skill61Attack(nSecPwr: Integer): Boolean;
  var
    nPower: Integer;
  begin
    Result := False;
    nPower := Round(nSecPwr * (g_Config.nSkill61Rate / 100));
    if AttackTarget <> nil then begin
      if AttackTarget <> nil then begin
        Result := DirectAttack(AttackTarget, nPower);
      end;
    end;
  end;

  function Skill62Attack(nSecPwr: Integer): Boolean;
  var
    nPower: Integer;
  begin
    Result := False;
    nPower := Round(nSecPwr * (g_Config.nSkill62Rate / 100));
    if AttackTarget <> nil then begin
      Result := DirectAttack(AttackTarget, nPower);
    end;
  end;

  //逐日剑法
  function ZRJFAttack(nSecPwr: Integer): Boolean;
  var
    nX, nY: Integer;
    BaseObject: TActorObject;
    I: Integer;
  begin
    Result := False;
    nX := m_nCurrX;
    nY := m_nCurrY;
    nSecPwr := Round(nSecPwr * (g_Config.nSkill77PowerRate / 100));
    for I := 1 to 5 do begin
      if m_PEnvir.GetNextPosition(nX, nY, m_btDirection, 1, nX, nY) then begin
        BaseObject := TActorObject(m_PEnvir.GetMovingObject(nX, nY, True));
        if (nSecPwr > 0) and (BaseObject <> nil) and IsProperTarget(BaseObject) then begin
          Result := DirectAttack(BaseObject, nSecPwr);
          SetTargetCreat(BaseObject);
        end;
      end;
    end;
  end;

  procedure sub_4C1E5C(nSecPwr: Integer);
  var
    btDir: Byte;
    nX, nY: Integer;
    BaseObject: TActorObject;
    procedure sub_4C1DC0();
    begin
      if m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, btDir, 1, nX, nY) then begin
        BaseObject := TActorObject(m_PEnvir.GetMovingObject(nX, nY, True));
        if (nSecPwr > 0) and (BaseObject <> nil) then begin
          Result := DirectAttack(BaseObject, nSecPwr);
        end;
      end;
    end;
  begin
    Result := False;
    btDir := m_btDirection;
    m_PEnvir.GetNextPosition(m_nCurrX, m_nCurrY, btDir, 1, nX, nY);
    sub_4C1DC0();
    btDir := sub_4B2F80(m_btDirection, 2);
    sub_4C1DC0();
    btDir := sub_4B2F80(m_btDirection, 6);
    sub_4C1DC0();
  end;
//------------------------------------------------------------------------------
  function Skill100Attack(nSecPwr: Integer; UserMagic: pTUserMagic): Boolean;
  var
    I, nPower: Integer;
  begin
    Result := False;
    if (AttackTarget <> nil) then begin

      nPower := Round(nSecPwr * (g_Config.SerieMagicPowerRate[UserMagic.wMagIdx - 100] / 100));
      if UserMagic.btLevel in [1..3] then begin
        for I := 0 to g_Config.SerieMagicBlasthitRate[UserMagic.wMagIdx - 100, UserMagic.btLevel] do begin
          if Random(100 - g_Config.SerieMagicBlasthitRate[UserMagic.wMagIdx - 100, UserMagic.btLevel]) = 0 then begin
            nPower := Round(nPower * (g_Config.SerieMagicBlasthitPowerRate[UserMagic.wMagIdx - 100] / 100));
            SendSerieMagicBlasthitMsg(UserMagic);
            break;
          end;
        end;
      end;
      //MainOutMessage('Skill100Attack:'+IntToStr(nPower));
      Result := DirectAttack(AttackTarget, nPower);
    end;
  end;

  function Skill101Attack(nSecPwr: Integer; UserMagic: pTUserMagic): Boolean;
 // var
   // I, nPower: Integer;
  begin
    Result := False;
    if (AttackTarget <> nil) then begin
      //DoRotationMotaebo(m_btDirection, UserMagic.btLevel);
      {nPower := Round(nSecPwr * (g_Config.SerieMagicPowerRate[UserMagic.wMagIdx - 100] / 100));
      if UserMagic.btLevel in [1..3] then begin
        for I := 0 to g_Config.SerieMagicBlasthitRate[UserMagic.wMagIdx - 100, UserMagic.btLevel] do begin
          if Random(100 - g_Config.SerieMagicBlasthitRate[UserMagic.wMagIdx - 100, UserMagic.btLevel]) = 0 then begin
            nPower := Round(nPower * (g_Config.SerieMagicBlasthitPowerRate[UserMagic.wMagIdx - 100] / 100));
            SendSerieMagicBlasthitMsg(UserMagic);
            break;
          end;
        end;
      end;
      //MainOutMessage('Skill101Attack:'+IntToStr(nPower));
      Result := DirectAttack(AttackTarget, nPower); }
    end;
  end;

  function Skill102Attack(nSecPwr: Integer; UserMagic: pTUserMagic): Boolean;
  var
    I, nPower: Integer;
    nPowerValue: Integer;
    BaseObjectList: TList;
    ActorObject: TActorObject;
    boBlasthit: Boolean;
  begin
    Result := False;
    if AttackTarget <> nil then begin
      nPower := Round(nSecPwr * (g_Config.SerieMagicPowerRate[UserMagic.wMagIdx - 100] / 100));
      nPowerValue := nPower;
      boBlasthit := False;
      if UserMagic.btLevel in [1..3] then begin
        for I := 0 to g_Config.SerieMagicBlasthitRate[UserMagic.wMagIdx - 100, UserMagic.btLevel] do begin
          if Random(100 - g_Config.SerieMagicBlasthitRate[UserMagic.wMagIdx - 100, UserMagic.btLevel]) = 0 then begin
            boBlasthit := True;
            break;
          end;
        end;
      end;

      BaseObjectList := TList.Create;
      GetMapActorObjects(m_PEnvir, AttackTarget.m_nCurrX, AttackTarget.m_nCurrY, g_Config.SerieMagicAttackRange[UserMagic.wMagIdx - 100], BaseObjectList);
      for I := 0 to BaseObjectList.Count - 1 do begin
        ActorObject := TActorObject(BaseObjectList.Items[I]);
        if (ActorObject <> nil) and IsProperTarget(ActorObject) then begin
          if boBlasthit then
            nPower := Round(nPowerValue * (g_Config.SerieMagicBlasthitPowerRate[UserMagic.wMagIdx - 100] / 100));
          Result := DirectAttack(ActorObject, nPower);
          SetTargetCreat(ActorObject);
        end;
      end;
      BaseObjectList.Free;

      if boBlasthit then
        SendSerieMagicBlasthitMsg(UserMagic);
    end;
  end;

  function Skill103Attack(nSecPwr: Integer; UserMagic: pTUserMagic): Boolean;
  var
    I, nPower: Integer;
    nPowerValue: Integer;
    BaseObjectList: TList;
    ActorObject: TActorObject;
    boBlasthit: Boolean;
  begin
    Result := False;
    if AttackTarget <> nil then begin
      nPower := Round(nSecPwr * (g_Config.SerieMagicPowerRate[UserMagic.wMagIdx - 100] / 100));
      nPowerValue := nPower;
      boBlasthit := False;
      if UserMagic.btLevel in [1..3] then begin
        for I := 0 to g_Config.SerieMagicBlasthitRate[UserMagic.wMagIdx - 100, UserMagic.btLevel] do begin
          if Random(100 - g_Config.SerieMagicBlasthitRate[UserMagic.wMagIdx - 100, UserMagic.btLevel]) = 0 then begin
            boBlasthit := True;
            break;
          end;
        end;
      end;

      BaseObjectList := TList.Create;
      GetMapActorObjects(m_PEnvir, AttackTarget.m_nCurrX, AttackTarget.m_nCurrY, g_Config.SerieMagicAttackRange[UserMagic.wMagIdx - 100], BaseObjectList);
      for I := 0 to BaseObjectList.Count - 1 do begin
        ActorObject := TActorObject(BaseObjectList.Items[I]);
        if (ActorObject <> nil) and IsProperTarget(ActorObject) then begin
          if boBlasthit then
            nPower := Round(nPowerValue * (g_Config.SerieMagicBlasthitPowerRate[UserMagic.wMagIdx - 100] / 100));
          Result := DirectAttack(ActorObject, nPower);
          SetTargetCreat(ActorObject);
        end;
      end;
      BaseObjectList.Free;

      if boBlasthit then
        SendSerieMagicBlasthitMsg(UserMagic);
    end;
  end;
var
  nPower, nSecPwr, nWeaponDamage: Integer;
  bo21: Boolean;
  n20: Integer;
  nCheckCode: Integer;
  UserMagic: pTUserMagic;
  nSec: Integer;
  TDBTargetCheck : Integer; //Milo
resourcestring
  sExceptionMsg = '[Exception] TActorObject::_Attack Name:= %s Code:=%d';
begin
  Result := False;
  nCheckCode := 0;
  bo21 := False;
  nWeaponDamage := 0;
  nPower := 0;
  nSecPwr := 0;


  try
    if AttackTarget <> nil then begin
      nPower := GetAttackPower(LoWord(m_WAbil.DC), Integer((HiWord(m_WAbil.DC) - LoWord(m_WAbil.DC))));
      if (wHitMode = 3) and m_boPowerHit then begin
        m_boPowerHit := False;
        Inc(nPower, m_nHitPlus);
        bo21 := True;
      end;
      if (wHitMode = 7) and m_boFireHitSkill then begin //烈火剑法
        m_boFireHitSkill := False;
        m_dwLatestFireHitTick := GetTickCount(); //Jacky 禁止双烈火
        nPower := nPower + Round(nPower / 100 * (m_nHitDouble * 10));
        bo21 := True; //攻杀
      end;
      if (wHitMode = 11) and m_boCIDHitSkill then begin
        m_boCIDHitSkill := False;
        m_dwLatestCIDHitTick := GetTickCount();
        nPower := nPower + Round(nPower / 100 * (m_nHitDouble * 10));
      end;
      if (wHitMode = 12) and m_boKTZHitSkill then begin
        m_boKTZHitSkill := False;
        m_dwLatestKTZHitTick := GetTickCount();
        nPower := nPower + Round(nPower / 100 * (m_nHitDouble * 10));
      end;
      if (wHitMode = 16) and m_boZRJFHitSkill then begin
        m_boZRJFHitSkill := False;
        m_dwLatestZRJFHitTick := GetTickCount();
        nPower := nPower + Round(nPower / 100 * (m_nHitDouble * 10));
      end;
    end else begin
      nPower := GetAttackPower(LoWord(m_WAbil.DC), Integer((HiWord(m_WAbil.DC) - LoWord(m_WAbil.DC))));
      if (wHitMode = 3) and m_boPowerHit then begin
        m_boPowerHit := False;
        Inc(nPower, m_nHitPlus);
        bo21 := True;
      end;
      //防止砍空刀刀烈火
      if (wHitMode = 7) and m_boFireHitSkill then begin
        m_boFireHitSkill := False;
        m_dwLatestFireHitTick := GetTickCount(); //禁止双烈火
      end;
      if (wHitMode = 11) and m_boCIDHitSkill then begin
        m_boCIDHitSkill := False;
        m_dwLatestCIDHitTick := GetTickCount();
      end;
      if (wHitMode = 12) and m_boKTZHitSkill then begin
        m_boKTZHitSkill := False;
        m_dwLatestKTZHitTick := GetTickCount();
      end;
      if (wHitMode = 16) and m_boZRJFHitSkill then begin
        m_boZRJFHitSkill := False;
        m_dwLatestZRJFHitTick := GetTickCount();
      end;
    end;

    if wHitMode = 4 then begin // 刺杀
      nSecPwr := 0;
      if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER] then begin
        if m_MagicErgumSkill <> nil then begin
          nSecPwr := Round(nPower / (m_MagicErgumSkill.MagicInfo.btTrainLv + 2) * (m_MagicErgumSkill.btLevel + 2));
        end;
      end else nSecPwr := nSecPwr;
      if nSecPwr > 0 then begin
        if not SwordLongAttack(nSecPwr) and g_Config.boLimitSwordLong then wHitMode := 0;
      end;
    end;

    if wHitMode = 5 then begin
      nSecPwr := 0;
      if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER] then begin
        if m_MagicBanwolSkill <> nil then begin
          nSecPwr := Round(nPower / (m_MagicBanwolSkill.MagicInfo.btTrainLv + 10) * (m_MagicBanwolSkill.btLevel + 2));
        end;
        if nSecPwr > 0 then SwordWideAttack(nSecPwr);
      end;
    end;

    if wHitMode = 6 then begin
      nSecPwr := 0;
      if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER] then begin
      end else nSecPwr := nSecPwr;
      if nSecPwr > 0 then sub_4C1E5C(nSecPwr);
    end;

    if wHitMode = 8 then begin //抱月
      nSecPwr := 0;
      if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER] then begin
        if m_MagicCrsSkill <> nil then begin
          nSecPwr := Round(nPower / (m_MagicCrsSkill.MagicInfo.btTrainLv + 10) * (m_MagicCrsSkill.btLevel + 2));
        end;
        if nSecPwr > 0 then CrsWideAttack(nSecPwr);
      end;
    end;

    if wHitMode = 11 then begin
      if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER] then begin
        nPower := Round(nPower * (g_Config.nSkill43PowerRate / 100));
        nSecPwr := nPower;
        if nSecPwr > 0 then CIDAttack(nSecPwr);
      end;
    end;

    if wHitMode = 12 then begin
      if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER] then begin
        nPower := Round(nPower * (g_Config.nSkill58PowerRate / 100));
        nSecPwr := nPower;
        if nSecPwr > 0 then KTZAttack(nSecPwr);
      end;
    end;

    if wHitMode = 13 then begin
      nSecPwr := 0;
      if (m_btRaceServer = RC_PLAYOBJECT) then begin
        if (TPlayObject(Self).m_MyHero = nil) or TPlayObject(Self).m_boDeath then Exit;
        UserMagic := THeroObject(TPlayObject(Self).m_MyHero).FindGroupMagic;
        if UserMagic = nil then Exit;
        nSecPwr := GetAttackPower(LoWord(m_WAbil.DC), Integer((HiWord(m_WAbil.DC) - LoWord(m_WAbil.DC))));
        nSecPwr := nSecPwr + GetAttackPower(LoWord(TPlayObject(Self).m_MyHero.m_WAbil.DC), Integer((HiWord(TPlayObject(Self).m_MyHero.m_WAbil.DC) - LoWord(TPlayObject(Self).m_MyHero.m_WAbil.DC))));
      end else
        if (m_btRaceServer = RC_HEROOBJECT) then begin
        if m_Master = nil then Exit;
        UserMagic := THeroObject(Self).FindGroupMagic;
        if UserMagic = nil then Exit;
        nSecPwr := GetAttackPower(LoWord(m_WAbil.DC), Integer((HiWord(m_WAbil.DC) - LoWord(m_WAbil.DC))));
        nSecPwr := nSecPwr + GetAttackPower(LoWord(m_Master.m_WAbil.DC), Integer((HiWord(m_Master.m_WAbil.DC) - LoWord(m_Master.m_WAbil.DC))));
      end;
      if nSecPwr > 0 then Skill60Attack(nSecPwr);
    end;
//==============================================================================
    if (wHitMode = 14) and (m_btRaceServer = RC_HEROOBJECT) then begin
      nSecPwr := 0;
      if (m_btRaceServer = RC_HEROOBJECT) then begin
        if m_Master = nil then Exit;
        UserMagic := THeroObject(Self).FindGroupMagic;
        if UserMagic = nil then Exit;
        nSecPwr := GetAttackPower(LoWord(m_WAbil.DC), Integer((HiWord(m_WAbil.DC) - LoWord(m_WAbil.DC))));
        nSecPwr := nSecPwr + GetAttackPower(LoWord(m_Master.m_WAbil.SC), Integer((HiWord(m_Master.m_WAbil.SC) - LoWord(m_Master.m_WAbil.SC))));
        if nSecPwr > 0 then Skill61Attack(nSecPwr);
      end;
    end;
//==============================================================================

    if (wHitMode = 15) and (m_btRaceServer = RC_HEROOBJECT) then begin
      nSecPwr := 0;
      if (m_btRaceServer = RC_HEROOBJECT) then begin
        if m_Master = nil then Exit;
        UserMagic := THeroObject(Self).FindGroupMagic;
        if UserMagic = nil then Exit;
        nSecPwr := GetAttackPower(LoWord(m_WAbil.DC), Integer((HiWord(m_WAbil.DC) - LoWord(m_WAbil.DC))));
        nSecPwr := nSecPwr + GetAttackPower(LoWord(m_Master.m_WAbil.MC), Integer((HiWord(m_Master.m_WAbil.MC) - LoWord(m_Master.m_WAbil.MC))));
        //nSecPwr := Round(nPower / (UserMagic.MagicInfo.btTrainLv + 10) * (UserMagic.btLevel + 2));
        if nSecPwr > 0 then Skill62Attack(nSecPwr);
      end;
    end;
//==============================================================================
    if wHitMode = 16 then begin //逐日剑法
      //nPower := Round(nPower * (g_Config.nSkill43PowerRate / 100));
      if m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER] then begin
        nSecPwr := nPower;
        if nSecPwr > 0 then ZRJFAttack(nSecPwr);
      end;
    end;
//==============================================================================
    if (wHitMode = 100) and (m_Magic100Skill <> nil) and //三绝杀
      (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) then begin
      nSecPwr := nPower;
      if nSecPwr > 0 then
        Skill100Attack(nSecPwr, m_Magic100Skill);
    end;
//==============================================================================
    if (wHitMode = 101) and (m_Magic101Skill <> nil) and //三绝杀
      (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) then begin
      nSecPwr := nPower;
      if nSecPwr > 0 then Skill101Attack(nSecPwr, m_Magic101Skill);
    end;
//==============================================================================
    if (wHitMode = 102) and (m_Magic102Skill <> nil) and //三绝杀
      (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) then begin
      nSecPwr := nPower;
      if nSecPwr > 0 then Skill102Attack(nSecPwr, m_Magic102Skill);
    end;
//==============================================================================
    if (wHitMode = 103) and (m_Magic103Skill <> nil) and //三绝杀
      (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT, RC_PLAYMOSTER]) then begin
      nSecPwr := nPower;
      if nSecPwr > 0 then Skill103Attack(nSecPwr, m_Magic103Skill);
    end;

    if AttackTarget = nil then Exit;

    if IsProperTarget(AttackTarget) then begin
      if (Random(AttackTarget.m_btSpeedPoint) >= m_btHitPoint) then begin //敏捷 > 准确度 攻击无效
        nPower := 0;
      end;
    end else nPower := 0;

    if nPower > 0 then begin
      AttackTarget.m_boNotDefendoof := GetNotDefendoof; //忽视目标防御
      nPower := nPower + GetAddPowerPoint(5, nPower);

      nPower := AttackTarget.GetHitStruckDamage(Self, nPower);
      nWeaponDamage := (Random(5) + 2) - m_AddAbil.btWeaponStrong;
    end;
    nCheckCode:=602;
    if m_bo42kill then begin
      m_bo42kill := false;
      AttackTarget.SendDelayMsg(TActorObject(RM_STRUCK),RM_10101,nPower,AttackTarget.m_WAbil.HP,AttackTarget.m_WAbil.MaxHP,Integer(Self),'',200);
      AttackTarget.SendDelayMsg(TActorObject(RM_STRUCK),RM_10101,nPower,AttackTarget.m_WAbil.HP,AttackTarget.m_WAbil.MaxHP,Integer(Self),'',400);

    if random(25 - m_Magic42Skill.btLevel * 5) = 0 then begin
        AttackTarget.MakePosion(POISON_LOCKSPELL,(m_Magic42Skill.btLevel*1),0); //yellow effect
        AttackTarget.m_boTDBeffect := True;
    TrainSkill(m_Magic42Skill, Random(3) + 1);
        if not CheckMagicLevelup(m_Magic42Skill) then begin
          nCheckCode := 609;
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_MagicOneSwordSkill.MagicInfo.wMagicId, m_MagicOneSwordSkill.btLevel, m_MagicOneSwordSkill.nTranPoint, '', 3000);
        end;
        nCheckCode := 610;
      end;
    end else begin
      AttackTarget.SendDelayMsg(TActorObject(RM_STRUCK),RM_10101,nPower,AttackTarget.m_WAbil.HP,AttackTarget.m_WAbil.MaxHP,Integer(Self),'',200);
    end;

    AttackTarget.StartNewShield();
    if nPower > 0 then begin
      AttackTarget.StruckDamage(nPower);
      AttackTarget.SendDelayMsg(TActorObject(RM_STRUCK), RM_10101, nPower, AttackTarget.m_WAbil.HP, AttackTarget.m_WAbil.MaxHP, Integer(Self), '', 200);
      if not AttackTarget.m_boUnParalysis and m_boParalysis and (Random(AttackTarget.m_btAntiPoison + g_Config.nAttackPosionRate {5}) = 0) then begin
        AttackTarget.MakePosion(POISON_STONE, g_Config.nAttackPosionTime {5}, 0);
      end;

      //虹魔，吸血
      if m_nHongMoSuite > 0 then begin
        m_db3B0 := nPower / 1.E2 * m_nHongMoSuite;
        if m_db3B0 >= 2.0 then begin
          n20 := Trunc(m_db3B0);
          m_db3B0 := n20;
          DamageHealth(-n20);
        end;
      end;

      if m_boPoisonItem and (Random(AttackTarget.m_btAntiPoison + g_Config.nAttackPosionRate {5}) = 0) then begin //中毒装备
        if (AttackTarget.m_wStatusTimeArr[POISON_DECHEALTH] = 0) then begin
          AttackTarget.MakePosion(POISON_DECHEALTH, nPower, Round(nPower / g_Config.nAmyOunsulPoint));
        end else begin
          if (AttackTarget.m_wStatusTimeArr[POISON_DAMAGEARMOR] = 0) then begin
            AttackTarget.MakePosion(POISON_DAMAGEARMOR, nPower, Round(nPower / g_Config.nAmyOunsulPoint));
          end;
        end;
      end;

      if AttackTarget.m_boDamageHPItem and (Random(10) >= 5) then begin //攻击对方自己也掉血
        DamageHealth(nPower);
      end;

      nPower := AttackTarget.GetAddPowerPoint(4, nPower); //反射伤害
      if nPower > 0 then begin
        //nPower := nPower + AttackTarget.GetAddPowerPoint(5, nPower);
        StruckDamage(nPower);
        SendDelayMsg(TActorObject(RM_STRUCK), RM_10101, nPower, m_WAbil.HP, m_WAbil.MaxHP, Integer(AttackTarget), '', 200);
      end;

      nCheckCode := 607;
      if (m_MagicOneSwordSkill <> nil) and
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (m_MagicOneSwordSkill.btLevel < 3) and
        (m_MagicOneSwordSkill.MagicInfo.TrainLevel[m_MagicOneSwordSkill.btLevel] <= m_Abil.Level) then begin
        nCheckCode := 608;
        TrainSkill(m_MagicOneSwordSkill, Random(3) + 1);
        if not CheckMagicLevelup(m_MagicOneSwordSkill) then begin
          nCheckCode := 609;
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_MagicOneSwordSkill.MagicInfo.wMagicId, m_MagicOneSwordSkill.btLevel, m_MagicOneSwordSkill.nTranPoint, '', 3000);
        end;
        nCheckCode := 610;
      end;

      if bo21 and (m_MagicPowerHitSkill <> nil) and
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (m_MagicPowerHitSkill.btLevel < 3) and
        (m_MagicPowerHitSkill.MagicInfo.TrainLevel[m_MagicPowerHitSkill.btLevel] <= m_Abil.Level) then begin
        TrainSkill(m_MagicPowerHitSkill, Random(3) + 1);
        if not CheckMagicLevelup(m_MagicPowerHitSkill) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_MagicPowerHitSkill.MagicInfo.wMagicId, m_MagicPowerHitSkill.btLevel, m_MagicPowerHitSkill.nTranPoint, '', 3000);
        end;
      end;

      if (wHitMode = 4) and (m_MagicErgumSkill <> nil) and
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (m_MagicErgumSkill.btLevel < 3) and
        (m_MagicErgumSkill.MagicInfo.TrainLevel[m_MagicErgumSkill.btLevel] <= m_Abil.Level) then begin
        nCheckCode := 61;
        TrainSkill(m_MagicErgumSkill, 1);
        if not CheckMagicLevelup(m_MagicErgumSkill) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_MagicErgumSkill.MagicInfo.wMagicId, m_MagicErgumSkill.btLevel, m_MagicErgumSkill.nTranPoint, '', 3000);
        end;
      end;

      if (wHitMode = 5) and (m_MagicBanwolSkill <> nil) and
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (m_MagicBanwolSkill.btLevel < 3) and
        (m_MagicBanwolSkill.MagicInfo.TrainLevel[m_MagicBanwolSkill.btLevel] <= m_Abil.Level) then begin
        TrainSkill(m_MagicBanwolSkill, 1);
        if not CheckMagicLevelup(m_MagicBanwolSkill) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_MagicBanwolSkill.MagicInfo.wMagicId, m_MagicBanwolSkill.btLevel, m_MagicBanwolSkill.nTranPoint, '', 3000);
        end;
      end;

      //加入4级烈火魔法
      if (wHitMode = 7) and (m_MagicFireSwordSkill <> nil) and
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (((m_btRaceServer = RC_PLAYOBJECT) and (m_MagicFireSwordSkill.btLevel < 3)) or ((m_btRaceServer = RC_HEROOBJECT) and (m_MagicFireSwordSkill.btLevel < 4))) then begin
        if (m_btRaceServer = RC_PLAYOBJECT) and (m_MagicFireSwordSkill.MagicInfo.TrainLevel[m_MagicFireSwordSkill.btLevel] <= m_Abil.Level) then begin
          TrainSkill(m_MagicFireSwordSkill, 1);
          if not CheckMagicLevelup(m_MagicFireSwordSkill) then begin
            SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_MagicFireSwordSkill.MagicInfo.wMagicId, m_MagicFireSwordSkill.btLevel, m_MagicFireSwordSkill.nTranPoint, '', 3000);
          end;
        end else begin
          if (m_btRaceServer = RC_HEROOBJECT) then begin
            if m_MagicFireSwordSkill.btLevel >= 3 then begin
              if (m_MagicFireSwordSkill.MagicInfo.TrainLevel[m_MagicFireSwordSkill.btLevel - 1] <= m_Abil.Level) then begin
                TrainSkill(m_MagicFireSwordSkill, 1);
                if not CheckMagicLevelup(m_MagicFireSwordSkill) then begin
                  SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_MagicFireSwordSkill.MagicInfo.wMagicId, m_MagicFireSwordSkill.btLevel, m_MagicFireSwordSkill.nTranPoint, '', 3000);
                end;
              end;
            end else begin
              if (m_MagicFireSwordSkill.MagicInfo.TrainLevel[m_MagicFireSwordSkill.btLevel] <= m_Abil.Level) then begin
                TrainSkill(m_MagicFireSwordSkill, 1);
                if not CheckMagicLevelup(m_MagicFireSwordSkill) then begin
                  SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_MagicFireSwordSkill.MagicInfo.wMagicId, m_MagicFireSwordSkill.btLevel, m_MagicFireSwordSkill.nTranPoint, '', 3000);
                end;
              end;
            end;
          end;
        end;
      end;

      if (wHitMode = 11) and (m_Magic43Skill <> nil) and
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (m_Magic43Skill.btLevel < 3) and
        (m_Magic43Skill.MagicInfo.TrainLevel[m_Magic43Skill.btLevel] <= m_Abil.Level) then begin
        TrainSkill(m_Magic43Skill, 1);
        if not CheckMagicLevelup(m_Magic43Skill) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_Magic43Skill.MagicInfo.wMagicId, m_Magic43Skill.btLevel, m_Magic43Skill.nTranPoint, '', 3000);
        end;
      end;

      if (wHitMode = 12) and (m_Magic58Skill <> nil) and
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (m_Magic58Skill.btLevel < 3) and
        (m_Magic58Skill.MagicInfo.TrainLevel[m_Magic58Skill.btLevel] <= m_Abil.Level) then begin
        TrainSkill(m_Magic58Skill, 1);
        if not CheckMagicLevelup(m_Magic58Skill) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_Magic58Skill.MagicInfo.wMagicId, m_Magic58Skill.btLevel, m_Magic58Skill.nTranPoint, '', 3000);
        end;
      end;

      if (wHitMode in [13..15]) and (UserMagic <> nil) and
        (m_btRaceServer in [RC_HEROOBJECT]) and
        (UserMagic.btLevel < 3) and
        (UserMagic.MagicInfo.TrainLevel[UserMagic.btLevel] <= m_Abil.Level) then begin

        TrainSkill(UserMagic, 1);
        if not CheckMagicLevelup(UserMagic) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, UserMagic.MagicInfo.wMagicId, UserMagic.btLevel, UserMagic.nTranPoint, '', 3000);
        end;
      end;


     { if (wHitMode = 13) and (m_Magic60Skill <> nil) and
        (m_btRaceServer = RC_HEROOBJECT) and
        (m_Magic60Skill.btLevel < 3) and
        (m_Magic60Skill.MagicInfo.TrainLevel[m_Magic60Skill.btLevel] <= m_Abil.Level) then begin
        TrainSkill(m_Magic60Skill, 1);
        if not CheckMagicLevelup(m_Magic60Skill) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_Magic60Skill.MagicInfo.wMagicId, m_Magic60Skill.btLevel, m_Magic60Skill.nTranPoint, '', 3000);
        end;
      end;}

      if (wHitMode = 8) and (m_MagicCrsSkill <> nil) and {抱月弯刀}
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (m_MagicCrsSkill.btLevel < 3) and
        (m_MagicCrsSkill.MagicInfo.TrainLevel[m_MagicCrsSkill.btLevel] <= m_Abil.Level) then begin
        TrainSkill(m_MagicCrsSkill, 1);
        if not CheckMagicLevelup(m_MagicCrsSkill) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_MagicCrsSkill.MagicInfo.wMagicId, m_MagicCrsSkill.btLevel, m_MagicCrsSkill.nTranPoint, '', 3000);
        end;
      end;

      if (wHitMode = 16) and (m_MagicZRJFSkill <> nil) and //逐日剑法
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (m_MagicZRJFSkill.btLevel < 3) and
        (m_MagicZRJFSkill.MagicInfo.TrainLevel[m_MagicZRJFSkill.btLevel] <= m_Abil.Level) then begin
        TrainSkill(m_MagicZRJFSkill, 1);
        if not CheckMagicLevelup(m_MagicZRJFSkill) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_MagicZRJFSkill.MagicInfo.wMagicId, m_MagicZRJFSkill.btLevel, m_MagicZRJFSkill.nTranPoint, '', 3000);
        end;
      end;

      if (wHitMode = 100) and (m_Magic100Skill <> nil) and //三绝杀
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (m_Magic100Skill.btLevel < 3) and
        (m_Magic100Skill.MagicInfo.TrainLevel[m_Magic100Skill.btLevel] <= m_Abil.Level) then begin
        TrainSkill(m_Magic100Skill, 1);
        if not CheckMagicLevelup(m_Magic100Skill) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_Magic100Skill.MagicInfo.wMagicId, m_Magic100Skill.btLevel, m_Magic100Skill.nTranPoint, '', 3000);
        end;
      end;
      if (wHitMode = 101) and (m_Magic101Skill <> nil) and //追心刺
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (m_Magic101Skill.btLevel < 3) and
        (m_Magic101Skill.MagicInfo.TrainLevel[m_Magic101Skill.btLevel] <= m_Abil.Level) then begin
        TrainSkill(m_Magic101Skill, 1);
        if not CheckMagicLevelup(m_Magic101Skill) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_Magic101Skill.MagicInfo.wMagicId, m_Magic101Skill.btLevel, m_Magic101Skill.nTranPoint, '', 3000);
        end;
      end;
      if (wHitMode = 102) and (m_Magic102Skill <> nil) and //断岳斩
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (m_Magic102Skill.btLevel < 3) and
        (m_Magic102Skill.MagicInfo.TrainLevel[m_Magic102Skill.btLevel] <= m_Abil.Level) then begin
        TrainSkill(m_Magic102Skill, 1);
        if not CheckMagicLevelup(m_Magic102Skill) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_Magic102Skill.MagicInfo.wMagicId, m_Magic102Skill.btLevel, m_Magic102Skill.nTranPoint, '', 3000);
        end;
      end;
      if (wHitMode = 103) and (m_Magic103Skill <> nil) and //横扫千军
        (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) and
        (m_Magic103Skill.btLevel < 3) and
        (m_Magic103Skill.MagicInfo.TrainLevel[m_Magic103Skill.btLevel] <= m_Abil.Level) then begin
        TrainSkill(m_Magic103Skill, 1);
        if not CheckMagicLevelup(m_Magic103Skill) then begin
          SendDelayMsg(Self, RM_MAGIC_LVEXP, 0, m_Magic103Skill.MagicInfo.wMagicId, m_Magic103Skill.btLevel, m_Magic103Skill.nTranPoint, '', 3000);
        end;
      end;
      Result := True;
    end;

    nCheckCode := 9;
    if (nWeaponDamage > 0) and (m_UseItems[U_WEAPON].wIndex > 0) then DoDamageWeapon(nWeaponDamage);

    if (AttackTarget <> nil) and (AttackTarget.m_btRaceServer <> RC_PLAYOBJECT) then
      AttackTarget.SendMsg(AttackTarget, RM_STRUCK, nPower, AttackTarget.m_WAbil.HP, AttackTarget.m_WAbil.MaxHP, Integer(Self), '');
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg, [m_sCharName, nCheckCode]));
      MainOutMessage(E.Message);
    end;
  end;
end;

procedure TActorObject.SendAttackMsg(wIdent: Word; btDir: Byte; nX, nY: Integer);
begin
  SendRefMsg(wIdent, btDir, nX, nY, 0, '');
end;

function TActorObject.GetBlind(TargeTActorObject: TActorObject): Boolean; //失明状态
var
 { I, } nPercent, nSelfPercent, nTargePercent: Integer;
begin
  Result := False;
  if TargeTActorObject = nil then Exit;
  nSelfPercent := _MIN(m_WAbil.AddPoint[12], 100); //减少进入失明状态
  nTargePercent := _MIN(TargeTActorObject.m_WAbil.AddPoint[10], 100); //增加进入失明状态

  nPercent := _MAX(nTargePercent - nSelfPercent, 0);

  if nPercent > 0 then begin
    if Random(abs(100 - nPercent)) = 0 then begin
      Result := True;
    end;
    {for I := 1 to nPercent do begin
      if Random(abs(100 - nPercent)) = 0 then begin
        Result := True;
        break;
      end;
    end;}
  end;
end;

function TActorObject.GetConfusion(TargeTActorObject: TActorObject): Boolean; //混乱状态
var
  {I, } nPercent, nSelfPercent, nTargePercent: Integer;
begin
  Result := False;
  if TargeTActorObject = nil then Exit;

  nSelfPercent := _MIN(m_WAbil.AddPoint[13], 100);
  nTargePercent := _MIN(TargeTActorObject.m_WAbil.AddPoint[11], 100);

  nPercent := _MAX(nTargePercent - nSelfPercent, 0);

  if nPercent > 0 then begin
    if Random(abs(100 - nPercent)) = 0 then begin
      Result := True;
    end;
    {for I := 1 to nPercent do begin
      if Random(abs(100 - nPercent)) = 0 then begin
        Result := True;
        break;
      end;
    end;}
  end;
end;

procedure TActorObject.SetNewStatus(Status: TNewStatus);
var
  ActorObject: TActorObject;
  LastSetStatus: TActorObject;
begin
  if m_NewStatus <> Status then begin
    LastSetStatus := nil;
    if (m_LastSetStatus <> nil) and (m_LastSetStatus.m_btRaceServer = RC_PLAYOBJECT) then
      LastSetStatus := m_LastSetStatus;
    if LastSetStatus = nil then
      if (m_LastSetStatus <> nil) and (m_LastSetStatus.m_Master <> nil) and (m_LastSetStatus.Master.m_btRaceServer = RC_PLAYOBJECT) then
        LastSetStatus := m_LastSetStatus.Master;

    case Status of
      sNone: begin

          m_LastSetStatus := nil;
        end;
      sBlind: begin
          if (LastSetStatus <> nil) then
            LastSetStatus.SysMsg(Format('[%s]Into blind, continuous automatic discharge after %d seconds', [GetShowNameEx, g_Config.StatusDelayTime[1]]), c_Green, t_Hint);
          if Self <> LastSetStatus then begin
            if (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) then begin
              SysMsg(Format('[%s]Into blind, continuous automatic discharge after %d seconds', [GetShowNameEx, g_Config.StatusDelayTime[1]]), c_Green, t_Hint);
            end else
              if m_Master <> nil then begin
              ActorObject := Master;
              if (ActorObject <> nil) and (ActorObject.m_btRaceServer = RC_PLAYOBJECT) then begin
                ActorObject.SysMsg(Format('[%s]Into blind, continuous automatic discharge after %d seconds', [GetShowNameEx, g_Config.StatusDelayTime[1]]), c_Green, t_Hint);
              end;
            end;
          end;
        end;
      sConfusion: begin
          if (LastSetStatus <> nil) then
            LastSetStatus.SysMsg(Format('[%s]Into chaos, continuous automatic discharge after %d seconds', [GetShowNameEx, g_Config.StatusDelayTime[2]]), c_Green, t_Hint);
          if Self <> LastSetStatus then begin
            if (m_btRaceServer in [RC_PLAYOBJECT, RC_HEROOBJECT]) then begin
              SysMsg(Format('[%s] Into chaos, continuous automatic discharge after %d seconds', [GetShowNameEx, g_Config.StatusDelayTime[2]]), c_Green, t_Hint);
            end else
              if m_Master <> nil then begin
              ActorObject := Master;
              if (ActorObject <> nil) and (ActorObject.m_btRaceServer = RC_PLAYOBJECT) then begin
                ActorObject.SysMsg(Format('[%s] Into chaos, continuous automatic discharge after %d seconds', [GetShowNameEx, g_Config.StatusDelayTime[2]]), c_Green, t_Hint);
              end;
            end;
          end;
        end;
    end;

    m_NewStatus := Status;
    m_dwStatusDelayTick := GetTickCount;
    m_nNewStatusX := m_nCurrX;
    m_nNewStatusY := m_nCurrY;
    case m_NewStatus of
      sNone: begin
          m_nStatusDelayTime := 0;
        end;
      sBlind: begin
          DelTargetCreat;
          //m_TargetCret := nil;
          m_nStatusDelayTime := g_Config.StatusDelayTime[Integer(m_NewStatus)];
        end;
      sConfusion: begin
          DelTargetCreat;
          //m_TargetCret := nil;
          m_nStatusDelayTime := g_Config.StatusDelayTime[Integer(m_NewStatus)];
        end;
    end;
    if (m_btRaceServer = RC_PLAYOBJECT) and (not m_boNotOnlineAddExp) and (not m_boAI) then
      TPlayObject(Self).SendDefMessage(SM_NEWSTATUS, Integer(Self), m_nStatusDelayTime, Integer(m_NewStatus), 0, '');
  end else begin
    if (m_btRaceServer = RC_PLAYOBJECT) and (not m_boNotOnlineAddExp) and (not m_boAI) then
      TPlayObject(Self).SendDefMessage(SM_NEWSTATUS, Integer(Self), m_nStatusDelayTime, Integer(m_NewStatus), 0, '');
  end;
end;

function TActorObject.GetHitStruckDamage(Target: TActorObject; nDamage: Integer): Integer; //获取物理防御
var
  n14, n15: Integer;
  UserMagic: pTUserMagic;
  ActorObject: TActorObject;
begin
  UserMagic := nil;
  ActorObject := Self;
  m_boStartNewShield := False;
  if m_btRaceServer = RC_PLAYOBJECT then begin
    UserMagic := TPlayObject(Self).GetMagicInfo(73);
    if (UserMagic = nil) then begin
      if (TPlayObject(Self).m_MyHero <> nil) then begin
        UserMagic := THeroObject(TPlayObject(Self).m_MyHero).FindMagic(73);
        ActorObject := TPlayObject(Self).m_MyHero;
      end;
    end;
  end else
    if m_btRaceServer = RC_HEROOBJECT then begin
    UserMagic := THeroObject(Self).FindMagic(73);
    if UserMagic = nil then begin
      if m_Master <> nil then begin
        ActorObject := m_Master;
        UserMagic := TPlayObject(m_Master).GetMagicInfo(73);
      end;
    end;
  end else
    if m_btRaceServer = RC_PLAYMOSTER then begin
    UserMagic := TAIObject(Self).FindMagic(73);
    if UserMagic = nil then begin
      if m_Master <> nil then begin
        ActorObject := m_Master;
        if m_Master.m_btRaceServer = RC_PLAYOBJECT then begin
          UserMagic := TPlayObject(m_Master).GetMagicInfo(73);
        end else
          if m_Master.m_btRaceServer = RC_HEROOBJECT then begin
          UserMagic := THeroObject(m_Master).FindMagic(73);
        end else
          if m_btRaceServer = RC_PLAYMOSTER then begin
          UserMagic := TAIObject(m_Master).FindMagic(73);
        end;
      end;
    end;
  end;

  if UserMagic <> nil then begin
    if GetTickCount > m_dwStartNewShield then begin
      if Random(g_Config.nNewShieldUseRate) = 0 then begin
        m_boStartNewShield := True;
      end;
    end;
  end;

  if m_boNotDefendoof then n14 := 0 else
    n14 := LoWord(m_WAbil.AC) + Random(Integer(HiWord(m_WAbil.AC) - LoWord(m_WAbil.AC)) + 1);

  if (UserMagic <> nil) and m_boStartNewShield then n15 := Random(nDamage + n14) * _MAX(UserMagic.btLevel, 1)
  else n15 := n14;

  nDamage := _MAX(0, nDamage - n15);

  m_btNewShieldType := 0;
  if (Random(g_Config.nNewShieldDamageRate) = 0) and (n14 > 0) and m_boStartNewShield then begin
    m_dwStartNewShield := GetTickCount + 1000 * g_Config.nNewShieldWaitTime;
    m_btNewShieldType := 1;
  end;

  if (ActorObject <> nil) and (UserMagic <> nil) then begin
    if (UserMagic.btLevel < 3) then begin
      if UserMagic.MagicInfo.TrainLevel[UserMagic.btLevel] <= ActorObject.m_Abil.Level then begin
        ActorObject.TrainSkill(UserMagic, Random(3) + 1);
        if not ActorObject.CheckMagicLevelup(UserMagic) then begin
          if (ActorObject.m_btRaceServer = RC_PLAYOBJECT) or (ActorObject.m_btRaceServer = RC_HEROOBJECT) then begin
            ActorObject.SendDelayMsg(ActorObject, RM_MAGIC_LVEXP, 0, UserMagic.MagicInfo.wMagicId, UserMagic.btLevel, UserMagic.nTranPoint, '', 1000);
          end;
        end;
      end;
    end;
  end;

  if (m_btLifeAttrib = LA_UNDEAD) and (Target <> nil) then begin
    Inc(nDamage, Target.m_AddAbil.bt1DF);
  end;
  if (nDamage > 0) and m_boAbilMagBubbleDefence then begin
    nDamage := Round((nDamage / 1.0E2) * (m_btMagBubbleDefenceLevel + 2) * 8.0);
    DamageBubbleDefence(nDamage);
  end;

  nDamage := GetAddPowerPoint(True, nDamage);
  m_boNotDefendoof := False;
  {
  if (nDamage > 0) and (m_NewStatus = sNone) then begin
    if (m_NewStatus <> sBlind) and GetBlind(Target) then begin //失明状态
      m_LastSetStatus := Target;
      SetNewStatus(sBlind);
    end else
      if (m_NewStatus <> sConfusion) and GetConfusion(Target) then begin //混乱状态
      m_LastSetStatus := Target;
      SetNewStatus(sConfusion);
    end;
  end;
  }
  if (nDamage > 0) and (m_NewStatus = sNone) then begin
    if Random(2) = 0 then begin
      if GetBlind(Target) then begin //失明状态
        m_LastSetStatus := Target;
        SetNewStatus(sBlind);
      end else
        if GetConfusion(Target) then begin //混乱状态
        m_LastSetStatus := Target;
        SetNewStatus(sConfusion);
      end;
    end else begin
      if GetConfusion(Target) then begin //混乱状态
        m_LastSetStatus := Target;
        SetNewStatus(sConfusion);
      end else
        if GetBlind(Target) then begin //失明状态
        m_LastSetStatus := Target;
        SetNewStatus(sBlind);
      end;
    end;
  end;
  Result := nDamage;
end;

function TActorObject.GetMagStruckDamage(BaseObject: TActorObject; nDamage: Integer): Integer; //获取魔防
var
  n14, n15: Integer;
  UserMagic: pTUserMagic;
  ActorObject: TActorObject;
begin
  UserMagic := nil;
  ActorObject := Self;
  m_boStartNewShield := False;
  if m_btRaceServer = RC_PLAYOBJECT then begin
    UserMagic := TPlayObject(Self).GetMagicInfo(73);
    if (UserMagic = nil) then begin
      if (TPlayObject(Self).m_MyHero <> nil) then begin
        UserMagic := THeroObject(TPlayObject(Self).m_MyHero).FindMagic(73);
        ActorObject := TPlayObject(Self).m_MyHero;
      end;
    end;
  end else
    if m_btRaceServer = RC_HEROOBJECT then begin
    UserMagic := THeroObject(Self).FindMagic(73);
    if UserMagic = nil then begin
      if m_Master <> nil then begin
        ActorObject := m_Master;
        UserMagic := TPlayObject(m_Master).GetMagicInfo(73);
      end;
    end;
  end else
    if m_btRaceServer = RC_PLAYMOSTER then begin
    UserMagic := TAIObject(Self).FindMagic(73);
    if UserMagic = nil then begin
      if m_Master <> nil then begin
        ActorObject := m_Master;
        if m_Master.m_btRaceServer = RC_PLAYOBJECT then begin
          UserMagic := TPlayObject(m_Master).GetMagicInfo(73);
        end else
          if m_Master.m_btRaceServer = RC_HEROOBJECT then begin
          UserMagic := THeroObject(m_Master).FindMagic(73);
        end else
          if m_btRaceServer = RC_PLAYMOSTER then begin
          UserMagic := TAIObject(m_Master).FindMagic(73);
        end;
      end;
    end;
  end;

  if UserMagic <> nil then begin
    if GetTickCount > m_dwStartNewShield then begin
      if Random(g_Config.nNewShieldUseRate) = 0 then begin
        m_boStartNewShield := True;
      end;
    end;
  end;

  if m_boNotDefendoof then n14 := 0 else //忽视防御
    n14 := LoWord(m_WAbil.MAC) + Random(Integer(HiWord(m_WAbil.MAC) - LoWord(m_WAbil.MAC)) + 1);

  if (UserMagic <> nil) and m_boStartNewShield then n15 := Random(nDamage + n14) * _MAX(UserMagic.btLevel, 1)
  else n15 := n14;

  nDamage := _MAX(0, nDamage - n15);

  m_btNewShieldType := 0;
  if (Random(g_Config.nNewShieldDamageRate) = 0) and (n14 > 0) and m_boStartNewShield then begin
    m_dwStartNewShield := GetTickCount + 1000 * g_Config.nNewShieldWaitTime;
    m_btNewShieldType := 1;
  end;

  if (ActorObject <> nil) and (UserMagic <> nil) then begin
    if (UserMagic.btLevel < 3) then begin
      if UserMagic.MagicInfo.TrainLevel[UserMagic.btLevel] <= ActorObject.m_Abil.Level then begin
        ActorObject.TrainSkill(UserMagic, Random(3) + 1);
        if not ActorObject.CheckMagicLevelup(UserMagic) then begin
          if (ActorObject.m_btRaceServer = RC_PLAYOBJECT) or (ActorObject.m_btRaceServer = RC_HEROOBJECT) then begin
            ActorObject.SendDelayMsg(ActorObject, RM_MAGIC_LVEXP, 0, UserMagic.MagicInfo.wMagicId, UserMagic.btLevel, UserMagic.nTranPoint, '', 1000);
          end;
        end;
      end;
    end;
  end;

  if (m_btLifeAttrib = LA_UNDEAD) and (BaseObject <> nil) then begin
    Inc(nDamage, m_AddAbil.bt1DF);
  end;
  if (nDamage > 0) and m_boAbilMagBubbleDefence then begin
    nDamage := Round((nDamage / 1.0E2) * (m_btMagBubbleDefenceLevel + 2) * 8.0);
    DamageBubbleDefence(nDamage);
  end;
  nDamage := GetAddPowerPoint(False, nDamage);
  m_boNotDefendoof := False;

  if (nDamage > 0) and (m_NewStatus = sNone) then begin
    if Random(2) = 0 then begin
      if GetBlind(BaseObject) then begin //失明状态
        m_LastSetStatus := BaseObject;
        SetNewStatus(sBlind);
      end else
        if GetConfusion(BaseObject) then begin //混乱状态
        m_LastSetStatus := BaseObject;
        SetNewStatus(sConfusion);
      end;
    end else begin
      if GetConfusion(BaseObject) then begin //混乱状态
        m_LastSetStatus := BaseObject;
        SetNewStatus(sConfusion);
      end else
        if GetBlind(BaseObject) then begin //失明状态
        m_LastSetStatus := BaseObject;
        SetNewStatus(sBlind);
      end;
    end;
  end;

  Result := nDamage;
end;

procedure TActorObject.StruckDamage(nDamage: Integer);
var
  I: Integer;
  nDam: Integer;
  nDura, nOldDura: Integer;
  PlayObject: TPlayObject;
  StdItem: pTStdItem;
  bo19: Boolean;
begin
  if nDamage <= 0 then Exit;
  nDam := Random(10) + 5;
  if m_wStatusTimeArr[POISON_DAMAGEARMOR {1 0x62}] > 0 then begin
    nDam := Round(nDam * (g_Config.nPosionDamagarmor / 10) {1.2});
    nDamage := Round(nDamage * (g_Config.nPosionDamagarmor / 10) {1.2});
  end;
  bo19 := False;
  if m_UseItems[U_DRESS].wIndex > 0 then begin
    nDura := m_UseItems[U_DRESS].Dura;
    nOldDura := Round(nDura / 1000);
    Dec(nDura, nDam);
    if nDura <= 0 then begin
      if m_btRaceServer = RC_PLAYOBJECT then begin
        PlayObject := TPlayObject(Self);
        PlayObject.SendDelItems(@m_UseItems[U_DRESS]);
        StdItem := UserEngine.GetStdItem(m_UseItems[U_DRESS].wIndex);
            //004BE088
        if StdItem.NeedIdentify = 1 then
          AddGameDataLog('3' + #9 +
            m_sMapName + #9 +
            IntToStr(m_nCurrX) + #9 +
            IntToStr(m_nCurrY) + #9 +
            m_sCharName + #9 +
                        //UserEngine.GetStdItemName(m_UseItems[U_DRESS].wIndex) + #9 +
            StdItem.Name + #9 +
            IntToStr(m_UseItems[U_DRESS].MakeIndex) + #9 +
            BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
            '0');
        m_UseItems[U_DRESS].wIndex := 0;
        FeatureChanged();
      end;
      m_UseItems[U_DRESS].wIndex := 0;
      m_UseItems[U_DRESS].Dura := 0;
      if I = U_HELMET then RefShowName;
      bo19 := True;
    end else begin
      m_UseItems[U_DRESS].Dura := nDura;
    end;
    if nOldDura <> Round(nDura / 1000) then begin
      SendMsg(Self, RM_DURACHANGE, U_DRESS, nDura, m_UseItems[U_DRESS].DuraMax, 0, '');
    end;
  end;
  for I := Low(THumanUseItems) to High(THumanUseItems) do begin
    if (m_UseItems[I].wIndex > 0) and (Random(8) = 0) then begin
      nDura := m_UseItems[I].Dura;
      nOldDura := Round(nDura / 1000);
      Dec(nDura, nDam);
      if nDura <= 0 then begin
        if m_btRaceServer = RC_PLAYOBJECT then begin
          PlayObject := TPlayObject(Self);
          PlayObject.SendDelItems(@m_UseItems[I]);
          StdItem := UserEngine.GetStdItem(m_UseItems[I].wIndex);
          if StdItem.NeedIdentify = 1 then
            AddGameDataLog('3' + #9 +
              m_sMapName + #9 +
              IntToStr(m_nCurrX) + #9 +
              IntToStr(m_nCurrY) + #9 +
              m_sCharName + #9 +
                        //UserEngine.GetStdItemName(m_UseItems[i].wIndex) + #9 +
              StdItem.Name + #9 +
              IntToStr(m_UseItems[I].MakeIndex) + #9 +
              BoolToIntStr(m_btRaceServer = RC_PLAYOBJECT) + #9 +
              '0');
          m_UseItems[I].wIndex := 0;
          FeatureChanged();
        end;
        m_UseItems[I].wIndex := 0;
        m_UseItems[I].Dura := 0;
        bo19 := True;
      end else begin
        m_UseItems[I].Dura := nDura;
        if nOldDura <> Round(nDura / 1000) then begin
          SendMsg(Self, RM_DURACHANGE, I, nDura, m_UseItems[I].DuraMax, 0, '');
        end;
      end;
    end;
  end;
  if bo19 then RecalcAbilitys();
  DamageHealth(nDamage);
end;

procedure TActorObject.StartNewShield(); //护体神盾
//var
//  UserMagic: pTUserMagic;
//  ActorObject: TActorObject;
begin
  if m_boStartNewShield then begin
    m_boStartNewShield := False;
    SendRefMsg(RM_STATE_BUBBLEDEFENCEUP, m_btNewShieldType, Integer(Self), 0, 0, '');
    if m_btNewShieldType <> 0 then begin
      SysMsg('Shield has been broken up!', c_Red, t_Hint);
    end;
    {UserMagic := nil;
    ActorObject := Self;
    if m_btRaceServer = RC_PLAYOBJECT then begin
      UserMagic := TPlayObject(Self).GetMagicInfo(73);
      if (UserMagic = nil) then begin
        if (TPlayObject(Self).m_MyHero <> nil) then begin
          UserMagic := THeroObject(TPlayObject(Self).m_MyHero).FindMagic(73);
          ActorObject := TPlayObject(Self).m_MyHero;
        end;
      end;
    end else
      if m_btRaceServer = RC_HEROOBJECT then begin
      UserMagic := THeroObject(Self).FindMagic(73);
      if UserMagic = nil then begin
        if m_Master <> nil then begin
          UserMagic := TPlayObject(m_Master).GetMagicInfo(73);
          ActorObject := m_Master;
        end;
      end;
    end else
      if m_btRaceServer = RC_PLAYMOSTER then begin
      UserMagic := TAIObject(Self).FindMagic(73);
      if UserMagic = nil then begin
        if m_Master <> nil then begin
          ActorObject := m_Master;
          if m_Master.m_btRaceServer = RC_PLAYOBJECT then begin
            UserMagic := TPlayObject(m_Master).GetMagicInfo(73);
          end else
            if m_Master.m_btRaceServer = RC_HEROOBJECT then begin
            UserMagic := THeroObject(m_Master).FindMagic(73);
          end else
            if m_btRaceServer = RC_PLAYMOSTER then begin
            UserMagic := TAIObject(m_Master).FindMagic(73);
          end;
        end;
      end;
    end;

    if (ActorObject <> nil) and (UserMagic <> nil) then begin
      if (UserMagic.btLevel < 3) then begin
        if UserMagic.MagicInfo.TrainLevel[UserMagic.btLevel] <= ActorObject.m_Abil.Level then begin
          ActorObject.TrainSkill(UserMagic, Random(3) + 1);
          if not ActorObject.CheckMagicLevelup(UserMagic) then begin
            if (ActorObject.m_btRaceServer = RC_PLAYOBJECT) or (ActorObject.m_btRaceServer = RC_HEROOBJECT) then begin
              ActorObject.SendDelayMsg(ActorObject, RM_MAGIC_LVEXP, 0, UserMagic.MagicInfo.wMagicId, UserMagic.btLevel, UserMagic.nTranPoint, '', 1000);
            end;
          end;
        end;
      end;
    end;
    }
  end;
end;

function TActorObject.GeTActorObjectInfo(): string;
begin
  Result := m_sCharName + ' ' +
    'Map:' + m_sMapName + '(' + m_PEnvir.sMapDesc + ') ' +
    'Cords:' + IntToStr(m_nCurrX) + '/' + IntToStr(m_nCurrY) + ' ' +
    'Level:' + IntToStr(m_Abil.Level) + ' ' +
    'Exp:' + IntToStr(m_Abil.Exp) + ' ' +
    'HP: ' + IntToStr(m_WAbil.HP) + '-' + IntToStr(m_WAbil.MaxHP) + ' ' +
    'MP: ' + IntToStr(m_WAbil.MP) + '-' + IntToStr(m_WAbil.MaxMP) + ' ' +
    'DC: ' + IntToStr(LoWord(m_WAbil.DC)) + '-' + IntToStr(HiWord(m_WAbil.DC)) + ' ' +
    'MC: ' + IntToStr(LoWord(m_WAbil.MC)) + '-' + IntToStr(HiWord(m_WAbil.MC)) + ' ' +
    'SC: ' + IntToStr(LoWord(m_WAbil.SC)) + '-' + IntToStr(HiWord(m_WAbil.SC)) + ' ' +
    'AC: ' + IntToStr(LoWord(m_WAbil.AC)) + '-' + IntToStr(HiWord(m_WAbil.AC)) + ' ' +
    'MAC: ' + IntToStr(LoWord(m_WAbil.MAC)) + '-' + IntToStr(HiWord(m_WAbil.MAC)) + ' ' +
    'ACC:' + IntToStr(m_btHitPoint) + ' ' +
    'Agility:' + IntToStr(m_btSpeedPoint);
end;

function TActorObject.GetBackPosition(var nX, nY: Integer): Boolean;
var
  Envir: TEnvirnoment;
begin
  Envir := m_PEnvir;
  nX := m_nCurrX;
  nY := m_nCurrY;
  case m_btDirection of
    DR_UP: if nY < (Envir.m_nHeight - 1) then Inc(nY);
    DR_DOWN: if nY > 0 then Dec(nY);
    DR_LEFT: if nX < (Envir.m_nWidth - 1) then Inc(nX);
    DR_RIGHT: if nX > 0 then Dec(nX);
    DR_UPLEFT: begin
        if (nX < (Envir.m_nWidth - 1)) and (nY < (Envir.m_nHeight - 1)) then begin
          Inc(nX);
          Inc(nY);
        end;
      end;
    DR_UPRIGHT: begin
        if (nX < (Envir.m_nWidth - 1)) and (nY > 0) then begin
          Dec(nX);
          Inc(nY);
        end
      end;
    DR_DOWNLEFT: begin
        if (nX > 0) and (nY < (Envir.m_nHeight - 1)) then begin
          Inc(nX);
          Dec(nY);
        end;
      end;
    DR_DOWNRIGHT: begin
        if (nX > 0) and (nY > 0) then begin
          Dec(nX);
          Dec(nY);
        end;
      end;
  end;
  Result := True;
end;

function TActorObject.GetBackPosition(btDirection: Byte; var nX, nY: Integer): Boolean;
var
  Envir: TEnvirnoment;
begin
  Envir := m_PEnvir;
  nX := m_nCurrX;
  nY := m_nCurrY;
  case btDirection of
    DR_UP: if nY < (Envir.m_nHeight - 1) then Inc(nY);
    DR_DOWN: if nY > 0 then Dec(nY);
    DR_LEFT: if nX < (Envir.m_nWidth - 1) then Inc(nX);
    DR_RIGHT: if nX > 0 then Dec(nX);
    DR_UPLEFT: begin
        if (nX < (Envir.m_nWidth - 1)) and (nY < (Envir.m_nHeight - 1)) then begin
          Inc(nX);
          Inc(nY);
        end;
      end;
    DR_UPRIGHT: begin
        if (nX < (Envir.m_nWidth - 1)) and (nY > 0) then begin
          Dec(nX);
          Inc(nY);
        end
      end;
    DR_DOWNLEFT: begin
        if (nX > 0) and (nY < (Envir.m_nHeight - 1)) then begin
          Inc(nX);
          Dec(nY);
        end;
      end;
    DR_DOWNRIGHT: begin
        if (nX > 0) and (nY > 0) then begin
          Dec(nX);
          Dec(nY);
        end;
      end;
  end;
  Result := True;
end;

procedure TAIObject.DelTargetCreat;
begin
  inherited;
  m_boTarget := False;
  m_dwSearchTargetTick := GetTickCount;
end;

function TActorObject.MakePosion(nType, nTime, nPoint: Integer): Boolean;
var
  nOldCharStatus: Integer;
begin
  Result := False;

  if m_boVirusImmunity then Exit; //毒免疫

  if nType < MAX_STATUS_ATTRIBUTE then begin
    nOldCharStatus := m_nCharStatus;
    if m_wStatusTimeArr[nType] > 0 then begin
      if m_wStatusTimeArr[nType] < nTime then begin
        m_wStatusTimeArr[nType] := nTime;
      end;
    end else begin //004C35FF
      m_wStatusTimeArr[nType] := nTime;
      if nType = POISON_DONTMOVE then begin //if got frozen then slow down hitspeed and walkspeed
        m_nnonfrzWalkSpeed := m_nWalkSpeed;
        m_nWalkSpeed:= m_nWalkSpeed * 3;
        m_nnonfrzNextHitTime := m_nNextHitTime;
        m_nNextHitTime := m_nNextHitTime * 3;
      end;
    end;
    m_wStatusTimeArr[nType] := _MIN(m_wStatusTimeArr[nType], 60000 - 1);

    m_dwStatusArrTick[nType] := GetTickCount();
    m_nCharStatus := GetCharStatus();
    m_btGreenPoisoningPoint := nPoint;
    if nOldCharStatus <> m_nCharStatus then StatusChanged();
    if m_btRaceServer = RC_PLAYOBJECT then begin
{$IF DEBUG = 1}
      SysMsg(Format('Poisoned! %d seconds %d points', [nTime, nPoint]), c_Red, t_Hint);
{$ELSE}
      SysMsg(sYouPoisoned, c_Red, t_Hint);
      SysMsg(Format('For %d seconds..', [nTime]), c_Red, t_Hint);
{$IFEND}
    end;
    Result := True;
  end;
end;

function TActorObject.sub_4DD704: Boolean;
var
  I: Integer;
  SendMessage: pTSendMessage;
begin
  Result := False;
  EnterCriticalSection(ProcessMsgCriticalSection);
  try
    for I := 0 to m_MsgList.Count - 1 do begin
      SendMessage := m_MsgList.Items[I];
      if (SendMessage <> nil) and (SendMessage.wIdent = RM_10401) then begin
        Result := True;
        Break;
      end;
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
end;

procedure TPlayObject.SendSaveItemList(nBaseObject: Integer);
var
  I: Integer;
//  nCount: Integer;
  Item: pTStdItem;
  sSENDMSG: string;
  ClientItem: TClientItem;
  OClientItem: TOClientItem;
  StdItem: TStdItem;
  UserItem: pTUserItem;
  sUserItemName: string;
//  ItemList: TStringList;
begin
  if m_boNotOnlineAddExp or m_boAI then Exit;
  if not m_boHeroVersion then begin
    sSENDMSG := '';
    for I := 0 to m_StorageItemList.Count - 1 do begin
      UserItem := m_StorageItemList.Items[I];
      Item := UserEngine.GetStdItem(UserItem.wIndex);
      if Item <> nil then begin
        StdItem := Item^;
        ItemUnit.GetItemAddValue(UserItem, StdItem);
        //Move(StdItem,ClientItem.S,SizeOf(TStdItem));
        CopyStdItemToOStdItem(@StdItem, @OClientItem.s);
        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName <> '' then
          OClientItem.s.Name := sUserItemName;
        OClientItem.Dura := UserItem.Dura;
        OClientItem.DuraMax := UserItem.DuraMax;
        OClientItem.MakeIndex := UserItem.MakeIndex;
        sSENDMSG := sSENDMSG + EncodeBuffer(@OClientItem, SizeOf(TOClientItem)) + '/';
      end;
    end;
    m_DefMsg := MakeDefaultMsg(SM_SAVEITEMLIST, nBaseObject, 0, 0, m_StorageItemList.Count);
    SendSocket(@m_DefMsg, sSENDMSG);
  end else begin
    sSENDMSG := '';
    //if m_StorageItemList.Count <= 20 then begin
    for I := 0 to m_StorageItemList.Count - 1 do begin
      UserItem := m_StorageItemList.Items[I];
      Item := UserEngine.GetStdItem(UserItem.wIndex);
      if Item <> nil then begin

        if (UserItem.AddValue[0] = 1) and (GetDayCount(UserItem.MaxDate, Now) <= 0) then Continue; //删除到期装备

        StdItem := Item^;
        ItemUnit.GetItemAddValue(UserItem, StdItem);
        Move(StdItem, ClientItem.s, SizeOf(TStdItem));
        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName <> '' then
          ClientItem.s.Name := sUserItemName;

        if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
          ClientItem.s.Name := '( )' + ClientItem.s.Name;
        end;

        ClientItem.Dura := UserItem.Dura;
        ClientItem.DuraMax := UserItem.DuraMax;
        ClientItem.MakeIndex := UserItem.MakeIndex;

        ClientItem.s.AddValue := UserItem.AddValue;
        ClientItem.s.AddPoint := UserItem.AddPoint;
        ClientItem.s.MaxDate := UserItem.MaxDate;
        //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

        sSENDMSG := sSENDMSG + EncodeBuffer(@ClientItem, SizeOf(TClientItem)) + '/';
      end;
    end;
    m_DefMsg := MakeDefaultMsg(SM_SAVEITEMLIST, nBaseObject, 0, 0, m_StorageItemList.Count);
    SendSocket(@m_DefMsg, sSENDMSG);
  end;
end;

procedure TPlayObject.SendSaveBigStorageItemList(nBaseObject: Integer; nPage: Integer);
var
  I: Integer;
  Item: pTStdItem;
  sSENDMSG: string;
  ClientItem: TClientItem;
  OClientItem: TOClientItem;
  StdItem: TStdItem;
  //UserItem: pTUserItem;
  sUserItemName: string;
//  List: TList;
  BigStorage: pTBigStorage;
  nCount: Integer;
  nMaxPageCount: Integer;
  nIndex: Integer;
  StorageList: TStorageList;
  ItemList: TStringList;

  function GetPageCount(nListCount: Integer): Integer;
  begin
    Result := 0;
    if nListCount >= 46 then begin
      Result := nListCount div 46;
      if (nListCount mod 46) > 0 then Inc(Result);
    end;
  end;
begin
  sSENDMSG := '';
  nCount := 0;
  StorageList := nil;
  if m_boNotOnlineAddExp or m_boAI then Exit;
  if (g_Storage.GetStorageList(m_sCharName, StorageList) >= 0) and (StorageList <> nil) then begin
    if not m_boHeroVersion then begin
      nMaxPageCount := GetPageCount(StorageList.Count);
      nIndex := nPage * 46;
      if nIndex >= StorageList.Count then begin
        nIndex := (nMaxPageCount - 1) * 46;
      end;
      if nIndex < 0 then nIndex := 0;
      if nIndex < StorageList.Count then begin
        for I := nIndex to StorageList.Count - 1 do begin
          BigStorage := StorageList.Items[I];
          Item := UserEngine.GetStdItem(BigStorage.UserItem.wIndex);
          if Item <> nil then begin
            StdItem := Item^;
            ItemUnit.GetItemAddValue(@BigStorage.UserItem, StdItem);
          //Move(StdItem,ClientItem.S,SizeOf(TStdItem));
            CopyStdItemToOStdItem(@StdItem, @OClientItem.s);
          //取自定义物品名称
            sUserItemName := '';
            if BigStorage.UserItem.btValue[13] = 1 then
              sUserItemName := ItemUnit.GetCustomItemName(BigStorage.UserItem.MakeIndex, BigStorage.UserItem.wIndex);
            if sUserItemName <> '' then
              OClientItem.s.Name := sUserItemName;
            OClientItem.Dura := BigStorage.UserItem.Dura;
            OClientItem.DuraMax := BigStorage.UserItem.DuraMax;
            OClientItem.MakeIndex := BigStorage.UserItem.MakeIndex;
            sSENDMSG := sSENDMSG + EncodeBuffer(@OClientItem, SizeOf(TOClientItem)) + '/';
            Inc(nCount);
            if nCount >= 46 then Break;
          end;
        end;
      end;
      m_DefMsg := MakeDefaultMsg(SM_SAVEITEMLIST, nBaseObject, 0, 0, nCount);
      SendSocket(@m_DefMsg, sSENDMSG);
    end else begin
      nMaxPageCount := GetPageCount(StorageList.Count);
      nIndex := nPage * 46;
      if nIndex >= StorageList.Count then begin
        nIndex := (nMaxPageCount - 1) * 46;
      end;
      if nIndex < 0 then nIndex := 0;
      if nIndex < StorageList.Count then begin
        //if StorageList.Count - nIndex <= 19 then begin
        for I := nIndex to StorageList.Count - 1 do begin
          BigStorage := StorageList.Items[I];
          Item := UserEngine.GetStdItem(BigStorage.UserItem.wIndex);
        //sItemNewName:=GetItemName(UserItem.MakeIndex);
          Item := UserEngine.GetStdItem(BigStorage.UserItem.wIndex);
          if Item <> nil then begin

            if (BigStorage.UserItem.AddValue[0] = 1) and (GetDayCount(BigStorage.UserItem.MaxDate, Now) <= 0) then Continue; //删除到期装备

            StdItem := Item^;
            ItemUnit.GetItemAddValue(@BigStorage.UserItem, StdItem);
            Move(StdItem, ClientItem.s, SizeOf(TStdItem));
          //取自定义物品名称
            sUserItemName := '';
            if BigStorage.UserItem.btValue[13] = 1 then
              sUserItemName := ItemUnit.GetCustomItemName(BigStorage.UserItem.MakeIndex, BigStorage.UserItem.wIndex);
            if sUserItemName <> '' then
              ClientItem.s.Name := sUserItemName;

            if CheckItemBindUse(@BigStorage.UserItem, False) = 2 then begin //绑定物品
              ClientItem.s.Name := '( )' + ClientItem.s.Name;
            end;

            ClientItem.Dura := BigStorage.UserItem.Dura;
            ClientItem.DuraMax := BigStorage.UserItem.DuraMax;
            ClientItem.MakeIndex := BigStorage.UserItem.MakeIndex;

            ClientItem.s.AddValue := BigStorage.UserItem.AddValue;
            ClientItem.s.AddPoint := BigStorage.UserItem.AddPoint;
            ClientItem.s.MaxDate := BigStorage.UserItem.MaxDate;
            //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, @BigStorage.UseItems);

            sSENDMSG := sSENDMSG + EncodeBuffer(@ClientItem, SizeOf(TClientItem)) + '/';
            Inc(nCount);
            if nCount >= 46 then Break;
          end;
        end;
        m_DefMsg := MakeDefaultMsg(SM_SAVEITEMLIST, nBaseObject, 0, 0, nCount);
        SendSocket(@m_DefMsg, sSENDMSG);
      end;
    end;
  end;
end;

procedure TPlayObject.SendChangeGuildName;
begin
  if m_boNotOnlineAddExp or m_boAI then Exit;
  if m_MyGuild <> nil then begin
    SendDefMessage(SM_CHANGEGUILDNAME, 0, 0, 0, 0, TGUild(m_MyGuild).sGuildName + '/' + m_sGuildRankName);
  end else begin
    SendDefMessage(SM_CHANGEGUILDNAME, 0, 0, 0, 0, '');
  end;
end;

procedure TPlayObject.SendDelItemList(ItemList: TStringList);
var
  I: Integer;
  s10: string;
begin
  if m_boNotOnlineAddExp or m_boAI then Exit;
  s10 := '';
  for I := 0 to ItemList.Count - 1 do begin
    s10 := s10 + ItemList.Strings[I] + '/' + IntToStr(Integer(ItemList.Objects[I])) + '/';
  end;
  m_DefMsg := MakeDefaultMsg(SM_DELITEMS, 0, 0, 0, ItemList.Count);
  SendSocket(@m_DefMsg, EncodeString(s10));
end;

procedure TPlayObject.SendDelItems(UserItem: pTUserItem);
var
  StdItem: pTStdItem;
  StdItem80: TStdItem;
  ClientItem: TClientItem;
  OClientItem: TOClientItem;
  sUserItemName: string;
begin
  if m_boNotOnlineAddExp or m_boAI then Exit;
  //sItemNewName:=GetItemName(UserItem.MakeIndex);
  if (not m_boHeroVersion) and (m_dwClientTick = 0) then begin
    StdItem := UserEngine.GetStdItem(UserItem.wIndex);
    if StdItem <> nil then begin
      StdItem80 := StdItem^;
      ItemUnit.GetItemAddValue(@UserItem, StdItem80);
      //Move(StdItem80,ClientItem.S,SizeOf(TStdItem));
      CopyStdItemToOStdItem(@StdItem80, @OClientItem.s);

      //取自定义物品名称
      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName <> '' then
        OClientItem.s.Name := sUserItemName;

      OClientItem.MakeIndex := UserItem.MakeIndex;
      OClientItem.Dura := UserItem.Dura;
      OClientItem.DuraMax := UserItem.DuraMax;
      if StdItem.StdMode = 50 then begin
        OClientItem.s.Name := OClientItem.s.Name + ' #' + IntToStr(UserItem.Dura);
      end;
      m_DefMsg := MakeDefaultMsg(SM_DELITEM, Integer(Self), 0, 0, 1);
      SendSocket(@m_DefMsg, EncodeBuffer(@OClientItem, SizeOf(TOClientItem)));
    end;
  end else begin
    StdItem := UserEngine.GetStdItem(UserItem.wIndex);
    if StdItem <> nil then begin
      StdItem80 := StdItem^;
      ItemUnit.GetItemAddValue(@UserItem, StdItem80);
      Move(StdItem80, ClientItem.s, SizeOf(TStdItem));
      //取自定义物品名称
      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName <> '' then
        ClientItem.s.Name := sUserItemName;

      if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        ClientItem.s.Name := '( )' + ClientItem.s.Name;
      end;

      ClientItem.MakeIndex := UserItem.MakeIndex;
      ClientItem.Dura := UserItem.Dura;
      ClientItem.DuraMax := UserItem.DuraMax;

      ClientItem.s.AddValue := UserItem.AddValue;
      ClientItem.s.AddPoint := UserItem.AddPoint;
      ClientItem.s.MaxDate := UserItem.MaxDate;
      //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

      if StdItem.StdMode = 50 then begin
        ClientItem.s.Name := ClientItem.s.Name + ' #' + IntToStr(UserItem.Dura);
      end;
      m_DefMsg := MakeDefaultMsg(SM_DELITEM, Integer(Self), 0, 0, 1);
      SendSocket(@m_DefMsg, EncodeBuffer(@ClientItem, SizeOf(TClientItem)));
    end;
  end;
end;

procedure TPlayObject.SendUpdateItem(nIndex: Integer; UserItem: pTUserItem);
var
  StdItem: pTStdItem;
  StdItem80: TStdItem;
  ClientItem: TClientItem;
  sUserItemName: string;
begin
  if m_boNotOnlineAddExp or m_boAI then Exit;
  StdItem := UserEngine.GetStdItem(UserItem.wIndex);
  if StdItem <> nil then begin
    StdItem80 := StdItem^;
    ItemUnit.GetItemAddValue(UserItem, StdItem80);
    ClientItem.s := StdItem80;
      //取自定义物品名称
    sUserItemName := '';
    if UserItem.btValue[13] = 1 then
      sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
    if sUserItemName <> '' then
      ClientItem.s.Name := sUserItemName;

    if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
      ClientItem.s.Name := '( )' + ClientItem.s.Name;
    end;

    ClientItem.MakeIndex := UserItem.MakeIndex;
    ClientItem.Dura := UserItem.Dura;
    ClientItem.DuraMax := UserItem.DuraMax;

    ClientItem.s.AddValue := UserItem.AddValue;
    ClientItem.s.AddPoint := UserItem.AddPoint;
    ClientItem.s.MaxDate := UserItem.MaxDate;
    //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

    if StdItem.StdMode = 50 then begin
      ClientItem.s.Name := ClientItem.s.Name + ' #' + IntToStr(UserItem.Dura);
    end;
    m_DefMsg := MakeDefaultMsg(SM_UPDATEITEM2, Integer(Self), nIndex, 0, 1);
    SendSocket(@m_DefMsg, EncodeBuffer(@ClientItem, SizeOf(TClientItem)));
  end;
end;

procedure TPlayObject.SendUpdateItem(UserItem: pTUserItem);
var
  StdItem: pTStdItem;
  StdItem80: TStdItem;
  ClientItem: TClientItem;
  OClientItem: TOClientItem;
  sUserItemName: string;
begin
  if m_boNotOnlineAddExp or m_boAI then Exit;
  if (not m_boHeroVersion) and (m_dwClientTick = 0) then begin
    StdItem := UserEngine.GetStdItem(UserItem.wIndex);
    if StdItem <> nil then begin
      StdItem80 := StdItem^;
      ItemUnit.GetItemAddValue(UserItem, StdItem80);
      //Move(StdItem80,ClientItem.S,SizeOf(TStdItem));
      CopyStdItemToOStdItem(@StdItem80, @OClientItem.s);

      //取自定义物品名称
      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName <> '' then
        OClientItem.s.Name := sUserItemName;

      OClientItem.MakeIndex := UserItem.MakeIndex;
      OClientItem.Dura := UserItem.Dura;
      OClientItem.DuraMax := UserItem.DuraMax;
      if StdItem.StdMode = 50 then begin
        OClientItem.s.Name := ClientItem.s.Name + ' #' + IntToStr(UserItem.Dura);
      end;
      m_DefMsg := MakeDefaultMsg(SM_UPDATEITEM, Integer(Self), 0, 0, 1);
      SendSocket(@m_DefMsg, EncodeBuffer(@OClientItem, SizeOf(TOClientItem)));
    end;
  end else begin
    StdItem := UserEngine.GetStdItem(UserItem.wIndex);
    if StdItem <> nil then begin
      StdItem80 := StdItem^;
      ItemUnit.GetItemAddValue(UserItem, StdItem80);
      ClientItem.s := StdItem80;

      //取自定义物品名称
      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName <> '' then
        ClientItem.s.Name := sUserItemName;

      if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        ClientItem.s.Name := '( )' + ClientItem.s.Name;
      end;

      ClientItem.MakeIndex := UserItem.MakeIndex;
      ClientItem.Dura := UserItem.Dura;
      ClientItem.DuraMax := UserItem.DuraMax;

      ClientItem.s.AddValue := UserItem.AddValue;
      ClientItem.s.AddPoint := UserItem.AddPoint;
      ClientItem.s.MaxDate := UserItem.MaxDate;
      //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

      if StdItem.StdMode = 50 then begin
        ClientItem.s.Name := ClientItem.s.Name + ' #' + IntToStr(UserItem.Dura);
      end;
      m_DefMsg := MakeDefaultMsg(SM_UPDATEITEM, Integer(Self), 0, 0, 1);
      SendSocket(@m_DefMsg, EncodeBuffer(@ClientItem, SizeOf(TClientItem)));
    end;
  end;
end;

procedure TPlayObject.SendChangeItemFail();
begin
  if m_boUpgradeNPC and (not m_boNotOnlineAddExp) and (not m_boAI) then
    SendMsg(Self, RM_SENDCHANGEITEM_FAIL, 0, 0, 0, 0, '');
end;

procedure TPlayObject.SendUpdateChangeItem(UserItem: pTUserItem);
var
  StdItem: pTStdItem;
  StdItem80: TStdItem;
  ClientItem: TClientItem;
//  OClientItem: TOClientItem;
  sUserItemName: string;
begin
  if m_boNotOnlineAddExp or m_boAI then Exit;
  if m_boUpgradeNPC then begin
    StdItem := UserEngine.GetStdItem(UserItem.wIndex);
    if StdItem <> nil then begin
      StdItem80 := StdItem^;
      ItemUnit.GetItemAddValue(UserItem, StdItem80);
      ClientItem.s := StdItem80;
    //取自定义物品名称
      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName <> '' then
        ClientItem.s.Name := sUserItemName;

      if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        ClientItem.s.Name := '( )' + ClientItem.s.Name;
      end;

      ClientItem.MakeIndex := UserItem.MakeIndex;
      ClientItem.Dura := UserItem.Dura;
      ClientItem.DuraMax := UserItem.DuraMax;


      ClientItem.s.AddValue := UserItem.AddValue;
      ClientItem.s.AddPoint := UserItem.AddPoint;
      ClientItem.s.MaxDate := UserItem.MaxDate;
      //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

      if StdItem.StdMode = 50 then begin
        ClientItem.s.Name := ClientItem.s.Name + ' #' + IntToStr(UserItem.Dura);
      end;
      m_DefMsg := MakeDefaultMsg(SM_SENDCHANGEITEM_OK, 0, 0, 0, 1);
      SendSocket(@m_DefMsg, EncodeBuffer(@ClientItem, SizeOf(TClientItem)));
    end;
  end else begin
    SendUpdateItem(0, UserItem);
  end;
end;

function TPlayObject.CheckTakeOnItems(nWhere: Integer; var StdItem: TStdItem): Boolean;
var
  Castle: TUserCastle;
begin
  Result := False;
  if (StdItem.StdMode = 10) and (m_btGender <> 0) then begin
    SysMsg(sWearNotOfWoMan, c_Red, t_Hint);
    Exit;
  end;
  if (StdItem.StdMode = 11) and (m_btGender <> 1) then begin
    SysMsg(sWearNotOfMan, c_Red, t_Hint);
    Exit;
  end;
  if (nWhere = 1) or (nWhere = 2) then begin
    if StdItem.Weight > m_WAbil.MaxHandWeight then begin
      SysMsg(sHandWeightNot, c_Red, t_Hint);
      Exit;
    end;
  end else begin
    if (StdItem.Weight + GetUserItemWeitht(nWhere)) > m_WAbil.MaxWearWeight then begin
      SysMsg(sWearWeightNot, c_Red, t_Hint);
      Exit;
    end;
  end;
  Castle := g_CastleManager.IsCastleMember(Self);
  case StdItem.Need of //
    0: begin
        if m_Abil.Level >= StdItem.NeedLevel then begin
          Result := True;
        end else begin
          SysMsg(g_sLevelNot, c_Red, t_Hint);
        end;
      end;
    1: begin
        if HiWord(m_WAbil.DC) >= StdItem.NeedLevel then begin
          Result := True;
        end else begin
          SysMsg(g_sDCNot, c_Red, t_Hint);
        end;
      end;
    10: begin
        if (m_btJob = LoWord(StdItem.NeedLevel)) and (m_Abil.Level >= HiWord(StdItem.NeedLevel)) then begin
          Result := True;
        end else begin
          SysMsg(g_sJobOrLevelNot, c_Red, t_Hint);
        end;
      end;
    11: begin
        if (m_btJob = LoWord(StdItem.NeedLevel)) and (HiWord(m_WAbil.DC) >= HiWord(StdItem.NeedLevel)) then begin
          Result := True;
        end else begin
          SysMsg(g_sJobOrDCNot, c_Red, t_Hint);
        end;
      end;
    12: begin
        if (m_btJob = LoWord(StdItem.NeedLevel)) and (HiWord(m_WAbil.MC) >= HiWord(StdItem.NeedLevel)) then begin
          Result := True;
        end else begin
          SysMsg(g_sJobOrMCNot, c_Red, t_Hint);
        end;
      end;
    13: begin
        if (m_btJob = LoWord(StdItem.NeedLevel)) and (HiWord(m_WAbil.SC) >= HiWord(StdItem.NeedLevel)) then begin
          Result := True;
        end else begin
          SysMsg(g_sJobOrSCNot, c_Red, t_Hint);
        end;
      end;
    2: begin
        if HiWord(m_WAbil.MC) >= StdItem.NeedLevel then begin
          Result := True;
        end else begin
          SysMsg(g_sMCNot, c_Red, t_Hint);
        end;
      end;
    3: begin
        if HiWord(m_WAbil.SC) >= StdItem.NeedLevel then begin
          Result := True;
        end else begin
          SysMsg(g_sSCNot, c_Red, t_Hint);
        end;
      end;
    4: begin
        if m_btReLevel >= StdItem.NeedLevel then begin
          Result := True;
        end else begin
          SysMsg(g_sReNewLevelNot, c_Red, t_Hint);
        end;
      end;
    40: begin
        if m_btReLevel >= LoWord(StdItem.NeedLevel) then begin
          if m_Abil.Level >= HiWord(StdItem.NeedLevel) then begin
            Result := True;
          end else begin
            SysMsg(g_sLevelNot, c_Red, t_Hint);
          end;
        end else begin
          SysMsg(g_sReNewLevelNot, c_Red, t_Hint);
        end;
      end;
    41: begin
        if m_btReLevel >= LoWord(StdItem.NeedLevel) then begin
          if HiWord(m_WAbil.DC) >= HiWord(StdItem.NeedLevel) then begin
            Result := True;
          end else begin
            SysMsg(g_sDCNot, c_Red, t_Hint);
          end;
        end else begin
          SysMsg(g_sReNewLevelNot, c_Red, t_Hint);
        end;
      end;
    42: begin
        if m_btReLevel >= LoWord(StdItem.NeedLevel) then begin
          if HiWord(m_WAbil.MC) >= HiWord(StdItem.NeedLevel) then begin
            Result := True;
          end else begin
            SysMsg(g_sMCNot, c_Red, t_Hint);
          end;
        end else begin
          SysMsg(g_sReNewLevelNot, c_Red, t_Hint);
        end;
      end;
    43: begin
        if m_btReLevel >= LoWord(StdItem.NeedLevel) then begin
          if HiWord(m_WAbil.SC) >= HiWord(StdItem.NeedLevel) then begin
            Result := True;
          end else begin
            SysMsg(g_sSCNot, c_Red, t_Hint);
          end;
        end else begin
          SysMsg(g_sReNewLevelNot, c_Red, t_Hint);
        end;
      end;
    44: begin
        if m_btReLevel >= LoWord(StdItem.NeedLevel) then begin
          if m_btCreditPoint >= HiWord(StdItem.NeedLevel) then begin
            Result := True;
          end else begin
            SysMsg(g_sCreditPointNot, c_Red, t_Hint);
          end;
        end else begin
          SysMsg(g_sReNewLevelNot, c_Red, t_Hint);
        end;
      end;
    5: begin
        if m_btCreditPoint >= StdItem.NeedLevel then begin
          Result := True;
        end else begin
          SysMsg(g_sCreditPointNot, c_Red, t_Hint);
        end;
      end;
    6: begin
        if (m_MyGuild <> nil) then begin
          Result := True;
        end else begin
          SysMsg(g_sGuildNot, c_Red, t_Hint);
        end;
      end;
    60: begin
        if (m_MyGuild <> nil) and (m_nGuildRankNo = 1) then begin
          Result := True;
        end else begin
          SysMsg(g_sGuildMasterNot, c_Red, t_Hint);
        end;
      end;
    7: begin
        //      if (m_MyGuild <> nil) and (UserCastle.m_MasterGuild = m_MyGuild) then begin
        if (m_MyGuild <> nil) and (Castle <> nil) then begin
          Result := True;
        end else begin
          SysMsg(g_sSabukHumanNot, c_Red, t_Hint);
        end;
      end;
    70: begin
        //      if (m_MyGuild <> nil) and (UserCastle.m_MasterGuild = m_MyGuild) and (m_nGuildRankNo = 1) then begin
        if (m_MyGuild <> nil) and (Castle <> nil) and (m_nGuildRankNo = 1) then begin
          if m_Abil.Level >= StdItem.NeedLevel then begin
            Result := True;
          end else begin
            SysMsg(g_sLevelNot, c_Red, t_Hint);
          end;
        end else begin
          SysMsg(g_sSabukMasterManNot, c_Red, t_Hint);
        end;
      end;
    8: begin
        if m_nMemberType <> 0 then begin
          Result := True;
        end else begin
          SysMsg(g_sMemberNot, c_Red, t_Hint);
        end;
      end;
    81: begin
        if (m_nMemberType = LoWord(StdItem.NeedLevel)) and (m_nMemberLevel >= HiWord(StdItem.NeedLevel)) then begin
          Result := True;
        end else begin
          SysMsg(g_sMemberTypeNot, c_Red, t_Hint);
        end;
      end;
    82: begin
        if (m_nMemberType >= LoWord(StdItem.NeedLevel)) and (m_nMemberLevel >= HiWord(StdItem.NeedLevel)) then begin
          Result := True;
        end else begin
          SysMsg(g_sMemberTypeNot, c_Red, t_Hint);
        end;
      end;
  end;
  //if not Result then SysMsg(g_sCanottWearIt,c_Red,t_Hint);
end;

function TActorObject.sub_4C5370(nX, nY: Integer; nRange: Integer; var nDX, nDY: Integer): Boolean; //004C5370
var
  I: Integer;
  II: Integer;
  III: Integer;
begin
  Result := False;
  if m_PEnvir.GetMovingObject(nX, nY, True) = nil then begin
    Result := True;
    nDX := nX;
    nDY := nY;
  end;
  if not Result then begin
    for I := 1 to nRange do begin
      for II := -I to I do begin
        for III := -I to I do begin
          nDX := nX + III;
          nDY := nY + II;
          if m_PEnvir.GetMovingObject(nDX, nDY, True) = nil then begin
            Result := True;
            Break;
          end;
        end;
        if Result then Break;
      end;
      if Result then Break;
    end;
  end;
  if not Result then begin
    nDX := nX;
    nDY := nY;
  end;
end;

function TPlayObject.GetUserItemWeitht(nWhere: Integer): Integer;
var
  I: Integer;
  n14: Integer;
  StdItem: pTStdItem;
begin
  n14 := 0;
  for I := Low(THumanUseItems) to High(THumanUseItems) do begin
    if (nWhere = -1) or (not (I = nWhere) and not (I = 1) and not (I = 2)) then begin
      StdItem := UserEngine.GetStdItem(m_UseItems[I].wIndex);
      if StdItem <> nil then Inc(n14, StdItem.Weight);
    end;
  end;
  Result := n14;
end;

function TPlayObject.EatItems(StdItem: pTStdItem): Boolean;
var
  bo06: Boolean;
  nOldStatus: Integer;
begin
  Result := False;
  if m_PEnvir.m_boNODRUG then begin
    SysMsg(sCanotUseDrugOnThisMap, c_Red, t_Hint);
    Exit;
  end;
  case StdItem.StdMode of
    0: begin
        case StdItem.Shape of
          1: begin
              IncHealthSpell(StdItem.AC, StdItem.MAC);
              Result := True;
            end;
          2: begin
              m_boUserUnLockDurg := True;
              Result := True;
            end;
        else begin
            {
            if ((StdItem.AC + m_nIncHealth) < 500) and (StdItem.AC > 0) then begin
              Inc(m_nIncHealth,StdItem.AC);
            end;
            if ((StdItem.MAC + m_nIncSpell) < 500) and (StdItem.MAC > 0) then begin
              Inc(m_nIncSpell,StdItem.MAC);
            end;
            }
            if (StdItem.AC > 0) then begin
              Inc(m_nIncHealth, StdItem.AC);
            end;
            if (StdItem.MAC > 0) then begin
              Inc(m_nIncSpell, StdItem.MAC);
            end;
            Result := True;
          end;
        end;
      end;
    1: begin
        nOldStatus := GetMyStatus();
        Inc(m_nHungerStatus, StdItem.DuraMax div 10);
        m_nHungerStatus := _MIN(5000, m_nHungerStatus);
        if nOldStatus <> GetMyStatus() then
          RefMyStatus();
        Result := True;
      end;
    2: Result := True;
    3: begin
        if StdItem.Shape = 12 then begin
          bo06 := False;
          if LoWord(StdItem.DC) > 0 then begin
            m_wStatusArrValue[0 {0x218}] := LoWord(StdItem.DC);
            m_dwStatusArrTimeOutTick[0 {0x220}] := GetTickCount + HiWord(StdItem.MAC) * 1000;
            SysMsg('Attack Power increased for ' + IntToStr(HiWord(StdItem.MAC)) + ' seconds', c_Green, t_Hint);
            bo06 := True;
          end;
          if LoWord(StdItem.MC) > 0 then begin
            m_wStatusArrValue[1 {0x219}] := LoWord(StdItem.MC);
            m_dwStatusArrTimeOutTick[1 {0x224}] := GetTickCount + HiWord(StdItem.MAC) * 1000;
            SysMsg('Magic Power increased for ' + IntToStr(HiWord(StdItem.MAC)) + ' seconds', c_Green, t_Hint);
            bo06 := True;
          end;
          if LoByte(StdItem.SC) > 0 then begin
            m_wStatusArrValue[2 {0x21A}] := LoWord(StdItem.SC);
            m_dwStatusArrTimeOutTick[2 {0x228}] := GetTickCount + HiWord(StdItem.MAC) * 1000;
            SysMsg('Soul Power increased for ' + IntToStr(HiWord(StdItem.MAC)) + ' seconds', c_Green, t_Hint);
            bo06 := True;
          end;
          if HiWord(StdItem.AC) > 0 then begin
            m_wStatusArrValue[3 {0x21B}] := HiWord(StdItem.AC);
            m_dwStatusArrTimeOutTick[3 {0x22C}] := GetTickCount + HiWord(StdItem.MAC) * 1000;
            SysMsg('Attack Speed increased for ' + IntToStr(HiWord(StdItem.MAC)) + ' seconds', c_Green, t_Hint);
            bo06 := True;
          end;
          if LoWord(StdItem.AC) > 0 then begin
            m_wStatusArrValue[4 {0x21C}] := LoWord(StdItem.AC);
            m_dwStatusArrTimeOutTick[4 {0x230}] := GetTickCount + HiWord(StdItem.MAC) * 1000;
            SysMsg('Defence increased for ' + IntToStr(HiWord(StdItem.MAC)) + ' seconds', c_Green, t_Hint);
            bo06 := True;
          end;
          if LoWord(StdItem.MAC) > 0 then begin
            m_wStatusArrValue[5 {0x21D}] := LoWord(StdItem.MAC);
            m_dwStatusArrTimeOutTick[5 {0x234}] := GetTickCount + HiWord(StdItem.MAC) * 1000;
            SysMsg('Magic Defence increased for ' + IntToStr(HiWord(StdItem.MAC)) + ' seconds', c_Green, t_Hint);
            bo06 := True;
          end;
          if bo06 then begin
            RecalcAbilitys();
            SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
            Result := True;
          end;
        end else begin
          Result := EatUseItems(StdItem.Shape);
        end;
      end;
  end;
end;

function TPlayObject.ReadBook(StdItem: pTStdItem): Boolean;
var
  Magic: pTMagic;
  UserMagic: pTUserMagic;
  PlayObject: TPlayObject;
begin
  Result := False;
  Magic := UserEngine.FindMagic(StdItem.Name);
  if Magic <> nil then begin
    if not IsTrainingSkill(Magic.wMagicId) then begin
      if (Magic.sDescr = '') and ((Magic.btJob = 99) or (Magic.btJob = m_btJob)) then begin
        if m_Abil.Level >= Magic.TrainLevel[0] then begin
          New(UserMagic);
          UserMagic.MagicInfo := Magic;
          UserMagic.wMagIdx := Magic.wMagicId;
          UserMagic.btKey := 0;
          UserMagic.btLevel := 0;
          UserMagic.nTranPoint := 0;
          m_MagicList.Add(UserMagic);
          RecalcAbilitys();
          if m_btRaceServer = RC_PLAYOBJECT then begin
            PlayObject := TPlayObject(Self);
            PlayObject.SendAddMagic(UserMagic);
          end;
          Result := True;
        end;
      end;
    end;
  end;
end;

function TActorObject.IsTrainingSkill(nIndex: Integer): Boolean;
var
  I: Integer;
  UserMagic: pTUserMagic;
begin
  Result := False;
  for I := 0 to m_MagicList.Count - 1 do begin
    UserMagic := m_MagicList.Items[I];
    if (UserMagic <> nil) and (UserMagic.wMagIdx = nIndex) then begin
      Result := True;
      Break;
    end;
  end;
end;

procedure TPlayObject.SendAddMagic(UserMagic: pTUserMagic);
var
  ClientMagic: TClientMagic;
begin
  if m_boNotOnlineAddExp or m_boAI then Exit;
  ClientMagic.Key := Char(UserMagic.btKey);
  ClientMagic.Level := UserMagic.btLevel;
  ClientMagic.CurTrain := UserMagic.nTranPoint;
  ClientMagic.Def := UserMagic.MagicInfo^;
  m_DefMsg := MakeDefaultMsg(SM_ADDMAGIC, 0, 0, 0, 1);
  SendSocket(@m_DefMsg, EncodeBuffer(@ClientMagic, SizeOf(TClientMagic)));
end;

procedure TPlayObject.SendDelMagic(UserMagic: pTUserMagic);
begin
  if m_boNotOnlineAddExp or m_boAI then Exit;
  m_DefMsg := MakeDefaultMsg(SM_DELMAGIC, UserMagic.wMagIdx, 0, 0, 1);
  SendSocket(@m_DefMsg, '');
end;

function TPlayObject.EatUseItems(nShape: Integer): Boolean;
var
  nX, nY: Integer;
  Castle: TUserCastle;
begin
  Result := False;
  case nShape of //
    1: begin
        SendRefMsg(RM_SPACEMOVE_FIRE, 0, 0, 0, 0, '');
        BaseObjectMove(m_sHomeMap, '', '');
        Result := True;
      end;
    2: begin
        if not m_PEnvir.m_boNORANDOMMOVE then begin
          SendRefMsg(RM_SPACEMOVE_FIRE, 0, 0, 0, 0, '');
          BaseObjectMove(m_sMapName, '', '');
          Result := True;
        end;
      end;
    3: begin
        SendRefMsg(RM_SPACEMOVE_FIRE, 0, 0, 0, 0, '');
        if PKLevel < 2 then begin
          GetRandomHomePoint(nX, nY);
          BaseObjectMove(m_sHomeMap, IntToStr(nX), IntToStr(nY));
        end else begin
          BaseObjectMove(g_Config.sRedHomeMap, IntToStr(g_Config.nRedHomeX), IntToStr(g_Config.nRedHomeY));
        end;
        Result := True;
      end;
    4: begin
        if WeaptonMakeLuck() then Result := True;
      end;
    5: begin
        if m_MyGuild <> nil then begin
          if not m_boInFreePKArea then begin
            Castle := g_CastleManager.IsCastleMember(Self);
            {
            if UserCastle.IsMasterGuild(TGuild(m_MyGuild)) then begin
              BaseObjectMove(UserCastle.m_sHomeMap,IntToStr(UserCastle.GetHomeX),IntToStr(UserCastle.GetHomeY));
            }
            if (Castle <> nil) and Castle.IsMasterGuild(TGUild(m_MyGuild)) then begin
              BaseObjectMove(Castle.m_sHomeMap, IntToStr(Castle.GetHomeX), IntToStr(Castle.GetHomeY));
            end else begin
              SysMsg('Invalid', c_Red, t_Hint);
            end;
            Result := True;
          end else begin
            SysMsg('Cannot use here.', c_Red, t_Hint);
          end;
        end;
      end;
    9: begin
        if RepairWeapon() then Result := True;
      end;
    10: begin
        if SuperRepairWeapon() then Result := True;
      end;
    11: begin
        if WinLottery() then Result := True;
      end;
  end;
end;

procedure TPlayObject.MoveToHome;
var
  nX, nY: Integer;
begin
  SendRefMsg(RM_SPACEMOVE_FIRE, 0, 0, 0, 0, '');
  GetRandomHomePoint(nX, nY);
  BaseObjectMove(m_sHomeMap, IntToStr(nX), IntToStr(nY));
end;

procedure TPlayObject.BaseObjectMove(sMAP, sX, sY: string);
var
  Envir: TEnvirnoment;
  nX, nY: Integer;
begin
  Envir := m_PEnvir;
  if sMAP = '' then sMAP := m_sMapName;
  if (sX <> '') and (sY <> '') then begin
    nX := Str_ToInt(sX, 0);
    nY := Str_ToInt(sY, 0);
    SpaceMove(sMAP, nX, nY, 0);
  end else begin
    MapRandomMove(sMAP, 0);
  end;
  if (Envir <> m_PEnvir) and (m_btRaceServer = RC_PLAYOBJECT) then begin
    m_boTimeRecall := False;
  end;
end;
//使用祝福油

function TPlayObject.WeaptonMakeLuck: Boolean;
var
  StdItem: pTStdItem;
  nRand: Integer;
  boMakeLuck: Boolean;
begin
  Result := False;
  if m_UseItems[U_WEAPON].wIndex <= 0 then Exit;
  nRand := 0;
  StdItem := UserEngine.GetStdItem(m_UseItems[U_WEAPON].wIndex);
  if StdItem <> nil then begin
    nRand := abs((HiWord(StdItem.DC) - LoWord(StdItem.DC))) div 5;
  end;
  if Random(g_Config.nWeaponMakeUnLuckRate {20}) = 1 then begin
    MakeWeaponUnlock();
  end else begin
    boMakeLuck := False;
    if m_UseItems[U_WEAPON].btValue[4] > 0 then begin
      Dec(m_UseItems[U_WEAPON].btValue[4]);
      SysMsg(g_sWeaptonMakeLuck {'武器被加幸运了...'}, c_Green, t_Hint);
      boMakeLuck := True;
    end else if m_UseItems[U_WEAPON].btValue[3] < g_Config.nWeaponMakeLuckPoint1 {1} then begin
      Inc(m_UseItems[U_WEAPON].btValue[3]);
      SysMsg(g_sWeaptonMakeLuck {'武器被加幸运了...'}, c_Green, t_Hint);
      boMakeLuck := True;
    end else if (m_UseItems[U_WEAPON].btValue[3] < g_Config.nWeaponMakeLuckPoint2 {3}) and (Random(nRand + g_Config.nWeaponMakeLuckPoint2Rate {6}) = 1) then begin
      Inc(m_UseItems[U_WEAPON].btValue[3]);
      SysMsg(g_sWeaptonMakeLuck {'武器被加幸运了...'}, c_Green, t_Hint);
      boMakeLuck := True;
    end else if (m_UseItems[U_WEAPON].btValue[3] < g_Config.nWeaponMakeLuckPoint3 {7}) and (Random(nRand * g_Config.nWeaponMakeLuckPoint3Rate {10 + 30}) = 1) then begin
      Inc(m_UseItems[U_WEAPON].btValue[3]);
      SysMsg(g_sWeaptonMakeLuck {'武器被加幸运了...'}, c_Green, t_Hint);
      boMakeLuck := True;
    end;
    if m_btRaceServer = RC_PLAYOBJECT then begin
      RecalcAbilitys();
      SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
      SendMsg(Self, RM_SUBABILITY, 0, 0, 0, 0, '');
    end;
    if not boMakeLuck then SysMsg(g_sWeaptonNotMakeLuck {'无效'}, c_Green, t_Hint);
  end;
  Result := True;
end;

function TPlayObject.RepairWeapon: Boolean;
var
  nDura: Integer;
  UserItem: pTUserItem;
begin
  Result := False;
  UserItem := @m_UseItems[U_WEAPON];
  if (UserItem.wIndex <= 0) or (UserItem.DuraMax <= UserItem.Dura) then Exit;
  if g_Config.nRepairItemDecDura = 0 then g_Config.nRepairItemDecDura := 1;
  Dec(UserItem.DuraMax, (UserItem.DuraMax - UserItem.Dura) div g_Config.nRepairItemDecDura {30});
  nDura := _MIN(5000, UserItem.DuraMax - UserItem.Dura);
  if nDura > 0 then begin
    Inc(UserItem.Dura, nDura);
    SendMsg(Self, RM_DURACHANGE, 1, UserItem.Dura, UserItem.DuraMax, 0, '');
    SysMsg(g_sWeaponRepairSuccess {'武器修复成功...'}, c_Green, t_Hint);
    Result := True;
  end;
end;

function TPlayObject.SuperRepairWeapon: Boolean;
begin
  Result := False;
  if m_UseItems[U_WEAPON].wIndex <= 0 then Exit;
  m_UseItems[U_WEAPON].Dura := m_UseItems[U_WEAPON].DuraMax;
  SendMsg(Self, RM_DURACHANGE, 1, m_UseItems[U_WEAPON].Dura, m_UseItems[U_WEAPON].DuraMax, 0, '');
  SysMsg(g_sWeaponRepairSuccess {'武器修复成功...'}, c_Green, t_Hint);
  Result := True;
end;

function TPlayObject.WinLottery: Boolean;
var
  nGold, nWinLevel, nRate: Integer;
begin
  nGold := 0;
  nWinLevel := 0;
  {
  case Random(30000) of
    0..4999: begin //004BD866
     if nWinLotteryCount < nNoWinLotteryCount then begin
       nGold:=500;
       nWinLevel:=6;
       Inc(nWinLotteryLevel6);
     end;
    end;
    14000..15999: begin //004BD895
     if nWinLotteryCount < nNoWinLotteryCount then begin
       nGold:=1000;
       nWinLevel:=5;
       Inc(nWinLotteryLevel5);
     end;
    end;
    16000..16149: begin //004BD8C4
     if nWinLotteryCount < nNoWinLotteryCount then begin
       nGold:=10000;
       nWinLevel:=4;
       Inc(nWinLotteryLevel4);
     end;
    end;
    16150..16169: begin //004BD8F0
     if nWinLotteryCount < nNoWinLotteryCount then begin
       nGold:=100000;
       nWinLevel:=3;
       Inc(nWinLotteryLevel3);
     end;
    end;
    16170..16179: begin //004BD918
     if nWinLotteryCount < nNoWinLotteryCount then begin
       nGold:=200000;
       nWinLevel:=2;
       Inc(nWinLotteryLevel2);
     end;
    end;
    16180 + 1820: begin //004BD940
     if nWinLotteryCount < nNoWinLotteryCount then begin
       nGold:=1000000;
       nWinLevel:=1;
       Inc(nWinLotteryLevel1);
     end;
    end;
  end;
  }
  nRate := Random(g_Config.nWinLotteryRate);
  if nRate in [g_Config.nWinLottery6Min..g_Config.nWinLottery6Max] then begin
    if g_Config.nWinLotteryCount < g_Config.nNoWinLotteryCount then begin
      nGold := g_Config.nWinLottery6Gold;
      nWinLevel := 6;
      Inc(g_Config.nWinLotteryLevel6);
    end;
  end else
    if nRate in [g_Config.nWinLottery5Min..g_Config.nWinLottery5Max] then begin
    if g_Config.nWinLotteryCount < g_Config.nNoWinLotteryCount then begin
      nGold := g_Config.nWinLottery5Gold;
      nWinLevel := 5;
      Inc(g_Config.nWinLotteryLevel5);
    end;
  end else
    if nRate in [g_Config.nWinLottery4Min..g_Config.nWinLottery4Max] then begin
    if g_Config.nWinLotteryCount < g_Config.nNoWinLotteryCount then begin
      nGold := g_Config.nWinLottery4Gold;
      nWinLevel := 4;
      Inc(g_Config.nWinLotteryLevel4);
    end;
  end else
    if nRate in [g_Config.nWinLottery3Min..g_Config.nWinLottery3Max] then begin
    if g_Config.nWinLotteryCount < g_Config.nNoWinLotteryCount then begin
      nGold := g_Config.nWinLottery3Gold;
      nWinLevel := 3;
      Inc(g_Config.nWinLotteryLevel3);
    end;
  end else
    if nRate in [g_Config.nWinLottery2Min..g_Config.nWinLottery2Max] then begin
    if g_Config.nWinLotteryCount < g_Config.nNoWinLotteryCount then begin
      nGold := g_Config.nWinLottery2Gold;
      nWinLevel := 2;
      Inc(g_Config.nWinLotteryLevel2);
    end;
  end else
    if nRate in [g_Config.nWinLottery1Min + g_Config.nWinLottery1Max] then begin
    if g_Config.nWinLotteryCount < g_Config.nNoWinLotteryCount then begin
      nGold := g_Config.nWinLottery1Gold;
      nWinLevel := 1;
      Inc(g_Config.nWinLotteryLevel1);
    end;
  end;
  if nGold > 0 then begin
    case nWinLevel of //
      1: SysMsg(g_sWinLottery1Msg {'祝贺您，中了一等奖。'}, c_Green, t_Hint);
      2: SysMsg(g_sWinLottery2Msg {'祝贺您，中了二等奖。'}, c_Green, t_Hint);
      3: SysMsg(g_sWinLottery3Msg {'祝贺您，中了三等奖。'}, c_Green, t_Hint);
      4: SysMsg(g_sWinLottery4Msg {'祝贺您，中了四等奖。'}, c_Green, t_Hint);
      5: SysMsg(g_sWinLottery5Msg {'祝贺您，中了五等奖。'}, c_Green, t_Hint);
      6: SysMsg(g_sWinLottery6Msg {'祝贺您，中了六等奖。'}, c_Green, t_Hint);
    end;
    if IncGold(nGold) then begin
      GoldChanged();
    end else begin
      DropGoldDown(nGold, True, nil, nil);
    end;

  end else begin
    Inc(g_Config.nNoWinLotteryCount, 500);
    SysMsg(g_sNotWinLotteryMsg {'等下次机会吧！！！'}, c_Red, t_Hint);
  end;
  Result := True;
end;

procedure TActorObject.DamageBubbleDefence(nInt: Integer);
begin
  if m_wStatusTimeArr[STATE_BUBBLEDEFENCEUP {0x76}] > 0 then begin
    if m_wStatusTimeArr[STATE_BUBBLEDEFENCEUP {0x76}] > 3 then
      Dec(m_wStatusTimeArr[STATE_BUBBLEDEFENCEUP {0x76}], 3)
    else m_wStatusTimeArr[STATE_BUBBLEDEFENCEUP {0x76}] := 1;
  end;
end;

function TActorObject.IsGuildMaster: Boolean;
begin
  Result := False;
  if (m_MyGuild <> nil) and (m_nGuildRankNo = 1) then
    Result := True;
end;

procedure TPlayObject.ChangeServerMakeSlave(SalveInfo: pTSlaveInfo);
var
  nSlavecount: Integer;
  BaseObject: TActorObject;
begin
  if m_btJob = 2 then begin
    nSlavecount := 1;
  end else begin
    nSlavecount := 5;
  end;
  BaseObject := MakeSlave(SalveInfo.sSalveName, 3, SalveInfo.btSalveLevel, nSlavecount, SalveInfo.dwRoyaltySec);
  if BaseObject <> nil then begin
    BaseObject.n294 := SalveInfo.nKillCount;
    BaseObject.m_btSlaveExpLevel := SalveInfo.btSlaveExpLevel;
    BaseObject.m_WAbil.HP := SalveInfo.nHP;
    BaseObject.m_WAbil.MP := SalveInfo.nMP;
    if (1500 - SalveInfo.btSalveLevel * 200) < BaseObject.m_nWalkSpeed then begin
      BaseObject.m_nWalkSpeed := 1500 - SalveInfo.btSalveLevel * 200;
    end;
    if Integer(2000 - SalveInfo.btSalveLevel * 200) < BaseObject.m_nNextHitTime then begin
      BaseObject.m_nWalkSpeed := 2000 - SalveInfo.btSalveLevel * 200;
    end;
    RecalcAbilitys();
  end;
end;

procedure TPlayObject.SendDelDealItem(UserItem: pTUserItem);
var
  StdItem: pTStdItem;
  ClientItem: TClientItem;
  OClientItem: TOClientItem;
  sUserItemName: string;
begin
  SendDefMessage(SM_DEALDELITEM_OK, 0, 0, 0, 0, '');
  if m_DealCreat <> nil then begin
    if not m_DealCreat.m_boHeroVersion then begin
      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if StdItem <> nil then begin
        //ClientItem.S:=StdItem^;
        CopyStdItemToOStdItem(StdItem, @OClientItem.s);
        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName <> '' then
          OClientItem.s.Name := sUserItemName;

        OClientItem.MakeIndex := UserItem.MakeIndex;
        OClientItem.Dura := UserItem.Dura;
        OClientItem.DuraMax := UserItem.DuraMax;
      end;
      m_DefMsg := MakeDefaultMsg(SM_DEALREMOTEDELITEM, Integer(Self), 0, 0, 1);
      m_DealCreat.SendSocket(@m_DefMsg, EncodeBuffer(@OClientItem, SizeOf(TOClientItem)));
    end else begin
      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if StdItem <> nil then begin
        ClientItem.s := StdItem^;
        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName <> '' then
          ClientItem.s.Name := sUserItemName;

        if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
          ClientItem.s.Name := '( )' + ClientItem.s.Name;
        end;

        ClientItem.MakeIndex := UserItem.MakeIndex;
        ClientItem.Dura := UserItem.Dura;
        ClientItem.DuraMax := UserItem.DuraMax;

        ClientItem.s.AddValue := UserItem.AddValue;
        ClientItem.s.AddPoint := UserItem.AddPoint;
        ClientItem.s.MaxDate := UserItem.MaxDate;
        //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

      end;
      m_DefMsg := MakeDefaultMsg(SM_DEALREMOTEDELITEM, Integer(Self), 0, 0, 1);
      TPlayObject(m_DealCreat).SendSocket(@m_DefMsg, EncodeBuffer(@ClientItem, SizeOf(TClientItem)));
    end;
    m_DealCreat.m_DealLastTick := GetTickCount();
    m_DealLastTick := GetTickCount();
  end;
end;

procedure TPlayObject.SendAddDealItem(UserItem: pTUserItem); //004DD464
var
  StdItem: pTStdItem;
  StdItem80: TStdItem;
  ClientItem: TClientItem;
  OClientItem: TOClientItem;
  sUserItemName: string;
//  sCheckItemName: string;
begin
  SendDefMessage(SM_DEALADDITEM_OK, 0, 0, 0, 0, '');
  if m_DealCreat <> nil then begin
    if not m_DealCreat.m_boHeroVersion then begin
      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if StdItem <> nil then begin
        StdItem80 := StdItem^;
        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName <> '' then
          StdItem80.Name := sUserItemName;

        ItemUnit.GetItemAddValue(UserItem, StdItem80);
        //OClientItem.S:=StdItem80;
        CopyStdItemToOStdItem(@StdItem80, @OClientItem.s);
        //if sItemNewName <> '' then
        //  OClientItem.S.Name:=sItemNewName;
        OClientItem.MakeIndex := UserItem.MakeIndex;
        OClientItem.Dura := UserItem.Dura;
        OClientItem.DuraMax := UserItem.DuraMax;
        m_DefMsg := MakeDefaultMsg(SM_DEALREMOTEADDITEM, Integer(Self), 0, 0, 1);
        m_DealCreat.SendSocket(@m_DefMsg, EncodeBuffer(@OClientItem, SizeOf(TOClientItem)));
        m_DealCreat.m_DealLastTick := GetTickCount();
        m_DealLastTick := GetTickCount();
      end;
    end else begin
      StdItem := UserEngine.GetStdItem(UserItem.wIndex);
      if StdItem <> nil then begin
        StdItem80 := StdItem^;
        ItemUnit.GetItemAddValue(UserItem, StdItem80);

        //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName <> '' then
          StdItem80.Name := sUserItemName;

        ClientItem.s := StdItem80;

        if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
          ClientItem.s.Name := '( )' + ClientItem.s.Name;
        end;

        ClientItem.MakeIndex := UserItem.MakeIndex;
        ClientItem.Dura := UserItem.Dura;
        ClientItem.DuraMax := UserItem.DuraMax;

        ClientItem.s.AddValue := UserItem.AddValue;
        ClientItem.s.AddPoint := UserItem.AddPoint;
        ClientItem.s.MaxDate := UserItem.MaxDate;
        //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

        m_DefMsg := MakeDefaultMsg(SM_DEALREMOTEADDITEM, Integer(Self), 0, 0, 1);
        m_DealCreat.SendSocket(@m_DefMsg, EncodeBuffer(@ClientItem, SizeOf(TClientItem)));
        m_DealCreat.m_DealLastTick := GetTickCount();
        m_DealLastTick := GetTickCount();
      end;
    end;
  end;
end;

procedure TPlayObject.OpenDealDlg(PlayObject: TPlayObject);
begin
  m_boDealing := True;
  m_DealCreat := PlayObject;
  GetBackDealItems();
  SendDefMessage(SM_DEALMENU, 0, 0, 0, 0, m_DealCreat.m_sCharName);
  m_DealLastTick := GetTickCount();
end;

procedure TPlayObject.SendDelDuelItem(UserItem: pTUserItem);
var
  StdItem: pTStdItem;
  ClientItem: TClientItem;
  //OClientItem: TOClientItem;
  sUserItemName: string;
begin
  SendDefMessage(SM_DuelDELITEM_OK, 0, 0, 0, 0, '');
  StdItem := UserEngine.GetStdItem(UserItem.wIndex);
  if StdItem <> nil then begin
    ClientItem.s := StdItem^;
        //取自定义物品名称
    sUserItemName := '';
    if UserItem.btValue[13] = 1 then
      sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
    if sUserItemName <> '' then
      ClientItem.s.Name := sUserItemName;

    if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
      ClientItem.s.Name := '( )' + ClientItem.s.Name;
    end;

    ClientItem.MakeIndex := UserItem.MakeIndex;
    ClientItem.Dura := UserItem.Dura;
    ClientItem.DuraMax := UserItem.DuraMax;

    ClientItem.s.AddValue := UserItem.AddValue;
    ClientItem.s.AddPoint := UserItem.AddPoint;
    ClientItem.s.MaxDate := UserItem.MaxDate;
    //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

  end;
  m_DefMsg := MakeDefaultMsg(SM_DuelREMOTEDELITEM, Integer(Self), 0, 0, 1);
  TPlayObject(m_DuelCreat).SendSocket(@m_DefMsg, EncodeBuffer(@ClientItem, SizeOf(TClientItem)));
  m_DuelCreat.m_DuelLastTick := GetTickCount();
  m_DuelLastTick := GetTickCount();
end;

procedure TPlayObject.SendAddDuelItem(UserItem: pTUserItem);
var
  StdItem: pTStdItem;
  StdItem80: TStdItem;
  ClientItem: TClientItem;
//  OClientItem: TOClientItem;
  sUserItemName: string;
//  sCheckItemName: string;
begin
  SendDefMessage(SM_DUELADDITEM_OK, 0, 0, 0, 0, '');
  if m_DuelCreat <> nil then begin
    StdItem := UserEngine.GetStdItem(UserItem.wIndex);
    if StdItem <> nil then begin
      StdItem80 := StdItem^;
      ItemUnit.GetItemAddValue(UserItem, StdItem80);

        //取自定义物品名称
      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName <> '' then
        StdItem80.Name := sUserItemName;

      ClientItem.s := StdItem80;

      if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        ClientItem.s.Name := '( )' + ClientItem.s.Name;
      end;

      ClientItem.MakeIndex := UserItem.MakeIndex;
      ClientItem.Dura := UserItem.Dura;
      ClientItem.DuraMax := UserItem.DuraMax;

      ClientItem.s.AddValue := UserItem.AddValue;
      ClientItem.s.AddPoint := UserItem.AddPoint;
      ClientItem.s.MaxDate := UserItem.MaxDate;
      //ClientItem.s.sDescr := UserEngine.GetStdItemDescr(ClientItem.s, UserItem);

      m_DefMsg := MakeDefaultMsg(SM_DuelREMOTEADDITEM, Integer(Self), 0, 0, 1);
      m_DuelCreat.SendSocket(@m_DefMsg, EncodeBuffer(@ClientItem, SizeOf(TClientItem)));
      m_DuelCreat.m_DuelLastTick := GetTickCount();
      m_DuelLastTick := GetTickCount();
    end;
  end;
end;

procedure TPlayObject.OpenDuelDlg(PlayObject: TPlayObject);
begin
  m_boDueling := True;
  m_DuelCreat := PlayObject;
  GetBackDuelItems();
  SendDefMessage(SM_DuelMENU, 0, 0, 0, 0, m_DuelCreat.m_sCharName);
  m_DuelLastTick := GetTickCount();
end;

procedure TPlayObject.JoinGroup(PlayObject: TPlayObject);
begin
  m_GroupOwner := PlayObject;
  //SendGroupText(m_sCharName + ' 已加入小组.');
  SendGroupText(Format(g_sJoinGroup, [m_sCharName]));
end;

procedure TPlayObject.AddGroupMember(sCharName: string; PlayObject: TPlayObject);
var
  I: Integer;
begin
  for I := 0 to m_GroupMembers.Count - 1 do begin
    if CompareText(m_GroupMembers.Strings[I], sCharName) = 0 then begin
      m_GroupMembers.Objects[I] := PlayObject;
      Exit;
    end;
  end;
  m_GroupMembers.AddObject(sCharName, PlayObject);
end;

function TActorObject.MagCanHitTarget(nX, nY: Integer;
  TargeTActorObject: TActorObject): Boolean;
var
  n14, n18, n1C, n20: Integer;
begin
  Result := False;
  if TargeTActorObject = nil then Exit;
  n20 := abs(nX - TargeTActorObject.m_nCurrX) + abs(nY - TargeTActorObject.m_nCurrY);
  n14 := 0;
  while (n14 < 13) do begin
    n18 := GetNextDirection(nX, nY, TargeTActorObject.m_nCurrX, TargeTActorObject.m_nCurrY);
    if m_PEnvir.GetNextPosition(nX, nY, n18, 1, nX, nY) and m_PEnvir.sub_4B5FC8(nX, nY) then begin
      if (nX = TargeTActorObject.m_nCurrX) and (nY = TargeTActorObject.m_nCurrY) then begin
        Result := True;
        Break;
      end else begin
        n1C := abs(nX - TargeTActorObject.m_nCurrX) + abs(nY - TargeTActorObject.m_nCurrY);
        if n1C > n20 then begin
          Result := True;
          Break;
        end;
        n1C := n20;
      end;
    end else begin
      Break;
    end;
    Inc(n14);
  end;
end;

function TActorObject.MagCanHitTarget(nX, nY, nTargetX, nTargetY: Integer): Boolean;
var
  n14, n18, n1C, n20: Integer;
begin
  Result := False;
  n20 := abs(nX - nTargetX) + abs(nY - nTargetY);
  n14 := 0;
  while (n14 < 13) do begin
    n18 := GetNextDirection(nX, nY, nTargetX, nTargetY);
    if m_PEnvir.GetNextPosition(nX, nY, n18, 1, nX, nY) and m_PEnvir.sub_4B5FC8(nX, nY) then begin
      if (nX = nTargetX) and (nY = nTargetY) then begin
        Result := True;
        Break;
      end else begin
        n1C := abs(nX - nTargetX) + abs(nY - nTargetY);
        if n1C > n20 then begin
          Result := True;
          Break;
        end;
        n1C := n20;
      end;
    end else begin
      Break;
    end;
    Inc(n14);
  end;
end;

function TActorObject.IsFriend(cret: TActorObject): Boolean;
var
  I: Integer;
begin
  Result := False;
  if cret = nil then Exit;
  if cret.m_btRaceServer = RC_PLAYOBJECT then begin
    case m_btAttatckMode of
      HAM_ALL: Result := True;
      HAM_PEACE: Result := True;
      HAM_DEAR: begin
          if (Self = cret) or (cret = TPlayObject(Self).m_DearHuman) then begin
            Result := True;
          end;
        end;
      HAM_MASTER: begin
          if (Self = cret) then begin
            Result := True;
          end else
            if TPlayObject(Self).m_boMaster then begin
            for I := 0 to TPlayObject(Self).m_MasterList.Count - 1 do begin
              if TPlayObject(Self).m_MasterList.Items[I] = cret then begin
                Result := True;
                Break;
              end;
            end;
          end else
            if TPlayObject(cret).m_boMaster then begin
            for I := 0 to TPlayObject(cret).m_MasterList.Count - 1 do begin
              if TPlayObject(cret).m_MasterList.Items[I] = Self then begin
                Result := True;
                Break;
              end;
            end;
          end;
        end;
      HAM_GROUP: begin
          if cret = Self then
            Result := True;
          if TPlayObject(Self).IsGroupMember(TPlayObject(cret)) then
            Result := True;
        end;
      HAM_GUILD: begin
          if cret = Self then
            Result := True;
          if m_MyGuild <> nil then begin
            if TGUild(m_MyGuild).IsMember(cret.m_sCharName) then
              Result := True;
            if m_boGuildWarArea and (cret.m_MyGuild <> nil) then begin
              if TGUild(m_MyGuild).IsAllyGuild(TGUild(cret.m_MyGuild)) then
                Result := True;
            end;
          end;
        end;
      HAM_PKATTACK: begin
          if cret = Self then Result := True;
          if PKLevel >= 2 then begin
            if cret.PKLevel < 2 then Result := True;
          end else begin
            if cret.PKLevel >= 2 then Result := True;
          end;
        end;
    end;
  end;
end;

function TActorObject.IsProperFriend(BaseObject: TActorObject): Boolean;
var
  MasterObject: TActorObject;
  SelfObject: TActorObject;
  boSelfMonster: Boolean;
  boBaseMonster: Boolean;
begin
  Result := False;
  if BaseObject = nil then Exit;
  if BaseObject = Self then begin
    Result := True;
    Exit;
  end;
  boSelfMonster := False;
  boBaseMonster := False;
  SelfObject := nil;

  if m_Master <> nil then begin
    MasterObject := Master;
    if MasterObject <> nil then begin
      if MasterObject.m_btRaceServer >= RC_ANIMAL then boSelfMonster := True;
    end;
  end else begin
    if m_btRaceServer >= RC_ANIMAL then boSelfMonster := True;
  end;

  if BaseObject.m_Master <> nil then begin
    MasterObject := BaseObject.Master;
    if MasterObject <> nil then begin
      if MasterObject.m_btRaceServer >= RC_ANIMAL then boBaseMonster := True;
    end;
  end else begin
    if BaseObject.m_btRaceServer >= RC_ANIMAL then boBaseMonster := True;
  end;

  if boSelfMonster then begin
    Result := boBaseMonster;
  end else begin
    if not boBaseMonster then begin
      if m_btRaceServer = RC_PLAYOBJECT then begin
        SelfObject := Self;
      end else begin
        if (m_Master <> nil) and (Master.m_btRaceServer = RC_PLAYOBJECT) then begin
          SelfObject := Master;
        end;
      end;
      if (SelfObject <> nil) and (SelfObject.m_btRaceServer = RC_PLAYOBJECT) then begin
        if BaseObject.Master <> nil then begin
          if BaseObject.Master = SelfObject then begin
            Result := True;
            Exit;
          end;
          Result := SelfObject.IsFriend(BaseObject.Master);
        end else begin
          Result := SelfObject.IsFriend(BaseObject);
        end;
      end;
    end;
  end;

  {if (m_btRaceServer >= RC_ANIMAL) then begin
    if (BaseObject.m_btRaceServer >= RC_ANIMAL) then
      Result := True;
    MasterObject := GetMasterObject(BaseObject);
    if (BaseObject.m_Master <> nil) and (MasterObject <> nil) then begin
      MasterObject.m_btRaceServer
        Result := False;

    end;
    Exit;
  end;

  if m_btRaceServer = RC_PLAYOBJECT then begin
    Result := IsFriend(BaseObject);
    if BaseObject.m_btRaceServer < RC_ANIMAL then Exit;
    if BaseObject.m_Master = Self then begin
      Result := True;
      Exit;
    end;
    if BaseObject.m_Master <> nil then begin
      Result := IsFriend(BaseObject.m_Master);
      Exit;
    end;
  end else Result := True;}
end;

(*function TActorObject.IsProperFriend(BaseObject: TActorObject): Boolean;
  function IsFriend(cret: TActorObject): Boolean;
  var
    I: Integer;
  begin
    Result := False;
    if cret.m_btRaceServer = RC_PLAYOBJECT then begin
      case m_btAttatckMode of
        HAM_ALL: Result := True;
        HAM_PEACE: Result := True;
        HAM_DEAR: begin
            if (Self = cret) or (cret = TPlayObject(Self).m_DearHuman) then begin
              Result := True;
            end;
          end;
        HAM_MASTER: begin
            if (Self = cret) then begin
              Result := True;
            end else
              if TPlayObject(Self).m_boMaster then begin
              for I := 0 to TPlayObject(Self).m_MasterList.Count - 1 do begin
                if TPlayObject(Self).m_MasterList.Items[I] = cret then begin
                  Result := True;
                  Break;
                end;
              end;
            end else
              if TPlayObject(cret).m_boMaster then begin
              for I := 0 to TPlayObject(cret).m_MasterList.Count - 1 do begin
                if TPlayObject(cret).m_MasterList.Items[I] = Self then begin
                  Result := True;
                  Break;
                end;
              end;
            end;
          end;
        HAM_GROUP: begin
            if cret = Self then
              Result := True;
            if TPlayObject(Self).IsGroupMember(TPlayObject(cret)) then
              Result := True;
          end;
        HAM_GUILD: begin
            if cret = Self then
              Result := True;
            if m_MyGuild <> nil then begin
              if TGUild(m_MyGuild).IsMember(cret.m_sCharName) then
                Result := True;
              if m_boGuildWarArea and (cret.m_MyGuild <> nil) then begin
                if TGUild(m_MyGuild).IsAllyGuild(TGUild(cret.m_MyGuild)) then
                  Result := True;
              end;
            end;
          end;
        HAM_PKATTACK: begin
            if cret = Self then Result := True;
            if PKLevel >= 2 then begin
              if cret.PKLevel < 2 then Result := True;
            end else begin
              if cret.PKLevel >= 2 then Result := True;
            end;
          end;
      end;
    end;
  end;
var
  MasterBaseObject: TActorObject;
  SelfMasterBaseObject: TActorObject;
begin
  Result := False;
  if BaseObject = nil then Exit;
 { MasterBaseObject := GetMasterObject(BaseObject);
  SelfMasterBaseObject := GetMasterObject(Self);

  if SelfMasterBaseObject <> nil then begin
    if MasterBaseObject = SelfMasterBaseObject then begin
      Result := True;
      Exit;
    end;
    if SelfMasterBaseObject.m_btRaceServer = RC_PLAYOBJECT then begin
      if MasterBaseObject <> nil then begin
        if MasterBaseObject.m_btRaceServer = RC_PLAYOBJECT then begin

        end;
      end;
    end else begin
      if MasterBaseObject.m_btRaceServer = RC_PLAYOBJECT then begin

      end;
    end;
  end;

  if MasterBaseObject <> nil then begin
    if MasterBaseObject.m_btRaceServer = RC_PLAYOBJECT then begin

    end else begin

    end;
  end;  }

  if (m_btRaceServer = RC_PLAYMOSTER) or (m_btRaceServer = RC_HEROOBJECT) then begin //分身检测
    if m_Master = nil then begin
      if (BaseObject.m_btRaceServer >= RC_ANIMAL) then
        Result := True;
      if (BaseObject.m_Master <> nil) or (BaseObject.m_btRaceServer = RC_PLAYOBJECT) then
        Result := False;
    end else begin
      if (BaseObject.m_btRaceServer >= RC_ANIMAL) then
        Result := False;
      if BaseObject.m_Master <> nil then
        Result := IsFriend(BaseObject.m_Master);
      if BaseObject.m_btRaceServer = RC_PLAYOBJECT then
        Result := IsFriend(BaseObject);
      if BaseObject = m_Master then
        Result := True;
      if BaseObject.m_Master = Self then
        Result := True;
      if BaseObject = Self then
        Result := True;
    end;
    Exit;
  end;

  if (m_btRaceServer >= RC_ANIMAL) then begin
    if (BaseObject.m_btRaceServer >= RC_ANIMAL) then
      Result := True;
    if BaseObject.m_Master <> nil then
      Result := False;
    Exit;
  end;

  if m_btRaceServer = RC_PLAYOBJECT then begin
    Result := IsFriend(BaseObject);
    if BaseObject.m_btRaceServer < RC_ANIMAL then Exit;
    if BaseObject.m_Master = Self then begin
      Result := True;
      Exit;
    end;
    if BaseObject.m_Master <> nil then begin
      Result := IsFriend(BaseObject.m_Master);
      Exit;
    end;
  end else Result := True;
end;  *)

function TActorObject.MagMakeDefenceArea(nX, nY, nRange, nSec: Integer;
  btState: Byte; boState: Boolean): Integer;
var
  III: Integer;
  I, II: Integer;
  nStartX, nStartY, nEndX, nEndY: Integer;
  MapCellInfo: pTMapCellinfo;
  BaseObject: TBaseObject;
  ActorObject: TActorObject;
  nPowerValue: Integer;
begin
  Result := 0;
  nStartX := nX - nRange;
  nEndX := nX + nRange;
  nStartY := nY - nRange;
  nEndY := nY + nRange;
  nPowerValue := nSec;
  for I := nStartX to nEndX do begin
    for II := nStartY to nEndY do begin
      if m_PEnvir.GetMapCellInfo(I, II, MapCellInfo) and (MapCellInfo.ObjList <> nil) then begin
        for III := 0 to MapCellInfo.ObjList.Count - 1 do begin
          BaseObject := TBaseObject(MapCellInfo.ObjList.Items[III]);
          if BaseObject.m_ObjType = t_Actor then begin
            ActorObject := TActorObject(MapCellInfo.ObjList.Items[III]);
            if (ActorObject <> nil) and (not ActorObject.m_boGhost) then begin
              if IsProperFriend(ActorObject) then begin

                nPowerValue := _GetAtomPower(ActorObject, AT_DARK, nSec);

                if btState = 0 then begin
                  ActorObject.DefenceUp(nPowerValue);
                end else begin
                  ActorObject.MagDefenceUp(nPowerValue);
                end;
                Inc(Result);
              end
            end;
          end;
        end;
      end;
    end;
  end;
end;

function TActorObject.MagMakeAbilityArea(nX, nY, nRange, nSec: Integer): Integer;
var
  III: Integer;
  I, II: Integer;
  nStartX, nStartY, nEndX, nEndY: Integer;
  MapCellInfo: pTMapCellinfo;
  BaseObject: TBaseObject;
  ActorObject: TActorObject;
  bo06: Boolean;
begin
  Result := 0;
  nStartX := nX - nRange;
  nEndX := nX + nRange;
  nStartY := nY - nRange;
  nEndY := nY + nRange;
  for I := nStartX to nEndX do begin
    for II := nStartY to nEndY do begin
      if m_PEnvir.GetMapCellInfo(I, II, MapCellInfo) and (MapCellInfo.ObjList <> nil) then begin
        for III := 0 to MapCellInfo.ObjList.Count - 1 do begin
          BaseObject := TBaseObject(MapCellInfo.ObjList.Items[III]);
          if BaseObject.m_ObjType = t_Actor then begin
            ActorObject := TActorObject(MapCellInfo.ObjList.Items[III]);
            if (ActorObject <> nil) and (not ActorObject.m_boGhost) then begin
              if IsProperTarget(ActorObject) then begin
                bo06 := False;
                case ActorObject.m_btJob of
                  0: begin
                      if ActorObject.m_wStatusArrValue[6] = 0 then begin
                        ActorObject.m_wStatusArrValue[6] := MakeLong(LoWord(ActorObject.m_WAbil.DC), HiWord(ActorObject.m_WAbil.DC) - 2 - (ActorObject.m_Abil.Level div 7));
                        ActorObject.m_dwStatusArrTimeOutTick[6] := GetTickCount + nSec * 1000;
                        ActorObject.SysMsg('Attack Power reduced for ' + IntToStr(nSec) + ' seconds', c_Green, t_Hint);
                        bo06 := True;
                      end;
                    end;
                  1: begin
                      if ActorObject.m_wStatusArrValue[7] = 0 then begin
                        ActorObject.m_wStatusArrValue[7] := MakeLong(LoWord(ActorObject.m_WAbil.MC), HiWord(ActorObject.m_WAbil.MC) - 2 - (ActorObject.m_Abil.Level div 7));
                        ActorObject.m_dwStatusArrTimeOutTick[7] := GetTickCount + nSec * 1000;
                        ActorObject.SysMsg('Magic Power reduced for ' + IntToStr(nSec) + ' seconds', c_Green, t_Hint);
                        bo06 := True;
                      end;
                    end;
                  2: begin
                      if ActorObject.m_wStatusArrValue[8] = 0 then begin
                        ActorObject.m_wStatusArrValue[8] := MakeLong(LoWord(ActorObject.m_WAbil.SC), HiWord(ActorObject.m_WAbil.SC) - 2 - (ActorObject.m_Abil.Level div 7));
                        ActorObject.m_dwStatusArrTimeOutTick[8] := GetTickCount + nSec * 1000;
                        ActorObject.SysMsg('Soul Power reduced for ' + IntToStr(nSec) + ' seconds', c_Green, t_Hint);
                        bo06 := True;
                      end;
                    end;
                end;
                if bo06 then begin
                  ActorObject.RecalcAbilitys();
                  ActorObject.SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
                end;
                Inc(Result);
              end
            end;
          end;
        end;
      end;
    end;
  end;
end;

function TPlayObject.AbilityUp(UserMagic: pTUserMagic): Boolean;
  function MPow(UserMagic: pTUserMagic): Integer;
  begin
    Result := UserMagic.MagicInfo.wPower + Random(UserMagic.MagicInfo.wMaxPower - UserMagic.MagicInfo.wPower);
  end;
  function GetPower(nPower: Integer): Integer;
  begin
    Result := Round(nPower / (UserMagic.MagicInfo.btTrainLv + 1) * (UserMagic.btLevel + 1)) + (UserMagic.MagicInfo.btDefPower + Random(UserMagic.MagicInfo.btDefMaxPower - UserMagic.MagicInfo.btDefPower));
  end;
var
  nSpellPoint, n14, Power: Integer;
begin
  Result := False;
  nSpellPoint := GetSpellPoint(UserMagic);
  if nSpellPoint > 0 then begin
    if m_WAbil.MP < nSpellPoint then Exit;
    n14 := GetAttackPower(GetPower(60) + 5 * LoWord(m_WAbil.SC), 5 * (ShortInt(HiWord(m_WAbil.SC) - LoWord(m_WAbil.SC)) + 1));
    m_dwStatusArrTimeOutTick[0] := GetTickCount + n14 * 1000;

    Power := ((HiWord(m_WAbil.SC)-1) div 5) + 1;
    if (Power >= 8) then begin
      Power := 8;
      m_wStatusArrValue[0] := 8;
    end else begin
      m_wStatusArrValue[0] := Power;
    end;

    SysMsg('Increased Physical Power By: ' + IntToStr(Power) + ' For: ' + IntToStr(n14) + ' Seconds', c_Green, t_Hint);
    RecalcAbilitys();
    SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
    Result := True;
  end;
end;

procedure TActorObject.MagDownHealth(nType, nTime, nPoint: Integer);
var
  nHealthType: Integer;
begin
  nHealthType := 9 + nType;
  if m_wStatusArrValue[nHealthType] = 0 then begin
    m_wStatusArrValue[nHealthType] := nPoint;
    m_dwStatusArrTimeOutTick[nHealthType] := GetTickCount + nTime * 1000;
  end;
end;

function TActorObject.DefenceDown(nSec: Integer): Boolean;
begin
  {Result := False;
  if m_wStatusTimeArr2[STATE_DEFENCEUP] > 0 then begin
    if m_wStatusTimeArr2[STATE_DEFENCEUP] < nSec then begin
      m_wStatusTimeArr2[STATE_DEFENCEUP] := nSec;
      Result := True;
    end;
  end else begin
    m_wStatusTimeArr2[STATE_DEFENCEUP] := nSec;
    Result := True;
  end;
  m_dwStatusArrTick2[STATE_DEFENCEUP] := GetTickCount;
  SysMsg(Format(g_sDefenceDownTime, [nSec]), c_Green, t_Hint);
  RecalcAbilitys();
  SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, ''); }
end;

function TActorObject.MagDefenceDown(nSec: Integer): Boolean;
begin
  Result := False;
  if m_wStatusTimeArr[STATE_MAGDEFENCEUP {0x74}] > 0 then begin
    if m_wStatusTimeArr[STATE_MAGDEFENCEUP {0x74}] < nSec then begin
      m_wStatusTimeArr[STATE_MAGDEFENCEUP {0x74}] := nSec;
      Result := True;
    end;
  end else begin
    m_wStatusTimeArr[STATE_MAGDEFENCEUP {0x74}] := nSec;
    Result := True;
  end;
  m_dwStatusArrTick[STATE_MAGDEFENCEUP {0x210}] := GetTickCount;
  SysMsg(Format(g_sMagDefenceDownTime, [nSec]), c_Green, t_Hint);
  RecalcAbilitys();
  SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
end;

function TActorObject.DefenceUp(nSec: Integer): Boolean;
begin
  Result := False;
  if m_wStatusTimeArr[STATE_DEFENCEUP {0x72}] > 0 then begin
    if m_wStatusTimeArr[STATE_DEFENCEUP {0x72}] < nSec then begin
      m_wStatusTimeArr[STATE_DEFENCEUP {0x72}] := nSec;
      Result := True;
    end;
  end else begin
    m_wStatusTimeArr[STATE_DEFENCEUP {0x72}] := nSec;
    Result := True;
  end;
  m_dwStatusArrTick[STATE_DEFENCEUP {0x20C}] := GetTickCount;
  SysMsg(Format(g_sDefenceUpTime, [nSec]), c_Green, t_Hint);
  RecalcAbilitys();
  SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
end;

function TActorObject.MagDefenceUp(nSec: Integer): Boolean;
begin
  Result := False;
  if m_wStatusTimeArr[STATE_MAGDEFENCEUP {0x74}] > 0 then begin
    if m_wStatusTimeArr[STATE_MAGDEFENCEUP {0x74}] < nSec then begin
      m_wStatusTimeArr[STATE_MAGDEFENCEUP {0x74}] := nSec;
      Result := True;
    end;
  end else begin
    m_wStatusTimeArr[STATE_MAGDEFENCEUP {0x74}] := nSec;
    Result := True;
  end;
  m_dwStatusArrTick[STATE_MAGDEFENCEUP {0x210}] := GetTickCount;
  SysMsg(Format(g_sMagDefenceUpTime, [nSec]), c_Green, t_Hint);
  RecalcAbilitys();
  SendMsg(Self, RM_ABILITY, 0, 0, 0, 0, '');
end;

//魔法盾

function TActorObject.MagBubbleDefenceUp(nLevel, nSec: Integer): Boolean;
var
  nOldStatus: Integer;
begin
  Result := False;
  if m_wStatusTimeArr[STATE_BUBBLEDEFENCEUP {0x76}] <> 0 then Exit;
  nOldStatus := m_nCharStatus;
  m_wStatusTimeArr[STATE_BUBBLEDEFENCEUP {0x76}] := nSec;
  m_dwStatusArrTick[STATE_BUBBLEDEFENCEUP {0x214}] := GetTickCount();
  m_nCharStatus := GetCharStatus();
  if nOldStatus <> m_nCharStatus then begin
    StatusChanged();
  end;
  m_boAbilMagBubbleDefence := True;
  m_btMagBubbleDefenceLevel := nLevel;
  Result := True;
end;

procedure TPlayObject.MakeMine;
  function RandomDrua(): Integer;
  begin
    Result := Random(g_Config.nStoneGeneralDuraRate {13000}) + g_Config.nStoneMinDura {3000};
    if Random(g_Config.nStoneAddDuraRate {20}) = 0 then begin
      Result := Result + Random(g_Config.nStoneAddDuraMax {10000});
    end;
  end;
var
  UserItem: pTUserItem;
  nRANDOM: Integer;
begin
  if m_ItemList.Count >= MAXBAGITEM then Exit;
  nRANDOM := Random(g_Config.nStoneTypeRate {120});
  if nRANDOM in [g_Config.nGoldStoneMin {1}..g_Config.nGoldStoneMax {2}] then begin
    New(UserItem);
    if UserEngine.CopyToUserItemFromName(g_Config.sGoldStone, UserItem) then begin
      UserItem.Dura := RandomDrua();
      m_ItemList.Add(UserItem);
      WeightChanged();
      SendAddItem(UserItem);
    end else Dispose(UserItem);
    Exit;
  end;
  if nRANDOM in [g_Config.nSilverStoneMin {3}..g_Config.nSilverStoneMax {20}] then begin
    New(UserItem);
    if UserEngine.CopyToUserItemFromName(g_Config.sSilverStone, UserItem) then begin
      UserItem.Dura := RandomDrua();
      m_ItemList.Add(UserItem);
      WeightChanged();
      SendAddItem(UserItem);
    end else Dispose(UserItem);
    Exit;
  end;
  if nRANDOM in [g_Config.nSteelStoneMin {21}..g_Config.nSteelStoneMax {45}] then begin
    New(UserItem);
    if UserEngine.CopyToUserItemFromName(g_Config.sSteelStone, UserItem) then begin
      UserItem.Dura := RandomDrua();
      m_ItemList.Add(UserItem);
      WeightChanged();
      SendAddItem(UserItem);
    end else Dispose(UserItem);
    Exit;
  end;
  if nRANDOM in [g_Config.nBlackStoneMin {46}..g_Config.nBlackStoneMax {56}] then begin
    New(UserItem);
    if UserEngine.CopyToUserItemFromName(g_Config.sBlackStone, UserItem) then begin
      UserItem.Dura := RandomDrua();
      m_ItemList.Add(UserItem);
      WeightChanged();
      SendAddItem(UserItem);
    end else Dispose(UserItem);
    Exit;
  end;
  New(UserItem);
  if UserEngine.CopyToUserItemFromName(g_Config.sCopperStone, UserItem) then begin
    UserItem.Dura := RandomDrua();
    m_ItemList.Add(UserItem);
    WeightChanged();
    SendAddItem(UserItem);
  end else Dispose(UserItem);
end;

function TPlayObject.QuestCheckItem(sItemName: string; var nCount,
  nParam: Integer; var nDura: Integer): pTUserItem;
var
  I: Integer;
  UserItem: pTUserItem;
  s1C: string;
begin
  Result := nil;
  nParam := 0;
  nDura := 0;
  nCount := 0;
  for I := 0 to m_ItemList.Count - 1 do begin
    UserItem := m_ItemList.Items[I];
    s1C := UserEngine.GetStdItemName(UserItem.wIndex);
    if CompareText(s1C, sItemName) = 0 then begin
      if UserItem.Dura > nDura then begin
        nDura := UserItem.Dura;
        Result := UserItem;
      end;
      Inc(nParam, UserItem.Dura);
      if Result = nil then
        Result := UserItem;
      Inc(nCount);
    end;
  end;
end;

function TActorObject.sub_4C4CD4(sItemName: string;
  var nCount: Integer): pTUserItem;
var
  I: Integer;
  sName: string;
begin
  Result := nil;
  nCount := 0;
  for I := Low(THumanUseItems) to High(THumanUseItems) do begin
    sName := UserEngine.GetStdItemName(m_UseItems[I].wIndex);
    if CompareText(sName, sItemName) = 0 then begin
      Result := @m_UseItems[I];
      Inc(nCount);
    end;
  end;
end;

function TPlayObject.QuestTakeCheckItem(CheckItem: pTUserItem): Boolean;
var
  I: Integer;
  UserItem: pTUserItem;
begin
  Result := False;
  for I := m_ItemList.Count - 1 downto 0 do begin
    if m_ItemList.Count <= 0 then Break;
    UserItem := m_ItemList.Items[I];
    if UserItem = CheckItem then begin
      SendDelItems(UserItem);
      m_ItemList.Delete(I);
      Dispose(UserItem);
      Result := True;
      Break;
    end;
  end;
  for I := Low(m_UseItems) to High(m_UseItems) do begin
    if @m_UseItems[I] = CheckItem then begin
      SendDelItems(@m_UseItems[I]);
      m_UseItems[I].wIndex := 0;
      Result := True;
      Break;
    end;
  end;
end;

procedure TPlayObject.ClientQueryRepairCost(nParam1, nInt: Integer;
  sMsg: string);
var
  I: Integer;
  UserItem: pTUserItem;
  UserItemA: pTUserItem;
  Merchant: TMerchant;
  sUserItemName: string;
begin
  UserItemA := nil;
  for I := 0 to m_ItemList.Count - 1 do begin
    UserItem := m_ItemList.Items[I];
    if (UserItem.MakeIndex = nInt) then begin
      //取自定义物品名称
      sUserItemName := '';
      if UserItem.btValue[13] = 1 then
        sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
      if sUserItemName = '' then
        sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

      if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
        sUserItemName := '(绑)' + sUserItemName;
      end;

      if (CompareText(sUserItemName, sMsg) = 0) then begin
        UserItemA := UserItem;
        Break;
      end;
      UserItemA := nil;
    end;
  end;
  if UserItemA = nil then Exit;
  Merchant := UserEngine.FindMerchant(TObject(nParam1));
  if (Merchant <> nil) and
    ((Merchant.m_PEnvir = m_PEnvir) and
    (abs(Merchant.m_nCurrX - m_nCurrX) < 15) and
    (abs(Merchant.m_nCurrY - m_nCurrY) < 15)) then
    Merchant.ClientQueryRepairCost(Self, UserItemA);
end;

procedure TPlayObject.ClientRepairItem(nParam1, nInt: Integer;
  sMsg: string);
var
  I: Integer;
  UserItem: pTUserItem;
  Merchant: TMerchant;
  sUserItemName: string;
  sCheckItemName: string;
  bo19: Boolean;
begin
  UserItem := nil;
  bo19 := False;
  for I := 0 to m_ItemList.Count - 1 do begin
    UserItem := m_ItemList.Items[I];
    if UserItem.MakeIndex = nInt then begin
      if Assigned(PlugInEngine.CheckCanRepairItem) then begin
        sCheckItemName := UserEngine.GetStdItemName(UserItem.wIndex);
        if not PlugInEngine.CheckCanRepairItem(Self, PChar(sCheckItemName), True) then Break;
      end;
    end;
    //取自定义物品名称
    sUserItemName := '';
    if UserItem.btValue[13] = 1 then
      sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
    if sUserItemName = '' then
      sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

    if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
      sUserItemName := '(绑)' + sUserItemName;
    end;

    if (UserItem.MakeIndex = nInt) and
      (CompareText(sUserItemName, sMsg) = 0) then begin
      bo19 := True;
      Break;
    end;
    UserItem := nil;
  end; // for
  if (UserItem = nil) or (not bo19) then Exit;

  Merchant := UserEngine.FindMerchant(TObject(nParam1));
  if (Merchant <> nil) and
    ((Merchant.m_PEnvir = m_PEnvir) and
    (abs(Merchant.m_nCurrX - m_nCurrX) < 15) and
    (abs(Merchant.m_nCurrY - m_nCurrY) < 15)) then
    Merchant.ClientRepairItem(Self, UserItem);
end;

procedure TPlayObject.ClientStorageItem(NPC: TObject;
  nItemIdx: Integer; sMsg: string);
var
  Merchant: TMerchant;
  I: Integer;
  UserItem: pTUserItem;
  bo19: Boolean;
  StdItem: pTStdItem;
  sUserItemName: string;
  sCheckItemName: string;
  StorageList: TStorageList;
begin
  StorageList := nil;
  Merchant := UserEngine.FindMerchant(NPC);
  if Merchant <> nil then begin
    bo19 := False;
    UserItem := nil;
    if Pos(' ', sMsg) >= 0 then //折分物品名称(信件物品的名称后面加了使用次数)
      GetValidStr3(sMsg, sMsg, [' ']);
    if (m_nPayMent = 1) and not g_Config.boTryModeUseStorage then begin
      SysMsg(g_sTryModeCanotUseStorage {'试玩模式不可以使用仓库功能！！！'}, c_Red, t_Hint);
      SendDefMessage(SM_STORAGE_FAIL, 0, 0, 0, 0, '');
      Exit;
    end;
    if Merchant.m_boBigStorage then begin
      if (g_Storage.GetStorageList(m_sCharName, StorageList) >= 0) and (StorageList <> nil) and (StorageList.Count > g_Config.nBigStorageLimitCount) then begin
        SysMsg(Format(g_sBigStorageCountIsNul, [g_Config.nBigStorageLimitCount]), c_Red, t_Hint);
        SendDefMessage(SM_STORAGE_FAIL, 0, 0, 0, 0, '');
        Exit;
      end;
    end;

    for I := m_ItemList.Count - 1 downto 0 do begin
      if m_ItemList.Count <= 0 then Break;
      UserItem := m_ItemList.Items[I];
      if UserItem.MakeIndex = nItemIdx then begin
        if Assigned(PlugInEngine.CheckCanStorageItem) then begin
          sCheckItemName := UserEngine.GetStdItemName(UserItem.wIndex);
          if not PlugInEngine.CheckCanStorageItem(Self, PChar(sCheckItemName), True) then begin
            Break;
          end;
        end;
      //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName = '' then
          sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

        if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
          sUserItemName := '(绑)' + sUserItemName;
        end;

        if CompareText(sUserItemName, sMsg) = 0 then begin
          if (((Merchant.m_PEnvir = m_PEnvir) and
            (abs(Merchant.m_nCurrX - m_nCurrX) < 15) and
            (abs(Merchant.m_nCurrY - m_nCurrY) < 15)) or (Merchant = g_FunctionNPC)) then begin
            if Merchant.m_boBigStorage then begin
              if g_Storage.Add(m_sCharName, UserItem) then begin
                m_ItemList.Delete(I);
                WeightChanged();
                SendDefMessage(SM_STORAGE_OK, 0, 0, 0, 0, '');
                StdItem := UserEngine.GetStdItem(UserItem.wIndex);
                if StdItem.NeedIdentify = 1 then
                  AddGameDataLog('1' + #9 +
                    m_sMapName + #9 +
                    IntToStr(m_nCurrX) + #9 +
                    IntToStr(m_nCurrY) + #9 +
                    m_sCharName + #9 +
                //UserEngine.GetStdItemName(UserItem.wIndex) + #9 +
                    StdItem.Name + #9 +
                    IntToStr(UserItem.MakeIndex) + #9 +
                    '1' + #9 +
                    '0');
                Dispose(UserItem);
              end else begin
                SendDefMessage(SM_STORAGE_FULL, 0, 0, 0, 0, '');
              end;
              bo19 := True;
            end else

              if Merchant.m_boStorage then begin
              if m_StorageItemList.Count < MAXBAGITEM then begin
                m_StorageItemList.Add(UserItem);
                m_ItemList.Delete(I);
                WeightChanged();
                SendDefMessage(SM_STORAGE_OK, 0, 0, 0, 0, '');
                StdItem := UserEngine.GetStdItem(UserItem.wIndex);
                if StdItem.NeedIdentify = 1 then
                  AddGameDataLog('1' + #9 +
                    m_sMapName + #9 +
                    IntToStr(m_nCurrX) + #9 +
                    IntToStr(m_nCurrY) + #9 +
                    m_sCharName + #9 +
                //UserEngine.GetStdItemName(UserItem.wIndex) + #9 +
                    StdItem.Name + #9 +
                    IntToStr(UserItem.MakeIndex) + #9 +
                    '1' + #9 +
                    '0');
              end else begin
                SendDefMessage(SM_STORAGE_FULL, 0, 0, 0, 0, '');
              end;
              bo19 := True;
            end;
          end;
        end;
        Break;
      end;
    end;
  end;
  if Merchant = nil then Exit;
  if not bo19 then SendDefMessage(SM_STORAGE_FAIL, 0, 0, 0, 0, '');
end;

procedure TPlayObject.ClientTakeBackStorageItem(NPC: TObject;
  nItemIdx: Integer; sMsg: string);
var
  Merchant: TMerchant;
  I: Integer;
  nListIndex, nIndex: Integer;
  UserItem: pTUserItem;
  StorageItem: TUserItem;
  bo19: Boolean;
  StdItem: pTStdItem;
  sUserItemName: string;
  StorageList: TStorageList;
begin
  StorageList := nil;
  Merchant := UserEngine.FindMerchant(NPC);
  if Merchant <> nil then begin
    bo19 := False;
    UserItem := nil;
    if (m_nPayMent = 1) and not g_Config.boTryModeUseStorage then begin
      SysMsg(g_sTryModeCanotUseStorage {'试玩模式不可以使用仓库功能！！！'}, c_Red, t_Hint);
      Exit;
    end;
    if not m_boCanGetBackItem then begin
      SendMsg(Merchant, RM_MENU_OK, 0, Integer(Self), 0, 0, g_sStorageIsLockedMsg + '\ \'
        + 'Warehouse unlock command: @' + g_GameCommand.UNLOCKSTORAGE.sCmd + '\'
        + 'Warehouse Lock command: @' + g_GameCommand.Lock.sCmd + '\'
        + 'Set Password command: @' + g_GameCommand.SETPASSWORD.sCmd + '\'
        + 'Change Password command: @' + g_GameCommand.CHGPASSWORD.sCmd);
      Exit;
    end;
    if Merchant.m_boBigGetBack then begin
      nListIndex := g_Storage.GetStorageList(m_sCharName, StorageList);
      if (StorageList <> nil) then begin
        nIndex := StorageList.GetItem(nItemIdx, sMsg, StorageItem);
      end;
      if (nListIndex >= 0) and (nIndex >= 0) then begin
        New(UserItem);
        UserItem^ := StorageItem;
        if (IsAddWeightAvailable(UserEngine.GetStdItemWeight(UserItem.wIndex))) then begin
          if (Merchant <> nil) and
            (((Merchant.m_PEnvir = m_PEnvir) and
            (abs(Merchant.m_nCurrX - m_nCurrX) < 15) and
            (abs(Merchant.m_nCurrY - m_nCurrY) < 15)) or (Merchant = g_FunctionNPC)) then begin
            if AddItemToBag(UserItem) then begin
              SendAddItem(UserItem);
              //Dispose(StorageList.Items[nIndex]);
              StorageList.DeleteA(nIndex);
              if StorageList.Count <= 0 then begin
                StorageList.Free;
                g_Storage.Delete(nListIndex);
              end;
              SendDefMessage(SM_TAKEBACKSTORAGEITEM_OK, nItemIdx, 0, 0, 0, '');
              StdItem := UserEngine.GetStdItem(UserItem.wIndex);
              if StdItem.NeedIdentify = 1 then
                AddGameDataLog('0' + #9 +
                  m_sMapName + #9 +
                  IntToStr(m_nCurrX) + #9 +
                  IntToStr(m_nCurrY) + #9 +
                  m_sCharName + #9 +
                //UserEngine.GetStdItemName(UserItem.wIndex) + #9 +
                  StdItem.Name + #9 +
                  IntToStr(UserItem.MakeIndex) + #9 +
                  '1' + #9 +
                  '0');
            end else begin
              Dispose(UserItem);
              SendDefMessage(SM_TAKEBACKSTORAGEITEM_FULLBAG, 0, 0, 0, 0, '');
            end;
            bo19 := True;
          end else Dispose(UserItem);
        end else begin
          Dispose(UserItem);
          SysMsg(g_sCanotGetItems {'无法携带更多的东西！！！'}, c_Red, t_Hint);
        end;
      end;
    end else
      if Merchant.m_boGetback then begin

      for I := m_StorageItemList.Count - 1 downto 0 do begin
        UserItem := m_StorageItemList.Items[I];
      //取自定义物品名称
        sUserItemName := '';
        if UserItem.btValue[13] = 1 then
          sUserItemName := ItemUnit.GetCustomItemName(UserItem.MakeIndex, UserItem.wIndex);
        if sUserItemName = '' then
          sUserItemName := UserEngine.GetStdItemName(UserItem.wIndex);

        if CheckItemBindUse(UserItem, False) = 2 then begin //绑定物品
          sUserItemName := '(绑)' + sUserItemName;
        end;

        if (UserItem.MakeIndex = nItemIdx) and
          (CompareText(sUserItemName, sMsg) = 0) then begin

          if (IsAddWeightAvailable(UserEngine.GetStdItemWeight(UserItem.wIndex))) then begin
            if (Merchant <> nil) and
              (Merchant.m_boGetback) and //检查NPC是否允许取物品
              (((Merchant.m_PEnvir = m_PEnvir) and
              (abs(Merchant.m_nCurrX - m_nCurrX) < 15) and
              (abs(Merchant.m_nCurrY - m_nCurrY) < 15)) or (Merchant = g_FunctionNPC)) then begin

              if AddItemToBag(UserItem) then begin
                SendAddItem(UserItem);
                m_StorageItemList.Delete(I);
                SendDefMessage(SM_TAKEBACKSTORAGEITEM_OK, nItemIdx, 0, 0, 0, '');
                StdItem := UserEngine.GetStdItem(UserItem.wIndex);
                if StdItem.NeedIdentify = 1 then
                  AddGameDataLog('0' + #9 +
                    m_sMapName + #9 +
                    IntToStr(m_nCurrX) + #9 +
                    IntToStr(m_nCurrY) + #9 +
                    m_sCharName + #9 +
                  //UserEngine.GetStdItemName(UserItem.wIndex) + #9 +
                    StdItem.Name + #9 +
                    IntToStr(UserItem.MakeIndex) + #9 +
                    '1' + #9 +
                    '0');
              end else begin
                SendDefMessage(SM_TAKEBACKSTORAGEITEM_FULLBAG, 0, 0, 0, 0, '');
              end;
              bo19 := True;
            end;
          end else begin
            SysMsg(g_sCanotGetItems {'无法携带更多的东西！！！'}, c_Red, t_Hint);
          end;
          Break;
        end;
      end;
    end;
  end;
  if Merchant = nil then Exit;
  if not bo19 then SendDefMessage(SM_TAKEBACKSTORAGEITEM_FAIL, 0, 0, 0, 0, '');
end;

function TActorObject.CheckItems(sItemName: string): pTUserItem;
var
  I: Integer;
  UserItem: pTUserItem;
begin
  Result := nil;
  for I := 0 to m_ItemList.Count - 1 do begin
    UserItem := m_ItemList.Items[I];
    if CompareText(UserEngine.GetStdItemName(UserItem.wIndex), sItemName) = 0 then begin
      Result := UserItem;
      Break;
    end;
  end; // for
end;

procedure TPlayObject.MakeSaveRcd(var HumanRcd: THumDataInfo);
var
  I: Integer;
  //HeroItems: pTHeroItems;
  HumData: pTHumData;
  HumItems: pTHumItems;
  HumAddItems: pTHumAddItems;
  BagItems: pTBagItems;
  HumMagics: pTHumMagics;
  UserMagic: pTUserMagic;
  StorageItems: pTStorageItems;
  //HeroData: pTHeroData;
begin
  HumanRcd.Header.boIsHero := False;
  HumanRcd.Header.boDeleted := False;
  HumanRcd.Header.sName := m_sCharName;

  HumData := @HumanRcd.Data;
  HumData.sChrName := m_sCharName;
  HumData.sCurMap := m_sMapName;
  HumData.wCurX := m_nCurrX;
  HumData.wCurY := m_nCurrY;
  HumData.btDir := m_btDirection;
  HumData.btHair := m_btHair;
  HumData.btSex := m_btGender;
  HumData.btJob := m_btJob;
  HumData.nGold := m_nGold;

  HumData.Abil.Level := m_Abil.Level;
  HumData.Abil.HP := m_Abil.HP;
  HumData.Abil.MP := m_Abil.MP;
  HumData.Abil.MaxHP := m_Abil.MaxHP;
  HumData.Abil.MaxMP := m_Abil.MaxMP;
  HumData.Abil.Exp := m_Abil.Exp;
  HumData.Abil.MaxExp := m_Abil.MaxExp;
  HumData.Abil.Weight := m_Abil.Weight;
  HumData.Abil.MaxWeight := m_Abil.MaxWeight;
  HumData.Abil.WearWeight := m_Abil.WearWeight;
  HumData.Abil.MaxWearWeight := m_Abil.MaxWearWeight;
  HumData.Abil.HandWeight := m_Abil.HandWeight;
  HumData.Abil.MaxHandWeight := m_Abil.MaxHandWeight;

  //HumData.Abil:=m_Abil;
  HumData.Abil.HP := m_WAbil.HP;
  HumData.Abil.MP := m_WAbil.MP;

  HumData.wStatusTimeArr := m_wStatusTimeArr;
  HumData.sHomeMap := m_sHomeMap;
  HumData.wHomeX := m_nHomeX;
  HumData.wHomeY := m_nHomeY;
  HumData.nPKPOINT := m_nPkPoint;
  HumData.BonusAbil := m_BonusAbil; // 08/09
  HumData.nBonusPoint := m_nBonusPoint; // 08/09
  HumData.sStoragePwd := m_sStoragePwd;
  HumData.btCreditPoint := m_btCreditPoint;
  HumData.btReLevel := m_btReLevel;
  HumData.boOnHorse := m_boOnHorse;
  HumData.sMasterName := m_sMasterName;
  HumData.boMaster := m_boMaster;
  HumData.sDearName := m_sDearName;

  HumData.nGameGold := m_nGameGold;
  HumData.nGamePoint := m_nGamePoint;

  if m_boAllowGroup then HumData.btAllowGroup := 1
  else HumData.btAllowGroup := 0;
  HumData.btF9 := btB2;
  HumData.btAttatckMode := m_btAttatckMode;
  HumData.btIncHealth := m_nIncHealth;
  HumData.btIncSpell := m_nIncSpell;
  HumData.btIncHealing := m_nIncHealing;
  HumData.btFightZoneDieCount := m_nFightZoneDieCount;
  HumData.sAccount := m_sUserID;
  HumData.btEE := nC4;
  HumData.boLockLogon := m_boLockLogon;
  HumData.wContribution := m_wContribution;
  //HumData.btEF := btC8;
  HumData.nHungerStatus := m_nHungerStatus;
  HumData.boAllowGuildReCall := m_boAllowGuildReCall;
  HumData.wGroupRcallTime := m_wGroupRcallTime;
  HumData.dBodyLuck := m_dBodyLuck;
  HumData.boAllowGroupReCall := m_boAllowGroupReCall;

  HumData.btLastOutStatus := m_btLastOutStatus; //2006-01-12增加 退出状态 1为死亡退出
  HumData.wMasterCount := m_wMasterCount; //出师徒弟数
  HumData.QuestFlag := m_QuestFlag;
  HumData.boHasHero := m_boHasHero;
  HumData.boIsHero := False;
  HumData.sHeroChrName := m_sHeroCharName;
  HumData.nGrudge := m_nGrudge;

  HumData.wStatusDelayTime := m_nStatusDelayTime;
  HumData.NewStatus := m_NewStatus; //1失明 2混乱 状态

  HumItems := @HumanRcd.Data.HumItems;
  HumItems[U_DRESS] := m_UseItems[U_DRESS];
  HumItems[U_WEAPON] := m_UseItems[U_WEAPON];
  HumItems[U_RIGHTHAND] := m_UseItems[U_RIGHTHAND];

  HumItems[U_HELMET] := m_UseItems[U_NECKLACE];
  HumItems[U_NECKLACE] := m_UseItems[U_HELMET];
  HumItems[U_ARMRINGL] := m_UseItems[U_ARMRINGL];
  HumItems[U_ARMRINGR] := m_UseItems[U_ARMRINGR];
  HumItems[U_RINGL] := m_UseItems[U_RINGL];
  HumItems[U_RINGR] := m_UseItems[U_RINGR];

  HumAddItems := @HumanRcd.Data.HumAddItems;
  HumAddItems[U_BUJUK] := m_UseItems[U_BUJUK];
  HumAddItems[U_BELT] := m_UseItems[U_BELT];
  HumAddItems[U_BOOTS] := m_UseItems[U_BOOTS];
  HumAddItems[U_CHARM] := m_UseItems[U_CHARM];
 // HumAddItems[U_MOUNT] := m_UseItems[U_MOUNT];

  BagItems := @HumanRcd.Data.BagItems;
  for I := 0 to m_ItemList.Count - 1 do begin
    if I >= MAXBAGITEM then Break;
    BagItems[I] := pTUserItem(m_ItemList.Items[I])^;
  end;
  HumMagics := @HumanRcd.Data.HumMagics;
  for I := 0 to m_MagicList.Count - 1 do begin
    if I >= MAXMAGIC then Break;
    UserMagic := m_MagicList.Items[I];
    HumMagics[I].wMagIdx := UserMagic.wMagIdx;
    HumMagics[I].btLevel := UserMagic.btLevel;
    HumMagics[I].btKey := UserMagic.btKey;
    HumMagics[I].nTranPoint := UserMagic.nTranPoint;
  end;
  StorageItems := @HumanRcd.Data.StorageItems;
  for I := 0 to m_StorageItemList.Count - 1 do begin
    if I >= MAXBAGITEM then Break;
    StorageItems[I] := pTUserItem(m_StorageItemList.Items[I])^;
  end;
end;

function TActorObject.sub_4C3538: Integer;
var
  nC, n10: Integer;
begin
  Result := 0;
  nC := -1;
  while (nC <> 2) do begin
    n10 := -1;
    while (n10 <> 2) do begin
      if not m_PEnvir.CanWalk(m_nCurrX + nC, m_nCurrY + n10, False) then begin
        if (nC <> 0) or (n10 <> 0) then
          Inc(Result);
      end;
      Inc(n10);
    end;
    Inc(nC);
  end;
end;

procedure TPlayObject.RefRankInfo(nRankNo: Integer; sRankName: string);
begin
  m_nGuildRankNo := nRankNo;
  m_sGuildRankName := sRankName;
  SendMsg(Self, RM_CHANGEGUILDNAME, 0, 0, 0, 0, '');
end;

function TActorObject.DelBagItem(nIndex: Integer): Boolean;
begin
  Result := False;
  if (nIndex < 0) or (nIndex >= m_ItemList.Count) then Exit;
  if pTUserItem(m_ItemList.Items[nIndex]) <> nil then begin
    Dispose(pTUserItem(m_ItemList.Items[nIndex]));
    m_ItemList.Delete(nIndex);
    Result := True;
  end;
  if Result then WeightChanged();
end;

function TActorObject.DelBagItem(nItemIndex: Integer;
  sItemName: string): Boolean;
var
  I: Integer;
  UserItem: pTUserItem;
begin
  Result := False;
  for I := 0 to m_ItemList.Count - 1 do begin
    UserItem := m_ItemList.Items[I];
    if (UserItem.MakeIndex = nItemIndex) and
      (CompareText(UserEngine.GetStdItemName(UserItem.wIndex), sItemName) = 0) then begin
      m_ItemList.Delete(I);
      Dispose(UserItem);
      Result := True;
      Break;
    end;
  end;
  if Result then WeightChanged();
end;

function TActorObject.DelBagItem(UserItem: pTUserItem): Boolean;
var
  I: Integer;
  Item: pTUserItem;
begin
  Result := False;
  for I := 0 to m_ItemList.Count - 1 do begin
    Item := m_ItemList.Items[I];
    if UserItem = Item then begin
      m_ItemList.Delete(I);
      Dispose(Item);
      Result := True;
      Break;
    end;
  end;
  if Result then WeightChanged();
end;

function TPlayObject.DelStorageItem(UserItem: pTUserItem): Boolean;
var
  I: Integer;
  Item: pTUserItem;
begin
  Result := False;
  for I := 0 to m_StorageItemList.Count - 1 do begin
    Item := m_StorageItemList.Items[I];
    if UserItem = Item then begin
      m_StorageItemList.Delete(I);
      Dispose(Item);
      Result := True;
      Break;
    end;
  end;
end;

function TPlayObject.DelStorageItem(nItemIndex: Integer;
  sItemName: string): Boolean;
var
  I: Integer;
  UserItem: pTUserItem;
begin
  Result := False;
  for I := 0 to m_StorageItemList.Count - 1 do begin
    UserItem := m_StorageItemList.Items[I];
    if (UserItem.MakeIndex = nItemIndex) and
      (CompareText(UserEngine.GetStdItemName(UserItem.wIndex), sItemName) = 0) then begin
      m_StorageItemList.Delete(I);
      Dispose(UserItem);
      Result := True;
      Break;
    end;
  end;
end;

procedure TPlayObject.GetOldAbil(var OAbility: TOAbility);
begin
  FillChar(OAbility, SizeOf(TOAbility), #0);
  OAbility.Level := m_WAbil.Level;
  OAbility.AC := MakeWord(_MIN(High(Byte), LoWord(m_WAbil.AC)), _MIN(High(Byte), HiWord(m_WAbil.AC)));
  OAbility.MAC := MakeWord(_MIN(High(Byte), LoWord(m_WAbil.MAC)), _MIN(High(Byte), HiWord(m_WAbil.MAC)));
  OAbility.DC := MakeWord(_MIN(High(Byte), LoWord(m_WAbil.DC)), _MIN(High(Byte), HiWord(m_WAbil.DC)));
  OAbility.MC := MakeWord(_MIN(High(Byte), LoWord(m_WAbil.MC)), _MIN(High(Byte), HiWord(m_WAbil.MC)));
  OAbility.SC := MakeWord(_MIN(High(Byte), LoWord(m_WAbil.SC)), _MIN(High(Byte), HiWord(m_WAbil.SC)));
  OAbility.HP := m_WAbil.HP;
  OAbility.MP := m_WAbil.MP;
  OAbility.MaxHP := m_WAbil.MaxHP;
  OAbility.MaxMP := m_WAbil.MaxMP;
  OAbility.Exp := m_WAbil.Exp;
  OAbility.MaxExp := m_WAbil.MaxExp;
  OAbility.Weight := m_WAbil.Weight;
  OAbility.MaxWeight := m_WAbil.MaxWeight;
  OAbility.WearWeight := _MIN(High(Byte), m_WAbil.WearWeight);
  OAbility.MaxWearWeight := _MIN(High(Byte), m_WAbil.MaxWearWeight);
  OAbility.HandWeight := _MIN(High(Byte), m_WAbil.HandWeight);
  OAbility.MaxHandWeight := _MIN(High(Byte), m_WAbil.MaxHandWeight);
end;

function TPlayObject.GetHitMsgCount: Integer;
var
  I: Integer;
  SendMessage: pTSendMessage;
begin
  Result := 0;
  try
    EnterCriticalSection(ProcessMsgCriticalSection);
    for I := 0 to m_MsgList.Count - 1 do begin
      SendMessage := m_MsgList.Items[I];
      if (SendMessage.wIdent = CM_HIT) or
        (SendMessage.wIdent = CM_HEAVYHIT) or
        (SendMessage.wIdent = CM_BIGHIT) or
        (SendMessage.wIdent = CM_POWERHIT) or
        (SendMessage.wIdent = CM_LONGHIT) or
        (SendMessage.wIdent = CM_WIDEHIT) or
        (SendMessage.wIdent = CM_CRSHIT) or
        (SendMessage.wIdent = CM_PKHIT) or
        (SendMessage.wIdent = CM_PHHIT) or
        (SendMessage.wIdent = CM_60HIT) or
        (SendMessage.wIdent = CM_FIREHIT) then begin
        Inc(Result);
      end;
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
end;

function TPlayObject.GetSpellMsgCount: Integer;
var
  I: Integer;
  SendMessage: pTSendMessage;
begin
  Result := 0;
  try
    EnterCriticalSection(ProcessMsgCriticalSection);
    for I := 0 to m_MsgList.Count - 1 do begin
      SendMessage := m_MsgList.Items[I];
      if (SendMessage.wIdent = CM_SPELL) then begin
        Inc(Result);
      end;
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
end;

function TPlayObject.GetRunMsgCount: Integer;
var
  I: Integer;
  SendMessage: pTSendMessage;
begin
  Result := 0;
  try
    EnterCriticalSection(ProcessMsgCriticalSection);
    for I := 0 to m_MsgList.Count - 1 do begin
      SendMessage := m_MsgList.Items[I];
      if (SendMessage.wIdent = CM_RUN) then begin
        Inc(Result);
      end;
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
end;

function TPlayObject.GetWalkMsgCount: Integer;
var
  I: Integer;
  SendMessage: pTSendMessage;
begin
  Result := 0;
  try
    EnterCriticalSection(ProcessMsgCriticalSection);
    for I := 0 to m_MsgList.Count - 1 do begin
      SendMessage := m_MsgList.Items[I];
      if (SendMessage.wIdent = CM_WALK) then begin
        Inc(Result);
      end;
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
end;

function TPlayObject.GetTurnMsgCount: Integer;
var
  I: Integer;
  SendMessage: pTSendMessage;
begin
  Result := 0;
  try
    EnterCriticalSection(ProcessMsgCriticalSection);
    for I := 0 to m_MsgList.Count - 1 do begin
      SendMessage := m_MsgList.Items[I];
      if (SendMessage.wIdent = CM_TURN) then begin
        Inc(Result);
      end;
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
end;

function TPlayObject.GetSiteDownMsgCount: Integer;
var
  I: Integer;
  SendMessage: pTSendMessage;
begin
  Result := 0;
  EnterCriticalSection(ProcessMsgCriticalSection);
  try
    for I := 0 to m_MsgList.Count - 1 do begin
      SendMessage := m_MsgList.Items[I];
      if (SendMessage.wIdent = CM_SITDOWN) then begin
        Inc(Result);
      end;
    end;
  finally
    LeaveCriticalSection(ProcessMsgCriticalSection);
  end;
end;

function TPlayObject.CheckActionStatus(wIdent: Word; var dwDelayTime: LongWord): Boolean;
var
  dwCheckTime: LongWord;
//  dwCurrTick: LongWord;
  dwActionIntervalTime: LongWord;
begin
  Result := False;
  dwDelayTime := 0;
  //检查人物弯腰停留时间
  if not g_Config.boDisableStruck then begin
    dwCheckTime := GetTickCount - m_dwStruckTick;
    if g_Config.dwStruckTime > dwCheckTime then begin
      dwDelayTime := g_Config.dwStruckTime - dwCheckTime;
      m_btOldDir := m_btDirection;
      Exit;
    end;
  end;

  //检查二个不同操作之间所需间隔时间
  dwCheckTime := GetTickCount - m_dwActionTick;

  if m_boTestSpeedMode then begin
    SysMsg('Interval: ' + IntToStr(dwCheckTime), c_Blue, t_Notice);
  end;

  if m_wOldIdent = wIdent then begin //当二次操作一样时，则将 boFirst 设置为 真 ，退出由调用函数本身检查二个相同操作之间的间隔时间
    Result := True;
    Exit;
  end;
  if not g_Config.boControlActionInterval then begin
    Result := True;
    Exit;
  end;

  dwActionIntervalTime := m_dwActionIntervalTime;
  case wIdent of
    CM_LONGHIT: begin
        //跑位刺杀
        if g_Config.boControlRunLongHit and (m_wOldIdent = CM_RUN) and (m_btOldDir <> m_btDirection) then begin
          dwActionIntervalTime := m_dwRunLongHitIntervalTime;
        end;
      end;
    CM_HIT: begin
        //走位攻击
        if g_Config.boControlWalkHit and (m_wOldIdent = CM_WALK) and (m_btOldDir <> m_btDirection) then begin
          dwActionIntervalTime := m_dwWalkHitIntervalTime;
        end;
        //跑位攻击
        if g_Config.boControlRunHit and (m_wOldIdent = CM_RUN) and (m_btOldDir <> m_btDirection) then begin
          dwActionIntervalTime := m_dwRunHitIntervalTime;
        end;
      end;
    CM_RUN: begin
        //跑位刺杀
        if g_Config.boControlRunLongHit and (m_wOldIdent = CM_LONGHIT) and (m_btOldDir <> m_btDirection) then begin
          dwActionIntervalTime := m_dwRunLongHitIntervalTime;
        end;
        //跑位攻击
        if g_Config.boControlRunHit and (m_wOldIdent = CM_HIT) and (m_btOldDir <> m_btDirection) then begin
          dwActionIntervalTime := m_dwRunHitIntervalTime;
        end;
        //跑位魔法
        if g_Config.boControlRunMagic and (m_wOldIdent = CM_SPELL) and (m_btOldDir <> m_btDirection) then begin
          dwActionIntervalTime := m_dwRunMagicIntervalTime;
        end;
      end;
    CM_WALK: begin
        //走位攻击
        if g_Config.boControlWalkHit and (m_wOldIdent = CM_HIT) and (m_btOldDir <> m_btDirection) then begin
          dwActionIntervalTime := m_dwWalkHitIntervalTime;
        end;
        //跑位刺杀
        if g_Config.boControlRunLongHit and (m_wOldIdent = CM_LONGHIT) and (m_btOldDir <> m_btDirection) then begin
          dwActionIntervalTime := m_dwRunLongHitIntervalTime;
        end;
      end;
    CM_SPELL: begin
        //跑位魔法
        if g_Config.boControlRunMagic and (m_wOldIdent = CM_RUN) and (m_btOldDir <> m_btDirection) then begin
          dwActionIntervalTime := m_dwRunMagicIntervalTime;
        end;
      end;
  end;

  //将几个攻击操作合并成一个攻击操作代码
  if (wIdent = CM_HIT) or
    (wIdent = CM_HEAVYHIT) or
    (wIdent = CM_BIGHIT) or
    (wIdent = CM_POWERHIT) or
    //     (wIdent = CM_LONGHIT) or

  (wIdent = CM_WIDEHIT) or
    (wIdent = CM_CRSHIT) or
    (wIdent = CM_PKHIT) or
    (wIdent = CM_KTHIT) or
    (wIdent = CM_60HIT) or
    (wIdent = CM_FIREHIT) then begin

    wIdent := CM_HIT;
  end;

  if dwCheckTime >= dwActionIntervalTime then begin
    m_dwActionTick := GetTickCount();
    Result := True;
  end else begin
    dwDelayTime := dwActionIntervalTime - dwCheckTime;
  end;
  m_wOldIdent := wIdent;
  m_btOldDir := m_btDirection;
  {
  dwCheckTime:=GetTickCount - m_dwActionTick;
  if dwCheckTime >= m_dwActionTime then begin
    m_dwActionTick:=GetTickCount();
    m_wOldIdent:=wIdent;
    Result:=True;
  end else begin
    dwDelayTime:=m_dwActionTime - dwCheckTime;
//    m_dwActionTime:=m_dwActionTime + 20;
  end;
  }
end;

procedure TPlayObject.SetScriptLabel(sLabel: string);
begin
  m_CanJmpScriptLableList.Clear;
  m_CanJmpScriptLableList.Add(sLabel);
end;
//取得当前脚本可以跳转的标签

procedure TPlayObject.GetScriptLabel(sMsg: string);
var
  sText: string;
  sData: string;
  sCmdStr, sLabel: string;
begin
  m_CanJmpScriptLableList.Clear;
  while (True) do begin
    if sMsg = '' then Break;
    sMsg := GetValidStr3(sMsg, sText, ['\']);
    if sText <> '' then begin
      sData := '';
      while (Pos('<', sText) > 0) and (Pos('>', sText) > 0) and (sText <> '') do begin
        if sText[1] <> '<' then begin
          sText := '<' + GetValidStr3(sText, sData, ['<']);
        end;
        sText := ArrestStringEx(sText, '<', '>', sCmdStr);
        sLabel := GetValidStr3(sCmdStr, sCmdStr, ['/']);
        if sLabel <> '' then
          m_CanJmpScriptLableList.Add(sLabel);
      end;
    end;
  end;
end;

function TPlayObject.LableIsCanJmp(sLabel: string): Boolean;
var
  I: Integer;
begin
  Result := False;
  if CompareText(sLabel, '@main') = 0 then begin
    Result := True;
    Exit;
  end;
  for I := 0 to m_CanJmpScriptLableList.Count - 1 do begin
    if CompareText(sLabel, m_CanJmpScriptLableList.Strings[I]) = 0 then begin
      Result := True;
      Break;
    end;
  end;
  if CompareText(sLabel, m_sPlayDiceLabel) = 0 then begin
    m_sPlayDiceLabel := '';
    Result := True;
    Exit;
  end;
end;

procedure TPlayObject.RecalcAbilitys;
begin
  inherited;
  RecalcAdjusBonus();
end;

procedure TPlayObject.ClearViewRange;
resourcestring
  sExceptionMsg = '[Exception] TPlayObject::ClearViewRange %d';
var
  I: Integer;
begin
  inherited;
  try
    for I := 0 to m_VisibleEvent.Count - 1 do begin
      Dispose(pTVisibleMapEvent(m_VisibleEvent.Items[I]));
    end;
  except
    MainOutMessage(Format(sExceptionMsg, [1]));
  end;

  try
    m_VisibleEvent.Clear;
  except
    MainOutMessage(Format(sExceptionMsg, [2]));
  end;
end;

procedure TPlayObject.SearchViewRange;
var
  I: Integer;
  nStartX: Integer;
  nEndX: Integer;
  nStartY: Integer;
  nEndY: Integer;
  n18: Integer;
  n1C: Integer;
  nIdx: Integer;
  n24: Integer;
  nCheckCode: Integer;
  MapCellInfo: pTMapCellinfo;
  BaseObject: TBaseObject;
  ActorObject: TActorObject;
  ItemObject: TItemObject;
  MapEvent: TEvent;

  VisibleBaseObject: pTVisibleBaseObject;
  VisibleMapItem: pTVisibleMapItem;
  VisibleMapEvent: pTVisibleMapEvent;
resourcestring
  sExceptionMsg1 = '[Exception] TPlayObject::SearchViewRange';
  sExceptionMsg2 = '[Exception] TPlayObject::SearchViewRange 1-%d %s %s %d %d %d';
begin
  if m_boNotOnlineAddExp then begin
    ClearViewRange;
    Exit; //2006-10-22 叶随风飘 修改 离线挂机不搜索
  end;
  nCheckCode := 0;
  try
    for I := 0 to m_VisibleActors.Count - 1 do begin
      pTVisibleBaseObject(m_VisibleActors.Items[I]).btVisibleFlag := 0;
    end;
    nCheckCode := 1;
    for I := 0 to m_VisibleItems.Count - 1 do begin
      pTVisibleMapItem(m_VisibleItems.Items[I]).btVisibleFlag := 0;
    end;
    nCheckCode := 2;
    for I := 0 to m_VisibleEvent.Count - 1 do begin
      pTVisibleMapEvent(m_VisibleEvent.Items[I]).btVisibleFlag := 0;
    end;
  except
    MainOutMessage(sExceptionMsg1);
  end;
  nCheckCode := 3;
  n24 := 0;
  nStartX := m_nCurrX - m_nViewRange;
  nEndX := m_nCurrX + m_nViewRange;
  nStartY := m_nCurrY - m_nViewRange;
  nEndY := m_nCurrY + m_nViewRange;
  try
    for n18 := nStartX to nEndX do begin
      for n1C := nStartY to nEndY do begin
        nCheckCode := 4;
        if m_PEnvir.GetMapCellInfo(n18, n1C, MapCellInfo) and (MapCellInfo.ObjList <> nil) then begin
          nCheckCode := 5;
          n24 := 1;
          nIdx := 0;
          while (True) do begin
            if (MapCellInfo.ObjList <> nil) and (MapCellInfo.ObjList.Count <= 0) then begin //200-11-1 增加
              nCheckCode := 6;
              FreeAndNil(MapCellInfo.ObjList);
              nCheckCode := 7;
              Break;
            end;
            nCheckCode := 8;
            if MapCellInfo.ObjList.Count <= nIdx then Break;
            nCheckCode := 9;
            BaseObject := TBaseObject(MapCellInfo.ObjList.Items[nIdx]);
            if (BaseObject <> nil) then begin
              if (BaseObject.m_ObjType = t_Actor) then begin
                if (GetTickCount - BaseObject.m_dwAddTime) >= 60 * 1000 then begin
                  nCheckCode := 10;

                  MapCellInfo.ObjList.Delete(nIdx);
                  nCheckCode := 11;
                  if MapCellInfo.ObjList.Count <= 0 then begin
                    nCheckCode := 12;
                    FreeAndNil(MapCellInfo.ObjList);
                    nCheckCode := 13;
                    Break;
                  end;
                  Continue;
                end;
                nCheckCode := 14;
                ActorObject := TActorObject(BaseObject);
                nCheckCode := 15;
                if (not ActorObject.m_boGhost) and (not ActorObject.m_boFixedHideMode) and (not ActorObject.m_boObMode) then begin
                  if (m_btRaceServer < RC_ANIMAL) or
                    (m_Master <> nil) or
                    m_boCrazyMode or
                    m_boWantRefMsg or
                    ((ActorObject.m_Master <> nil) and (abs(ActorObject.m_nCurrX - m_nCurrX) <= 3) and (abs(ActorObject.m_nCurrY - m_nCurrY) <= 3)) or
                    (ActorObject.m_btRaceServer = RC_PLAYOBJECT) then begin
                    nCheckCode := 16;
                    UpdateVisibleGay(ActorObject);
                    nCheckCode := 17;
                  end;
                end;
              end else //if (BaseObject.m_ObjType = t_Actor) then begin
                if (BaseObject.m_ObjType = t_Item) then begin
                ItemObject := TItemObject(BaseObject);
                nCheckCode := 18;
                if not ItemObject.m_boGhost then begin
               { ItemObject.m_PEnvir := nil;
                MapCellInfo.ObjList.Delete(nIdx);
                if (MapCellInfo.ObjList.Count <= 0) then begin
                  FreeAndNil(MapCellInfo.ObjList);
                  Break;
                end;}

                  nCheckCode := 19;
                  UpdateVisibleItemGay(ItemObject);
                  nCheckCode := 20;
                end;
              end else //if (BaseObject.m_ObjType = t_Item) then begin
                if (BaseObject.m_ObjType = t_Event) then begin
                MapEvent := TEvent(BaseObject);
                nCheckCode := 21;
                if MapEvent.m_boVisible then begin
                  UpdateVisibleEventGay(MapEvent);
                end;
                nCheckCode := 22;
              end;
            end;
            Inc(nIdx);
          end; //while (True) do begin
        end;
      end; //for n1C:= n10 to n14  do begin
    end; //for n18:= n8 to nC do begin
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg2, [n24, m_sCharName, m_sMapName, m_nCurrX, m_nCurrY, nCheckCode]));
      MainOutMessage(E.Message);
      KickException();
    end;
  end;

  n24 := 2;
  try
    n18 := 0;
    while (True) do begin
      if m_VisibleActors.Count <= n18 then Break;
      VisibleBaseObject := m_VisibleActors.Items[n18];
      if VisibleBaseObject.btVisibleFlag = 0 then begin
        if {(m_btRaceServer = RC_PLAYOBJECT) and}(not m_boAI) and (not m_boNotOnlineAddExp) then begin
          ActorObject := TActorObject(VisibleBaseObject.BaseObject);
          if (not ActorObject.m_boFixedHideMode) and (not ActorObject.m_boGhost) then begin //01/21 修改防止人物退出时发送重复的消息占用带宽，人物进入隐身模式时人物不消失问题
            SendMsg(ActorObject, RM_DISAPPEAR, 0, 0, 0, 0, '');
          end;
        end;
        m_VisibleActors.Delete(n18);
        Dispose(VisibleBaseObject);
        Continue;
      end;
      if { (m_btRaceServer = RC_PLAYOBJECT) and }(VisibleBaseObject.btVisibleFlag = 2) and (not m_boAI) and (not m_boNotOnlineAddExp) then begin
        ActorObject := TActorObject(VisibleBaseObject.BaseObject);
        if (ActorObject <> Self) then begin
          if ActorObject.m_boDeath then begin
            if ActorObject.m_boSkeleton then begin
              SendMsg(ActorObject, RM_SKELETON, ActorObject.m_btDirection, ActorObject.m_nCurrX, ActorObject.m_nCurrY, 0, '');
            end else begin
              SendMsg(ActorObject, RM_DEATH, ActorObject.m_btDirection, ActorObject.m_nCurrX, ActorObject.m_nCurrY, 0, '');
            end;
          end else begin
            SendMsg(ActorObject, RM_TURN, ActorObject.m_btDirection, ActorObject.m_nCurrX, ActorObject.m_nCurrY, 0, ActorObject.GetShowName);
          end;
        end;
      end;
      Inc(n18);
    end;
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg2, [n24, m_sCharName, m_sMapName, m_nCurrX, m_nCurrY, nCheckCode]));
    {MainOutMessage(m_sCharName + ',' +
                   m_sMapName + ',' +
                   IntToStr(m_nCurrX) + ',' +
                   IntToStr(m_nCurrY) + ',' +
                   ' SearchViewRange 2');}
      KickException();
    end;
  end;

  try
    n18 := 0;
    while (True) do begin
      if m_VisibleItems.Count <= n18 then Break;
      VisibleMapItem := m_VisibleItems.Items[n18];
      if VisibleMapItem.btVisibleFlag = 0 then begin
        if (not m_boAI) and (not m_boNotOnlineAddExp) then begin
          SendMsg(Self, RM_ITEMHIDE, 0, Integer(VisibleMapItem.BaseObject), VisibleMapItem.nX, VisibleMapItem.nY, '');
        end;
        m_VisibleItems.Delete(n18);
        Dispose(VisibleMapItem);
        Continue;
      end;
      if (VisibleMapItem.btVisibleFlag = 2) and (not m_boAI) and (not m_boNotOnlineAddExp) then begin
        SendMsg(Self, RM_ITEMSHOW, VisibleMapItem.wLooks, Integer(VisibleMapItem.BaseObject), VisibleMapItem.nX, VisibleMapItem.nY, VisibleMapItem.sName);
      end;
      Inc(n18);
    end;

    n18 := 0;
    while (True) do begin
      if m_VisibleEvent.Count <= n18 then Break;
      VisibleMapEvent := m_VisibleEvent.Items[n18];
      if VisibleMapEvent.btVisibleFlag = 0 then begin
        if (not m_boAI) and (not m_boNotOnlineAddExp) then begin
          SendMsg(Self, RM_HIDEEVENT, 0, Integer(VisibleMapEvent.BaseObject), VisibleMapEvent.nX, VisibleMapEvent.nY, '');
        end;
        m_VisibleEvent.Delete(n18);
        Dispose(VisibleMapEvent);
        Continue;
      end;
      if (VisibleMapEvent.btVisibleFlag = 2) and (not m_boAI) and (not m_boNotOnlineAddExp) then begin
        SendMsg(Self, RM_SHOWEVENT, TEvent(VisibleMapEvent.BaseObject).m_nEventType, Integer(VisibleMapEvent.BaseObject), MakeLong(TEvent(VisibleMapEvent.BaseObject).m_nX, TEvent(VisibleMapEvent.BaseObject).m_nEventParam), TEvent(VisibleMapEvent.BaseObject).m_nY, '');
      end;
      Inc(n18);
    end;

  except
    MainOutMessage(m_sCharName + ',' +
      m_sMapName + ',' +
      IntToStr(m_nCurrX) + ',' +
      IntToStr(m_nCurrY) + ',' +
      ' SearchViewRange 3');

    KickException();
  end;
end;

function TPlayObject.GetShowName: string;
var
  sShowName: string;
  sCharName: string;
  sGuildName: string;
  sDearName: string;
  sMasterName: string;
  Castle: TUserCastle;
resourcestring
  sExceptionMsg = '[Exception] TPlayObject::GetShowName';
begin
  try
    if m_boIsUnknowActor then begin
    //sShowName:=m_sCharName;
     { sCharName := '';
      sGuildName := '';
      sDearName := '';
      sMasterName := '';
      if m_MyGuild <> nil then begin
        Castle := g_CastleManager.IsCastleMember(Self);
        if Castle <> nil then begin
          sGuildName := AnsiReplaceText(g_sCastleGuildName, '%castlename', Castle.m_sName);
          sGuildName := AnsiReplaceText(sGuildName, '%guildname', TGUild(m_MyGuild).sGuildName);
          sGuildName := AnsiReplaceText(sGuildName, '%rankname', m_sGuildRankName);
        end else begin
          Castle := g_CastleManager.InCastleWarArea(Self);
        //01/25 多城堡
        //if g_Config.boShowGuildName or (UserCastle.m_boUnderWar and (m_boInFreePKArea or UserCastle.InCastleWarArea(m_PEnvir,m_nCurrX,m_nCurrY))) then begin
          if g_Config.boShowGuildName or (((Castle <> nil) and Castle.m_boUnderWar) or m_boInFreePKArea) then begin
            sGuildName := AnsiReplaceText(g_sNoCastleGuildName, '%guildname', TGUild(m_MyGuild).sGuildName);
            sGuildName := AnsiReplaceText(sGuildName, '%rankname', m_sGuildRankName);
          end;
        end;
      end;

        if not g_Config.boShowRankLevelName then begin
          if m_btReLevel > 0 then begin
            case m_btJob of
              0: sCharName := AnsiReplaceText(g_sWarrReNewName, '%chrname', g_Config.sUnKnowName);
              1: sCharName := AnsiReplaceText(g_sWizardReNewName, '%chrname', g_Config.sUnKnowName);
              2: sCharName := AnsiReplaceText(g_sTaosReNewName, '%chrname', g_Config.sUnKnowName);
            end;
          end else begin
            sCharName := g_Config.sUnKnowName;
          end;
        end else begin
          sCharName := Format(m_sRankLevelName, [g_Config.sUnKnowName]);
        end;


      if m_sMasterName <> '' then begin
        if m_boMaster then begin
        //sMasterName:= m_sMasterName + '的师傅';
          sMasterName := Format(g_sMasterName, [m_sMasterName]);
        end else begin
        //sMasterName:= m_sMasterName + '的徒弟';
          sMasterName := Format(g_sNoMasterName, [m_sMasterName]);
        end;
      end;
      if m_sDearName <> '' then begin
        if m_btGender = 0 then begin
        //sDearName:= m_sDearName + '的老公';
          sDearName := Format(g_sManDearName, [m_sDearName]);
        end else begin
          sDearName := Format(g_sWoManDearName, [m_sDearName]); // + '的老婆';
        end;
      end;
      sShowName := AnsiReplaceText(g_sHumanShowName, '%chrname', sCharName);
      sShowName := AnsiReplaceText(sShowName, '%guildname', sGuildName);
      sShowName := AnsiReplaceText(sShowName, '%dearname', sDearName);
      sShowName := AnsiReplaceText(sShowName, '%mastername', sMasterName); }
      Result := g_Config.sUnKnowName;
    end else begin
    //sShowName:=m_sCharName;
      sCharName := '';
      sGuildName := '';
      sDearName := '';
      sMasterName := '';
      if m_MyGuild <> nil then begin
        Castle := g_CastleManager.IsCastleMember(Self);
      {
      if UserCastle.IsMasterGuild(TGuild(m_MyGuild)) then begin
        sGuildName:=AnsiReplaceText(g_sCastleGuildName,'%castlename',UserCastle.m_sName);
        sGuildName:=AnsiReplaceText(sGuildName,'%guildname',TGuild(m_MyGuild).sGuildName);
        sGuildName:=AnsiReplaceText(sGuildName,'%rankname',m_sGuildRankName);
        }
        if Castle <> nil then begin
          sGuildName := AnsiReplaceText(g_sCastleGuildName, '%castlename', Castle.m_sName);
          sGuildName := AnsiReplaceText(sGuildName, '%guildname', TGUild(m_MyGuild).sGuildName);
          sGuildName := AnsiReplaceText(sGuildName, '%rankname', m_sGuildRankName);
        end else begin
          Castle := g_CastleManager.InCastleWarArea(Self);
        //01/25 多城堡
        //if g_Config.boShowGuildName or (UserCastle.m_boUnderWar and (m_boInFreePKArea or UserCastle.InCastleWarArea(m_PEnvir,m_nCurrX,m_nCurrY))) then begin
          if g_Config.boShowGuildName or (((Castle <> nil) and Castle.m_boUnderWar) or m_boInFreePKArea) then begin
            sGuildName := AnsiReplaceText(g_sNoCastleGuildName, '%guildname', TGUild(m_MyGuild).sGuildName);
            sGuildName := AnsiReplaceText(sGuildName, '%rankname', m_sGuildRankName);
          end;
        end;
      end;
      {
      if not g_Config.boShowRankLevelName then begin
        if m_btReLevel > 0 then begin
          case m_btJob of
            0: sCharName := AnsiReplaceText(g_sWarrReNewName, '%chrname', m_sCharName);
            1: sCharName := AnsiReplaceText(g_sWizardReNewName, '%chrname', m_sCharName);
            2: sCharName := AnsiReplaceText(g_sTaosReNewName, '%chrname', m_sCharName);
          end;
        end else begin
          sCharName := m_sCharName;
        end;
      end else begin
        sCharName := Format(m_sRankLevelName, [m_sCharName]);
      end;
       }

      if m_boShowRankLevelName then begin
        case m_btJob of
          0: sCharName := AnsiReplaceText(g_sWarrReNewName, '%chrname', m_sCharName);
          1: sCharName := AnsiReplaceText(g_sWizardReNewName, '%chrname', m_sCharName);
          2: sCharName := AnsiReplaceText(g_sTaosReNewName, '%chrname', m_sCharName);
        end;
      end else begin
        sCharName := m_sCharName;
      end;

      sCharName := Format(m_sRankLevelName, [sCharName]);

      if m_sMasterName <> '' then begin
        if m_boMaster then begin
        //sMasterName:= m_sMasterName + '的师傅';
          sMasterName := Format(g_sMasterName, [m_sMasterName]);
        end else begin
        //sMasterName:= m_sMasterName + '的徒弟';
          sMasterName := Format(g_sNoMasterName, [m_sMasterName]);
        end;
      end;
      if m_sDearName <> '' then begin
        if m_btGender = 0 then begin
        //sDearName:= m_sDearName + '的老公';
          sDearName := Format(g_sManDearName, [m_sDearName]);
        end else begin
          sDearName := Format(g_sWoManDearName, [m_sDearName]); // + '的老婆';
        end;
      end;
      sShowName := AnsiReplaceText(g_sHumanShowName, '%chrname', sCharName);
      sShowName := AnsiReplaceText(sShowName, '%guildname', sGuildName);
      sShowName := AnsiReplaceText(sShowName, '%dearname', sDearName);
      sShowName := AnsiReplaceText(sShowName, '%mastername', sMasterName);
      Result := sShowName;
    end;
  except
    on E: Exception do begin
      MainOutMessage(sExceptionMsg);
      MainOutMessage(E.Message);
    end;
  end;
end;

function TPlayObject.CheckItemsNeed(StdItem: pTStdItem): Boolean;
var
  Castle: TUserCastle;
begin
  Result := True;
  Castle := g_CastleManager.IsCastleMember(Self);
  case StdItem.Need of
    6: begin
        if (m_MyGuild = nil) then begin
          Result := False;
        end;
      end;
    60: begin
        if (m_MyGuild = nil) or (m_nGuildRankNo <> 1) then begin
          Result := False;
        end;
      end;
    7: begin
        //if (m_MyGuild = nil) or (UserCastle.m_MasterGuild <> m_MyGuild) then begin
        if Castle = nil then begin
          Result := False;
        end;
      end;
    70: begin
        //if (m_MyGuild = nil) or (UserCastle.m_MasterGuild <> m_MyGuild) or (m_nGuildRankNo <> 1) then begin
        if (Castle = nil) or (m_nGuildRankNo <> 1) then begin
          Result := False;
        end;
      end;
    8: begin
        if m_nMemberType = 0 then Result := False;
      end;
    81: begin
        if (m_nMemberType <> LoWord(StdItem.NeedLevel)) or (m_nMemberLevel < HiWord(StdItem.NeedLevel)) then
          Result := False;
      end;
    82: begin
        if (m_nMemberType < LoWord(StdItem.NeedLevel)) or (m_nMemberLevel < HiWord(StdItem.NeedLevel)) then
          Result := False;
      end;
  end;
end;

procedure TPlayObject.ReAlive();
var
  I: Integer;
//  Human: TPlayObject;
begin
  inherited ReAlive();
  if m_sDearName <> '' then begin //复活后检测夫妻关系
    m_DearHuman := UserEngine.GetPlayObject(m_sDearName);
    if m_DearHuman <> nil then begin
      m_DearHuman.m_DearHuman := Self;
    end;
  end;

  //复活后检测师徒关系
  if m_sMasterName = '' then Exit;
  if m_boMaster then begin
    //师父上线通知
    m_MasterHuman := UserEngine.GetPlayObject(m_sMasterName);
    if m_MasterHuman <> nil then begin
      m_MasterHuman.m_MasterHuman := Self;
      if m_MasterList.Count > 0 then begin
        for I := m_MasterList.Count - 1 downto 0 do begin
          if m_MasterList.Items[I] = m_MasterHuman then begin
            Exit;
          end;
        end;
      end;
      m_MasterList.Add(m_MasterHuman);
    end;
  end else begin
    //徒弟上线通知
    if m_sMasterName <> '' then begin
      m_MasterHuman := UserEngine.GetPlayObject(m_sMasterName);
      if (m_MasterHuman <> nil) and (m_MasterHuman.m_sMasterName = m_sCharName) then begin
        m_MasterHuman.m_MasterHuman := Self;
        if m_MasterHuman.m_MasterList.Count > 0 then begin
          for I := m_MasterHuman.m_MasterList.Count - 1 downto 0 do begin
            if m_MasterHuman.m_MasterList.Items[I] = Self then begin
              Exit;
            end;
          end;
        end;
        m_MasterHuman.m_MasterList.Add(Self);
      end;
    end;
  end;
end;

procedure TPlayObject.CheckMarry;
var
  boIsfound: Boolean;
  sUnMarryFileName: string;
  LoadList: TStringList;
  I: Integer;
  sSayMsg: string;
begin
  boIsfound := False;
  sUnMarryFileName := g_Config.sEnvirDir + 'UnMarry.txt';
  if FileExists(sUnMarryFileName) then begin
    LoadList := TStringList.Create;
    LoadList.LoadFromFile(sUnMarryFileName);
    if LoadList.Count > 0 then begin
      for I := LoadList.Count - 1 downto 0 do begin
        if LoadList.Count <= 0 then Break;
        if CompareText(LoadList.Strings[I], m_sCharName) = 0 then begin
          LoadList.Delete(I);
          boIsfound := True;
          Break;
        end;
      end;
    end;
    LoadList.SaveToFile(sUnMarryFileName);
    LoadList.Free;
  end;
  if boIsfound then begin
    if m_btGender = 0 then begin
      sSayMsg := AnsiReplaceText(g_sfUnMarryManLoginMsg, '%d', m_sDearName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sDearName);
    end else begin
      sSayMsg := AnsiReplaceText(g_sfUnMarryWoManLoginMsg, '%d', m_sCharName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sCharName);
    end;
    SysMsg(sSayMsg, c_Red, t_Hint);
    m_sDearName := '';
    RefShowName;
  end;
  m_DearHuman := UserEngine.GetPlayObject(m_sDearName);
  if m_DearHuman <> nil then begin
    m_DearHuman.m_DearHuman := Self;
    if m_btGender = 0 then begin
      sSayMsg := AnsiReplaceText(g_sManLoginDearOnlineSelfMsg, '%d', m_sDearName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sCharName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%m', m_DearHuman.m_PEnvir.sMapDesc);
      sSayMsg := AnsiReplaceText(sSayMsg, '%x', IntToStr(m_DearHuman.m_nCurrX));
      sSayMsg := AnsiReplaceText(sSayMsg, '%y', IntToStr(m_DearHuman.m_nCurrY));
      SysMsg(sSayMsg, c_Blue, t_Hint);

      sSayMsg := AnsiReplaceText(g_sManLoginDearOnlineDearMsg, '%d', m_sDearName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sCharName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%m', m_PEnvir.sMapDesc);
      sSayMsg := AnsiReplaceText(sSayMsg, '%x', IntToStr(m_nCurrX));
      sSayMsg := AnsiReplaceText(sSayMsg, '%y', IntToStr(m_nCurrY));
      m_DearHuman.SysMsg(sSayMsg, c_Blue, t_Hint);
    end else begin
      sSayMsg := AnsiReplaceText(g_sWoManLoginDearOnlineSelfMsg, '%d', m_sDearName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sCharName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%m', m_DearHuman.m_PEnvir.sMapDesc);
      sSayMsg := AnsiReplaceText(sSayMsg, '%x', IntToStr(m_DearHuman.m_nCurrX));
      sSayMsg := AnsiReplaceText(sSayMsg, '%y', IntToStr(m_DearHuman.m_nCurrY));
      SysMsg(sSayMsg, c_Blue, t_Hint);

      sSayMsg := AnsiReplaceText(g_sWoManLoginDearOnlineDearMsg, '%d', m_sDearName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sCharName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%m', m_PEnvir.sMapDesc);
      sSayMsg := AnsiReplaceText(sSayMsg, '%x', IntToStr(m_nCurrX));
      sSayMsg := AnsiReplaceText(sSayMsg, '%y', IntToStr(m_nCurrY));
      m_DearHuman.SysMsg(sSayMsg, c_Blue, t_Hint);
    end;
  end else begin
    if m_btGender = 0 then begin
      SysMsg(g_sManLoginDearNotOnlineMsg, c_Red, t_Hint);
    end else begin
      SysMsg(g_sWoManLoginDearNotOnlineMsg, c_Red, t_Hint);
    end;
  end;
end;

procedure TPlayObject.CheckMaster;
var
  boIsfound: Boolean;
  sSayMsg: string;
  I: Integer;
  Human: TPlayObject;
begin
  //处理强行脱离师徒关系
  boIsfound := False;
  g_UnForceMasterList.Lock;
  try
    if g_UnForceMasterList.Count > 0 then begin
      for I := g_UnForceMasterList.Count - 1 downto 0 do begin
        if g_UnForceMasterList.Count <= 0 then Break;
        if CompareText(g_UnForceMasterList.Strings[I], m_sCharName) = 0 then begin
          g_UnForceMasterList.Delete(I);
          SaveUnForceMasterList();
          boIsfound := True;
          Break;
        end;
      end;
    end;
  finally
    g_UnForceMasterList.UnLock;
  end;

  if boIsfound then begin
    if m_boMaster then begin
      sSayMsg := AnsiReplaceText(g_sfUnMasterLoginMsg, '%d', m_sMasterName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sMasterName);
    end else begin
      sSayMsg := AnsiReplaceText(g_sfUnMasterListLoginMsg, '%d', m_sMasterName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sMasterName);
    end;
    SysMsg(sSayMsg, c_Red, t_Hint);
    m_sMasterName := '';
    RefShowName;
  end;

  if (m_sMasterName <> '') and not m_boMaster then begin
    if m_Abil.Level >= g_Config.nMasterOKLevel then begin
      Human := UserEngine.GetPlayObject(m_sMasterName);
      if (Human <> nil) and (not Human.m_boDeath) and (not Human.m_boGhost) then begin
        sSayMsg := AnsiReplaceText(g_sYourMasterListUnMasterOKMsg, '%d', m_sCharName);
        Human.SysMsg(sSayMsg, c_Red, t_Hint);
        SysMsg(g_sYouAreUnMasterOKMsg, c_Red, t_Hint);

        //如果大徒弟则将师父上的名字去掉
        if m_sCharName = Human.m_sMasterName then begin
          Human.m_sMasterName := '';
          Human.RefShowName;
        end;
        if Human.m_MasterList.Count > 0 then begin
          for I := Human.m_MasterList.Count - 1 downto 0 do begin
            if Human.m_MasterList.Count <= 0 then Break;
            if Human.m_MasterList.Items[I] = Self then begin
              Human.m_MasterList.Delete(I);
              Break;
            end;
          end;
        end;
        m_sMasterName := '';
        RefShowName;
        Inc(Human.m_wMasterCount); //增加出师徒弟数
        if Human.m_btCreditPoint + g_Config.nMasterOKCreditPoint <= High(Integer) then begin
          Inc(Human.m_btCreditPoint, g_Config.nMasterOKCreditPoint);
        end;
        Inc(Human.m_nBonusPoint, g_Config.nMasterOKBonusPoint);
        Human.SendMsg(Human, RM_ADJUST_BONUS, 0, 0, 0, 0, '');
        if g_FunctionNPC <> nil then begin //出师触发
          Human.m_nScriptGotoCount := 0;
          g_FunctionNPC.GotoLable(Human, '@MasterOK', False);
        end;
      end else begin
        //如果师父不在线则保存到记录表中
        g_UnMasterList.Lock;
        try
          boIsfound := False;
          for I := 0 to g_UnMasterList.Count - 1 do begin
            if CompareText(g_UnMasterList.Strings[I], m_sCharName) = 0 then begin
              boIsfound := True;
              Break;
            end;
          end;
          if not boIsfound then begin
            g_UnMasterList.Add(m_sMasterName);
          end;
        finally
          g_UnMasterList.UnLock;
        end;
        if not boIsfound then begin
          SaveUnMasterList();
        end;
        SysMsg(g_sYouAreUnMasterOKMsg, c_Red, t_Hint);
        m_sMasterName := '';
        RefShowName;
      end;
    end;
  end;

  //处理出师记录
  boIsfound := False;
  g_UnMasterList.Lock;
  try
    if g_UnMasterList.Count > 0 then begin
      for I := g_UnMasterList.Count - 1 downto 0 do begin //for i := 0 to g_UnMasterList.Count - 1 do begin
        if CompareText(g_UnMasterList.Strings[I], m_sCharName) = 0 then begin
          g_UnMasterList.Delete(I);
          SaveUnMasterList();
          boIsfound := True;
          Break;
        end;
      end;
    end;
  finally
    g_UnMasterList.UnLock;
  end;

  if boIsfound and m_boMaster then begin
    SysMsg(g_sUnMasterLoginMsg, c_Red, t_Hint);

    m_sMasterName := '';
    RefShowName;

    Inc(m_wMasterCount); //增加出师徒弟数
    if m_btCreditPoint + g_Config.nMasterOKCreditPoint <= High(Integer) then begin
      Inc(m_btCreditPoint, g_Config.nMasterOKCreditPoint);
    end;
    Inc(m_nBonusPoint, g_Config.nMasterOKBonusPoint);
    SendMsg(Self, RM_ADJUST_BONUS, 0, 0, 0, 0, '');
    if g_FunctionNPC <> nil then begin //出师触发
      m_nScriptGotoCount := 0;
      g_FunctionNPC.GotoLable(Self, '@MasterOK', False);
    end;
  end;

  if m_sMasterName = '' then Exit;
  if m_boMaster then begin
    //师父上线通知
    m_MasterHuman := UserEngine.GetPlayObject(m_sMasterName);
    if m_MasterHuman <> nil then begin
      m_MasterHuman.m_MasterHuman := Self;
      m_MasterList.Add(m_MasterHuman);

      sSayMsg := AnsiReplaceText(g_sMasterOnlineSelfMsg, '%d', m_sMasterName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sCharName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%m', m_MasterHuman.m_PEnvir.sMapDesc);
      sSayMsg := AnsiReplaceText(sSayMsg, '%x', IntToStr(m_MasterHuman.m_nCurrX));
      sSayMsg := AnsiReplaceText(sSayMsg, '%y', IntToStr(m_MasterHuman.m_nCurrY));
      SysMsg(sSayMsg, c_Blue, t_Hint);

      sSayMsg := AnsiReplaceText(g_sMasterOnlineMasterListMsg, '%d', m_sMasterName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sCharName);
      sSayMsg := AnsiReplaceText(sSayMsg, '%m', m_PEnvir.sMapDesc);
      sSayMsg := AnsiReplaceText(sSayMsg, '%x', IntToStr(m_nCurrX));
      sSayMsg := AnsiReplaceText(sSayMsg, '%y', IntToStr(m_nCurrY));
      m_MasterHuman.SysMsg(sSayMsg, c_Blue, t_Hint);
    end else begin
      SysMsg(g_sMasterNotOnlineMsg, c_Red, t_Hint);
    end;
  end else begin
    //徒弟上线通知
    if m_sMasterName <> '' then begin
      m_MasterHuman := UserEngine.GetPlayObject(m_sMasterName);
      if m_MasterHuman <> nil then begin

        if m_MasterHuman.m_sMasterName = m_sCharName then begin
          m_MasterHuman.m_MasterHuman := Self;
        end;

        m_MasterHuman.m_MasterList.Add(Self);

        sSayMsg := AnsiReplaceText(g_sMasterListOnlineSelfMsg, '%d', m_sMasterName);
        sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sCharName);
        sSayMsg := AnsiReplaceText(sSayMsg, '%m', m_MasterHuman.m_PEnvir.sMapDesc);
        sSayMsg := AnsiReplaceText(sSayMsg, '%x', IntToStr(m_MasterHuman.m_nCurrX));
        sSayMsg := AnsiReplaceText(sSayMsg, '%y', IntToStr(m_MasterHuman.m_nCurrY));
        SysMsg(sSayMsg, c_Blue, t_Hint);

        sSayMsg := AnsiReplaceText(g_sMasterListOnlineMasterMsg, '%d', m_sMasterName);
        sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sCharName);
        sSayMsg := AnsiReplaceText(sSayMsg, '%m', m_PEnvir.sMapDesc);
        sSayMsg := AnsiReplaceText(sSayMsg, '%x', IntToStr(m_nCurrX));
        sSayMsg := AnsiReplaceText(sSayMsg, '%y', IntToStr(m_nCurrY));
        m_MasterHuman.SysMsg(sSayMsg, c_Blue, t_Hint);
      end else begin
        SysMsg(g_sMasterListNotOnlineMsg, c_Red, t_Hint);
      end;
    end;
  end;
end;

procedure TPlayObject.MakeGhost;
var
  I: Integer;
  sSayMsg: string;
  Human: TPlayObject;
resourcestring
  sExceptionMsg = '[Exception] TPlayObject::MakeGhost';
begin

  try
    ClearAllDelayLabel;

    if (m_boStore) then StoreCancel; //摆摊物品放入包裹

    if (m_MyHero <> nil) then begin
      THeroObject(m_MyHero).LogOut();
    end;
    m_MyHero := nil;
    m_dwRecallHeroTick := GetTickCount;

    if (g_HighLevelHuman = Self) then g_HighLevelHuman := nil;
    if (g_HighPKPointHuman = Self) then g_HighPKPointHuman := nil;
    if (g_HighDCHuman = Self) then g_HighDCHuman := nil;
    if (g_HighMCHuman = Self) then g_HighMCHuman := nil;
    if (g_HighSCHuman = Self) then g_HighSCHuman := nil;
    if (g_HighOnlineHuman = Self) then g_HighOnlineHuman := nil;

    //人物下线后通知配偶，并把对方的相关记录清空
    if m_DearHuman <> nil then begin
      if m_btGender = 0 then begin
        sSayMsg := AnsiReplaceText(g_sManLongOutDearOnlineMsg, '%d', m_sDearName);
        sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sCharName);
        sSayMsg := AnsiReplaceText(sSayMsg, '%m', m_PEnvir.sMapDesc);
        sSayMsg := AnsiReplaceText(sSayMsg, '%x', IntToStr(m_nCurrX));
        sSayMsg := AnsiReplaceText(sSayMsg, '%y', IntToStr(m_nCurrY));
        m_DearHuman.SysMsg(sSayMsg, c_Red, t_Hint);
      end else begin
        sSayMsg := AnsiReplaceText(g_sWoManLongOutDearOnlineMsg, '%d', m_sDearName);
        sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sCharName);
        sSayMsg := AnsiReplaceText(sSayMsg, '%m', m_PEnvir.sMapDesc);
        sSayMsg := AnsiReplaceText(sSayMsg, '%x', IntToStr(m_nCurrX));
        sSayMsg := AnsiReplaceText(sSayMsg, '%y', IntToStr(m_nCurrY));
        m_DearHuman.SysMsg(sSayMsg, c_Red, t_Hint);
      end;
      m_DearHuman.m_DearHuman := nil;
      m_DearHuman := nil;
    end;
    if (m_MasterHuman <> nil) or (m_MasterList.Count > 0) then begin
      if m_boMaster then begin
        for I := 0 to m_MasterList.Count - 1 do begin
          Human := TPlayObject(m_MasterList.Items[I]);
          sSayMsg := AnsiReplaceText(g_sMasterLongOutMasterListOnlineMsg, '%s', m_sCharName);
          sSayMsg := AnsiReplaceText(sSayMsg, '%m', m_PEnvir.sMapDesc);
          sSayMsg := AnsiReplaceText(sSayMsg, '%x', IntToStr(m_nCurrX));
          sSayMsg := AnsiReplaceText(sSayMsg, '%y', IntToStr(m_nCurrY));
          Human.SysMsg(sSayMsg, c_Red, t_Hint);
          Human.m_MasterHuman := nil;
        end;
      end else begin
        if m_MasterHuman = nil then Exit;
        sSayMsg := AnsiReplaceText(g_sMasterListLongOutMasterOnlineMsg, '%d', m_sMasterName);
        sSayMsg := AnsiReplaceText(sSayMsg, '%s', m_sCharName);
        sSayMsg := AnsiReplaceText(sSayMsg, '%m', m_PEnvir.sMapDesc);
        sSayMsg := AnsiReplaceText(sSayMsg, '%x', IntToStr(m_nCurrX));
        sSayMsg := AnsiReplaceText(sSayMsg, '%y', IntToStr(m_nCurrY));
        m_MasterHuman.SysMsg(sSayMsg, c_Red, t_Hint);

        //如果为大徒弟则将对方的记录清空
        if m_MasterHuman.m_sMasterName = m_sCharName then begin
          m_MasterHuman.m_MasterHuman := nil;
        end;

        for I := m_MasterHuman.m_MasterList.Count - 1 downto 0 do begin //for i := 0 to m_MasterHuman.m_MasterList.Count - 1 do begin
          if m_MasterHuman.m_MasterList.Count <= 0 then Break;
          if m_MasterHuman.m_MasterList.Items[I] = Self then begin
            m_MasterHuman.m_MasterList.Delete(I);
            Break;
          end;
        end;
      end;
    end;
  except
    on E: Exception do begin
      MainOutMessage(sExceptionMsg);
      MainOutMessage(E.Message);
    end;
  end;
  inherited;
end;

function TPlayObject.GetMyInfo: string;
var
  sMyInfo: string;
begin
  try
    sMyInfo := g_sMyInfo;
    sMyInfo := AnsiReplaceText(sMyInfo, '%name', m_sCharName);
    sMyInfo := AnsiReplaceText(sMyInfo, '%map', m_PEnvir.sMapDesc);
    sMyInfo := AnsiReplaceText(sMyInfo, '%x', IntToStr(m_nCurrX));
    sMyInfo := AnsiReplaceText(sMyInfo, '%y', IntToStr(m_nCurrY));
    sMyInfo := AnsiReplaceText(sMyInfo, '%level', IntToStr(m_Abil.Level));
    sMyInfo := AnsiReplaceText(sMyInfo, '%gold', IntToStr(m_nGold));
    sMyInfo := AnsiReplaceText(sMyInfo, '%pk', IntToStr(m_nPkPoint));
    sMyInfo := AnsiReplaceText(sMyInfo, '%minhp', IntToStr(m_WAbil.HP));
    sMyInfo := AnsiReplaceText(sMyInfo, '%maxhp', IntToStr(m_WAbil.MaxHP));
    sMyInfo := AnsiReplaceText(sMyInfo, '%minmp', IntToStr(m_WAbil.MP));
    sMyInfo := AnsiReplaceText(sMyInfo, '%maxmp', IntToStr(m_WAbil.MaxMP));
    sMyInfo := AnsiReplaceText(sMyInfo, '%mindc', IntToStr(LoWord(m_WAbil.DC)));
    sMyInfo := AnsiReplaceText(sMyInfo, '%maxdc', IntToStr(HiWord(m_WAbil.DC)));
    sMyInfo := AnsiReplaceText(sMyInfo, '%minmc', IntToStr(LoWord(m_WAbil.MC)));
    sMyInfo := AnsiReplaceText(sMyInfo, '%maxmc', IntToStr(HiWord(m_WAbil.MC)));
    sMyInfo := AnsiReplaceText(sMyInfo, '%minsc', IntToStr(LoWord(m_WAbil.SC)));
    sMyInfo := AnsiReplaceText(sMyInfo, '%maxsc', IntToStr(HiWord(m_WAbil.SC)));
    sMyInfo := AnsiReplaceText(sMyInfo, '%logontime', DateTimeToStr(m_dLogonTime));
    sMyInfo := AnsiReplaceText(sMyInfo, '%logonlong', IntToStr((GetTickCount - m_dwLogonTick) div 60000));
    Result := sMyInfo;
  except
    Result := '????';
  end;
end;

function TActorObject.CheckItemBindUse(UserItem: pTUserItem; boHint: Boolean): Integer;
var
  I: Integer;
  ItemBind: pTItemBind;
begin
  Result := 0;
  if m_btRaceServer = RC_PLAYOBJECT then begin
    g_ItemBindAccount.Lock;
    try
      for I := 0 to g_ItemBindAccount.Count - 1 do begin
        ItemBind := g_ItemBindAccount.Items[I];
        if (ItemBind.nMakeIdex = UserItem.MakeIndex) and
          (ItemBind.nItemIdx = UserItem.wIndex) then begin
          Result := 1;
          if (CompareText(ItemBind.sBindName, TPlayObject(Self).m_sUserID) = 0) then begin
            Result := 2;
          end else begin
            if boHint then
              SysMsg(g_sItemIsNotThisAccount, c_Red, t_Hint);
          end;
          Break;
        end;
      end;
    finally
      g_ItemBindAccount.UnLock;
    end;


    if Result in [1..2] then Exit;

    g_ItemBindIPaddr.Lock;
    try
      for I := 0 to g_ItemBindIPaddr.Count - 1 do begin
        ItemBind := g_ItemBindIPaddr.Items[I];
        if (ItemBind.nMakeIdex = UserItem.MakeIndex) and
          (ItemBind.nItemIdx = UserItem.wIndex) then begin
          Result := 1;
          if (CompareText(ItemBind.sBindName, TPlayObject(Self).m_sIPaddr) = 0) then begin
            Result := 2;
          end else begin
            if boHint then
              SysMsg(g_sItemIsNotThisIPaddr, c_Red, t_Hint);
          end;
          Break;
        end;
      end;
    finally
      g_ItemBindIPaddr.UnLock;
    end;
    if Result in [1..2] then Exit;
  end;

  g_ItemBindCharName.Lock;
  try
    for I := 0 to g_ItemBindCharName.Count - 1 do begin
      ItemBind := g_ItemBindCharName.Items[I];
      if (ItemBind.nMakeIdex = UserItem.MakeIndex) and
        (ItemBind.nItemIdx = UserItem.wIndex) then begin
        Result := 1;
        if (CompareText(ItemBind.sBindName, m_sCharName) = 0) then begin
          Result := 2;
        end else begin
          if boHint then
            SysMsg(g_sItemIsNotThisCharName, c_Red, t_Hint);
        end;
        Break;
      end;
    end;
  finally
    g_ItemBindCharName.UnLock;
  end;
end;

procedure TPlayObject.ProcessClientPassword(ProcessMsg: pTProcessMessage);
var
  nLen: Integer;
  sData: string;
begin
  //  SysMsg(ProcessMsg.sMsg,c_Red,t_Hint);
  if ProcessMsg.wParam = 0 then begin
    ProcessUserLineMsg('@' + g_GameCommand.UnLock.sCmd);
    Exit;
  end;
  sData := ProcessMsg.sMsg;
  nLen := Length(sData);
  if m_boSetStoragePwd then begin
    m_boSetStoragePwd := False;
    if (nLen > 3) and (nLen < 8) then begin
      m_sTempPwd := sData;
      m_boReConfigPwd := True;
      SysMsg(g_sReSetPasswordMsg, c_Green, t_Hint); {'请重复输入一次仓库密码：'}
      SendMsg(Self, RM_PASSWORD, 0, 0, 0, 0, '');
    end else begin
      SysMsg(g_sPasswordOverLongMsg, c_Red, t_Hint); {'输入的密码长度不正确！！！，密码长度必须在 4 - 7 的范围内，请重新设置密码。'}
    end;
    Exit;
  end;
  if m_boReConfigPwd then begin
    m_boReConfigPwd := False;
    if CompareStr(m_sTempPwd, sData) = 0 then begin
      m_sStoragePwd := sData;
      m_boPasswordLocked := True;
      m_sTempPwd := '';
      SysMsg(g_sReSetPasswordOKMsg, c_Blue, t_Hint); {'密码设置成功！！，仓库已经自动上锁，请记好您的仓库密码，在取仓库时需要使用此密码开锁。'}
    end else begin
      m_sTempPwd := '';
      SysMsg(g_sReSetPasswordNotMatchMsg, c_Red, t_Hint);
    end;
    Exit;
  end;
  if m_boUnLockPwd or m_boUnLockStoragePwd then begin
    if CompareStr(m_sStoragePwd, sData) = 0 then begin
      m_boPasswordLocked := False;
      if m_boUnLockPwd then begin
        if g_Config.boLockDealAction then m_boCanDeal := True;
        if g_Config.boLockDropAction then m_boCanDrop := True;
        if g_Config.boLockWalkAction then m_boCanWalk := True;
        if g_Config.boLockRunAction then m_boCanRun := True;
        if g_Config.boLockHitAction then m_boCanHit := True;
        if g_Config.boLockSpellAction then m_boCanSpell := True;
        if g_Config.boLockSendMsgAction then m_boCanSendMsg := True;
        if g_Config.boLockUserItemAction then m_boCanUseItem := True;
        if g_Config.boLockInObModeAction then begin
          m_boObMode := False;
          m_boAdminMode := False;
        end;
        m_boLockLogoned := True;
        SysMsg(g_sPasswordUnLockOKMsg, c_Blue, t_Hint);
      end;
      if m_boUnLockStoragePwd then begin
        if g_Config.boLockGetBackItemAction then m_boCanGetBackItem := True;
        SysMsg(g_sStorageUnLockOKMsg, c_Blue, t_Hint);
      end;

    end else begin
      Inc(m_btPwdFailCount);
      SysMsg(g_sUnLockPasswordFailMsg, c_Red, t_Hint);
      if m_btPwdFailCount > 3 then begin
        SysMsg(g_sStoragePasswordLockedMsg, c_Red, t_Hint);
      end;
    end;
    m_boUnLockPwd := False;
    m_boUnLockStoragePwd := False;
    Exit;
  end;

  if m_boCheckOldPwd then begin
    m_boCheckOldPwd := False;
    if m_sStoragePwd = sData then begin
      SendMsg(Self, RM_PASSWORD, 0, 0, 0, 0, '');
      SysMsg(g_sSetPasswordMsg, c_Green, t_Hint);
      m_boSetStoragePwd := True;
    end else begin
      Inc(m_btPwdFailCount);
      SysMsg(g_sOldPasswordIncorrectMsg, c_Red, t_Hint);
      if m_btPwdFailCount > 3 then begin
        SysMsg(g_sStoragePasswordLockedMsg, c_Red, t_Hint);
        m_boPasswordLocked := True;
      end;
    end;
    Exit;
  end;
end;

procedure TPlayObject.ScatterBagItems(ItemOfCreat: TActorObject);
var
  I, DropWide: Integer;
  UserItem: pTUserItem;
//  StdItem: pTStdItem;
  DelList: TStringList;
  boDropall: Boolean;
  //boCheckCanNotDrop: Boolean;
  sCheckItemName: string;

  PlayObject: TPlayObject;
  ActorObject, ActorObject18: TActorObject;
  nC, n10: Integer;
resourcestring
  sExceptionMsg = '[Exception] TPlayObject::ScatterBagItems';
begin
  DelList := nil;
  if m_boAngryRing or m_boNoDropItem or not m_boDropUseItem then Exit; //不死戒指

  PlayObject := nil;
  if (ItemOfCreat <> nil) and (ItemOfCreat.m_btRaceServer = RC_PLAYOBJECT) then
    PlayObject := TPlayObject(ItemOfCreat)
  else begin
    ActorObject18 := nil;
    n10 := 9999;
    for I := 0 to m_VisibleActors.Count - 1 do begin
      ActorObject := TActorObject(pTVisibleBaseObject(m_VisibleActors.Items[I]).BaseObject);
      if (ActorObject.m_btRaceServer = RC_PLAYOBJECT) then begin
        if (not ActorObject.m_boHideMode or m_boCoolEye) then begin
          nC := abs(m_nCurrX - ActorObject.m_nCurrX) + abs(m_nCurrY - ActorObject.m_nCurrY);
          if nC < n10 then begin
            n10 := nC;
            ActorObject18 := ActorObject;
          end;
        end;
      end;
    end;
    if ActorObject18 <> nil then
      PlayObject := TPlayObject(ActorObject18)
    else
      PlayObject := Self;
  end;

  boDropall := False;

  DropWide := 2;
  if g_Config.boDieRedScatterBagAll and (PKLevel >= 2) then begin
    boDropall := True;
  end;

  //非红名掉1/3 //红名全掉
  try
    for I := m_ItemList.Count - 1 downto 0 do begin
      if m_ItemList.Count <= 0 then Break;
//      boCheckCanNotDrop := True;
      UserItem := pTUserItem(m_ItemList[I]);
      if g_Config.boBindItemNoScatter and (CheckItemBindUse(UserItem, False) = 2) then Continue; //绑定物品禁止爆

      if (UserItem.AddValue[0] = 1) and (GetDayCount(UserItem.MaxDate, Now) <= 0) then begin //删除到期装备
        Dispose(UserItem);
        m_ItemList.Delete(I);
        Continue;
      end;

      sCheckItemName := UserEngine.GetStdItemName(UserItem.wIndex);

      if g_Config.boBindItemNoScatter and (CheckItemBindUse(UserItem, False) = 2) then Continue; //绑定物品禁止爆

      if Assigned(PlugInEngine.CheckCanDieScatterItem) then begin //死亡必爆
        if PlugInEngine.CheckCanDieScatterItem(Self, PChar(sCheckItemName), False) then begin
          boDropall := True;
        end;
      end;

      if Assigned(PlugInEngine.CheckNotCanScatterItem) then begin //禁止爆出
        if PlugInEngine.CheckNotCanScatterItem(Self, PChar(sCheckItemName), False) then begin
          Continue;
        end;
      end;

      if boDropall or (Random(g_Config.nDieScatterBagRate {3}) = 0) then begin

        if DropItemDown(UserItem, DropWide, True, ItemOfCreat, Self) then begin
          if m_btRaceServer = RC_PLAYOBJECT then begin
            if DelList = nil then DelList := TStringList.Create;
            DelList.AddObject(UserEngine.GetStdItemName(UserItem.wIndex), TObject(UserItem.MakeIndex));
          end;
          if (PlayObject <> nil) and (g_FunctionNPC <> nil) then begin
            PlayObject.m_sScatterItemName := UserEngine.GetStdItemName(UserItem.wIndex);
            PlayObject.m_sScatterItemOwnerName := m_sCharName;
            PlayObject.m_sScatterItemMapName := m_sMapName;
            PlayObject.m_sScatterItemMapDesc := m_PEnvir.sMapDesc;
            PlayObject.m_nScatterItemX := m_nScatterItemX;
            PlayObject.m_nScatterItemY := m_nScatterItemY;
            if IsAllowScatterItem(PlayObject.m_sScatterItemName) then begin
              try
                g_FunctionNPC.GotoLable(PlayObject, '@ScatterBagItems', False);
              except
                MainOutMessage(sExceptionMsg + ' FunctionNPC::GotoLable');
              end;
            end;
          end;
          m_ItemList.Delete(I);
          Dispose(UserItem);
        end;
      end;
    end;
    if (DelList <> nil) then begin
      SendMsg(Self, RM_SENDDELITEMLIST, 0, Integer(DelList), 0, 0, '');
    end;
  except
    MainOutMessage(sExceptionMsg);
  end;
end;

procedure TPlayObject.RecallHuman(sHumName: string);
var
  PlayObject: TPlayObject;
  nX, nY, n18, n1C: Integer;
begin
  PlayObject := UserEngine.GetPlayObject(sHumName);
  if PlayObject <> nil then begin
    if GetFrontPosition(nX, nY) then begin
      if sub_4C5370(nX, nY, 3, n18, n1C) then begin
        PlayObject.SendRefMsg(RM_SPACEMOVE_FIRE, 0, 0, 0, 0, '');
        PlayObject.SpaceMove(m_sMapName, n18, n1C, 0);
      end;
    end else begin
      SysMsg('Recall failed.', c_Red, t_Hint);
    end;
  end else begin
    SysMsg(Format(g_sNowNotOnLineOrOnOtherServer, [sHumName]), c_Red, t_Hint);
  end;
end;

procedure TPlayObject.ReQuestGuildWar(sGuildName: string);
var
  Guild: TGUild;
  WarGuild: pTWarGuild;
  boReQuestOK: Boolean;
begin
  if not IsGuildMaster then begin
    SysMsg('Only Guild Leader can request a War', c_Red, t_Hint);
    Exit;
  end;
  if nServerIndex <> 0 then begin
    SysMsg('Now allowed on this server.', c_Red, t_Hint);
    Exit;
  end;
  Guild := g_GuildManager.FindGuild(sGuildName);
  if Guild = nil then begin
    SysMsg('Guild not found.', c_Red, t_Hint);
    Exit;
  end;
  boReQuestOK := False;
  WarGuild := TGUild(m_MyGuild).AddWarGuild(Guild);
  if WarGuild <> nil then begin
    if Guild.AddWarGuild(TGUild(m_MyGuild)) = nil then begin
      WarGuild.dwWarTick := 0;
    end else begin
      boReQuestOK := True;
    end;
  end;
  {if boReQuestOK then begin
    UserEngine.SendServerGroupMsg(SS_207, nServerIndex, TGUild(m_MyGuild).sGuildName);
    UserEngine.SendServerGroupMsg(SS_207, nServerIndex, Guild.sGuildName);
  end;}
end;

function TPlayObject.CheckDenyLogon(): Boolean;
begin
  Result := False;
  if GetDenyIPaddrList(m_sIPaddr) then begin
    SysMsg(g_sYourIPaddrDenyLogon, c_Red, t_Hint);
    Result := True;
  end else
    if GetDenyAccountList(m_sUserID) then begin
    SysMsg(g_sYourAccountDenyLogon, c_Red, t_Hint);
    Result := True;
  end else
    if GetDenyChrNameList(m_sCharName) then begin
    SysMsg(g_sYourCharNameDenyLogon, c_Red, t_Hint);
    Result := True;
  end;
  if Result then begin
    m_boEmergencyClose := True;
    m_boPlayOffLine := False;
  end;
end;

function TPlayObject.IsGotoLabel(sMapName: string; nX, nY, nRange, nQuestFalgStatus: Integer; boQuestFalgStatus: Boolean; sItemName1, sItemName2: string; boNeedGroup: Boolean; nRandomCount: Integer): Boolean;
var
  n01: Integer;
  nMaxCurrX, nMaxCurrY, nMinCurrX, nMinCurrY: Integer;
  function GetAllowItem: Boolean;
  begin
    Result := False;
    if sItemName1 = '*' then begin
      Result := True;
    end else
      if CompareText(sItemName1, sItemName2) = 0 then begin
      Result := True;
    end;
  end;
  function GetRandomCount: Boolean;
  begin
    Result := False;
    if nRandomCount = 0 then Result := True
    else if (nRandomCount > 0) and (Random(nRandomCount div 2 + nRandomCount) = nRandomCount) then Result := True;
  end;
  function GetGroup: Boolean;
  begin
    Result := False;
    if not boNeedGroup then Result := True
    else if m_GroupOwner <> nil then Result := True;
  end;
begin
  Result := False;
  nMaxCurrX := nX + nRange;
  nMaxCurrY := nY + nRange;
  nMinCurrX := nX - nRange;
  nMinCurrY := nY - nRange;
  if (CompareText(sMapName, m_sMapName) = 0) and (m_nCurrX <= nMaxCurrX) and (m_nCurrX >= nMinCurrX) and (m_nCurrY <= nMaxCurrY) and (m_nCurrY >= nMinCurrY) then begin
    if nQuestFalgStatus > 0 then begin
      n01 := GetQuestFalgStatus(nQuestFalgStatus);
      if (n01 = 0) and (not boQuestFalgStatus) then begin
        if GetAllowItem and GetGroup and GetRandomCount then begin
          Result := True;
        end;
      end else
        if (n01 <> 0) and (boQuestFalgStatus) then begin
        if GetAllowItem and GetGroup and GetRandomCount then begin
          Result := True;
        end;
      end;
    end else begin
      if GetAllowItem and GetGroup and GetRandomCount then begin
        Result := True;
      end;
    end;
  end;
end;

function TPlayObject.StartMapEventOfDropItem(sItemName: string): Boolean;
var
  I: Integer;
  boGotoLabel: Boolean;
  MapEvent: pTMapEvent;
begin
  Result := False;
  MapEvent := nil;
  boGotoLabel := False;
  for I := 0 to g_MapEventListOfDropItem.Count - 1 do begin
    MapEvent := pTMapEvent(g_MapEventListOfDropItem.Items[I]);
    if IsGotoLabel(MapEvent.m_sMapName,
      MapEvent.m_nCurrX,
      MapEvent.m_nCurrY,
      MapEvent.m_nRange,
      MapEvent.m_MapFlag.nQuestUnit,
      MapEvent.m_MapFlag.boOpen,
      MapEvent.m_Condition.sItemName,
      sItemName,
      MapEvent.m_Condition.boNeedGroup, MapEvent.m_nRandomCount) then begin
      boGotoLabel := True;
      Break;
    end;
  end;
  if boGotoLabel and (MapEvent <> nil) then begin
    Result := True;
    case MapEvent.m_StartScript.nLable of
      0: ;
      1: begin
          if g_FunctionNPC <> nil then begin
            m_nScriptGotoCount := 0;
            g_FunctionNPC.GotoLable(Self, MapEvent.m_StartScript.sLable, False);
          end;
        end;
    end;
  end;
end;

procedure TPlayObject.StartMapEventOfPickUpItem(sItemName: string);
var
  I: Integer;
  boGotoLabel: Boolean;
  MapEvent: pTMapEvent;
begin
  MapEvent := nil;
  boGotoLabel := False;
  for I := 0 to g_MapEventListOfPickUpItem.Count - 1 do begin
    MapEvent := pTMapEvent(g_MapEventListOfPickUpItem.Items[I]);
    if IsGotoLabel(MapEvent.m_sMapName,
      MapEvent.m_nCurrX,
      MapEvent.m_nCurrY,
      MapEvent.m_nRange,
      MapEvent.m_MapFlag.nQuestUnit,
      MapEvent.m_MapFlag.boOpen,
      MapEvent.m_Condition.sItemName,
      sItemName,
      MapEvent.m_Condition.boNeedGroup, MapEvent.m_nRandomCount) then begin
      boGotoLabel := True;
      Break;
    end;
  end;
  if boGotoLabel and (MapEvent <> nil) then begin
    case MapEvent.m_StartScript.nLable of
      0: ;
      1: begin
          if g_FunctionNPC <> nil then begin
            m_nScriptGotoCount := 0;
            g_FunctionNPC.GotoLable(Self, MapEvent.m_StartScript.sLable, False);
          end;
        end;
    end;
  end;
end;

procedure TPlayObject.StartMapEventOfMine();
var
  I: Integer;
  boGotoLabel: Boolean;
  MapEvent: pTMapEvent;
begin
  MapEvent := nil;
  boGotoLabel := False;
  for I := 0 to g_MapEventListOfMine.Count - 1 do begin
    MapEvent := pTMapEvent(g_MapEventListOfMine.Items[I]);
    if IsGotoLabel(MapEvent.m_sMapName,
      MapEvent.m_nCurrX,
      MapEvent.m_nCurrY,
      MapEvent.m_nRange,
      MapEvent.m_MapFlag.nQuestUnit,
      MapEvent.m_MapFlag.boOpen,
      MapEvent.m_Condition.sItemName,
      '*',
      MapEvent.m_Condition.boNeedGroup, MapEvent.m_nRandomCount) then begin
      boGotoLabel := True;
      Break;
    end;
  end;
  if boGotoLabel and (MapEvent <> nil) then begin
    case MapEvent.m_StartScript.nLable of
      0: ;
      1: begin
          if g_FunctionNPC <> nil then begin
            m_nScriptGotoCount := 0;
            g_FunctionNPC.GotoLable(Self, MapEvent.m_StartScript.sLable, False);
          end;
        end;
    end;
  end;
end;

procedure TPlayObject.StartMapEventOfWalk();
var
  I: Integer;
  boGotoLabel: Boolean;
  MapEvent: pTMapEvent;
begin
  MapEvent := nil;
  boGotoLabel := False;
  for I := 0 to g_MapEventListOfWalk.Count - 1 do begin
    MapEvent := pTMapEvent(g_MapEventListOfWalk.Items[I]);
    if IsGotoLabel(MapEvent.m_sMapName,
      MapEvent.m_nCurrX,
      MapEvent.m_nCurrY,
      MapEvent.m_nRange,
      MapEvent.m_MapFlag.nQuestUnit,
      MapEvent.m_MapFlag.boOpen,
      MapEvent.m_Condition.sItemName,
      '*',
      MapEvent.m_Condition.boNeedGroup, MapEvent.m_nRandomCount) then begin
      boGotoLabel := True;
      Break;
    end;
  end;
  if boGotoLabel and (MapEvent <> nil) then begin
    case MapEvent.m_StartScript.nLable of
      0: ;
      1: begin
          if g_FunctionNPC <> nil then begin
            m_nScriptGotoCount := 0;
            g_FunctionNPC.GotoLable(Self, MapEvent.m_StartScript.sLable, False);
          end;
        end;
    end;
  end;
end;

procedure TPlayObject.StartMapEventOfRun();
var
  I: Integer;
  boGotoLabel: Boolean;
  MapEvent: pTMapEvent;
begin
  MapEvent := nil;
  boGotoLabel := False;
  for I := 0 to g_MapEventListOfRun.Count - 1 do begin
    MapEvent := pTMapEvent(g_MapEventListOfRun.Items[I]);
    if IsGotoLabel(MapEvent.m_sMapName,
      MapEvent.m_nCurrX,
      MapEvent.m_nCurrY,
      MapEvent.m_nRange,
      MapEvent.m_MapFlag.nQuestUnit,
      MapEvent.m_MapFlag.boOpen,
      MapEvent.m_Condition.sItemName,
      '*',
      MapEvent.m_Condition.boNeedGroup, MapEvent.m_nRandomCount) then begin
      boGotoLabel := True;
      Break;
    end;
  end;
  if boGotoLabel and (MapEvent <> nil) then begin
    case MapEvent.m_StartScript.nLable of
      0: ;
      1: begin
          if g_FunctionNPC <> nil then begin
            m_nScriptGotoCount := 0;
            g_FunctionNPC.GotoLable(Self, MapEvent.m_StartScript.sLable, False);
          end;
        end;
    end;
  end;
end;

procedure TPlayObject.ProcessUserCmd(sParam1, sParam2, sParam3, sParam4,
  sParam5, sParam6, sParam7: string);
begin
  SysMsg(sParam2, c_Green, t_Hint);
end;

{----------------------------------TAIObject------------------------------------}

constructor TAIPlayObject.Create();
begin
  inherited;
  m_nSoftVersionDate := CLIENT_VERSION_NUMBER;
  m_nSoftVersionDateEx := GetExVersionNO(CLIENT_VERSION_NUMBER, m_nSoftVersionDate);
  m_boHeroVersion := True;
  //m_btAttatckMode := HAM_PKATTACK;

  AbilCopyToWAbil();
  m_boAI := True;
  m_boLoginNoticeOK := True;
  m_boAIStart := False; //开始挂机
  m_ManagedEnvir := nil; //挂机地图
  m_Path := nil;
  m_nPostion := -1;
  m_sFilePath := '';
  m_sConfigFileName := '';
  m_sHeroConfigFileName := '';
  m_sConfigListFileName := '';
  m_PointManager := TPointManager.Create(Self);
end;

destructor TAIPlayObject.Destroy;
begin
  m_Path := nil;
  m_PointManager.Free;
  inherited;
end;

procedure TAIPlayObject.Start(PathType: TPathType);
begin
  if (not m_boGhost) and
    (not m_boDeath) and
    (not m_boAIStart) then begin
    m_ManagedEnvir := m_PEnvir;
    m_PointManager.PathType := PathType;
    m_PointManager.Initialize(m_PEnvir);
    m_boAIStart := True;
    m_nMoveFailCount := 0;
    //MainOutMessage(m_sCharName + ' TAIObject.Start:' + IntToStr(m_PointManager.Count));
    if g_FunctionNPC <> nil then begin
      m_nScriptGotoCount := 0;
      g_FunctionNPC.GotoLable(Self, '@AIStart', False);
    end;
  end;
end;

procedure TAIPlayObject.Stop;
begin
  if m_boAIStart then begin
    m_boAIStart := False;
    m_ManagedEnvir := nil;
    m_nMoveFailCount := 0;
    m_Path := nil;
    m_nPostion := -1;
    //MainOutMessage(m_sCharName + ' TAIObject.Stop:' + IntToStr(m_PointManager.Count));
    if g_FunctionNPC <> nil then begin
      m_nScriptGotoCount := 0;
      g_FunctionNPC.GotoLable(Self, '@AIStop', False);
    end;
  end;
end;

procedure TAIPlayObject.MakeGhost;
begin
  if m_boAIStart then begin
    m_boAIStart := False;
    m_ManagedEnvir := nil;
  end;
  inherited;
end;

procedure TAIPlayObject.Whisper(whostr, saystr: string);
var
  PlayObject: TPlayObject;
//  svidx: Integer;
//  sSendStr: string;
begin
  PlayObject := UserEngine.GetPlayObject(whostr);
  if PlayObject <> nil then begin
    if not PlayObject.m_boReadyRun then begin
      //SysMsg(whostr + g_sCanotSendmsg {'无法发送信息.'}, c_Red, t_Hint);
      Exit;
    end;
    if not PlayObject.m_boHearWhisper or PlayObject.IsBlockWhisper(m_sCharName) then begin
      // SysMsg(whostr + g_sUserDenyWhisperMsg {' 拒绝私聊！！！'}, c_Red, t_Hint);
      Exit;
    end;
    if m_btPermission > 0 then begin
      PlayObject.SendMsg(PlayObject, RM_WHISPER, 0, g_Config.btGMWhisperMsgFColor, g_Config.btGMWhisperMsgBColor, 0, GetUnknowCharName + '=> ' + saystr);
      //取得私聊信息
      //m_GetWhisperHuman 侦听私聊对象
      if (m_GetWhisperHuman <> nil) and (not m_GetWhisperHuman.m_boGhost) then
        m_GetWhisperHuman.SendMsg(m_GetWhisperHuman, RM_WHISPER, 0, g_Config.btGMWhisperMsgFColor, g_Config.btGMWhisperMsgBColor, 0, GetUnknowCharName + '=>' + PlayObject.GetUnknowCharName + ' ' + saystr);
      if (PlayObject.m_GetWhisperHuman <> nil) and (not PlayObject.m_GetWhisperHuman.m_boGhost) then
        PlayObject.m_GetWhisperHuman.SendMsg(PlayObject.m_GetWhisperHuman, RM_WHISPER, 0, g_Config.btGMWhisperMsgFColor, g_Config.btGMWhisperMsgBColor, 0, GetUnknowCharName + '=>' + PlayObject.GetUnknowCharName + ' ' + saystr);
    end else begin
      PlayObject.SendMsg(PlayObject, RM_WHISPER, 0, {g_Config.btWhisperMsgFColor} m_btWhisperMsgFColor, g_Config.btWhisperMsgBColor, 0, GetUnknowCharName + '=> ' + saystr);
      if (m_GetWhisperHuman <> nil) and (not m_GetWhisperHuman.m_boGhost) then
        m_GetWhisperHuman.SendMsg(m_GetWhisperHuman, RM_WHISPER, 0, {g_Config.btWhisperMsgFColor} m_btWhisperMsgFColor, g_Config.btWhisperMsgBColor, 0, GetUnknowCharName + '=>' + PlayObject.GetUnknowCharName + ' ' + saystr);

      if (PlayObject.m_GetWhisperHuman <> nil) and (not PlayObject.m_GetWhisperHuman.m_boGhost) then
        PlayObject.m_GetWhisperHuman.SendMsg(PlayObject.m_GetWhisperHuman, RM_WHISPER, 0, {g_Config.btWhisperMsgFColor} m_btWhisperMsgFColor, g_Config.btWhisperMsgBColor, 0, GetUnknowCharName + '=>' + PlayObject.GetUnknowCharName + ' ' + saystr);
    end;
  end;
end;

procedure TAIPlayObject.ProcessSayMsg(sData: string);
var
  boDisableSayMsg: Boolean;
  SC, sCryCryMsg, sParam1: string;
const
  s01 = '%d %d';
  s02 = '%s %d/%d';
resourcestring
  sExceptionMsg = '[Exception] TAIPlayObject.ProcessSayMsg Msg = %s';
begin
  if sData = '' then Exit;
  try
    if Length(sData) > g_Config.nSayMsgMaxLen then begin
      sData := Copy(sData, 1, g_Config.nSayMsgMaxLen);
    end;
    (*
    if {(sData = m_sOldSayMsg) and}((GetTickCount - m_dwSayMsgTick) < g_Config.dwSayMsgTime {3 * 1000}) then begin
      Inc(m_nSayMsgCount);
      if m_nSayMsgCount >= g_Config.nSayMsgCount {2} then begin
        m_boDisableSayMsg := True;
        m_dwDisableSayMsgTick := GetTickCount + g_Config.dwDisableSayMsgTime {60 * 1000};
        SysMsg(Format(g_sDisableSayMsg, [g_Config.dwDisableSayMsgTime div (60 * 1000)]), c_Red, t_Hint);
        //'[由于你重复发相同的内容，%d分钟内你将被禁止发言...]'
      end;
    end else begin
      m_dwSayMsgTick := GetTickCount();
      m_nSayMsgCount := 0;
    end;

    if GetTickCount >= m_dwDisableSayMsgTick then m_boDisableSayMsg := False;
    boDisableSayMsg := m_boDisableSayMsg;
    g_DenySayMsgList.Lock;
    try
      if g_DenySayMsgList.GetIndex(m_sCharName) >= 0 then boDisableSayMsg := True;
    finally
      g_DenySayMsgList.UnLock;
    end;
    *)

    if GetTickCount >= m_dwDisableSayMsgTick then m_boDisableSayMsg := False;
    boDisableSayMsg := m_boDisableSayMsg;
    g_DenySayMsgList.Lock;
    try
      if g_DenySayMsgList.GetIndex(m_sCharName) >= 0 then boDisableSayMsg := True;
    finally
      g_DenySayMsgList.UnLock;
    end;

    if not boDisableSayMsg then begin
      m_sOldSayMsg := sData;
      if sData[1] = '/' then begin
        SC := Copy(sData, 2, Length(sData) - 1);
        {if CompareText(Trim(SC), Trim(g_GameCommand.WHO.sCmd)) = 0 then begin
          if (m_btPermission < g_GameCommand.WHO.nPermissionMin) then begin
            SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
            Exit;
          end;
          HearMsg(Format(g_sOnlineCountMsg, [UserEngine.PlayObjectCount]));
          Exit;
        end;
        if CompareText(Trim(SC), Trim(g_GameCommand.TOTAL.sCmd)) = 0 then begin
          if (m_btPermission < g_GameCommand.TOTAL.nPermissionMin) then begin
            SysMsg(g_sGameCommandPermissionTooLow, c_Red, t_Hint);
            Exit;
          end;
          HearMsg(Format(g_sTotalOnlineCountMsg, [g_nTotalHumCount]));
          Exit;
        end;}
        SC := GetValidStr3(SC, sParam1, [' ']);
        if not m_boFilterSendMsg then
          Whisper(sParam1, SC);
        Exit;
      end;

      if sData[1] = '!' then begin
        if Length(sData) >= 2 then begin
          if sData[2] = '!' then begin
            SC := Copy(sData, 3, Length(sData) - 2);
            SendGroupText(GetUnknowCharName + ': ' + SC);
            Exit;
          end;
          if sData[2] = '~' then begin
            if m_MyGuild <> nil then begin
              SC := Copy(sData, 3, Length(sData) - 2);
              TGUild(m_MyGuild).SendGuildMsg(GetUnknowCharName + ': ' + SC);
              //UserEngine.SendServerGroupMsg(SS_208, nServerIndex, TGUild(m_MyGuild).sGuildName + '/' + GetUnknowCharName + '/' + SC);
            end;
            Exit;
          end;
        end;

        if not m_PEnvir.m_boQUIZ then begin
          if (GetTickCount - m_dwShoutMsgTick) > 10 * 1000 then begin
            if m_Abil.Level <= g_Config.nCanShoutMsgLevel then begin
              //SysMsg('你的等级要在' + IntToStr(g_nCanShoutMsgLevel + 1) + '级以上才能用此功能！！！',c_Red,t_Hint);
              SysMsg(Format(g_sYouNeedLevelMsg, [g_Config.nCanShoutMsgLevel + 1]), c_Red, t_Hint);
              Exit;
            end;
            m_dwShoutMsgTick := GetTickCount();
            SC := Copy(sData, 2, Length(sData) - 1);
            sCryCryMsg := '(!)' + GetUnknowCharName + ': ' + SC;
            if m_boFilterSendMsg then begin
              SendMsg(nil, RM_CRY, 0, 0, $FFFF, 0, sCryCryMsg);
            end else begin
              UserEngine.CryCry(RM_CRY, m_PEnvir, m_nCurrX, m_nCurrY, 50, g_Config.btCryMsgFColor, g_Config.btCryMsgBColor, sCryCryMsg);
            end;
            Exit;
          end;
          SysMsg(Format(g_sYouCanSendCyCyLaterMsg, [10 - (GetTickCount - m_dwShoutMsgTick) div 1000]), c_Red, t_Hint);
          Exit;
        end;
        SysMsg(g_sThisMapDisableSendCyCyMsg {'本地图不允许喊话！！！'}, c_Red, t_Hint);
        Exit;
      end;

      if m_boFilterSendMsg then begin //如果禁止发信息，则只向自己发信息
        //SendMsg(Self, RM_HEAR, 0, {g_Config.btHearMsgFColor} m_btHearMsgFColor, g_Config.btHearMsgBColor, 0, GetUnknowCharName + ':' + sData);
      end else begin
        SendRefMsg(RM_HEAR, 0, {g_Config.btHearMsgFColor} m_btHearMsgFColor, g_Config.btHearMsgBColor, 0, GetUnknowCharName + ':' + sData);
      end;
    end;
    //SysMsg(g_sYouIsDisableSendMsg {'禁止聊天'}, c_Red, t_Hint);
  except
    on E: Exception do begin
      MainOutMessage(Format(sExceptionMsg, [sData]));
      MainOutMessage(E.Message);
    end;
  end;
end;

procedure TAIPlayObject.Hear(nIndex: Integer; sMsg: string);
var
  nPos: Integer;
  boDisableSayMsg: Boolean;
  sChrName, sSendMsg: string;
begin
  case nIndex of
    RM_HEAR: begin
        if m_btPermission > 9 then begin
          nPos := Pos(':', sMsg);
          if nPos > 0 then begin
            //sChrName := Copy(sMsg, 1, nPos - 1);
            sSendMsg := Copy(sMsg, nPos + 1, Length(sMsg) - nPos - 1);
            g_AI3.Teach(sSendMsg);
          end;
        end;
      end;
    RM_WHISPER: begin
        if GetTickCount >= m_dwDisableSayMsgTick then m_boDisableSayMsg := False;
        boDisableSayMsg := m_boDisableSayMsg;
        g_DenySayMsgList.Lock;
        try
          if g_DenySayMsgList.GetIndex(m_sCharName) >= 0 then boDisableSayMsg := True;
        finally
          g_DenySayMsgList.UnLock;
        end;
        if not boDisableSayMsg then begin
          nPos := Pos('=>', sMsg);
          if nPos > 0 then begin
            sChrName := Copy(sMsg, 1, nPos - 1);
            sSendMsg := Copy(sMsg, nPos + 3, Length(sMsg) - nPos - 2);
            Whisper(sChrName, g_AI3.Ask(sSendMsg));
          end;
        end;
      end;
    RM_CRY: ;
    RM_SYSMESSAGE: ;
    RM_GROUPMESSAGE: ;
    RM_GUILDMESSAGE: ;
    RM_MERCHANTSAY: ;
    RM_MOVEMESSAGE: ;
    RM_DELAYMESSAGE: ;
    RM_CENTERMESSAGE: ;
  end;
end;

function TAIPlayObject.GetRandomConfigFileName: string;
var
  I, nIndex: Integer;

  sFileName: string;
  LoadList: TStringList;
begin
  Result := '';
  if (m_sConfigListFileName <> '') and FileExists(m_sConfigListFileName) then begin
    LoadList := TStringList.Create;
    try
      LoadList.LoadFromFile(m_sConfigListFileName);
    except

    end;
    nIndex := Random(LoadList.Count - 1);
    if (nIndex >= 0) and (nIndex < LoadList.Count) then begin
      Result := m_sFilePath + LoadList.Strings[nIndex];
    end;
    LoadList.Free;
  end;
end;

procedure TAIPlayObject.Initialize;
var
  I, nIndex: Integer;

  sFileName: string;
  ItemIni: TIniFile;

  TempList: TStringList;
  sCopyHumBagItems: string;
  UserItem: pTUserItem;

  sLineText: string;
  sMagicName: string;
  sItemName: string;
  Magic: pTMagic;
  MagicInfo: pTMagic;
  UserMagic: pTUserMagic;
  StdItem: pTStdItem;
  LoadList: TStringList;
begin
  m_sHeroCharName := GetAIHeroName(m_sCharName);
  m_boHasHero := m_sHeroCharName <> '';

  sFileName := GetRandomConfigFileName;
  if (sFileName = '') or (not FileExists(sFileName)) then begin
    if (m_sConfigFileName <> '') and FileExists(m_sConfigFileName) then begin
      sFileName := m_sConfigFileName;
    end;
  end;

  if (sFileName <> '') and FileExists(sFileName) then begin
    //MainOutMessage(m_sCharName + ' m_sConfigFileName1:' + m_sConfigFileName);
    ItemIni := TIniFile.Create(sFileName);
    if ItemIni <> nil then begin
      m_btJob := ItemIni.ReadInteger('BaseInfo', 'Job', 0);
      m_btGender := ItemIni.ReadInteger('BaseInfo', 'Gender', 0);
      m_btHair := ItemIni.ReadInteger('BaseInfo', 'Hair', 0);
      m_Abil.Level := ItemIni.ReadInteger('BaseInfo', 'Level', 1);
      m_boSuperItem := ItemIni.ReadBool('BaseInfo', 'SuperItem', False);
      //MainOutMessage(m_sCharName + ' m_WAbil.Level:' + IntToStr(m_WAbil.Level));

      sLineText := ItemIni.ReadString('BaseInfo', 'Magic', '');
      if sLineText <> '' then begin
        TempList := TStringList.Create;
        ExtractStrings(['|', '\', '/', ','], [], PChar(sLineText), TempList);
        for I := 0 to TempList.Count - 1 do begin
          sMagicName := Trim(TempList.Strings[I]);
          if FindMagic(sMagicName) = nil then begin
            Magic := UserEngine.FindMagic(sMagicName);
            if Magic <> nil then begin
              if (Magic.btJob = 99) or (Magic.btJob = m_btJob) then begin
                New(UserMagic);
                UserMagic.MagicInfo := Magic;
                UserMagic.wMagIdx := Magic.wMagicId;
                UserMagic.btLevel := 3;
                UserMagic.btKey := VK_F1;
                UserMagic.nTranPoint := Magic.MaxTrain[3];
                m_MagicList.Add(UserMagic);
              end;
            end;
          end;
        end;
        TempList.Free;
      end;

      sLineText := ItemIni.ReadString('BaseInfo', 'BagItem', '');
      if sLineText <> '' then begin
        TempList := TStringList.Create;
        ExtractStrings(['|', '\', '/', ','], [], PChar(sLineText), TempList);
        for I := 0 to TempList.Count - 1 do begin
          sItemName := Trim(TempList.Strings[I]);

          StdItem := UserEngine.GetStdItem(sItemName);
          if StdItem <> nil then begin
            New(UserItem);
            if UserEngine.CopyToUserItemFromName(sItemName, UserItem) then begin

              if m_boSuperItem or (Random(g_Config.nScriptRandomAddValue {10}) = 0) then //几率控制
                UserEngine.RandomUpgradeItem(UserItem); //生成极品装备

              UserEngine._RandomItemLimitDay(UserItem, g_Config.nScriptRandomNotLimit);

              if m_boSuperItem or (Random(g_Config.nScriptRandomNewAddValue) = 0) then
                UserEngine._RandomUpgradeItem(UserItem);

              if m_boSuperItem or (Random(g_Config.nScriptRandomAddPoint) = 0) then
                UserEngine.RandomItemAddPoint(UserItem);


              UserEngine._RandomItemLimitDay(UserItem, g_Config.nMonRandomNotLimit);

              if StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26] then begin
                if (StdItem.Shape = 130) or (StdItem.Shape = 131) or (StdItem.Shape = 132) then begin
                  UserEngine.GetUnknowItemValue(UserItem);

                  UserEngine._GetUnknowItemValue(UserItem);
                end;
              end;

              if not AddItemToBag(UserItem) then begin
                Dispose(UserItem);
                break;
              end;
              m_BagItemNames.Add(StdItem.Name);
            end else Dispose(UserItem);
          end;

        end;
        TempList.Free;
      end;

      m_UseItemNames[U_DRESS] := ItemIni.ReadString('UseItems', 'DRESSNAME', ''); // '衣服';
      m_UseItemNames[U_WEAPON] := ItemIni.ReadString('UseItems', 'WEAPONNAME', ''); // '武器';
      m_UseItemNames[U_RIGHTHAND] := ItemIni.ReadString('UseItems', 'RIGHTHANDNAME', ''); // '照明物';
      m_UseItemNames[U_NECKLACE] := ItemIni.ReadString('UseItems', 'NECKLACENAME', ''); // '项链';
      m_UseItemNames[U_HELMET] := ItemIni.ReadString('UseItems', 'HELMETNAME', ''); // '头盔';
      m_UseItemNames[U_ARMRINGL] := ItemIni.ReadString('UseItems', 'ARMRINGLNAME', ''); // '左手镯';
      m_UseItemNames[U_ARMRINGR] := ItemIni.ReadString('UseItems', 'ARMRINGRNAME', ''); // '右手镯';
      m_UseItemNames[U_RINGL] := ItemIni.ReadString('UseItems', 'RINGLNAME', ''); // '左戒指';
      m_UseItemNames[U_RINGR] := ItemIni.ReadString('UseItems', 'RINGRNAME', ''); // '右戒指';
      m_UseItemNames[U_BUJUK] := ItemIni.ReadString('UseItems', 'BUJUKNAME', ''); // '物品';
      m_UseItemNames[U_BELT] := ItemIni.ReadString('UseItems', 'BELTNAME', ''); // '腰带';
      m_UseItemNames[U_BOOTS] := ItemIni.ReadString('UseItems', 'BOOTSNAME', ''); // '鞋子';
      m_UseItemNames[U_CHARM] := ItemIni.ReadString('UseItems', 'CHARMNAME', ''); // '宝石';
 //     m_UseItemNames[U_MOUNT] := ItemIni.ReadString('UseItems', 'MOUNTNAME', '');

      m_nDieDropUseItemRate := ItemIni.ReadInteger('UseItems', 'DieDropUseItemRate', 100);
      m_boButchItemEx := ItemIni.ReadBool('UseItems', 'ButchItem', False);
      m_boButchItem := m_boButchItemEx;
      m_boNoDropItem := ItemIni.ReadBool('UseItems', 'NoDropItem', False);
      m_boNoDropUseItem := ItemIni.ReadBool('UseItems', 'NoDropUseItem', False);

      for I := U_DRESS to U_CHARM do begin
        if m_UseItemNames[I] <> '' then begin
          StdItem := UserEngine.GetStdItem(m_UseItemNames[I]);
          if StdItem <> nil then begin
            New(UserItem);
            if UserEngine.CopyToUserItemFromName(m_UseItemNames[I], UserItem) then begin

              if m_boSuperItem or (Random(g_Config.nScriptRandomAddValue {10}) = 0) then //几率控制
                UserEngine.RandomUpgradeItem(UserItem); //生成极品装备

              UserEngine._RandomItemLimitDay(UserItem, g_Config.nScriptRandomNotLimit);

              if m_boSuperItem or (Random(g_Config.nScriptRandomNewAddValue) = 0) then
                UserEngine._RandomUpgradeItem(UserItem);

              if m_boSuperItem or (Random(g_Config.nScriptRandomAddPoint) = 0) then
                UserEngine.RandomItemAddPoint(UserItem);

              UserEngine._RandomItemLimitDay(UserItem, g_Config.nMonRandomNotLimit);

              if StdItem.StdMode in [15, 19, 20, 21, 22, 23, 24, 26] then begin
                if (StdItem.Shape = 130) or (StdItem.Shape = 131) or (StdItem.Shape = 132) then begin
                  UserEngine.GetUnknowItemValue(UserItem);

                  UserEngine._GetUnknowItemValue(UserItem);
                end;
              end;
            end;
            m_UseItems[I] := UserItem^;
            Dispose(UserItem);
              //end;
          end;
        end;
      end;

      ItemIni.Free;

      m_WAbil.HP := m_WAbil.MaxHP;
      m_WAbil.MP := m_WAbil.MaxMP;

      //AbilCopyToWAbil();
        //HasLevelUp(0);
    end;
  end;
  inherited;
end;

function TAIPlayObject.FollowMaster: Boolean;
begin
  //inner power
end;

procedure TAIPlayObject.Wondering();
var
  nX, nY: Integer;
begin
  if m_boAIStart and
    (m_TargetCret = nil) and
    (not m_boGhost) and
    (not m_boDeath) and
    (not m_boFixedHideMode) and
    (not m_boStoneMode) and
    (m_wStatusTimeArr[POISON_STONE] = 0) then begin
    //if not m_PointManager.Finish then begin
    nX := m_nCurrX;
    nY := m_nCurrY;
      //MainOutMessage(Format('TAIObject.Wondering0:(%d / %d)', [nX, nY]));
      //m_PointManager.SetThroughPoint(m_nCurrX, m_nCurrY);
    if (Length(m_Path) > 0) and (m_nPostion < Length(m_Path)) then begin
      if not GotoNextOne(m_Path[m_nPostion].X, m_Path[m_nPostion].Y, True) then begin
        m_Path := nil;
        m_nPostion := -1;
        Inc(m_nMoveFailCount);
        Inc(m_nPostion);
      end else begin
        m_nMoveFailCount := 0;
        //MainOutMessage(m_sCharName + ' GotoNextOne FindPath:' + IntToStr(Length(m_Path)));
        Exit;
      end;
    end else begin
      m_Path := nil;
      m_nPostion := -1;
    end;

    if m_PointManager.GetPoint(nX, nY) then begin
      if (abs(nX - m_nCurrX) > 2) or (abs(nY - m_nCurrY) > 2) then begin
        g_FindPath.BaseObject := Self;
        m_Path := g_FindPath.FindPath(m_PEnvir, m_nCurrX, m_nCurrY, nX, nY, True, False);
        m_nPostion := 0;
        //MainOutMessage(m_sCharName + ' FindPath:' + IntToStr(Length(m_Path)));
        if (Length(m_Path) > 0) and (m_nPostion < Length(m_Path)) then begin
          if not GotoNextOne(m_Path[m_nPostion].X, m_Path[m_nPostion].Y, True) then begin
            m_Path := nil;
            m_nPostion := -1;
            Inc(m_nMoveFailCount);
          end else begin
            m_nMoveFailCount := 0;
            Inc(m_nPostion);
            Exit;
          end;
        end else begin
          m_Path := nil;
          m_nPostion := -1;
          Inc(m_nMoveFailCount);
        end;
      end else begin
        if GotoNextOne(nX, nY, True) then begin
          m_nMoveFailCount := 0;
        end else begin
          Inc(m_nMoveFailCount);
        end;
      end;
      //MainOutMessage(Format('%s TAIObject.Wondering1:(%d / %d)', [m_sCharName, nX, nY]));
        //if GotoNextOne(nX, nY, True) then MainOutMessage(Format('%s TAIObject.Wondering2:(%d / %d)', [m_sCharName, nX, nY])) else MainOutMessage(Format('%s TAIObject.Wondering3:(%d / %d)', [m_sCharName, nX, nY]));
    end else Stop;
    //end else Stop;
  end;
  if m_nMoveFailCount >= 3 then Stop;
end;

procedure TAIPlayObject.Struck(hiter: TActorObject);
  function MINXY(AObject, BObject: TActorObject): TActorObject;
  var
    nA, nB: Integer;
  begin
    nA := abs(m_nCurrX - AObject.m_nCurrX) + abs(m_nCurrY - AObject.m_nCurrY);
    nB := abs(m_nCurrX - BObject.m_nCurrX) + abs(m_nCurrY - BObject.m_nCurrY);
    if nA > nB then Result := BObject else Result := AObject;
  end;
var
  boDisableSayMsg: Boolean;
  nError: Integer;
begin
  m_dwStruckTick := GetTickCount;
  if hiter <> nil then begin
    if (m_TargetCret = nil) and IsProperTarget(hiter) then begin
      SetTargetCreat(hiter);
      //Start(TPathType(0));
      //MainoutMessage('SetTargetCreat(hiter)1');
    end else begin
      if (hiter.m_btRaceServer = RC_PLAYOBJECT) or ((hiter.m_Master <> nil) and (hiter.Master.m_btRaceServer = RC_PLAYOBJECT)) then begin
        if (m_TargetCret <> nil) and ((m_TargetCret.m_btRaceServer = RC_PLAYOBJECT) or ((m_TargetCret.m_Master <> nil) and (m_TargetCret.Master.m_btRaceServer = RC_PLAYOBJECT))) then begin
          if (MINXY(m_TargetCret, hiter) = hiter) or (Random(6) = 0) then begin
            SetTargetCreat(hiter);
            //Start(TPathType(0));
            //MainoutMessage('SetTargetCreat(hiter)2');
          end;
        end else begin
          SetTargetCreat(hiter);
         // Start(TPathType(0));
          //MainoutMessage('SetTargetCreat(hiter)3');
        end;
      end else begin
        if ((m_TargetCret <> nil) and (MINXY(m_TargetCret, hiter) = hiter)) or (Random(6) = 0) then begin
          if (m_btJob > 0) or ((m_TargetCret <> nil) and (GetTickCount() - m_dwTargetFocusTick > 1000 * 3)) then
            if IsProperTarget(hiter) then SetTargetCreat(hiter);
        end;
      end;
    end;

    if (hiter.m_btRaceServer = RC_PLAYOBJECT) and (not hiter.m_boAI) and (m_TargetCret = hiter) then begin
      if (Random(6) = 0) then begin
        if GetTickCount >= m_dwDisableSayMsgTick then m_boDisableSayMsg := False;
        boDisableSayMsg := m_boDisableSayMsg;
        g_DenySayMsgList.Lock;
        try
          if g_DenySayMsgList.GetIndex(m_sCharName) >= 0 then boDisableSayMsg := True;
        finally
          g_DenySayMsgList.UnLock;
        end;
        if not boDisableSayMsg then begin
          SendRefMsg(RM_HEAR, 0, {g_Config.btHearMsgFColor} m_btHearMsgFColor, g_Config.btHearMsgBColor, 0, m_sCharName + ':' + g_AI3.Ask(''));
        end;
      end;
    end;
  end;

  {except
    MainoutMessage('TAIPlayObject::Struck:'+IntToStr(nError));
  end;}


  if m_boAnimal then begin
    m_nMeatQuality := m_nMeatQuality - Random(300);
    if m_nMeatQuality < 0 then m_nMeatQuality := 0;
  end;
  m_dwHitTick := m_dwHitTick + LongWord(150 - _MIN(130, m_Abil.Level * 4));
end;

procedure TAIPlayObject.SearchTarget();
begin
  if ((m_TargetCret = nil) or (GetTickCount - m_dwSearchTargetTick > 1000)) and
    (m_boAIStart) and
    (not m_boTarget) and
    (not InSafeZone) then begin
    m_dwSearchTargetTick := GetTickCount();
    if (m_TargetCret = nil) or
      (not ((m_TargetCret <> nil) and (m_TargetCret.m_btRaceServer = RC_PLAYOBJECT)) or ((m_TargetCret.m_Master <> nil) and (m_TargetCret.Master.m_btRaceServer = RC_PLAYOBJECT)) or (GetTickCount - m_dwStruckTick > 1000 * 10)) then begin
      inherited;
    end;
  end;
end;

procedure TAIPlayObject.Die;
begin
  if m_boAIStart then begin
    m_boAIStart := False;
    m_ManagedEnvir := nil;
  end;
  inherited;
end;

function TAIPlayObject.Operate(ProcessMsg: pTProcessMessage): Boolean;
var
  nError: Integer;
  AttackBaseObject: TActorObject;
const
  sExceptionMsg0 = '[Exception] TAIPlayObject::Operate %s Ident:%d Sender:%d wP:%d nP1:%d nP2:%d np3:%d Msg:%s';
begin
  //  Result:=False;
  try
    if ProcessMsg.wIdent = RM_STRUCK then begin
    //MainOutMessage('TAIPlayObject::Operate:' + m_sCharName);
      nError := 0;

      if TActorObject(ProcessMsg.BaseObject) = Self then begin
        nError := 1;
        AttackBaseObject := TActorObject(ProcessMsg.nParam3);
        if AttackBaseObject <> nil then begin
          nError := 2;
          if AttackBaseObject.m_btRaceServer = RC_PLAYOBJECT then begin
            nError := 3;
            SetPKFlag(AttackBaseObject);
            nError := 4;
          end;
          nError := 5;
          SetLastHiter(AttackBaseObject);
          nError := 6;

          Struck(AttackBaseObject);
          nError := 7;
          BreakHolySeizeMode();
          nError := 8;
                {
                //反复活
                if TActorObject(ProcessMsg.nParam3).m_boUnRevival then
                  m_boRevival:=False;
                }
        end; //004D8B67
        if PKLevel >= 2 then m_dw5D4 := GetTickCount();
              //if UserCastle.IsMasterGuild(TGuild(m_MyGuild)) and (TActorObject(ProcessMsg.nParam3) <> nil) then begin
        nError := 9;
        if (g_CastleManager.IsCastleMember(Self) <> nil) and (AttackBaseObject <> nil) then begin
          nError := 10;
          AttackBaseObject.bo2B0 := True;
          nError := 11;
          AttackBaseObject.m_dw2B4Tick := GetTickCount();
          nError := 12;
        end;
        nError := 13;
        m_nHealthTick := 0;
        m_nSpellTick := 0;
        Dec(m_nPerHealth);
        Dec(m_nPerSpell);
        m_dwStruckTick := GetTickCount(); //09/10
        nError := 14;
      end; //4D8BE1
      Result := True;
    end else begin
      Result := inherited Operate(ProcessMsg);
    end;
  except
    MainOutMessage('TAIPlayObject::Operate:' + IntToStr(nError));
    MainOutMessage(Format(sExceptionMsg0, [m_sCharName,
      ProcessMsg.wIdent,
        Integer(ProcessMsg.BaseObject),
        ProcessMsg.wParam,
        ProcessMsg.nParam1,
        ProcessMsg.nParam2,
        ProcessMsg.nParam3,
        ProcessMsg.sMsg]));
  end;
end;

procedure TAIPlayObject.Run;
var
  nSelectMagic: Integer;
begin
  if (not m_boGhost) and
    (not m_boDeath) and
    (not m_boFixedHideMode) and
    (not m_boStoneMode) and
    (m_wStatusTimeArr[POISON_STONE] = 0) then begin

    if (m_TargetCret <> nil) and
      (m_TargetCret.m_boDeath or m_TargetCret.m_boGhost) then
      DelTargetCreat;

    if (not m_boAIStart) then
      DelTargetCreat();

    SearchTarget();

    if (m_TargetCret <> nil) then
      nSelectMagic := SelectMagic;

    //if (m_Master <> nil) and m_PEnvir.m_boDuel and (not Master.m_boStartDuel) then
     // DelTargetCreat; //非比赛人员禁止攻击  m_PEnvir.m_boDueling

   { boUseGroupSpell := False;
    if (m_TargetCret <> nil) then begin
      if m_boUseGroupSpell then begin //合击
        if (abs(m_TargetCret.m_nCurrX - m_nCurrX) <= 12) and (abs(m_TargetCret.m_nCurrY - m_nCurrY) <= 12)
          and (abs(m_Master.m_nCurrX - m_nCurrX) <= 12) and (abs(m_Master.m_nCurrY - m_nCurrY) <= 12)
          and (abs(m_Master.m_nCurrX - m_TargetCret.m_nCurrX) <= 12) and (abs(m_Master.m_nCurrY - m_TargetCret.m_nCurrY) <= 12) then begin
          boUseGroupSpell := True;
        end else m_boUseGroupSpell := False;

        if not m_boUseGroupSpell then begin
          nSelectMagic := SelectMagic;
        end;
      end;
    end; }

    if (m_TargetCret <> nil) then begin
     { if boUseGroupSpell then begin //合击
        if ActThink(nSelectMagic) then begin

        end;
        if (m_TargetCret <> nil) and AttackTarget(nSelectMagic) then begin
          m_dwHitTick := GetTickCount();
          Dec(m_nAngryValue, g_Config.nMaxAngryValue);
          SendMsg(Self, RM_HEROANGERVALUE, m_nAngryValue, 0, g_Config.nMaxAngryValue, 0, '');
          if (nSelectMagic > 0) and (nSelectMagic <= High(m_SkillUseTick)) then begin
            m_SkillUseTick[nSelectMagic] := GetTickCount;
          end;
        end;
        m_boUseGroupSpell := False;
      end else begin }
      case m_btJob of
        0: begin
            if (m_TargetCret <> nil) and (Integer(GetTickCount - m_dwHitTick) > g_Config.dwHeroWarrorAttackTime) then begin
              m_dwHitTick := GetTickCount();
              if ActThink(nSelectMagic) then begin
                //inherited;
                //Exit;
              end;

              if (m_TargetCret <> nil) and StartAttack(nSelectMagic) then begin
                if (nSelectMagic > 0) and (nSelectMagic <= High(m_SkillUseTick)) then begin
                  m_SkillUseTick[nSelectMagic] := GetTickCount;
                end;
              end;
            end;
          end;
        1: begin
            if (m_TargetCret <> nil) and (Integer(GetTickCount - m_dwHitTick) > g_Config.dwHeroWizardAttackTime) then begin
              m_dwHitTick := GetTickCount();
              if ActThink(nSelectMagic) then begin
                //inherited;
                //Exit;
              end;

              if (m_TargetCret <> nil) and StartAttack(nSelectMagic) then begin
                if (nSelectMagic >= 0) and (nSelectMagic <= High(m_SkillUseTick)) then begin
                  m_SkillUseTick[nSelectMagic] := GetTickCount;
                end;
              end;
            end;
          end;
        2: begin
            if (m_TargetCret <> nil) and (Integer(GetTickCount - m_dwHitTick) > g_Config.dwHeroTaoistAttackTime) then begin
              m_dwHitTick := GetTickCount();
              if ActThink(nSelectMagic) then begin
                //inherited;
                //Exit;
              end;
              if (m_TargetCret <> nil) and StartAttack(nSelectMagic) then begin
                if (nSelectMagic >= 0) and (nSelectMagic <= High(m_SkillUseTick)) then begin
                  m_SkillUseTick[nSelectMagic] := GetTickCount;
                end;
              end;
            end;
          end;
      end;
    end;
   // end;
  end;

  if m_ManagedEnvir <> m_PEnvir then begin
    m_ManagedEnvir := m_PEnvir;
    DelTargetCreat();
  end;
  inherited;
end;

function TAIPlayObject.IsProtectTarget(BaseObject: TActorObject): Boolean;
begin
  Result := inherited IsProtectTarget(BaseObject);
end;

function TAIPlayObject.IsAttackTarget(BaseObject: TActorObject): Boolean;
begin
  Result := inherited IsAttackTarget(BaseObject);
end;

function TAIPlayObject.IsProperTarget(BaseObject: TActorObject): Boolean;
begin
  Result := False;
  if inherited IsProperTarget(BaseObject) then begin
    Result := True;
    if BaseObject.m_btRaceServer = RC_ARCHERGUARD then begin //不主动攻击弓箭手
      if BaseObject.m_TargetCret <> Self then Result := False;
    end;
    if BaseObject.m_btRaceServer in [110, 111] then begin //不主动攻击沙巴克城门 沙巴克左城墙
      Result := False;
    end;
  end;
end;

function TAIPlayObject.IsProperFriend(BaseObject: TActorObject): Boolean;
begin
  Result := inherited IsProperFriend(BaseObject);
end;
end.

