Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Denzil Quixode

#141
After some fiddling I think I'm halfway there now - but I still have the problem that the PropertyChanged() method on my IEditorComponent gets the name of the property that got changed (and its old value), but I can't seem to see a way to determine which ContentDocument tab the property change was for.

Edit: Another question  :-[ - I can successfully add a tab-specific menu or toolbar by assigning to the MainMenu and ToolbarCommands fields in the ContentDocument, but my IEditorComponent does not seem to receive the event when the items are actually selected. Is there a way to get them?
#142
Excellent ;D Thanks! I've got a good idea of how to call pretty much any of the built in functions now... very useful.

Now I have a Runtime Plugin problem. Is there a good example somewhere of using your own custom "PropertyGridObjectList" for a ContentDocument? All I need at the moment is to be able to pick from a dropdown of options, for each of the ContentDocument tabs spawned by the plugin.
#143
Thanks so much for taking the time to help! It is very gratifying to solve these things so quickly  :D

I have a follow-up question, now...

Is there a way to use engine->GetScriptFunctionAddress() to get hold of struct methods like Character.Say() and Character.Think(), which take a variable number of input parameters (used for formatting values into the string)? I really only want to be able to call them with a single string, and not actually use the string formatting feature at all, but I need to know the number of parameters for GetScriptFunctionAddress ("Character::Say^...?")
#144
OK, it took me a bit of time to twig... engine->GetScriptFunctionAddress("inventory") does indeed return an address, pointing at what looks like an array of 64-bit integers. All I have to do is offset that address by the ID of the inventory item I want to get the InventoryItem* pointer - the addresses I get with this method are consistent with what I get back from the "activeinv" trick I mentioned in the previous post.

(It still feels a little bit fragile, as I am hard-coding this to expect an array of 8-byte records at the "inventory" address and I know that things like this could always change in future versions of AGS.)

Anyway, to go back to the code snippets in the first post, here's is how I would apply it to that:

Code: ags
typedef int (*GETNUMINVENTORYITEMS)();

void IterateInventoryItems() {
	GETNUMINVENTORYITEMS GetNumInventoryItems = (GETNUMINVENTORYITEMS)engine->GetScriptFunctionAddress("Game::get_InventoryItemCount");
	long long* inventory = (long long*)engine->GetScriptFunctionAddress("inventory");
	if (!GetNumInventoryItems) {
		engine->AbortGame("Cannot find Game.InventoryItemCount static property");
	}
	if (!inventory) {
		engine->AbortGame("Cannot find inventory array");
	}
	for (i = 1; i <= GetNumInventoryItems(); i++) {
		void* item = (void*)&inventory[i];
		// ...
	}
}
#145
Aha! That's ideal, if it works - I will experiment and post my findings, in case there's anyone in the future who wants to know the same thing.

(Much better than the hacky way I had just thought of - first set the "activeinv" field of the AGSCharacter struct to the number of each inventory item, and then use Player::get_ActiveInventory to pull out the pointer :P then set the activeinv field back to what it originally was at the end...)
#146
The IAGSEngine interface available to runtime plugins lets you loop over every AGSCharacter* quite easily:

Code: ags
void IterateCharacters() {
	int i;
	for (i = 0; i < engine->GetNumCharacters(); i++) {
		AGSCharacter* c = engine->GetCharacter(i);
		// do something with the AGSCharacter pointer ...
	}
}


However, I would really like to be able to do similar things for other AGS objects than Characters (edit: and also Room Objects). This is as far as I've got for InventoryItem:

Code: ags
typedef int (*GETNUMINVENTORYITEMS)();

void IterateInventoryItems() {
	GETNUMINVENTORYITEMS GetNumInventoryItems = (GETNUMINVENTORYITEMS)engine->GetScriptFunctionAddress("Game::get_InventoryItemCount");
	if (!GetNumInventoryItems) {
		engine->AbortGame("Cannot find Game.InventoryItemCount static property");
	}
	for (i = 0; i < GetNumInventoryItems(); i++) {
		// ... but how to get the pointer for the "i-th" InventoryItem?
	}
}


All I want is the pointer, so I can pass it to other functions and methods that take an InventoryItem. I'm focusing on InventoryItem as an example, but there's other things like Dialogs. Is there any existing way to get the pointers? I don't mind it being a bit weird, as long as it works  :=
#147
Thanks for the support  :) It's still quite early days though. What I really want to do is start making AGS functions available to the Lua scripts (I mean the in-built AGS functions, not the ones you define yourself in the global/room scripts - I don't think there is a way to call those from a plugin) and add objects to the Lua universe that represent AGS characters, inventory items etc. These objects would support the standard methods (for a character: Say, WalkTo, etc.) but also, allow you to add your own custom methods and fields.

For example, eventually you should be able to do this (Lua):

Code: ags
-- get an AGS character object from a special "ags" table managed by the plugin
local cEgo = ags.cEgo

-- add a new custom method to it (note the colon instead of a dot - this is Lua "method" syntax)
function cEgo:Greeting()
	-- "self" inside a Lua method is like "this" in AGS-Script, Javascript etc. - like an invisible parameter
	-- that refers to the parent object (in this case, cEgo, although we could reuse this function for different
	-- characters)
	self:Say("I feel... odd...")
	self:Say("Lua... is... controlling me...")
