/*
* LuaHelper.cs
* Authors: August Zinsser
*
* Copyright Matthew Belmonte 2007
*/
using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Reflection;
using System.Security;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Nuclex.Fonts;
using Pina3D.Particles;
using Pina3D;
using LuaInterface;
namespace tAC_Engine
{
#region Helper Classes
//
// Helper classes are based on code found at http://www.gamedev.net/reference/articles/article2275.asp
//
///
/// Flag C# functions with this Attribute derivative to have RegisterLuaCallbacks find it and register it with the LuaVM
///
public class LuaCallback : Attribute
{
private String mFunctionName;
private String[] mFunctionParameters = null;
///
/// Constructor
///
///
///
public LuaCallback(String funcName, params String[] funcParams)
{
mFunctionName = funcName;
mFunctionParameters = funcParams;
}
///
/// Constructor
///
///
public LuaCallback(String funcName)
{
mFunctionName = funcName;
}
///
/// Returns the Lua name of the function
///
///
public String GetFunctionName()
{
return mFunctionName;
}
///
/// Returns the parameters
///
///
public String[] GetFunctionParameters()
{
return mFunctionParameters;
}
}
///
/// Necessary?
///
public class LuaFuncDescriptor
{
private String FunctionName;
private String FunctionDoc;
private ArrayList FunctionParameters;
private ArrayList FunctionParamDocs;
private String FunctionDocString;
public LuaFuncDescriptor(String strFuncName, String strFuncDoc, ArrayList strParams,
ArrayList strParamDocs)
{
FunctionName = strFuncName;
FunctionDoc = strFuncDoc;
FunctionParameters = strParams;
FunctionParamDocs = strParamDocs;
String strFuncHeader = strFuncName + "(%params%) - " + strFuncDoc;
String strFuncBody = "\n\n";
String strFuncParams = "";
Boolean bFirst = true;
for (int i = 0; i < strParams.Count; i++)
{
if (!bFirst)
strFuncParams += ", ";
strFuncParams += strParams[i];
strFuncBody += "\t" + strParams[i] + "\t\t" + strParamDocs[i] + "\n";
bFirst = false;
}
strFuncBody = strFuncBody.Substring(0, strFuncBody.Length - 1);
if (bFirst)
strFuncBody = strFuncBody.Substring(0, strFuncBody.Length - 1);
FunctionDocString = strFuncHeader.Replace("%params%", strFuncParams) + strFuncBody;
}
public String getFuncName()
{
return FunctionName;
}
public String getFuncDoc()
{
return FunctionDoc;
}
public ArrayList getFuncParams()
{
return FunctionParameters;
}
public ArrayList getFuncParamDocs()
{
return FunctionParamDocs;
}
public String getFuncHeader()
{
if (FunctionDocString.IndexOf("\n") == -1)
return FunctionDocString;
return FunctionDocString.Substring(0, FunctionDocString.IndexOf("\n"));
}
public String getFuncFullDoc()
{
return FunctionDocString;
}
}
#endregion
///
/// Provides the links necessary to use C# functions in Lua scripts
///
public static class LuaHelper
{
///
/// An instantiable class whose sole purpose is to register Lua functions when the static class
/// GenericLuaHelper tells it to do so.
///
private class LuaFunctionWrapper
{
///
/// Registers this class's functions
///
public LuaFunctionWrapper()
{
LuaHelper.RegisterLuaCallbacks(this);
}
/*
* Accessors for generic global variables
*/
[LuaCallback("SetShowTimePass")]
public static void SetShowTimePass(bool value) { GenericBaseApplication.GameManager.ShowTimePass = value; }
[LuaCallback("SetShowCursor")]
public static void SetShowCursor(bool value) { HUD.ShowMouseCursor = value; }
[LuaCallback("GetScreenWidth")]
public static int GetScreenWidth()
{
return GenericBaseApplication.GameManager.ScreenWidth;
}
[LuaCallback("GetScreenHeight")]
public static int GetScreenHeight()
{
return GenericBaseApplication.GameManager.ScreenHeight;
}
[LuaCallback("ScreenWidth")]
public static int GetScreenWidth(float percentage)
{
return (int)(percentage * GenericBaseApplication.GameManager.ScreenWidth);
}
[LuaCallback("ScreenHeight")]
public static int GetScreenHeight(float percentage)
{
return (int)(percentage * GenericBaseApplication.GameManager.ScreenHeight);
}
[LuaCallback("SetScreenDimensions")]
public static void SetScreenPhysicaDimensions(float physicalWidth, float physicalHeight, float physicalDistance)
{
GenericBaseApplication.GameManager.ScreenPhysicalWidth = physicalWidth;
GenericBaseApplication.GameManager.ScreenPhysicalHeight = physicalHeight;
GenericBaseApplication.GameManager.ScreenPhysicalDistance = physicalDistance;
}
[LuaCallback("SetMuteAll")]
public static void SetMuteAll(bool value) { SoundManager.MuteAll = value; }
[LuaCallback("SetMuteSFX")]
public static void SetMuteSoundEffects(bool value) { SoundManager.MuteSoundEffects = value; }
[LuaCallback("SetMuteMusic")]
public static void SetMuteMusic(bool value) { SoundManager.MuteMusic = value; }
/*
* Datatype wrappers
*/
[LuaCallback("Color")]
public static Color Color(byte r, byte g, byte b)
{
return new Color(r, g, b);
}
[LuaCallback("Vector2")]
public static Vector2 Vector2(float x, float y)
{
return new Vector2(x, y);
}
[LuaCallback("Vector3")]
public static Vector3 Vector3(float x, float y, float z)
{
return new Vector3(x, y, z);
}
[LuaCallback("Vector4")]
public static Vector4 Vector4(float x, float y, float z, float w)
{
return new Vector4(x, y, z, w);
}
/*
* Generic script functions
*/
///
/// Quits immediately. Skips the shut down procedure so should only be used for debugging purposes.
///
[LuaCallback("Quit")]
public static void Quit()
{
//ScriptManager.Enqueue(new ScriptEvent("Quit", null, 0f));
GenericBaseApplication.GameManager.Exit();
}
///
/// Interprets the given lua file assuming it ends in ".lua"
///
/// The name of the script file with no extension
[LuaCallback("RunScript")]
public static void RunScript(string scriptPath)
{
GenericBaseApplication.GameManager.InterpretLuaFileImmediately(scriptPath);
}
///
/// Returns true if the given script exists (assumes file ends in ".lua")
///
/// /// The name of the script file with no extension
[LuaCallback("ScriptExists")]
public static bool ScriptExists(string scriptPath)
{
return File.Exists(scriptPath) || File.Exists(scriptPath + ".lua");
}
///
/// Creates a new script or erases an existing script (assumes file ends in ".lua")
///
/// /// The name of the script file with no extension
[LuaCallback("CreateScript")]
public static void CreateScript(string scriptPath)
{
File.Create(scriptPath);
}
///
/// Inserts a pause before executing the next line in a Lua file
///
///
[LuaCallback("Wait")]
public static void Wait(float seconds)
{
// Tell the Lua interpreter to sleep
ScriptManager.Sleep(seconds);
}
///
/// Causes the LuaVM to sleep until it recieves the specified code
///
[LuaCallback("WaitFor")]
public static void WaitFor(string code)
{
ScriptManager.Sleep(code);
}
///
/// Tells the Game Manager to stop updating the main game and minigames. Useful for cutscenes and tutorials.
///
[LuaCallback("PauseGames")]
public static void PauseGames()
{
GenericBaseApplication.GameManager.PauseGames = true;
}
///
/// Tells the Game Manager to update the main game and minigames. Cutscenes should always call this sometime after calling PauseGames().
///
[LuaCallback("ResumeGames")]
public static void ResumeGames()
{
GenericBaseApplication.GameManager.PauseGames = false;
}
///
/// Tells the HUD to show a dialog with animated portrait
///
[LuaCallback("ShowAnimatedDialog")]
public static void ShowDialog(string label, string text, Animation animation, string clip, string sound, string position)
{
HUD.ShowDialog(label, text, animation, clip, sound, GetPosition(position), 0, true);
}
///
/// Tells the HUD to show a dialog that persists until the user presses next
///
[LuaCallback("ShowStaticDialog")]
public static void ShowDialog(string label, string text, string imagePath, string soundPath, string position)
{
Texture2D texture = null;
if (imagePath != "" && imagePath != null)
texture = TextureManager.Load(imagePath);
HUD.ShowDialog(label, text, texture, soundPath, GetPosition(position), 0, true);
}
///
/// Tells the HUD to show a dialog that can only disappear through code
///
[LuaCallback("ShowPersistentStaticDialog")]
public static void ShowNonSkippableDialog(string label, string text, string imagePath, string soundPath, string position)
{
Texture2D texture = null;
if (imagePath != "" && imagePath != null)
texture = TextureManager.Load(imagePath);
HUD.ShowDialog(label, text, texture, soundPath, GetPosition(position), 0, false);
}
///
/// Tells the HUD to show a dialog that persists for the specified interval
///
[LuaCallback("ShowTimedStaticDialog")]
public static void ShowDialog(string label, string text, string imagePath, string soundPath, string position, float seconds)
{
Texture2D texture = null;
if (imagePath != "" && imagePath != null)
texture = TextureManager.Load(imagePath);
HUD.ShowDialog(label, text, texture, soundPath, GetPosition(position), seconds, false);
}
///
/// Clears all dialog boxes from the HUD
///
[LuaCallback("ClearDialogs")]
public static void ClearDialogs()
{
HUD.ClearDialogs();
}
///
/// Plays the given sound effect
///
///
[LuaCallback("PlaySoundEffect")]
public static void PlaySoundEffect(string name)
{
SoundManager.PlayEffect(name);
}
///
/// Sets the background music to the track specified
///
///
[LuaCallback("SetMusic")]
public static void SetMusic(string name)
{
SoundManager.SetMusic(name);
}
///
/// Stops the current music track
///
[LuaCallback("StopMusic")]
public static void StopMusic()
{
SoundManager.StopMusic();
}
///
/// Pauses the current music track
///
[LuaCallback("PauseMusic")]
public static void PauseMusic()
{
SoundManager.PauseMusic();
}
///
/// Unpauses the current music track
///
[LuaCallback("ResumeMusic")]
public static void ResumeMusic()
{
SoundManager.ResumeMusic();
}
///
/// "High" "Medium" or "Low"
///
///
[LuaCallback("SetTextureLOD")]
public static void SetTextureLevelOfDetail(string lod)
{
if (lod.ToLower() == "high" || lod.ToLower() == "hi")
TextureManager.LOD = TextureManager.LevelOfDetail.High;
else if (lod.ToLower() == "medium" || lod.ToLower() == "med")
TextureManager.LOD = TextureManager.LevelOfDetail.Medium;
else if (lod.ToLower() == "low" || lod.ToLower() == "lo")
TextureManager.LOD = TextureManager.LevelOfDetail.Low;
else
Debug.Assert(false, "Unreckognized texture level of detail: " + lod + "\nSpecify \"High\" \"Medium\" or \"Low\"");
}
///
/// Clears the heads-up-display
///
[LuaCallback("ClearHUD")]
public static void ClearHUD()
{
HUD.Clear();
}
///
/// Wrapper for Ticker.Display(...)
///
[LuaCallback("DisplayText")]
public static void DisplayText(string text, float offsetFromCenterX, float offsetFromCenterY, float velocityX, float velocityY, string font, byte colorR, byte colorG, byte colorB, byte colorA, float seconds, bool fade)
{
// Get the appropriate font
Ticker.Font tickerFont = Ticker.Font.Standard;
if (font == "Space_Big")
tickerFont = Ticker.Font.Space_Big;
if (font == "Standard_Big")
tickerFont = Ticker.Font.Standard_Big;
// Get the appropriate color
Color color = new Color(colorR, colorG, colorB);
float opacity = (float)colorA / 255f;
Ticker.Display(text, null, Microsoft.Xna.Framework.Vector2.Zero, new Vector2(offsetFromCenterX, offsetFromCenterY), new Vector2(velocityX, velocityY), tickerFont, color, opacity, seconds, fade);
}
///
/// Loads the specified texture at the appropriate resolution (based on user settings)
///
[LuaCallback("LoadTexture")]
public static Texture2D LoadTexture(string texturePath)
{
return TextureManager.Load(texturePath);
}
/////
///// Loads the specified animation at the appropriate resolution (based on user settings)
/////
//[LuaCallback("CreateAnimation")]
//public static Texture2D CreateAnimation(string texturePath, int frames, int frameOffset)
//{
// // TOOD: Change resolution tag as appropriate
// string resTag = "_Hi";
//}
///
/// Creates an entity with the given texture
///
///
[LuaCallback("CreateEntity")]
public static Entity CreateEntity(Texture2D texture)
{
Entity entity = new Entity(texture);
ScriptManager.AddEntity(entity);
return entity;
}
///
/// Removes the Entity from the ScriptManager. Note that the data cannot be garbage collected until the LuaVM lets go of it.
///
///
[LuaCallback("DestroyEntity")]
public static void DestroyEntity(Entity entity)
{
ScriptManager.RemoveEntity(entity);
}
///
/// Removes all entities from the ScriptManager
///
[LuaCallback("FlushEntities")]
public static void FlushEntities()
{
ScriptManager.FlushEntities();
}
///
/// Loads and immediately runs a new particle effect at the specified location
///
///
///
[LuaCallback("LoadParticleEffect")]
public static Emitter LoadParticleEffect(string particleEffectPath, float x, float y, float z)
{
Emitter emitter = Sparx.LoadParticleEffect(particleEffectPath);
emitter.Position3D = new Vector3(x, y, z);
Sparx.AddEmitter(emitter);
return emitter;
}
///
/// Flushes all particles in the particle engine
///
[LuaCallback("FlushParticles")]
public static void FlushParticles()
{
Sparx.Flush();
}
///
/// Loads a ColladaFile from a .dae asset. This data is then passed on to instances of PinaModel.
///
///
///
[LuaCallback("LoadColladaSource")]
public static ColladaFile LoadColladaSource(string assetPath)
{
ColladaFile sourceData = new ColladaFile(assetPath);
return sourceData;
}
///
/// Removes the ColladaModel from the render queue. Note that the data cannot be garbage collected until the LuaVM lets go of it.
///
///
[LuaCallback("DestroyModel")]
public static void DestroyModel(PinaModel model)
{
Pina.RemoveRenderable(model);
}
///
/// Creates an instanced renderable based on the given model and adds it to the RenderQueue
///
[LuaCallback("CreateModel")]
public static PinaModel CreateModel(ColladaFile sourceData)
{
PinaModel model = new PinaModel(new PinaState(Matrix.Identity), sourceData);
Pina.AddRenderable(model);
return model;
}
///
/// Creates a new camera that can be used to look at a Pina scene
///
///
[LuaCallback("CreateCamera")]
public static Camera CreateCamera()
{
Camera camera = new Camera();
return camera;
}
///
/// Sets Pina's current camera to this one
///
///
[LuaCallback("LookThroughCamera")]
public static void LookThroughCamera(Camera camera)
{
Pina.Camera = camera;
}
///
/// Sets Pina's directional light direction
///
[LuaCallback("SetLightDir")]
public static void SetSceneLightDirection(float x, float y, float z)
{
Pina.GlobalLightDirection = new Vector3(x, y, z);
}
///
/// Sets Pina's directional light power
///
[LuaCallback("SetLightPower")]
public static void SetSceneLightPower(float p)
{
Pina.GlobalLightPower = p;
}
///
/// Sets Pina's ambient light power
///
[LuaCallback("SetLightAmbient")]
public static void SetSceneLightAmbientPower(float p)
{
Pina.GlobalLightAmbient = p;
}
///
/// Sets Pina's ambient light color
///
[LuaCallback("SetLightColor")]
public static void SetSceneLightColor(Color color)
{
Pina.GlobalLightColor = color;
}
///
/// Creates a new phase queue or overwrites an existing phase queue for a specific minigame.
/// Invalid data may not be caught until that minigame attempts to use it.
///
/// Name of the minigame
/// The number of parameters for the specific minigame's test constructor (usually 2; phaseName, trials)
/// A comma-seperated list of each phase parameter in order. ie: phaseParams = "PhaseTypeA, 1, PhaseTypeB, 5, PhaseTypeA, 2, ...
[LuaCallback("SetPhaseQueue")]
public static void SetPhaseQueue(string gameName, int phaseParamStride, string phaseParams)
{
// Split the phaseParams into an array
string[] paramArray = phaseParams.Split(',');
// Generate a new phase queue
GenericPhaseQueue phaseQueue = new GenericPhaseQueue();
// Do some basic validation on the phase queue
if (paramArray.Length < phaseParamStride)
GenericBaseApplication.GameManager.PrintAndDie("Malformed phase queue in config file for game: " + gameName + ". Phase queue must contain at least 1 test phase.");
if (paramArray.Length % phaseParamStride != 0)
GenericBaseApplication.GameManager.PrintAndDie("Malformed phase queue in config file for game: " + gameName + ". Phase parameters do not agree with phase parameter stride.");
// Generate each test and then add it to the phaseQueue
for (int i = 0; i < paramArray.Length; i += phaseParamStride)
{
// Get name and trials
GenericTest test = new GenericTest(paramArray[i].Trim(), int.Parse(paramArray[i + 1].Trim()), null);
// Get remaining params
for (int j = 2; j < phaseParamStride; j++)
test.SpecificParameters.Add(paramArray[j].Trim());
phaseQueue.Enqueue(test);
}
GenericBaseApplication.GameManager.SetPhaseQueueForMiniGame(gameName, phaseQueue);
}
///
/// Converts the string position to the enum Dialog.Position
///
///
///
private static Dialog.Position GetPosition(string position)
{
if (position.ToLower() == "bottomleft")
return Dialog.Position.BottomLeft;
else if (position.ToLower() == "bottomright")
return Dialog.Position.BottomRight;
else if (position.ToLower() == "topleft")
return Dialog.Position.TopLeft;
else if (position.ToLower() == "topright")
return Dialog.Position.TopRight;
else
throw new Exception("Position Parameter is not a valid Dialog.Position");
}
}
static LuaFunctionWrapper mFuncWrapper; // Instance member with the ability to register its functions
///
/// Registers this class's functions with the Lua VM. Assumes the Lua VM already exists.
///
static LuaHelper()
{
// Since static classes can't register their functions the way the function registration process
// works, declare an instance member who can.
mFuncWrapper = new LuaFunctionWrapper();
}
///
/// Call this from any class to register any of that class's functions that contain a LuaCallback attribute.
/// Based on code found at http://www.gamedev.net/reference/articles/article2275.asp
///
/// A reference to the class to look for LuaCallback annotations
public static void RegisterLuaCallbacks(Object cSharpClass)
{
// Don't check if the LuaVM has been created so an exception gets thrown
// Get the C# type
Type cSharpType = cSharpClass.GetType();
// Search each function reflection for a LuaCallback
foreach (MethodInfo info in cSharpType.GetMethods())
{
foreach (Attribute attribute in Attribute.GetCustomAttributes(info))
{
if (attribute.GetType() == typeof(LuaCallback))
{
// Found one
LuaCallback luaCallback = (LuaCallback)attribute;
// Parse the attribute
string funcName = luaCallback.GetFunctionName();
string[] funcParams = luaCallback.GetFunctionParameters();
// Get the C# parameter info
ParameterInfo[] paramInfo = info.GetParameters();
// Make sure the LuaCallback params match the C# params
if (funcParams != null && (funcParams.Length != paramInfo.Length))
{
System.Diagnostics.Debug.Assert(false, "The function " + funcName + " has an LuaCallback attribute whose parameters do not match the C# function.");
// Ignore it and move on
break;
}
// Register the function with the Lua VM
GenericBaseApplication.GameManager.LuaGameManagerVM.RegisterFunction(funcName, cSharpClass, info);
}
}
}
}
}
}