Author Topic: Progress Report on Lua Conversion  (Read 4098 times)

Offline SharkD

  • Hero Member
  • *****
  • Posts: 1009
    • View Profile
    • Isometricland
Re: Progress Report on Lua Conversion
« Reply #15 on: May 03, 2011, 02:10:38 AM »
Binary, as in there are only two outcomes: accept_quest and deny_quest. The third dialog option in your example doesn't count since all it amounts to is, "Could you repeat options #1 and #2?"

Anyway, here's something I whipped up:

Code: [Select]

today_date = 1

plotlines =
{
main =
{
-- run this script every 1 days
check_every = 1,
-- initialize to day zero
last_checked = 0,
do_script = function()
-- if today is day #100 or later...
if (today_date >= 100) then
plotlines.smuggler_plotline.do_script()
end
-- run the defense mission plotline every so often
if ((today_date - plotlines.defense_mission.last_checked) > plotlines.defense_mission.check_every) then
plotlines.defense_mission.do_script()
plotlines.defense_mission.last_checked = today_date
end
today_date = today_date + 1
end,
},
smuggler_plotline =
{
has_bought_contraband = false,
do_script = function()
plotlines.smuggler_plotline.has_bought_contraband = prompt("Would you like to buy contraband?")
end,
},
defense_mission =
{
check_every = 20,
last_checked = 0,
do_script = function()
if (plotlines.smuggler_plotline.has_bought_contraband == true) then
say("Sorry, I can't offer you this mission. You have purchased contraband.")
else
say("You're clean. Here's a mission for you!")
plotlines.third_plotline.do_script()
end
end,
},
third_plotline =
{
-- etc. etc. etc.
},
}

The "main" plotline is run automatically at the start (the code to actually start it is not in the example however...). It controls the other plotlines.
« Last Edit: May 03, 2011, 02:13:16 AM by SharkD »

Offline Joseph Hewitt

  • Administrator
  • Hero Member
  • *****
  • Posts: 2552
    • View Profile
    • http://www.gearheadrpg.com
Re: Progress Report on Lua Conversion
« Reply #16 on: May 03, 2011, 07:17:08 AM »
Binary, as in there are only two outcomes: accept_quest and deny_quest. The third dialog option in your example doesn't count since all it amounts to is, "Could you repeat options #1 and #2?"

Then there would be other prompts with different scripts, as many as needed and with scripts doing whatever you need them to do. I'm afraid I don't understand why this is a point of confusion.

Quote
Anyway, here's something I whipped up:

I take it that this is supposed to be a plot for GearHead? Before writing code, you should probably describe how you want this plot to work from the perspective of the player.

If I understand your intentions correctly, this plot involves two characters: a smuggler and someone who'll give the PC a defense mission. The mission-giver will offer the PC a mission once every day until the PC accepts, which triggers a subplot. The smuggler will wait until the 100th day and then offer the PC some contraband. If the PC accepts the contraband, the mission-giver will no longer offer the defense mission. Is this correct?

This should be modeled as a single plot. The NPCs would be selected as elements by the plot, and each one would be given a conversation inside the plot. The plot would also request a subplot for the defense mission, but we don't have to worry about the specifics of that- once the buck gets passed to the next plot in the plotline this plot is over.

I'll use the same conversation pseudocode as described earlier in this thread, since the Lua standards haven't been finalized yet. Conditions and scripts are written in Lua.

Code: [Select]
Plot
Element1 <Character Smuggler>
Element2 <Character Police>

# SubPlot1 is equivalent to "third_plotline"
SubPlot1 <*DefenseMission 2>

sub
    Persona 1
    # The conversation for the smuggler...
    say <Would you like to buy contraband?>
    condition  if days >= 100 then return( true ) end
        prompt <Yes I would!>
            say <Here's your contraband.>
            script   plot.v.has_bought_contraband = true
        prompt <No thanks>
            say <Then get the hell out of my shop!>
    say <Come back later. There's nothing interesting to see yet.>

    Persona 2
    # The conversation for the mission-giver...
    say <Sorry, I can't give you this mission, you've bought contraband.>
    condition  return( plot.v.has_bought_contraband )
    say <Here's a mission for you!>
    script  SubPlot1.activate
