package org.luawars.LuaJScripting; import org.luaj.vm2.*; import org.luaj.vm2.ast.Chunk; import org.luaj.vm2.lib.OneArgFunction; import org.luaj.vm2.lib.TwoArgFunction; import org.luaj.vm2.lib.ZeroArgFunction; import org.luaj.vm2.lib.jse.JsePlatform; import org.luaj.vm2.parser.LuaParser; import org.luaj.vm2.parser.ParseException; import org.luawars.Log; import rts.Launch; import rts.core.engine.Player; import rts.core.engine.PlayerInput; import rts.core.engine.ingamegui.GuiButton; import rts.core.engine.ingamegui.GuiMenu; import rts.core.engine.ingamegui.GuiPanel; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.HashMap; /** * Created with IntelliJ IDEA. * User: Trung * Date: 3/23/13 * Time: 8:43 PM * To change this template use File | Settings | File Templates. * * This class is an interface between Lua and Java. The code in this class mainly interacts * with static global variables in the me().global.java file. * * @TODO * attack/target * being attacked * setting up base * set priorities * timing * selecting specific units * * IMPORTANT STUFF TO NOTE: * - If you want to access pretty much anything in the game, I made a static variable, g, in Launch.java. * With this you can access pretty much anything in the game, e.g. the engine, gui stuff, anything else. * Just do something like: * Launch.g.getEngine().whatever else * * - Also, if you want to create a new function, create a class (you'll see plenty of examples in here) * And add it to the CallLua.call() (just copy previous examples in the function). * Then you can call the function in your lua script. * * - I've been testing everything in GuiInGame.java in the keyPressed() function. You can turn it on by pressing 'y' * and submit the command by pressing enter. */ public class CallLua { public static Globals G = JsePlatform.standardGlobals(); private static HashMap<Integer, CallLua> intToCallLua = new HashMap<Integer, CallLua>(); GuiMenu menu; PlayerInput input; Player player; LuaJGlobal global; public CallLua(GuiMenu menu, PlayerInput input) { this.menu = menu; this.input = input; global = new LuaJGlobal(menu, input, null); // player is uninitialzed right now } public void reset(Player player) { menu.clear(); this.player = player; // initialize lua j globals //global.init(); runScript("resources/Lua Scripts/myScript.lua"); } /** * initializes the lua global variable LUA_PATH which determines where Lua should look to open up Lua scripts * @param path - path to set LUA_PATH to */ public static void initLuaPath(String path) { CallLua.G.package_.setLuaPath(path); Log.debug("LUA_PATH set to {}", G.package_.path); } private static CallLua me() { // Log.debug(Log.me() + " fetching: " + G.get("cuid").toint()); return intToCallLua.get(G.get("cuid").toint()); } /** * General method to run a Lua script. * Note: run script must be called before you can call callFunction. * I'm not exactly sure why. I think it is because Lua puts all the functions' addresses on a table by * calling runScript, and once the functions are on a table, calling a function will make Lua * look at the table to see what address to go to * * @param scriptFileName - script to run * @return */ public void runScript(String scriptFileName) { Log.trace("running script {}", scriptFileName); try { // to see how to use lua parser look at this //https://github.com/headius/luaj/blob/master/README.html // scroll down to parser section //System.out.println("Calling " + folderPath + scriptFileName); LuaParser parser = new LuaParser(new FileInputStream(scriptFileName)); parser.Chunk(); int hash = System.identityHashCode(this); G.set("cuid", hash); intToCallLua.put(hash, this); G.loadFile(scriptFileName).call(); // if we want our game to put anything, then put error message displays here } catch(FileNotFoundException e) { Log.error("FILE NOT FOUND: " + scriptFileName); } catch(ParseException e) { Log.error("PARSE FAILED: " + e); } catch(LuaError e) { Log.error("LUA ERROR: " + e); } } /** * After the script has been run, you can call functions using this method. * Note that runScript must be called before you can use this. * IF YOU'RE READING THIS SOURCE CODE, YOU DON'T REALLY NEED THESE TO WRITE YOUR LUA SCRIPTS. * THESE FUNCTIONS ARE ONLY IF YOU WANT TO CALL FUNCTIONS FROM LUA CODE IN YOUR JAVA CODE. * @param functionName * @param arg * @return */ public static LuaValue callFunction(String functionName, LuaValue arg) { Log.trace("Calling function {} with argument {}", functionName, arg); return G.get(functionName).call(arg); } public static LuaValue callFunction(String functionName, LuaValue arg1, LuaValue arg2) { LuaValue[] args = {arg1, arg2}; Log.trace("Calling function {} with arguments {}", functionName, args); return G.get(functionName).call(arg1, arg2); } public static LuaValue callFunction(String functionName, LuaValue arg1, LuaValue arg2, LuaValue arg3) { LuaValue[] args = {arg1, arg2, arg3}; Log.trace("Calling function {} with arguments {}", functionName, args); return G.get(functionName).call(arg1, arg2, arg3); } /** * Allows you to call a Lua function in a script without having to call runScript before (although internally, * it is just calling the script before you call the Lua function). * IF YOU'RE READING THIS SOURCE CODE, YOU DON'T REALLY NEED THESE TO WRITE YOUR LUA SCRIPTS. * THESE FUNCTIONS ARE ONLY IF YOU WANT TO CALL FUNCTIONS FROM LUA CODE IN YOUR JAVA CODE. * * @param scriptFileName * @param functionName * @param arg * @return */ public static LuaValue callFunctionFromScript(String scriptFileName, String functionName, LuaValue arg) { String[] function = {scriptFileName, functionName}; Log.trace("Calling script function {} with argument {}", function, arg); G.loadFile(scriptFileName).call(); return G.get(functionName).call(arg); } public static LuaValue callFunctionFromScript(String scriptFileName, String functionName, LuaValue arg1, LuaValue arg2) { String[] function = {scriptFileName, functionName}; LuaValue[] args = {arg1, arg2}; Log.trace("Calling script function {} with arguments {}", function, args); G.loadFile(scriptFileName).call(); return G.get(functionName).call(arg1, arg2); } public static LuaValue callFunctionFromScript(String scriptFileName, String functionName, LuaValue arg1, LuaValue arg2, LuaValue arg3) { String[] function = {scriptFileName, functionName}; LuaValue[] args = {arg1, arg2, arg3}; Log.trace("Calling script function {} with arguments {}", function, args); G.loadFile(scriptFileName).call(); return G.get(functionName).call(arg1, arg2, arg3); } /** * To create a method for Lua to call, we create a class that extends a LuaFunction (e.g. OneArgFunction, * TwoArgFunction, etc). Then we implement the call function. * * NOTE: I ALSO MADE A SevenArgFunction class. You can use this if you need to create a function with more than * 3 arguments instead of using VarArgFunction. */ public static void createUnit(int panelId, int buttonNum) { Log.debug("calling createUnit function with panelId {}, buttonNum {}", panelId, buttonNum); me().menu.getPanels().get(panelId).getButtons().get(buttonNum).launchCreateEntityProcess(); } // NOTE: THIS COULD MESS UP BECAUSE THERE ARE MULTIPLE UNITS THAT HAVE THE NAME BUILDER, // LIKEWISE THERE ARE MULTIPLE UNITS WITH THE NAME SCOUT public static void selectUnits(int tileX, int tileY, float radius, int numUnits, String unitType) { // make it return a list of the selected units // right now selectUnitsAt returns an arraylist of active entities // might need to convert them into a lua list // if unit type is NIL, then make tempUnitType null, // or if the unit name is provided then select that unit String tempUnitType = unitType == null ? null : unitType; me().input.selectUnitsAt(tileX, tileY, radius, numUnits, tempUnitType); } public static void deselectUnits() { CallLua f = me(); if (f == null) Log.error("me() is null!!!!!!!!!!!"); else if (f.input == null) Log.error("f.input == null!!!!"); me().input.deselectUnits(); } // the lua version takes in tile coordinates // however moveOrSpecialAction takes in x, y coordinates (i.e. pixel coordinates) // so we need to convert the tiles to pixel coordinates public static void moveOrSpecialAction(int tileX, int tileY) { me().input.moveOrSpecialAction(tileX * Launch.g.getEngine().getTileW(), tileY * Launch.g.getEngine().getTileH()); } /** * Returns a global variable for the player to use * Also updates all the global variables when called */ public static String getLuaJGlobal(String key) { return me().global.getLuaJGlobal(key); } // right now it places the first building it finds (from the panels) // even if you have both a building (from panel 0) and a wall (from panel 2) to place. // I might need to extend it to use 4 arguments, panel and building, but for now, I'll leave it public static void placeBuilding(int xLoc, int yLoc, int panel, int button) { me().menu.getPanels().get(panel).getButtons().get(button).placeBuilding(xLoc, yLoc); } public static void setUpBase() { me().input.setUpBase(); } public static void drawText(int xCoordinate, int yCoordinate, String text) { Launch.g.getEngine().getContainer().getGraphics().drawString(text, xCoordinate, yCoordinate); } public static void addPriority(LuaValue functionCall, LuaValue parameters, int priority, int index) { if (priority < 0) priority = 0; me().global.AIpriorityQueue.add(new AIGamePriorities(functionCall, parameters, priority, index)); } public static boolean isUnitReady(int panelId, int buttonID) { return me().menu.getPanels().get(panelId).getButtons().get(buttonID).hasProcessReady(); } public static int numPrios() { return me().global.AIpriorityQueue.size(); } public static int removeTopPriority() { int topPriority = 0; if(me().global.AIpriorityQueue.size() > 0) { topPriority = me().global.AIpriorityQueue.peek().index; me().global.AIpriorityQueue.poll(); Log.trace("queue size: " + me().global.AIpriorityQueue.size()); } return topPriority; } public static int getTopPriority() { AIGamePriorities p = me().global.AIpriorityQueue.peek(); return p == null ? 0 : p.index; } public static void clearPriorities() { me().global.AIpriorityQueue.clear(); } }