有用的代码,用于制作脚本等

崩溃
这是一个置顶的主题.
X
X
 
  • 时间
  • 显示
清除所有
新帖子
  • admin
    管理员
    • 05.24.20
    • 422

    有用的代码,用于制作脚本等


    ################################################## ###########################

    Name: Current Zone, XYZ & Weather
    --------------------------------------------------
    ClearLog();
    Log("Zone Id: {0} | Raw Zone Id: {1} | Subzone Id: {2}",WorldManager.ZoneId,WorldManager.RawZoneId,W o rldManager.SubZoneId);
    Log("Current XYZ: {0}",Core.Player.Location);
    Log("Current Weather: {0} | Current Weather Id: {1}",WorldManager.CurrentWeather,WorldManager.Cur r entWeatherId);
    Log("============================================= =====");
    foreach(var Aetheryte in WorldManager.AetheryteIdsForZone(WorldManager.Zone Id))
    {
    Log("Current Zone Aetheryte ID: {0}",Aetheryte.Item1);
    }
    --------------------------------------------------

    Name: Nearby Objects (Closest at the Top)
    --------------------------------------------------
    ClearLog();
    var units = GameObjectManager.GameObjects;
    foreach(var unit in units.OrderBy(r=>r.Distance()))
    {
    Log("Name:{0}, Type:{3}, ID:{1}, Obj:{2}",unit,unit.NpcId,unit.ObjectId,unit.GetTyp e());
    }
    Log("============================================= =====");
    foreach (var x in GameObjectManager.GetObjectsOfType<BattleCharacter >(true).OrderBy(r=>r.Distance()))
    {
    Log("Name: " + x.EnglishName + ", Level: " + x.ClassLevel);
    }
    --------------------------------------------------

    Name: Aetheryte ID List
    --------------------------------------------------
    ClearLog();
    foreach(var item in DataManager.AetheryteCache)
    {
    Log("ID: {0} | Location: {1}",item.Key,item.Value);
    }
    --------------------------------------------------

    Name: Current Quests
    --------------------------------------------------
    ClearLog();
    foreach(var quest in QuestLogManager.Quests())
    {
    Log(quest);
    }
    --------------------------------------------------

    Name: FishSpot Generator
    --------------------------------------------------
    ClearLog();
    string location = Core.Player.Location.ToString().Remove(0, 1);
    location = location.Remove(location.Length - 1, 1);
    Log("<FishSpot XYZ="" + location + "" Heading="" + Core.Me.Heading + "" />");
    --------------------------------------------------

    Name: Inventory Items
    --------------------------------------------------
    ClearLog();
    Log("============================================= =====");
    Log("======================GEAR=================== =====");
    Log("============================================= =====");
    Log(" ");

    foreach(var item in InventoryManager.GetBagByInventoryBagId(ff14bot.En ums.InventoryBagId.EquippedItems).FilledSlots)
    {
    Log("Name: " + item.Item.EnglishName + "\tItemCategory: " + item.Item.EquipmentCatagory + "\tId: " + item.Item.Id);
    }
    Log(" ");
    Log("============================================= =====");
    Log("====================INVENTORY================ =====");
    Log("============================================= =====");
    Log(" ");

    foreach(var item in ff14bot.Managers.InventoryManager.FilledSlots.Wher e(x => x.BagId == InventoryBagId.Bag1 || x.BagId == InventoryBagId.Bag2 || x.BagId == InventoryBagId.Bag3 || x.BagId == InventoryBagId.Bag4))
    {
    Log("Name: " + item.Item.EnglishName + "\tItemCategory: " + item.Item.EquipmentCatagory + "\tId: " + item.Item.Id);
    }
    Log(" ");
    Log("============================================= =====");
    Log("====================KEY ITEMS=====================");
    Log("============================================= =====");
    Log(" ");
    foreach(var bagslot in InventoryManager.GetBagByInventoryBagId(ff14bot.En ums.InventoryBagId.KeyItems).FilledSlots)
    {
    Log(bagslot);
    }
    --------------------------------------------------

    Name: Detailed Target Information (Target Something!)
    --------------------------------------------------
    ClearLog();
    GameObject s = GameObjectManager.GetObjectByNPCId(Core.Target.Npc Id);
    Log("Can Attack - " + s.CanAttack.ToString());
    Log("Combat Reach - " + s.CombatReach.ToString());
    Log("Current Health Percent - " + s.CurrentHealthPercent.ToString());
    Log("English Name - " + s.EnglishName.ToString());
    Log("Fate ID - " + s.FateId.ToString());
    Log("Heading - " + s.Heading.ToString());
    Log("Id Location - " + s.IdLocation.ToString());
    Log("Is Behind - " + s.IsBehind.ToString());
    Log("Is Fate Gone - " + s.IsFateGone.ToString());
    Log("Is Flanking - " + s.IsFlanking.ToString());
    Log("Is Me - " + s.IsMe.ToString());
    Log("Is Targetable - " + s.IsTargetable.ToString());
    Log("Is Valid - " + s.IsValid.ToString());
    Log("Is Visble - " + s.IsVisible.ToString());
    Log("Location - " + s.Location.ToString());
    Log("LUA String - " + s.LuaString.ToString());
    Log("Max Health - " + s.MaxHealth.ToString());
    Log("Name - " + s.Name.ToString());
    Log("NPC ID - " + s.NpcId.ToString());
    Log("Object Id - " + s.ObjectId.ToString());
    Log("Type - " + s.Type.ToString());
    --------------------------------------------------

    Name: Remote Window Open Check
    --------------------------------------------------
    ClearLog();
    Log("ColosseumRecord Open? {0}",ff14bot.RemoteWindows.ColosseumRecord.IsOpen ) ;
    Log("ContentsFinder Open? {0}",ff14bot.RemoteWindows.ContentsFinder.IsOpen) ;
    Log("ContentsFinderConfirm Open? {0}",ff14bot.RemoteWindows.ContentsFinderConfirm. I sOpen);
    Log("ContentsFinderReady Open? {0}",ff14bot.RemoteWindows.ContentsFinderReady.Is O pen);
    Log("CraftingLog Open? {0}",ff14bot.RemoteWindows.CraftingLog.IsOpen);
    Log("HousingGardening Open? {0}",ff14bot.RemoteWindows.HousingGardening.IsOpe n );
    Log("JournalAccept Open? {0}",ff14bot.RemoteWindows.JournalAccept.IsOpen);
    Log("JournalResult Open? {0}",ff14bot.RemoteWindows.JournalResult.IsOpen);
    Log("MaterializeDialog Open? {0}",ff14bot.RemoteWindows.MaterializeDialog.IsOp e n);
    Log("Repair Open? {0}",ff14bot.RemoteWindows.Repair.IsOpen);
    Log("Request Open? {0}",ff14bot.RemoteWindows.Request.IsOpen);
    Log("SelectIconString Open? {0}",ff14bot.RemoteWindows.SelectIconString.IsOpe n );
    Log("SelectString Open? {0}",ff14bot.RemoteWindows.SelectString.IsOpen);
    Log("SelectYesno Open? {0}",ff14bot.RemoteWindows.SelectYesno.IsOpen);
    Log("Synthesis Open? {0}",ff14bot.RemoteWindows.Synthesis.IsOpen);
    Log("Talk Open? {0}",ff14bot.RemoteWindows.Talk.DialogOpen);
    --------------------------------------------------

    Name: Weather List
    --------------------------------------------------
    ClearLog();
    foreach(var item in WorldManager.WeatherDictionary)
    {
    Log("ID: {0} | Type: {1}",item.Key,item.Value);
    }
    --------------------------------------------------

    Name: Get Todo Arguments (XXXXX = Quest ID)
    --------------------------------------------------
    ClearLog();
    Log(QuestLogManager.GetQuestById(XXXXX).GetTodoArg s(0));
    Log(QuestLogManager.GetQuestById(XXXXX).GetTodoArg s(1));
    --------------------------------------------------

    Name: Quest Arguments (XXXXX = Quest ID)
    --------------------------------------------------
    ClearLog();
    Log(ff14bot.NeoProfiles.ConditionParser.IsQuestAcc eptQualified(XXXXX));
    Log(ff14bot.NeoProfiles.ConditionParser.IsQuestCom pleted(XXXXX));
    Log(ff14bot.NeoProfiles.ConditionParser.HasQuest(X XXXX));
    Log(ff14bot.NeoProfiles.ConditionParser.GetQuestSt ep(XXXXX));
    Log(ff14bot.NeoProfiles.ConditionParser.GetQuestBy Id(XXXXX).QuestI8AH);
    --------------------------------------------------

    Name: Active FATE Properties
    --------------------------------------------------
    ClearLog();
    foreach (var fate in FateManager.ActiveFates)
    {
    Log("IsValid: " + fate.IsValid + " | Level: " + fate.Level + " | Status: " + fate.Status + " | " + fate.Name);
    }
    --------------------------------------------------

    Name: Distance Check
    --------------------------------------------------
    ClearLog();
    Log(Core.Me.Location.Distance3D(new Vector3(0.0f, 0.0f, 0.0f)));
    --------------------------------------------------

    Name: Current Actions
    --------------------------------------------------
    ClearLog();
    foreach(var action in Actionmanager.CurrentActions) { Log(action); }
    --------------------------------------------------

    Name: Open Windows
    --------------------------------------------------
    ClearLog();
    foreach (var window in RaptureAtkUnitManager.Controls)
    {
    Log(window);
    }
    --------------------------------------------------

    <LisbethTravel ZoneId="957" XYZ="441.9648, 6.760745, 254.1012"/> LisbethTravel Nav
    <GeTto ZoneId="957" XYZ="441.9648, 6.760745, 254.1012"/> RB Nav
    最后由 admin 编辑; 09-21-2023, 02:13 AM
  • admin
    管理员
    • 05.24.20
    • 422

    #2
    Introduction

    I've decided to write a little bit of what I've learned regarding making stuff for Rebornbuddy. I don't have access to any of Rebornbuddy's internals, so what I'm writing here can be incorrect and is mostly based off of assumptions and my own personal experience. If you're starting to program or don't know C#, I'd highly recommend that you learn it doing something simpler. Coding for Rebornbuddy is mostly about understanding the bot's architecture and if on top of that you also don't know C# then things will get complicated. There are easier things you can do to learn C# out there.

    What You Must Learn


    Unless you want to extend the Rebornbuddy API, you don't really need to concern yourself with how the bot injects things into the game and reads the game's process memory. What you have to concern yourself with is what the API exposes.

    1. How do I read data from the game with the API?
    2. How do I act inside the game through the API?
    3. How is my code executed by Rebornbuddy?

    The third point might not be completely obvious for newcomers, and is where most of the complication lies. There are things that you could do on other environments, like sleeping a thread or making new threads, which are either not correct to do here or you need to do it in a very specific way. This was the hardest part for me to fully grasp, since you have to dig deep to find any documentation.

    How It Works


    You can interact with Rebornbuddy through three different routes:

    1. Making a Combat Routine. Go this route if what you want is to make combat logic which is independent of the bot base used.

    2. Making a Plugin. Go this route if what you want to do is a secondary activity to the main one being performed by the bot base. Repairing is a good example of a secondary activity.

    3. Making a BotBase. Go this route if what you want to do is the main activity of the bot, and OrderBot cannot do it already through a profile. You gain complete control over what the bot does with this; which also means you must take care of everything you want to do.

    All three routes share some stuff in common. You’ll be inheriting from a class or interface in the Rebornbuddy assembly to create either of them. This derived class of yours is your main point of entry to the bot, and provides the interface that the bot will use to interact with whatever you make. These derived classes and all their references must be placed on folders specific to each once inside the Rebornbuddy installation folder.

    In all three cases, what you package into your final folder will be the source of your code. This will be compiled by Rebornbuddy upon initialization of the application. You cannot use external pre-compiled libraries with your stuff unless Rebornbuddy already references them internally. You must also be careful about conflicts between classes with the same names; always use a unique namespace for your code.

    Any data files, images, or other resources that your code might need must also be included in the final folder that you put on the Rebornbuddy installation directory. All three approaches are based around a single overall design pattern: behavior trees.


    Behavior Trees



    With few exceptions, almost everything you code will be executed inside a behavior tree. Behavior trees are a design concept used mostly for game artificial intelligence. From FPS to MMOs, many games out there use them for NPC intelligence. Behavior trees are a design pattern, and as such it’s important that you understand them from a conceptual level independent from the particular implementation of them in Rebornbuddy. Here are some good articles that explain them:

    1. Gamasutra: Chris Simpson's Blog - Behavior trees for AI: How they work
    2. Game AI ? An Introduction to Behaviour Trees | Against the Grain ? Game Development

    Behavior trees, like everything else, have their compromises and benefits; and it's important that you learn when to use them and when you’d be better served using something else. Generally, if you need to make a single complex decision, then they can be convenient. They let you make cleaner code and avoid a bunch of nested conditionals that are hard to understand and maintain. If what you want is to make a sequence of steps or simple decisions, then something like a coroutine is better. A coroutine’s greatest benefit is that they are easier to debug. In short, behavior trees are a more powerful version of finite state machines.

    The basis of a behavior tree is its nodes. In Rebornbuddy, a node is implemented on the Composite class or any class derived from it. The Composite contains the interface common to all nodes in the tree, required to make them play nice with one another independent of their own logic. In general, there are two kinds of composites: those that control the flow of the tree, and those that actually do something with the game. The tree starts with a top node that has no parent: the root. This root node is ticked approximately 30 times per second when the bot is running. The tick will be propagated to every child node according to the flow established by other composites like priority selectors, decorators and sequences. A leaf composite is one who has no child composites. They represent the end of the tree, and it is inside these that you’ll usually interact with the game.

    Action
    The action composite can be considered as a “leaf” of the tree: a node that has no child node. It is in here that most of the code to interact with the game lies. If you want to move the character inside the game, you’ll probably put that code inside an action composite.

    RunActionCoroutine
    A special composite that wraps an asynchronous method inside it. This is the node that permits you to use coroutines inside the behavior tree. When the async method inside it returns a false boolean result, this composite in turn returns a RunStatus.Failure value to its parent. When it returns true, this gets translated to a RunStatus.Success value. When the async method is stuck on an await keyword, it’ll instead return a RunStatus.Running value. This last value causes the next tick to not start from the top of the tree as usual, but instead tick this composite directly.

    Decorator
    This composite acts as an IF statement. It has a single child node, and the only way the tick reaches that node is if the conditional statement of the decorator is true. If the conditional is false, the decorator will return a RunStatus.Failure value back to its parent.

    Priority Selector
    A composite that has many child composites. It will tick each one by one until one of them returns a RunStatus.Success value back; at which point it’ll return that value to its own parent.

    Sequence
    A composite that has many child composites. It will also tick each child one by one, and continue doing so only if each child returns a RunStatus.Success value back. When a RunStatus.Failure value is returned from any of the children, it’ll return that value to its own parent too.

    There are many other less popular composites. You can dig for them inside the TreeSharp namespace. I HIGHLY recommend that you read those articles if you're serious about writing something for Rebornbuddy. If anything, they'll teach you an AI technique that you could use elsewhere. I think that after reading them, most things will be clear.

    The way composites are implemented in Rebornbuddy permits you to build them quickly by using lambda expressions inside their constructor. This, however, is what causes them to be hard to debug. I would suggest that you don’t use too many lambda expressions to build Actions, but instead write actual methods that you then pass on their constructor. This way debugging is better since you’ll know which method is causing the problem and can step on them. You can also extend the Composite or Action class to create your own kind of composite with custom logic.

    Plugins



    To create a plugin, you must create a class that inherits from BotPlugin, which is inside the Rebornbuddy assembly. This interface provides you with methods that Rebornbuddy will call during different times on execution, and a few properties that you have to fill out with your own information. The methods are the entry point to your code. You might be tempted to write your code normally on these methods without using composites or the behavior tree, but this goes against the very design of the bot and is usually a bad idea. Here is what the methods in the interface look like:




    As you can see, their names give you a very good hint of when they care called by Rebornbuddy. What you should aim for is creating all your logic inside a composite, and then hooking that composite into the Rebornbuddy tree when your plugin is enabled, while unhooking it when the plugin is disabled. To make hooking things more organized, the main behavior tree inside Rebornbuddy has "categories" that you can hook into. These segments of the tree correspond to a particular activity. You can hook your own behavior composites to any part of the tree, but try and choose the category you consider categorizes what you're trying to do. This is the code I usually use to hook into the tree for a plugin:




    The times when you should hook your logic are:
    • The plugin is enabled and the bot is already running.
    • The plugin was enabled beforehand and the bot just started running.

    The times when you should unhook your logic are:
    • The bot is running and your plugin gets disabled.
    • The bot is stopped.

    Let me explain what exactly is happening in the hook method.
    The first **** is simply loading a settings Yaml file from disk. This file represents an object with all my plugin's settings. Some people prefer to use static variables for their settings, but I prefer the encapsulation that this method provides. The second **** is creating my actual behavior in the form of a composite. The creation of this composite is done on the TeleportLogic class separate from this one. The third **** is what actually hooks my logic into the tree. For this plugin, the composite is being hooked into the very start of the tree. You can think of categories as a PrioritySelector composite, where each category name corresponds to position on the selector, "TreeStart" being the first node. The 0 on the method represents the position within that category.


    C# is very similar to Java if you've ever programmed in that. If you haven't programmed at all, then perhaps a couple of "beginner" tutorials out there would help. Always do C# on Visual Studio, best thing out there. You could start with a simple plugin perhaps. I know that learning is mostly about motivation and making something interesting, and what's more interesting than making your character in a game do stuff.

    Glad it helped. The behavior tree implementation in RB is probably what keeps most people not understanding fully how to do stuff. It also doesn't help that since you can make composites by passing lambdas on their constructors a lot of the time the formatting makes it very hard to understand what's happening.

    最后由 admin 编辑; 05-16-2022, 06:17 AM

    评论

    • rebornbuddy
      Administrator
      • 10.28.19
      • 396

      #3
      <Profile>
      <Name>Alexander - The Burden of the Father</Name>
      <BehaviorDirectory></BehaviorDirectory>
      <Order>
      <While condition="not HasAtLeast(13587,30)">
      <If condition="not DutyManager.InInstance">
      <LLJoinDuty DutyId="115"/>
      </If>

      <If condition="DutyManager.InInstance">

      <!-- Defeat the Manipulator 0/1 -->
      <If Condition="GetInstanceTodo(0) == 0">
      <Log Message="Defeat the Manipulator 0/1" />
      <Grind GrindRef="FinalBoss" while="not GetInstanceTodo(0) == 1"/>
      <If Condition="&openchest; == 1">
      <MoveTo XYZ="0.2756959, 10.60451, -6.587952" />
      <LLOpenChest/>
      </If>
      <While condition="DutyManager.InInstance">
      <WaitTimer WaitTime="5" />
      <LLLeaveDuty/>
      </While>
      </If>
      </If>

      </While>
      </Order>
      <GrindAreas>
      <GrindArea name="FinalBoss">
      <Hotspots>
      <Hotspot Radius="90" XYZ="0, 10.74299, 19.20038" name="Name"/>
      </Hotspots>
      <TargetMobs>
      <TargetMob Id="4347" Weight="1"/> <!-- Left Foreleg -->
      <TargetMob Id="4346" Weight="1"/> <!-- Right Foreleg -->
      <TargetMob Id="3772" Weight="1"/> <!-- The Manipulator -->
      </TargetMobs>
      </GrindArea>
      </GrindAreas>
      <CodeChunks>
      </CodeChunks>
      </Profile>​
      <Profile>:这是整个XML文件的根元素,标志着整个文档的开始。

      <Name>Alexander - The Burden of the Father</Name>:定义了该Profile的名称。

      <BehaviorDirectory></BehaviorDirectory>:定义了行为文件的目录,留空则使用默认目录。

      <Order>:定义了顺序控制块,包含一些控制程序执行顺序的指令。

      <While condition="not HasAtLeast(13587,30)">:使用While循环指令,只要条件成立,就一直执行循环内 部的代码块。该条件的意思是,如果物品ID为13587的物品数量小于30,则条件成立。

      <If condition="not DutyManager.InInstance">:使用If条件语句指令,根据条件是否成立来决定执行哪 些指令。该条件的意思是,如果当前不在副本内,则条件成立。

      <LLJoinDuty DutyId="115"/>:这是一个自定义指令,用于加入指定ID的任务或副本。这里指定的DutyId为115。

      </If>:结束If条件语句的块。

      <If condition="DutyManager.InInstance">:使用If条件语句指令,根据条 件是否成立来决定执行哪些指令。该条件的意思是,如果当前在副本内,则条件成立。

      <If Condition="GetInstanceTodo(0) == 0">:使用If条件语句指令,根据条件是否成立来决定执行哪些指令。GetInstanceTod o函数 用于获取当前副本的进度,并且0代表第一个目标,即Defeat the Manipulator 0/1。该条件的意思是,如果该目标的进度为0,则条件成立。

      <Log Message="Defeat the Manipulator 0/1" />:这是一个自定义指令,用于在日志中记录一条消息。

      <Grind GrindRef="FinalBoss" while="not GetInstanceTodo(0) == 1"/>:使用Grind指令,进行击杀怪物的行为。GrindRef属性指定了所使用的GrindArea 的名 称,while属性指定了当GetInstanceTodo(0)返回值为1时停止击杀的条件,即Defe at the Manipulator 1/1。

      <If Condition="&openchest; == 1">:使用If条件语句指令,根据条件是否成立来决定执行哪些指令。&openchest;是一个 自定义 变量,表示是否需要打开宝箱。如果该变量为1,则条件成立。

      <MoveTo XYZ="0.2756959, 10.60451, -6.587952" />:这是一个自定义指令,用于移动到指定的坐标。

      <LLOpenChest/>:这是一个自定义指令,用于打开宝箱。

      </If>:结束If条件语句的块。

      <While condition="DutyManager.InInstance">:使用While循环指令,只要 条件成立
      • <Grind GrindRef="FinalBoss" while="not GetInstanceTodo(0) == 1"/>:当未完成任务目标“打败操纵者(Defeat the Manipulator)”时,就进入“FinalBoss”区域杀怪,该区域包含一个半径为90的热点( Hotspot),并且要击杀3种怪物:左前腿(ID为4347)、右前腿(ID为4346)和操纵者(I D为3772)。GrindRef参数告诉脚本使用哪个GrindArea,这里是“FinalBoss” 。
      • <If Condition="&openchest; == 1">:如果打败了操纵者并且获得了宝箱,就执行下面的代码。&openchest;是一个特殊的变 量,如 果它的值为1,则表示已经打开了宝箱。
      • <MoveTo XYZ="0.2756959, 10.60451, -6.587952" />:移动到指定坐标,这里是宝箱的位置。
      • <LLOpenChest/>:打开宝箱。
      • <While condition="DutyManager.InInstance">:如果正在进行实例副本,就一直 执行下面的代码块。
      • <WaitTimer WaitTime="5" />:等待5秒钟。
      • <LLLeaveDuty/>:离开实例副本。

      最后的 </Profile> 表示 Profile 的结束。这个 Profile 的目的是通过杀死 FinalBoss 区域中的怪物来完成 Alexander - The Burden of the Father 副本任务,并打开获得的宝箱。
      本网站所收集的部分公开资料来源于互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其 观点和对其真实性负责,也不构成任何其他建议。
      本站部分作品是由网友自主投稿和发布、编辑整理上传,对此类作品本站仅提供交流平台,不为其版权负责。如果 您发现网站上有侵犯您的知识产权的作品,请与我们取得联系,我们会及时修改或删除。
      本网站所提供的信息,只供参考之用。本网站不保证信息的准确性、有效性、及时性和完整性。
      本网站及其雇员一概毋须以任何方式就任何信息传递或传送的失误、不准确或错误,对用户或任何其他人士负任何 直接或间接责任。
      在法律允许的范围内,本网站在此声明,不承担用户或任何人士就使用或未能使用本网站所提供的信息或任何链接 所引致的任何直接、间接、附带、从属、特殊、惩罚性或惩戒性的损害赔偿。

      评论

      • moon
        初级会员
        • 10.29.22
        • 4

        #4
        谢谢你的分享,再根据以上案例我学到了很多新的知识,
        但是现在有一个问题我不能解决,
        “如何在特定的一个副本聚集区域内所有怪物再进行战斗,而不是遇到怪物立刻原地杀死怪物以后再继续前进。” 如果我需要对一个现有的脚本进行这样的调整,请问我需要如何调整代码。在尝试了多次调整代码但依旧未能成功 ,希望获得帮助。

        评论

        • dalong988314
          初级会员
          • 12.13.24
          • 3

          #5
          这个指令怎么用,想查看NPC的 ID,创建一下XML文档复制进去没有用,序列读不了
          Name: Detailed Target Information (Target Something!)
          --------------------------------------------------
          ClearLog();
          GameObject s = GameObjectManager.GetObjectByNPCId(Core.Target.Npc Id);
          Log("Can Attack - " + s.CanAttack.ToString());
          Log("Combat Reach - " + s.CombatReach.ToString());
          Log("Current Health Percent - " + s.CurrentHealthPercent.ToString());
          Log("English Name - " + s.EnglishName.ToString());
          Log("Fate ID - " + s.FateId.ToString());
          Log("Heading - " + s.Heading.ToString());
          Log("Id Location - " + s.IdLocation.ToString());
          Log("Is Behind - " + s.IsBehind.ToString());
          Log("Is Fate Gone - " + s.IsFateGone.ToString());
          Log("Is Flanking - " + s.IsFlanking.ToString());
          Log("Is Me - " + s.IsMe.ToString());
          Log("Is Targetable - " + s.IsTargetable.ToString());
          Log("Is Valid - " + s.IsValid.ToString());
          Log("Is Visble - " + s.IsVisible.ToString());
          Log("Location - " + s.Location.ToString());
          Log("LUA String - " + s.LuaString.ToString());
          Log("Max Health - " + s.MaxHealth.ToString());
          Log("Name - " + s.Name.ToString());
          Log("NPC ID - " + s.NpcId.ToString());
          Log("Object Id - " + s.ObjectId.ToString());
          Log("Type - " + s.Type.ToString());

          评论

          工作中...