end

This subplot doesn't need to be manually activated because it is a root plot- the root plot is automatically activated when a megaplot is generated.

There are a load of problems as it stands now. 100 days is a very very long time in GearHead. The plot will only end when the PC speaks to the mission-giver and is given the job... so if the mission-giver dies or the PC accepts the contraband, this plot will never end. Code should be added to make sure the PC can afford the contraband, then to give it over and take the PC's money. The contraband itself could be added as a third element.
« Last Edit: May 03, 2011, 07:22:24 AM by Joseph Hewitt »

Offline SharkD

  • Hero Member
  • *****
  • Posts: 1009
    • View Profile
    • Isometricland
Re: Progress Report on Lua Conversion
« Reply #17 on: May 04, 2011, 12:56:43 AM »
Then there would be other prompts with different scripts, as many as needed and with scripts doing whatever you need them to do. I'm afraid I don't understand why this is a point of confusion.

You didn't provide an example though.

I'll use the same conversation pseudocode as described earlier in this thread, since the Lua standards haven't been finalized yet. Conditions and scripts are written in Lua.

I'm more interested in what the Lua syntax will look like at this point. I find Pascal strange and have trouble reading it.
« Last Edit: May 04, 2011, 01:01:53 AM by SharkD »

Offline Joseph Hewitt

  • Administrator
  • Hero Member
  • *****
  • Posts: 2552
    • View Profile
    • http://www.gearheadrpg.com
Re: Progress Report on Lua Conversion
« Reply #18 on: May 04, 2011, 01:51:28 AM »
You didn't provide an example though.


To add three options you'd do this:

Code: [Select]
say <How many apples do you want?>
  prompt <One apple.>
  script  self.v.apples = 1;
  prompt <Two apples.>
  script self.v.apples = 2;
  prompt <Three apples.>
  script self.v.apples = 3;

To change this so that there are four options, you would do the following:

Code: [Select]
say <How many apples do you want?>
  prompt <One apple.>
  script  self.v.apples = 1;
  prompt <Two apples.>
  script self.v.apples = 2;
  prompt <Three apples.>
  script self.v.apples = 3;
  prompt <Four apples.>
  script self.v.apples = 4;

If you need examples of how to add six, seven, or eight options, I can show you that as well.

Quote
I'm more interested in what the Lua syntax will look like at this point. I find Pascal strange and have trouble reading it.


Fair enough. Let me write a conversation in pseudocode first, then show the Lua it uses. Here is the conversation I've been using for testing purposes:

Code: [Select]
say <I thought you said you didn't like this!?>
condition  return( self.v.DidNotLike );
say <Trying out the conversation system.>
  prompt <I like it!>
    say <Thank you. It was a lot more work than it looks.>
    script  self.v.BeenBefore = true;
  prompt <Looks kinda dumb.>
    say <Well screw you, then.>
    script  self.v.DidNotLike = true;
  prompt <I already told you...>
  condition  return( self.v.BeenBefore );
    say <Oh, I guess you did...>

This gets compiled into the following Lua code:

Code: [Select]
P.node_101 = {}
P.node_101.msg = "I thought you said you didn't like this!?"
P.node_101.condition = function( self ) return( self.v.DidNotLike ) end
P.node_101.nextid = "node_111"

P.node_111 = {}
P.node_111.msg = "Trying out the conversation system."
P.node_111.prompts = {}
P.node_111.prompts[ 102 ] = {}
P.node_111.prompts[ 102 ].msg = "I like it!"
P.node_111.prompts[ 103 ] = {}
P.node_111.prompts[ 103 ].msg = "Looks kinda dumb."
P.node_111.prompts[ 105 ] = {}
P.node_111.prompts[ 105 ].msg = "I already told you."
P.node_111.prompts[ 105 ].condition = function( self ) return( self.v.BeenBefore ) end

P.node_103 = {}
P.node_103.msg = "Well screw you, then."
P.node_103.effect = function( self ) self.v.DidNotLike = true end

