Well, ok, I'm writing this quickie script in this message, so it might not work, but you should get a general idea. I'll include my modified XmlLuaScript.cs as well, but I think you may need to modify LuaCodeBase and LuaHelper, as I renamed some of the properties to stuff that makes better sense to me (instead of "Code" and "Script", I named them "Script" and "ScriptName" respectively.) If you use Visual Studio, refactoring the changes should be easy to do (do Script -> ScriptName first, then Code -> Script), or you can just modify my copy of XmlLuaScript and change the interface properties back to their original names.

I added in a few methods to patch into BaseXmlSpawner's condition and actions.

ExecuteGump(Mobile mob, String gumpStr)
ExecuteAction(Mobile mob, String actionStr)
CheckCondition(Mobile mob, String conditionStr)

These allow you to use BaseXmlSpawner stuff to do things like check for attachments or carried items, or execute actions, such as giving the player a questholder, and bring up generic gumps which are easy to construct from a single string. Check XmlSpawner2 documentation for how this all works, specificly the XmlDialog stuff and you'll quickly understand how it ties together. I also added this method:

GenerateRewardItem(String itemType, Container cont)
or
GenerateRewardItem(String itemType, Container cont, int maxProps, int minIntensity, int maxIntensity)

These both just use the LootPack stuff to generate a single magic item, within the ranges specified and of the itemType specified (you have to be sure the string will resolve to a valid type, see ScriptCompiler.FindTypeByName) The one without props/intensities generates items of "average" quality (see LootPack.cs)

I also added some timer functions to handle anything I need a timer for.

StartTimer(Mobile mob, int seconds)
StopTimer(Mobile mob)

Lastly, a new callback method is added:

GumpResponse(Mobile from, object invoker, string response)

This will call the function OnGumpResponse in your Lua script, passing mobile and response string. That ties in with ExecuteGump so you can handle the result of your gump.

Now, a simple script, in Lua.

Code:
-- simplequest.lua

QuestStage = {}
LastMovement = {}
TimeoutValue = 10
GumpTimeout = 120

function OnInitialize()
  GreetingMsgs = {}
  GreetingMsgs[1] = "Hello there.  Got a moment?"
  GreetingMsgs[2] = "Looking for some work?"
  GreetingMsgs[3] = "Want to earn some coin?"
  DoneMsgs = {}
  DoneMsgs[1] = "Welcome back.  Are the wolves dead?"
  DoneMsgs[2] = "Greetings, have you killed the wolves?"
end

-- this handles expired gump timers, see comment below for more info.
function OnTimerExpired(mobile)
  if (QuestStage[mobile.Serial] ~= nil) then
    QuestStage[mobile.Serial] = nil
  end
end

-- this will watch for players moving near the quest giving npc and greet them if
-- they've not been greeted recently.  after getting a greeting, a player may double
-- click the quest giver to get the quest offer gump.
function OnMovement(args)
  if (args.Mobile ~= nil and this:InRange(args.Mobile,3) and args.Mobile.Player) then
    if (LastMovement[args.Mobile.Serial] == nil or (os.difftime(os.time, LastMovement[args.Mobile.Serial]) > TimeoutValue) then
      -- here we use CheckCondition to make sure they haven't already done this quest
      -- and that they're not already doing it.
      if (QuestStage[args.Mobile.Serial] == nil and owner:CheckCondition(args.Mobile,"~GETONTRIGMOB,[ATTACHMENT,XmlQuestAttachment,Kill the wolves,deleted]=false") and owner:CheckCondition(args.Mobile,"~GETONCARRIED,Kill the wolves,visible=true")) then
        LastMovement[args.Mobile.Serial] = os.time()
        this:Say(GreetingMsgs[math.random(1, #GreetingMsgs)])
        QuestStage[args.Mobile.Serial] = 1
      -- here we check if they're returning with a completed quest.
      elseif (owner:CheckCondition("GETONCARRIED,Kill the wolves,completed1=true")) then
        this:Say(DoneMsgs[math.random(1, #DoneMsgs)])
        QuestStage[args.Mobile.Serial] = 4
      end
    end
  end
end

function OnUse(from)
  local stage = QuestStage[from.Serial]
  if (stage ~= nil) then
    if (stage == 1) then
      QuestStage1(from)
    elseif (stage == 4) then
      QuestStage4(from)
    end
  end
end

function BlockDefaultOnUse(from, target)
  local stage = QuestStage[from.Serial]
  if (stage ~= nil) then
    if (stage == 1 or stage == 4) then
      return true
    end
  end
  return false
end

function QuestStage1(from)
  owner:ExecuteGump("GUMP,Kill the wolves,4/Greetings "..from.Name.."!  I have a job for you.  Go into the forest and slay 10 wolves for me.  Do you accept?;I accept!;accept;No thanks;decline")
  owner:StartTimer(from, GumpTimeout)
  QuestStage[from.Serial] = 2
end

function QuestStage4(from)
  owner:ExecuteGump("GUMP,Kill the wolves,0/Welcome back "..from.Name.."!  Thank you for killing those wolves.  As promised, here's some coin.")
  owner:StartTimer(from, GumpTimeout)
  QuestStage[from.Serial] = 5
end

-- this will handle responses from any gumps we sent to the player.
-- I often use a timeout timer so that if a player doesn't respond
-- in a certain amount of time, it resets to a usable state again, in
-- case the player loses connection or something.
function OnGumpResponse(from, response)
  local stage = QuestStage[from.Serial]
  if (stage ~= nil) then
    if (stage == 2) then
      owner:StopTimer(from)
      if (response == "accept") then
        this:Say("Return when they're dead!")
        owner:ExecuteAction("GIVE/<questholder/name/Kill the wolves/notestring/Go to the forest and kill 10 dire wolves./objective1/KILL,direwolf,10/objective2/Return to Joe./autoreward/true/titlestring/Kill the Wolves>")
        QuestStage[from.Serial] = 3
      elseif (response == "decline") then
        this:Say("Return if you change your mind.")
        QuestStage[from.Serial] = nil
      end
    elseif (stage == 5) then
      if (response == "done") then
        owner:ExecuteAction("GIVE/gold,500")
        owner:ExecuteAction("SETONCARRIED,Kill the wolves,questholder/completed2/true")
        owner:ExecuteAction("TAKE/Kill the wolves")
        QuestStage[from.Serial] = nil
      end
    end
  end
end
I didn't actually try to run that script, so might have made a typo, or something.

It's pretty basic, but should give you a good feel for scripting a quest. Seems like a lot of code, but much of it you can copy and adjust to create new quests.

You can also get substantially more complex with it, chaining quests, having quests start with one npc and end with another. Endless possibilities, really.