Binding correctly to NPC
This commit is contained in:
parent
25163296c5
commit
afcc468803
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,25 +135,29 @@ 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;
|
||||
Queue<Tuple<string,Socket>> sockBufferIn;//Request & client
|
||||
|
||||
|
||||
Dictionary<string, Socket> sockClients;//PNJ tag => Client socket
|
||||
|
||||
private void Debug(string msg){
|
||||
|
@ -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…
Reference in New Issue
Block a user