P.node_104 = {}
P.node_104.msg = "Thank you. It was a lot more work than it looks."
P.node_104.effect = function( self ) self.v.BeenBefore = true end

P.node_105 = {}
P.node_105.msg = "Oh, I guess you did."

The conversation handler calls this record with a particular NodeID. The conversation node prints its message, executes its script, and builds a new conversation menu before returning. Here's the implementation of that:

Code: [Select]
function proto_persona.usenode( self , node )
-- Clear the menu.
-- Note that "self" is an object, and this is one of its methods.
gh_initchatmenu( true )

-- If an effect script exists, run that now.
if node.effect ~= nil then
node.effect( self )
end

-- Set the chat message.
gh_setchatmsg( gh_formatstring( node.msg , self ) )

-- If there are any children, add them to the menu.
if node.prompts ~= nil then
local k,v
for k,v in pairs( node.prompts ) do
if ( v.condition == nil ) or v.condition( self ) then
gh_addchatmenuitem( k , gh_formatstring( v.msg ) )
end
end
end
end


function gh_conversation( gearptr, nodeid )
-- We've been asked to run a conversation node.
-- 1. Locate the node record.
-- 2. If it has a conditional script, make sure it evaluates to true.
--  2a. If false, jump to the node's next sibling instead.
-- 3. If we have a valid node speak its message, construct its menu,
--    and execute its effect script if one exists.
local self = gh[gearptr]
if self ~= nil then
local pnode = nil

repeat
pnode = self[ nodeid ]
if ( pnode ~= nil ) then
-- We've found a node, but there's a nontrivial chance
-- that it's the wrong node. Check to see if it has a
-- conditional function, and if so see if it's true.
if pnode.condition ~= nil then
if not pnode.condition( self ) then
-- Crap. This node is passing the buck...
-- Set nodeid to the next sibling.
nodeid = pnode.nextid
pnode = nil
end
end
else
-- The requested node could not be found. This is
-- a big problem... terminate the loop.
break
end

until ( pnode ~= nil )

-- Check to see if we have a valid node. If so, do whatever needs
-- to be done.
if pnode ~= nil then
self.usenode( self , pnode )
else
-- Serious problem: if the node can't be found, this means
-- that the persona is broken. Print an error message.
error( 'Cannot find persona node ' .. nodeid )
end
end
end

Offline SharkD

  • Hero Member
  • *****
  • Posts: 1009
    • View Profile
    • Isometricland
Re: Progress Report on Lua Conversion
« Reply #19 on: May 04, 2011, 06:14:23 PM »
Instead of:

Code: [Select]
P.node_111 = {}
P.node_111.msg = "Trying out the conversation system."
P.node_111.prompts = {}
P.node_111.prompts[ 102 ] = {}
P.node_111.prompts[ 102 ].msg = "I like it!"
P.node_111.prompts[ 103 ] = {}
P.node_111.prompts[ 103 ].msg = "Looks kinda dumb."
P.node_111.prompts[ 105 ] = {}
P.node_111.prompts[ 105 ].msg = "I already told you."
P.node_111.prompts[ 105 ].condition = function( self ) return( self.v.BeenBefore ) end

I would do:
Code: [Select]
P.node_111 = {}
P.node_111.msg = "Trying out the conversation system."
P.node_111.prompts = {}
P.node_111.prompts[ 1 ] = {}
P.node_111.prompts[ 1 ].msg = "I like it!"
P.node_111.prompts[ 1 ].nextid = 102
P.node_111.prompts[ 2 ] = {}
P.node_111.prompts[ 2 ].msg = "Looks kinda dumb."
P.node_111.prompts[ 2 ].nextid = 103
P.node_111.prompts[ 3 ] = {}
P.node_111.prompts[ 3 ].msg = "I already told you."
P.node_111.prompts[ 3 ].condition = function( self ) return( self.v.BeenBefore ) end
P.node_111.prompts[ 3 ].nextid = 105

I'm worried that somewhere a 105 element array is being generated and initialized...

