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.socket;
|
||||||
import std.conv;
|
import std.conv;
|
||||||
import std.utf;
|
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() {
|
void main() {
|
||||||
writeln("Start");
|
writeln("Start");
|
||||||
auto sock = new TcpSocket;
|
auto sock = new TcpSocket;
|
||||||
sock.blocking = 1;
|
sock.blocking = true;
|
||||||
sock.connect(new InternetAddress("127.0.0.1", 8080));
|
sock.connect(new InternetAddress("127.0.0.1", 8080));
|
||||||
writeln("Connected");
|
writeln("Connected");
|
||||||
|
|
||||||
sock.send(`{"action":"bind","target":"testnpc"}`.toUTF8);
|
sock.sendWait(`{"action":"bind","target":"testnpc"}`);
|
||||||
writeln("Sent");
|
writeln(sock.receiveWait);
|
||||||
Thread.sleep(dur!"seconds"(1));
|
|
||||||
sock.send(`{"action":"getenv","target":"testnpc"}`.toUTF8);
|
sock.shutdown(SocketShutdown.BOTH);
|
||||||
writeln("Sent2");
|
|
||||||
Thread.sleep(dur!"seconds"(1));
|
|
||||||
}
|
}
|
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);
|
DelayCommand(0.5f, Heartbeat);
|
||||||
|
|
||||||
//Process queued requests
|
//Process queued requests
|
||||||
Debug("Proc cmds");
|
//Debug("Proc cmds");
|
||||||
|
|
||||||
while(sockBufferIn.Count>0){
|
while(sockBufferIn.Count>0){
|
||||||
Tuple<string, Socket> req = sockBufferIn.Dequeue();
|
Tuple<string, Socket> req = sockBufferIn.Dequeue();
|
||||||
|
var socket = req.Item2;
|
||||||
|
|
||||||
var json = JsonConvert.DeserializeObject<dynamic>(req.Item1);
|
var json = JsonConvert.DeserializeObject<dynamic>(req.Item1);
|
||||||
|
//TODO: check if json correctly parsed
|
||||||
switch ((string)json.action) {
|
switch ((string)json.action) {
|
||||||
case "bind": {
|
case "bind": {
|
||||||
Debug("Bind request to '"+json.target+"'");
|
Debug("Bind request to '"+json.target+"'");
|
||||||
if (!sockClients.ContainsKey(json.target)) {
|
|
||||||
sockClients.Add(json.target, req.Item2);
|
try {
|
||||||
//Reply ok
|
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 {
|
catch (Exception e) {
|
||||||
//Reply Already binded
|
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;
|
sockClient.Blocking = true;
|
||||||
|
|
||||||
while (sockClient.Connected) {
|
while (sockClient.Connected) {
|
||||||
byte[] rawData = new byte[sockClient.Available];
|
|
||||||
if (sockClient.Available > 0) {
|
if (sockClient.Available > 0) {
|
||||||
|
byte[] rawData = new byte[sockClient.Available];
|
||||||
sockClient.Receive(rawData, rawData.Length, 0);
|
sockClient.Receive(rawData, rawData.Length, 0);
|
||||||
|
|
||||||
string data = Encoding.UTF8.GetString(rawData);
|
string data = Encoding.UTF8.GetString(rawData);
|
||||||
//Debug("Queued "+data);
|
Debug("Queued "+data);
|
||||||
sockBufferIn.Enqueue(new Tuple<string,Socket>(data, sockClient));
|
sockBufferIn.Enqueue(new Tuple<string,Socket>(data, sockClient));
|
||||||
}
|
}
|
||||||
else
|
Thread.Sleep(10);
|
||||||
Thread.Sleep(100);//TODO: Find another way than sleeping
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Remove stored bind
|
||||||
|
var sock = sockClients.First(kvp => kvp.Value==sockClient);
|
||||||
|
sockClients.Remove(sock.Key);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket sockServer;
|
Socket sockServer;
|
||||||
Queue<Tuple<string,Socket>> sockBufferIn;//Request & client
|
Queue<Tuple<string,Socket>> sockBufferIn;//Request & client
|
||||||
|
|
||||||
Dictionary<string, Socket> sockClients;//PNJ tag => Client socket
|
Dictionary<string, Socket> sockClients;//PNJ tag => Client socket
|
||||||
|
|
||||||
private void Debug(string msg){
|
private void Debug(string msg){
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Agent.cs" />
|
||||||
<Compile Include="nwn2ai_onmoduleload.cs" />
|
<Compile Include="nwn2ai_onmoduleload.cs" />
|
||||||
<Compile Include="..\CLRScriptSDK\NWScriptActions.cs" />
|
<Compile Include="..\CLRScriptSDK\NWScriptActions.cs" />
|
||||||
<Compile Include="..\CLRScriptSDK\NWScriptConstants.cs" />
|
<Compile Include="..\CLRScriptSDK\NWScriptConstants.cs" />
|
||||||
|
Loading…
Reference in New Issue
Block a user