end


...and then, you would also be able to call these Lua methods from AGS-Script, in a similar way to calling a global function using Lua.Call:

Code: ags

function room_AfterFadeIn()
{
    cEgo.LuaMethod("Greeting", null, null);        // Call cEgo:Greeting() with no parameters or return values
}


(This is speculative, it isn't implemented yet, but I think it should all be possible.)
#148
Quote from: Edmundito on Tue 08/09/2009 01:41:23I have done Lua integration in the past, and there's a couple of things that bugged me overall that are huge "gotchas" when you learn:

1. Counting starts at 1, not 0. Oh my!
I agree, it's a bummer. There's not a lot you can do except get used to it and warn people as quickly as possible. Hopefully it not a deal-breaker for too many people. (Some people try to hack Lua to go from 0, but I'm not going down that route.)
Quote2. Unless you defined a variable local inside a function it will be global, like JavaScript.
Yeah, I was annoyed about this too, but it turns out that making variables local by default is an even worse choice, because of the way Lua does variable-scoping (which is really good, and honestly not worth sacrificing to avoid having to write "local" all over the place, IMHO). Maybe in some future version they will be clever enough to find a solution for it, but it really is a more complicated situation than just a bad design choice.
Quote3. There's no decent debugger implementation that can be plugged in. You have to write your own.

That being said, adding this sort of scripting could be the missing piece for a project that I was working on in AGS earlier... actually, can you distribute a game with open Lua scripts? As in I got my agsgame.exe file somewhere, and I let the player write their own script. Is that possible?
Sure - The standard Lua functions loadstring() and loadfile() are available to compile script strings/files into new functions.
#150
Hey, thanks, SSH! Much appreciated.

(Also, it's funny you should mention puns - Lua was created in Brazil, named after the Portuguese word they use for "moon". Apparently it was originally intended to replace a previous scripting language they were using called "Simple Object Language", or "SOL" - and "sol" is also the Portuguese word for "sun"...)

(...I know way too much about this.  :P)
#151
Haha  ;D. It's true that Grim Fandango was scripted in Lua, and other games like Psychonauts, and I believe companies like Telltale and PopCap use it in the frameworks for their games. It's popular because it's very small and light, very cleanly coded so it compiles on almost anything, very permissively-licensed (you can use it freely in commercial and non-commercial things without paying for it or agreeing to release any of your own code) and very fast (again, for a dynamic scripting language, not compared to C++ or something).

Okay, this is the first in a short series of posts I'm gonna do about some of Lua's features. This is partly an introduction of how to use Lua, and partly explanation of what I think Lua is good at & valuable for. So, if you're interested in learning Lua, don't solely rely on these, as I might be skipping things that you'd like to know in favour of things I think are cool. All feedback is welcome! :)

Edit: Moved to http://lua-for-ags.wikispaces.com/DataDefinition
#152
Edit: This post used to be some out-of-date information about calling Lua functions from AGS.
See this Wiki page for the most up-to-date info on that: http://lua-for-ags.wikispaces.com/LuaValueLists
#153
I have not yet had time to properly benchmark them against each other, but Lua is considered very fast for a dynamically-typed language. It probably is still not fast enough to do real-time graphics effects that need to do lots of pixel manipulation every frame or something, but still, it should be fast enough for you not to be able to tell the difference most of the time.
#154
Modules, Plugins & Tools / PLUGIN: Lua for AGS
Fri 04/09/2009 19:30:22
Edit: I decided to update my original post for the new version rather than trying to have several threads, or post updated versions throughout this one. Sorry if this confuses anyone, it just seemed like the best option.


Hello!

This is my first plugin. It is quite experimental, but I'm fairly pleased with the way it's gone so far. I've developed it against version 3.1.2SP1 (build 3.1.2.82) and haven't had a chance to try it with other versions, so I'm not sure how well it works with them.

Download it here:


The purpose of the plugin is to allow you to write parts of your game's code in Lua, an alternate scripting language to AGS-Script. It is intended for people who are already fairly comfortable with scripting and are interested in trying something a bit different.

The files included in the package are five DLLs - AGS.Plugin.Lua.dll, AGSPlugin.SciLexer.dll, agslua.dll, lua51.dll and lua5.1.dll. Copy all of them into the AGS application home directory. Once you have these DLLs in place and you fire up AGS, there will be a new icon in the project tree called simply "Lua", and also a new entry in "Plugins" called "Lua Run-Time Component". Both of these are disabled by default, and should have no effect until you choose to activate them for a particular game. You need to have both of them active or inactive or you will probably get odd errors. (I'd recommend trying it first on a demo game rather than something important, just in case.)


To create your first Lua script, expand the "Lua" icon, right-click on the "Lua Scripts" icon inside it and select "New Lua script...". Choose a suitable name for it in the window that pops up, and click "Create".


Okay, you will now have to enter some Lua code. How to write Lua itself is not something I'll go into too deeply just yet, but to get you started:

  • Lua has strings, numbers and boolean values, just like in AGS-Script. (However, there is only one "kind" of number, rather than int, short, float etc.)
  • You do not need to declare the type of your variables - i.e. just 'a = 5' is fine, you do not need to tell Lua that 'a' is a number.
  • However, you do need to declare that a variable should be local to a function, by putting 'local' before it the first time you use it, e.g. 'local a = 5'. Otherwise, you are setting the value of a global variable called 'a' that other functions could overwrite.
  • In Lua, zero is not considered to be "false". For example, in Lua if you write "if (0) then ... end", the inside of the block is run - compare this to AGS-Script, Javascript and other "C"-based languages where "if (0) { ... }" will not run the statements inside the block. (That is a fakey example, a more realistic one is that you might have a function that returns 0 or 1 to indicate success or failure.) The only values considered "false" are the boolean value false, and nil (which is basically what Lua calls null).
  • You do not need to end your statements with a semicolon - but you can if you want;
  • Blocks start and end with words, instead of C-style { } curly braces:
  • function funcname(param1,param2,param3)  [...]  end
  • if [condition] then  [...]  elseif [condition] then  [...]  else  [...]  end
  • while [condition] do  [...]  end
  • If you want a series of if-else-if conditions, you will most likely want to use use elseif (all one word) rather than "else if", which is valid but not what you might expect.
The scripts are saved in a subfolder of your game project directory called "lscripts". You can right-click on the "Lua Scripts" icon and select "Refresh" to reflect changes to the lscripts directory made outside of the AGS Editor.

Once you have written a Lua script, you will want to run it as the game is starting up. If you don't do this, any functions you define inside the script will not be available later on. Open the GlobalScript.asc file and add Lua.RunScript("test.lua"); into the game_start() function (replace "test.lua" with the name of your script if you called it something different):

Code: Lua
function game_start()
{
  Lua.RunScript("test.lua")
  // (... the rest of your initialisation code ...)
}


Okay. Now to test calling your function. Go to a room script and in the handler function for some activity (looking at a hotspot or something), add:

Code: Lua
function hHotspot1_Look()
{
  LuaValue* result = Lua.Call("hello");
  player.Say(result.AsString);
}


What this does is to call the Lua global function "hello", convert the return value to a string, and pass it to player.Say(). It is the equivalent of player.Say(hello());, if hello() was an AGS function rather than a Lua one.

Okay! You are now ready to try compiling and running your game. Hopefully, your player character will say the string returned from the Lua function. (If not, please let me know!)

If you are wondering how using this plugin affects things:

  • A file called lscripts.dat will be created in your "Compiled" folder. This file contains all of the files from the lscripts directory. It is from this that Lua.RunScript() is actually loading them, not from the original source code files. This file is not a plain-text file and should provide enough protection to stop casual curious people from snooping in your scripts (it's not strongly encrypted or anything, but it doesn't make it easy).
  • Save games are bigger - by a few dozen kilobytes, even if you don't define your own functions. The more you add into the Lua "universe", the bigger this will get. But it should remain sensible - let me know if it seems to be making your save files really huge.
I have put up a page explaining more about what you can do with the plugin here: http://lua-for-ags.wikispaces.com/UsingLuaForAGS Link not working
#155
I really like this game so far, I appreciate effort to customise the interface and the humour works, but I'm stuck embarassingly early:
Spoiler
Ok, I've got the newspaper, postcard, ornament, the broken and unbroken bottles and the label. I've got no idea how to proceed next, I'm guessing I've probably overlooked something in the University book place but I dunno.
[close]
#156
Maybe try using Bit Torrent? I know The Underdogs uses it for some of the larger games.
#157
Advanced Technical Forum / Re: StrGetCharAt
Thu 09/06/2005 00:48:37
Chars can be useful in certain circumstances since you can use ranges to find if a particular character is an upper case letter, a lower case letter, a number, etc. as opposed to StrComping a string to every single value.

It might be useful to have a substring-extracting function aswell, though.
#158
General Discussion / Re: Gmail? oh come on!
Wed 20/10/2004 17:35:55
Quote from: spoofer4ever on Wed 20/10/2004 10:16:54
what i find sorta wierd is that a computer bot comes and reads your e-mails and personalises the adds
What about Hotmail's (or any) spam filter? That does pretty much exactly the same thing, checks for keywords in email content and reacts accordingly. I don't see why people are that wary of it.

If anyone still doesn't have a gmail address and wants one (which can't be that many by now, it's increasingly easy) http://isnoop.net/gmailomatic.php should get you one pretty fast.
#159
I have no idea if Ron Gilbert's "Grumpy Gamer" blog is already being checked by everyone here on a regular basis, but anyway, he's just put up a very interesting entry about what trying to do a commercial 2D adventure today would entail: http://www.grumpygamer.com/4904226
#160
Critics' Lounge / Re:First "decent" BG's
Wed 10/03/2004 23:37:56
Here, I fixed the window for you:

... ;)

Seriously though, I like it. The lights on the buildings don't look too good though, it looks like colour dithering or something.
SMF spam blocked by CleanTalk