NWN2-AI-Server/nwn2ai_onmoduleload/Agent.cs

198 lines
6.4 KiB
C#

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
{
using NWScript = nwn2ai_onmoduleload;
class Agent{
public uint objref;
public Agent(uint obj){
objref = obj;
}
public float facing{
get{
return _facing;
}
set{
_facing = value;
NWScript.get.AssignCommand(objref, ()=>{NWScript.get.SetFacing(_facing, 1);});
}
}
public NWLocation loc{
get{
return _loc;
}
set{
_loc = value;
NWScript.get.AssignCommand(objref, ()=>{NWScript.get.ActionForceMoveToLocation(_loc, 1, 6f);});
//Link effect
NWScript.get.RemoveSEFFromObject(objref, "nwai_beam_loc");
NWScript.get.ApplyEffectToObject(
NWScript.DURATION_TYPE_PERMANENT,
NWScript.get.EffectNWN2SpecialEffectFile("nwai_beam_loc", NWScript.OBJECT_INVALID, NWScript.get.GetPositionFromLocation(_loc)),
objref, -1f);
}
}
//Detection
//=======================================================================
public bool hasWallFront(float distance=10f){
Vector3 pointFront = Add(NWScript.get.GetPositionFromLocation(loc), Multiply(NWScript.get.AngleToVector(facing), distance));
return NWScript.get.LineOfSightVector(NWScript.get.GetPosition(objref), pointFront)==1;
}
public bool isInCombat(){
return NWScript.get.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 = NWScript.get.GetNearestCreature(
NWScript.CREATURE_TYPE_REPUTATION, NWScript.REPUTATION_TYPE_ENEMY,
objref, 1,
NWScript.CREATURE_TYPE_PERCEPTION, NWScript.PERCEPTION_SEEN_AND_HEARD,
NWScript.CREATURE_TYPE_IS_ALIVE, NWScript.CREATURE_ALIVE_TRUE);
float dist = NWScript.get.GetDistanceBetween(nearestEnemy, objref);
return JsonConvert.DeserializeObject<dynamic>("{"
+ "\"valid\":\""+(NWScript.get.GetIsObjectValid(nearestEnemy)==1 && dist<=distanceMax).ToString()+"\","
+ "\"id\":\""+(nearestEnemy).ToString()+"\","
+ "\"name\":\""+NWScript.get.GetName(nearestEnemy)+"\""
+ "\"distance\":\""+(dist).ToString()+"\","
+ "\"difficulty\":\""+NWScript.get.GetChallengeRating(nearestEnemy)+"\""
+ "\"hp\":\""+NWScript.get.GetCurrentHitPoints(nearestEnemy)+"\""
+ "}");
}
//Json containing
// {"valid":"true", "id":"366642", "name":"Guethenoc", "distance":"13.37"}
public dynamic getNearestPlayer(float distanceMax=15f){
uint pc = NWScript.get.GetNearestCreature(NWScript.CREATURE_TYPE_PLAYER_CHAR, NWScript.PLAYER_CHAR_IS_PC, objref, 1, -1,-1,-1,-1);
float dist = NWScript.get.GetDistanceBetween(pc, objref);
return JsonConvert.DeserializeObject<dynamic>("{"
+ "\"valid\":\""+(NWScript.get.GetIsObjectValid(pc)==1 && dist<=distanceMax).ToString()+"\","
+ "\"id\":\""+(pc).ToString()+"\","
+ "\"name\":\""+NWScript.get.GetName(pc)+"\""
+ "\"distance\":\""+(dist).ToString()+"\","
+ "}");
}
//Actions
//=======================================================================
public bool moveForward(float distance=10f){
if(hasWallFront(distance))
return false;
//See NWN2DataLib.AreaSurfaceMesh.PathExists
Vector3 pointFront = Add(NWScript.get.GetPositionFromLocation(loc), Multiply(NWScript.get.AngleToVector(facing), distance));
loc = NWScript.get.Location(NWScript.get.GetAreaFromLocation(loc), pointFront, facing);
return true;
}
public bool attackNearest(float distanceMax=15f){
var nearest = getNearestEnemy(distanceMax);
if(!nearest.valid)
return false;
NWScript.get.AssignCommand(objref, ()=>{NWScript.get.ActionAttack(nearest.id, 0);});
return true;
}
public bool turn(float angleDeg){
facing+=angleDeg;
return true;
}
public bool say(string message){
NWScript.get.AssignCommand(objref, ()=>{NWScript.get.SpeakString(message, NWScript.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;
}
}
}