Offline Joseph Hewitt

  • Administrator
  • Hero Member
  • *****
  • Posts: 2552
    • View Profile
    • http://www.gearheadrpg.com
Re: Progress Report on Lua Conversion
« Reply #20 on: May 04, 2011, 06:42:24 PM »
I'm worried that somewhere a 105 element array is being generated and initialized...


No 105-element array is being generated and initialized. Arrays in Lua are exactly the same as tables; if you don't explicitly define something, it doesn't exist.

http://www.lua.org/pil/11.2.html

Offline SharkD

  • Hero Member
  • *****
  • Posts: 1009
    • View Profile
    • Isometricland
Re: Progress Report on Lua Conversion
« Reply #21 on: May 11, 2011, 02:06:30 PM »
I'm guessing the "v" table stores miscellaneous variables that can be checked at any time? But what is "self" pointing to, the "P" table? Sorry, object oriented programming is not something I've attempted in Lua yet.

One thing I'd ask is that you support *both* the shorthand dialogue language you created *as well as* pure Lua for the plotline scripts (with preference given to one or the other) in case it is needed by someone for some reason. That is, assuming it's possible to save scripts in individual separate files as opposed to one giant file.

Otherwise the code looks OK. I'm trying to think of cases where it might fail to achieve something desired by coders/modders at some point in the future, but my lack of familiarity with RPG dialog scripting is kind of a hindrance. My experience is with writing game rules for an RTS. :(

Also, I've been told that compiling the Lua scripts using LuaC.exe results in a slight performance boost compared with leaving them as plain text. It might be negligable, but every bit helps. And best of all it's optional! (At least in my experience.)

[edit]
Also, here's an interesting article if you haven't read it already.
« Last Edit: May 12, 2011, 06:05:09 PM by SharkD »

Offline SharkD

  • Hero Member
  • *****
  • Posts: 1009
    • View Profile
    • Isometricland
Re: Progress Report on Lua Conversion
« Reply #22 on: June 12, 2011, 08:43:45 AM »
Wow, someone who actually read the thread first before spamming!

Offline Onisuzume

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 562
    • View Profile
Re: Progress Report on Lua Conversion
« Reply #23 on: June 17, 2011, 12:43:06 PM »
Wow, someone who actually read the thread first before spamming!

Should be simply to code a bot to do something like that, though.

Offline SharkD

  • Hero Member
  • *****
  • Posts: 1009
    • View Profile
    • Isometricland
Re: Progress Report on Lua Conversion
« Reply #24 on: June 18, 2011, 10:06:32 AM »
I noticed that later. I didn't see the similarity at first.

Offline Joseph Hewitt

  • Administrator
  • Hero Member
  • *****
  • Posts: 2552
    • View Profile
    • http://www.gearheadrpg.com
Re: Progress Report on Lua Conversion
« Reply #25 on: August 17, 2011, 07:49:23 AM »
The Lua conversation system is now up and running, complete with persona fragments and indented construction. Here's an example of a working conversation tree:

Code: [Select]
Persona 1

PFrag "*NiceToMeetYou"



Say "You didn't like this one either."

condition <return( self.v.DidNotLike )>



Say "Test of the conversation linking system. Do you like this one?"

reply "I like it!"

say "Thank you. It was a lot more work than it looks."

label <GoLikePersona>

effect <self.v.BeenBefore = true>



reply "Looks kinda dumb."

say "Would you like to revise your opinion?"

effect <self.v.BeenBefore = true>

reply "No, not at all."

say "Well screw you over here too!"

effect <self.v.DidNotLike = true>

reply "Yes, I may have been a bit hasty."

goto "GoLikePersona"



reply "I already told you."

condition <return( self.v.BeenBefore )>

say "Oh, I guess you did."


It's pretty much the same as the previous example, except that it comes with a *NiceToMeetYou node tacked onto the beginning. I think this is fairly easy to read but it could probably still benefit from a proper conversation editor.

You can download from SVN, branch "pluslua".
https://gearhead2.svn.sourceforge.net/svnroot/gearhead2/pluslua