Binding correctly to NPC
This commit is contained in:
		
							parent
							
								
									25163296c5
								
							
						
					
					
						commit
						afcc468803
					
				
					 4 changed files with 262 additions and 19 deletions
				
			
		
							
								
								
									
										38
									
								
								TestingAI.d
									
										
									
									
									
								
							
							
						
						
									
										38
									
								
								TestingAI.d
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -5,18 +5,42 @@ import std.stdio;
 | 
			
		|||
import std.socket;
 | 
			
		||||
import std.conv;
 | 
			
		||||
import std.utf;
 | 
			
		||||
import std.string;
 | 
			
		||||
 | 
			
		||||
string receiveWait(Socket sock){
 | 
			
		||||
	bool blocking = sock.blocking;
 | 
			
		||||
 | 
			
		||||
	sock.blocking = false;
 | 
			
		||||
	ubyte[] rawData;
 | 
			
		||||
	ubyte[256] buf;
 | 
			
		||||
	int recv;
 | 
			
		||||
	while((recv = sock.receive(buf))>=0 || rawData.length==0){
 | 
			
		||||
		if(recv>0){
 | 
			
		||||
			rawData ~= buf[0..recv];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Thread.sleep(dur!"msecs"(10));
 | 
			
		||||
	}
 | 
			
		||||
	sock.blocking = blocking;
 | 
			
		||||
	return (cast(char*)rawData.ptr).fromStringz.to!string;
 | 
			
		||||
}
 | 
			
		||||
void sendWait(Socket sock, string data){
 | 
			
		||||
	bool blocking = sock.blocking;
 | 
			
		||||
 | 
			
		||||
	sock.blocking = true;
 | 
			
		||||
	sock.send(data.toUTF8);
 | 
			
		||||
	sock.blocking = blocking;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
	writeln("Start");
 | 
			
		||||
	auto sock = new TcpSocket;
 | 
			
		||||
	sock.blocking = 1;
 | 
			
		||||
	sock.blocking = true;
 | 
			
		||||
	sock.connect(new InternetAddress("127.0.0.1", 8080));
 | 
			
		||||
	writeln("Connected");
 | 
			
		||||
 | 
			
		||||
	sock.send(`{"action":"bind","target":"testnpc"}`.toUTF8);
 | 
			
		||||
	writeln("Sent");
 | 
			
		||||
	Thread.sleep(dur!"seconds"(1));
 | 
			
		||||
	sock.send(`{"action":"getenv","target":"testnpc"}`.toUTF8);
 | 
			
		||||
	writeln("Sent2");
 | 
			
		||||
	Thread.sleep(dur!"seconds"(1));
 | 
			
		||||
	sock.sendWait(`{"action":"bind","target":"testnpc"}`);
 | 
			
		||||
	writeln(sock.receiveWait);
 | 
			
		||||
 | 
			
		||||
	sock.shutdown(SocketShutdown.BOTH);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										193
									
								
								nwn2ai_onmoduleload/Agent.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								nwn2ai_onmoduleload/Agent.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,193 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
using CLRScriptFramework;
 | 
			
		||||
using NWScript;
 | 
			
		||||
using NWScript.ManagedInterfaceLayer.NWScriptManagedInterface;
 | 
			
		||||
 | 
			
		||||
using NWEffect = NWScript.NWScriptEngineStructure0;
 | 
			
		||||
using NWEvent = NWScript.NWScriptEngineStructure1;
 | 
			
		||||
using NWLocation = NWScript.NWScriptEngineStructure2;
 | 
			
		||||
using NWTalent = NWScript.NWScriptEngineStructure3;
 | 
			
		||||
using NWItemProperty = NWScript.NWScriptEngineStructure4;
 | 
			
		||||
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace CLRScript
 | 
			
		||||
{
 | 
			
		||||
    class Agent : CLRScriptBase{
 | 
			
		||||
	    public uint objref;
 | 
			
		||||
 | 
			
		||||
        public Agent(uint obj){
 | 
			
		||||
            objref = obj;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
	    public float facing{
 | 
			
		||||
		    get{
 | 
			
		||||
			    return _facing;
 | 
			
		||||
		    }
 | 
			
		||||
		    set{
 | 
			
		||||
			    _facing = value;
 | 
			
		||||
			    AssignCommand(objref, ()=>{SetFacing(_facing, TRUE);});
 | 
			
		||||
		    }
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    public NWLocation loc{
 | 
			
		||||
		    get{
 | 
			
		||||
			    return _loc;
 | 
			
		||||
		    }
 | 
			
		||||
		    set{
 | 
			
		||||
			    _loc = value;
 | 
			
		||||
			    AssignCommand(objref, ()=>{ActionForceMoveToLocation(_loc, TRUE, 6f);});
 | 
			
		||||
 | 
			
		||||
			    //Link effect
 | 
			
		||||
			    RemoveSEFFromObject(objref, "nwai_beam_loc");
 | 
			
		||||
			    ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectNWN2SpecialEffectFile("nwai_beam_loc", OBJECT_INVALID, GetPositionFromLocation(_loc)), objref, -1f);
 | 
			
		||||
		    }
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    //Detection
 | 
			
		||||
	    //=======================================================================
 | 
			
		||||
 | 
			
		||||
	    public bool hasWallFront(float distance=10f){
 | 
			
		||||
		    Vector3 pointFront = Add(GetPositionFromLocation(loc), Multiply(AngleToVector(facing), distance));
 | 
			
		||||
		    return LineOfSightVector(GetPosition(objref), pointFront)==1;
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    public bool isInCombat(){
 | 
			
		||||
		    return GetIsInCombat(objref)==1;
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    //Json containing
 | 
			
		||||
	    // {"valid":"true", "id":"366642", "name":"Goblin", "distance":"13.37", "difficulty":"13", "hp":"12"}
 | 
			
		||||
	    public dynamic getNearestEnemy(float distanceMax=15f){
 | 
			
		||||
		    uint nearestEnemy = GetNearestCreature(
 | 
			
		||||
			    CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
 | 
			
		||||
			    objref, 1,
 | 
			
		||||
			    CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN_AND_HEARD,
 | 
			
		||||
			    CREATURE_TYPE_IS_ALIVE, CREATURE_ALIVE_TRUE);
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		    float dist = GetDistanceBetween(nearestEnemy, objref);
 | 
			
		||||
		    return JsonConvert.DeserializeObject<dynamic>("{"
 | 
			
		||||
			    + "\"valid\":\""+(GetIsObjectValid(nearestEnemy)==TRUE && dist<=distanceMax).ToString()+"\","
 | 
			
		||||
			    + "\"id\":\""+(nearestEnemy).ToString()+"\","
 | 
			
		||||
			    + "\"name\":\""+GetName(nearestEnemy)+"\""
 | 
			
		||||
			    + "\"distance\":\""+(dist).ToString()+"\","
 | 
			
		||||
			    + "\"difficulty\":\""+GetChallengeRating(nearestEnemy)+"\""
 | 
			
		||||
			    + "\"hp\":\""+GetCurrentHitPoints(nearestEnemy)+"\""
 | 
			
		||||
			    + "}");
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    //Json containing
 | 
			
		||||
	    // {"valid":"true", "id":"366642", "name":"Guethenoc", "distance":"13.37"}
 | 
			
		||||
	    public dynamic getNearestPlayer(float distanceMax=15f){
 | 
			
		||||
		    uint pc = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, objref, 1, -1,-1,-1,-1);
 | 
			
		||||
 | 
			
		||||
		    float dist = GetDistanceBetween(pc, objref);
 | 
			
		||||
		    return JsonConvert.DeserializeObject<dynamic>("{"
 | 
			
		||||
			    + "\"valid\":\""+(GetIsObjectValid(pc)==TRUE && dist<=distanceMax).ToString()+"\","
 | 
			
		||||
			    + "\"id\":\""+(pc).ToString()+"\","
 | 
			
		||||
			    + "\"name\":\""+GetName(pc)+"\""
 | 
			
		||||
			    + "\"distance\":\""+(dist).ToString()+"\","
 | 
			
		||||
			    + "}");
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    //Actions
 | 
			
		||||
	    //=======================================================================
 | 
			
		||||
 | 
			
		||||
	    public bool moveForward(float distance=10f){
 | 
			
		||||
		    if(hasWallFront(distance))
 | 
			
		||||
			    return false;
 | 
			
		||||
		
 | 
			
		||||
		    //See NWN2DataLib.AreaSurfaceMesh.PathExists
 | 
			
		||||
 | 
			
		||||
		    Vector3 pointFront = Add(GetPositionFromLocation(loc), Multiply(AngleToVector(facing), distance));
 | 
			
		||||
		    loc = Location(GetAreaFromLocation(loc), pointFront, facing);
 | 
			
		||||
		    return true;
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    public bool attackNearest(float distanceMax=15f){
 | 
			
		||||
		    var nearest = getNearestEnemy(distanceMax);
 | 
			
		||||
		    if(!nearest.valid)
 | 
			
		||||
			    return false;
 | 
			
		||||
 | 
			
		||||
		    AssignCommand(objref, ()=>{ActionAttack(nearest.id, FALSE);});
 | 
			
		||||
		    return true;
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    public bool turn(float angleDeg){
 | 
			
		||||
		    facing+=angleDeg;
 | 
			
		||||
		    return true;
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    public bool say(string message){
 | 
			
		||||
		    AssignCommand(objref, ()=>{SpeakString(message, TALKVOLUME_TALK);});
 | 
			
		||||
		
 | 
			
		||||
		    return getNearestPlayer().valid;
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    public string callCommand(string command)
 | 
			
		||||
	    {
 | 
			
		||||
		    Regex regexMethod = new Regex(@"([a-zA-Z_]+?)\s*\((.*?)\)");
 | 
			
		||||
		    Regex regexParam = new Regex("(\".*?\"|[0-9\\.]+)");
 | 
			
		||||
 | 
			
		||||
		    string methodName;
 | 
			
		||||
		    List<string> unparsedParams = new List<string>();
 | 
			
		||||
 | 
			
		||||
		    MatchCollection matches = regexMethod.Matches(command);
 | 
			
		||||
		    if(matches.Count == 1){
 | 
			
		||||
			    //Match name
 | 
			
		||||
			    methodName = matches[0].Groups[1].ToString();
 | 
			
		||||
 | 
			
		||||
			    //March parameters
 | 
			
		||||
			    string methodParamList = matches[0].Groups[2].ToString();
 | 
			
		||||
			    MatchCollection paramMatches = regexParam.Matches(methodParamList);
 | 
			
		||||
			    foreach(var p in paramMatches){
 | 
			
		||||
				    unparsedParams.Add(p.ToString().Trim('"'));
 | 
			
		||||
			    }
 | 
			
		||||
		    }
 | 
			
		||||
		    else{
 | 
			
		||||
			    //Failed to parse command
 | 
			
		||||
			    return "";
 | 
			
		||||
		    }
 | 
			
		||||
 | 
			
		||||
		    MethodInfo method = this.GetType().GetMethod(methodName);
 | 
			
		||||
		    ParameterInfo[] methodParams = method.GetParameters();
 | 
			
		||||
 | 
			
		||||
		    if(methodParams.Length != unparsedParams.Count){
 | 
			
		||||
			    //Incorrect number of parameters
 | 
			
		||||
			    return "";
 | 
			
		||||
		    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		    List<Object> parsedParams = new List<Object>();
 | 
			
		||||
		    for(int i=0 ; i<methodParams.Length ; i++){
 | 
			
		||||
			    //Handle defaults?
 | 
			
		||||
			    var typeConverter = TypeDescriptor.GetConverter(methodParams[i].ParameterType);
 | 
			
		||||
			    parsedParams.Add(typeConverter.ConvertFromString(unparsedParams[i]));
 | 
			
		||||
		    }
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		    return method.Invoke(this, parsedParams.ToArray()).ToString();
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	    private float _facing;
 | 
			
		||||
	    private NWLocation _loc;
 | 
			
		||||
 | 
			
		||||
        private Vector3 Multiply(Vector3 v, float f) {
 | 
			
		||||
            v.x*=f; v.y*=f; v.z*=f;
 | 
			
		||||
            return v;
 | 
			
		||||
        }
 | 
			
		||||
        private Vector3 Add(Vector3 v, Vector3 w) {
 | 
			
		||||
            v.x*=w.x; v.y*=w.y; v.z*=w.z;
 | 
			
		||||
            return v;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -74,23 +74,44 @@ namespace CLRScript
 | 
			
		|||
            DelayCommand(0.5f, Heartbeat);
 | 
			
		||||
 | 
			
		||||
            //Process queued requests
 | 
			
		||||
            Debug("Proc cmds");
 | 
			
		||||
            //Debug("Proc cmds");
 | 
			
		||||
            
 | 
			
		||||
            while(sockBufferIn.Count>0){
 | 
			
		||||
                Tuple<string, Socket> req = sockBufferIn.Dequeue();
 | 
			
		||||
                var socket = req.Item2;
 | 
			
		||||
 | 
			
		||||
                var json = JsonConvert.DeserializeObject<dynamic>(req.Item1);
 | 
			
		||||
                //TODO: check if json correctly parsed
 | 
			
		||||
                switch ((string)json.action) {
 | 
			
		||||
                    case "bind": {
 | 
			
		||||
                        Debug("Bind request to '"+json.target+"'");
 | 
			
		||||
                        if (!sockClients.ContainsKey(json.target)) {
 | 
			
		||||
                            sockClients.Add(json.target, req.Item2);
 | 
			
		||||
                            //Reply ok
 | 
			
		||||
 | 
			
		||||
                        try {
 | 
			
		||||
                            if (!sockClients.ContainsKey((string)json.target)) {
 | 
			
		||||
 | 
			
		||||
                                sockClients.Add((string)json.target, socket);
 | 
			
		||||
                                socket.Send(Encoding.UTF8.GetBytes(
 | 
			
		||||
                                    "{\"action\":\"bindack\", \"result\":\"ok\"}"
 | 
			
		||||
                                ));
 | 
			
		||||
 | 
			
		||||
                                //handle env
 | 
			
		||||
                            }
 | 
			
		||||
                            else {
 | 
			
		||||
                                socket.Send(Encoding.UTF8.GetBytes(
 | 
			
		||||
                                    "{\"action\":\"bindack\", \"result\":\"failed\", \"message\":\"Socket already binded with this NPC\"}"
 | 
			
		||||
                                ));
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else {
 | 
			
		||||
                            //Reply Already binded
 | 
			
		||||
                        catch (Exception e) {
 | 
			
		||||
                            socket.Send(Encoding.UTF8.GetBytes(
 | 
			
		||||
                                "{\"action\":\"bindack\", \"result\":\"failed\", \"message\":\"Thrown exception: "+e.ToString().Replace("\"","\\\"")+"\"}"
 | 
			
		||||
                            ));
 | 
			
		||||
                        }
 | 
			
		||||
                        }break;
 | 
			
		||||
                    }break;
 | 
			
		||||
                    case "decision": {
 | 
			
		||||
                        
 | 
			
		||||
 | 
			
		||||
                    }break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -114,20 +135,24 @@ namespace CLRScript
 | 
			
		|||
            sockClient.Blocking = true;
 | 
			
		||||
 | 
			
		||||
            while (sockClient.Connected) {
 | 
			
		||||
                byte[] rawData = new byte[sockClient.Available];
 | 
			
		||||
                
 | 
			
		||||
                if (sockClient.Available > 0) {
 | 
			
		||||
                    byte[] rawData = new byte[sockClient.Available];
 | 
			
		||||
                    sockClient.Receive(rawData, rawData.Length, 0);
 | 
			
		||||
 | 
			
		||||
                    string data = Encoding.UTF8.GetString(rawData);
 | 
			
		||||
                    //Debug("Queued "+data);
 | 
			
		||||
                    Debug("Queued "+data);
 | 
			
		||||
                    sockBufferIn.Enqueue(new Tuple<string,Socket>(data, sockClient));
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    Thread.Sleep(100);//TODO: Find another way than sleeping
 | 
			
		||||
                Thread.Sleep(10);
 | 
			
		||||
 | 
			
		||||
                
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //Remove stored bind
 | 
			
		||||
            var sock = sockClients.First(kvp => kvp.Value==sockClient);
 | 
			
		||||
            sockClients.Remove(sock.Key);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Socket sockServer;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,7 @@
 | 
			
		|||
    <Reference Include="System.Xml" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <Compile Include="Agent.cs" />
 | 
			
		||||
    <Compile Include="nwn2ai_onmoduleload.cs" />
 | 
			
		||||
    <Compile Include="..\CLRScriptSDK\NWScriptActions.cs" />
 | 
			
		||||
    <Compile Include="..\CLRScriptSDK\NWScriptConstants.cs" />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue