Daimonin
Members login
Sign Up!      problems?
N F
* Daimonin Forum
Home Help Search Calendar

Welcome Guest, you have to register to post here.
Search

News

Stats
75654 Posts in 6829 Topics by 8142 Members
Latest Member: mattj
Daimonin Forum  |  Project News  |  Official announcements  |  Programmers' blogs  |  Topic: SENTInce « previous next »
Pages: [1] 2 3 ... 8 Go Down Print
Topic: SENTInce  (Read 5801 times)
smacky
Administrator
*
*
*
*


Karma: +120/-101
Posts: 5380



View Profile
« on: April 23, 2007, 08:06:33 pm »

Here is /scripts/church.lua reworked for SENTInce. I hope you'll agree it kicks the old version's arse (not sure who I just insulted Wink).

To run it you need to fetch streams/SENTInce/maps/lua/*.lua (well, ib and tl) and copy them over your local versions. It won't work without, but old scripts will continue to work with.

For the moment ignore the first line and just copy this script over /scripts/church.lua.

Note that this is only a bare bones version, so it's not intended to do much more (ie, have the priest talk about other topics) than the original and I have not added any synonyms to tl really.

The reason I am posting it here rather than committing it is because I feel B4 should not use the stuff in /scripts/, but its own non-public scripts (as I posted somewhere before) and this is intended as the basis for that.

Oh, ignore any warnings you get about truncation (which doesn't seem to work ATM). I think that is still in development.

Code:
-- /planes/demon_plane/stoneglow/priest.lua

require("interface_builder")
require("topic_list")

local activator = event.activator
local me        = event.me

local ib = InterfaceBuilder()
local tl = TopicList()

local topic_greeting = function ()

    ib:SetMsg("Welcome to the Holy Church of the Tabernacle! I am " .. me:GetName() ..".\n")
    ib:AddMsg("\nIf you are confused by my services, I can ^explain^ them further.\n")
    ib:AddMsg("\nHow can I help, my child?")
    ib:AddLink("Teach me Minor Healing", "teach minor healing")
    ib:AddLink("Cast Remove Death Sickness on me", "cast remove death sickness")
    ib:AddLink("Cast Remove Depletion on me", "cast remove depletion")
    ib:AddLink("Cast Restoration on me", "cast restoration")
    ib:AddLink("Cast Cure Disease on me", "cast cure disease")
    ib:AddLink("Cast Cure Poison on me", "cast cure poison")
    ib:AddLink("Cast Remove Curse on my inventory", "cast remove curse")
    ib:AddLink("Cast Remove Damnation on my inventory", "cast remove damnation")
    ib:AddLink("Please share some food", "share food")

end

local topic_explain = function (service)

    if service == "minor healing" then

        ib:SetMsg("I will ^teach Minor Healing^ to you for no charge.")

        return "explain", "explain services"

    elseif service == "death sickness" then

        ib:SetMsg("Death sickness is a stronger form of ^depletion^.\n")
        ib:AddMsg("\nEverytime you die your stats are depleted by death sickness.\n")
        ib:AddMsg("\nI will ^cast Remove Death Sickness^ on you for " .. activator:ShowCost(100 + (4 * activator.level * activator.level)))

        return "explain", "explain services"

    elseif service == "depletion" then

        ib:SetMsg("I will ^cast Remove Depletion^ on you for ".. activator:ShowCost(5 * activator.level))

        return "explain", "explain services"

    elseif service == "restoration" then

        ib:SetMsg("I will ^cast Restoration^ on you for 150 copper")

        return "explain", "explain services"

    elseif service == "disease" then

        ib:SetMsg("I will ^cast Cure Disease^ on you for ".. activator:ShowCost(100 * activator.level))

        return "explain", "explain services"

    elseif service == "poison" then

        ib:SetMsg("I will ^cast Cure Poison^ for " .. activator:ShowCost(5 * activator.level))

        return "explain", "explain services"

    elseif service == "curse" then

        ib:SetMsg("I will ^cast Remove Curse^ over your inventory for ".. activator:ShowCost(100 * activator.level))

        return "explain", "explain services"

    elseif service == "damnation" then

        ib:SetMsg("I will ^cast Remove Damnation^ over your inventory for " .. activator:ShowCost(100 + (3 * activator.level * activator.level)))

        return "explain", "explain services"

    else -- if not services then

        ib:SetMsg("I can teach you basic prayers that you can cast yourself.\n")
        ib:AddMsg("\nI can also cure various ailments and exorcise evil spirits from your items.")
        ib:AddLink("Explain Minor Healing to me", "explain minor healing")
        ib:AddLink("Explain Death Sickness to me", "explain death sickness")
        ib:AddLink("Explain Depletion to me", "explain depletion")
        ib:AddLink("Explain Restoration to me", "explain restoration")
        ib:AddLink("Explain Disease to me", "explain disease")
        ib:AddLink("Explain Poison to me", "explain poison")
        ib:AddLink("Explain Curse to me", "explain curse")
        ib:AddLink("Explain Damnation to me", "explain damnation")

        return "services"

    end

end

local topic_teach = function (prayer) -- Clumsy. Should be rewritten if the priest can ever teach more than one spell.

    if prayer == "minor healing" then

        local prayernr = game:GetSpellNr(prayer)

        if activator:DoKnowSpell(prayernr) then

            ib:SetMsg("You already know the prayer '" .. prayer .. "'!")

            return "services"

        else

            local skill = "divine prayers"
            local skillnr = game:GetSkillNr(skill)

            ib:SetMsg("Yes, you seem worthy of that knowledge.\n")

            if activator:FindSkill(skillnr) == nil then

                ib:AddMsg("\nFirst I will teach you the skill you need to cast prayers.\n")
                ib:AddMsg("\n|** You learn '" .. skill .. "' **|")
                activator:AcquireSkill(skillnr, game.LEARN)

            end

            activator:AcquireSpell(prayernr, game.LEARN)
            ib:AddMsg("\n|** You learn the prayer '" .. prayer .. "' **|")

        end

    else

        ib:SetMsg("I don't think I know that one.")

    end

    return "services"

end

local topic_cast = function (prayer)

    local sum = 0

    if prayer == "remove death sickness" then

        sum = 100 + (4 * activator.level * activator.level)

    elseif prayer == "remove depletion" then

        sum = 5 * activator.level

    elseif prayer == "restoration" then

        sum = 150

    elseif prayer == "cure disease" then

        sum = 100 * activator.level

    elseif prayer == "cure poison" then

        sum = 5 * activator.level

    elseif prayer == "remove curse" then

        sum = 100 * activator.level

    elseif prayer == "remove damnation" then

        sum = 100 + (3 * activator.level * activator.level)

    else

        ib:SetMsg("I don't think I know that one.")

    end

    if sum then

        if activator:PayAmount(sum) == 1 then

            me:CastSpell(activator, game:GetSpellNr(prayer), 1, 0, "")
            ib:SetMsg("|** " .. me:GetName() .. " takes your money **|\n")
            ib:AddMsg("\nHold still... done!")

        else

            ib:SetMsg("You have not the funds!")

        end

    end

    return "services"

end

local topic_sharefood = function ()

    if activator.food < 150 then

        activator.food = 500
        ib:SetMsg("Eat, my child, eat.\n")
        ib:AddMsg("\n|** Your stomach is filled again **|")

    else

        ib:SetMsg("You don't look very hungry to me.")

    end

    return "services"

end

tl:SetDefault("I am not quite getting your meaning. Please try other words.")
tl:AddTopics({ unpack(tl.synonyms.greeting), "services?" }, topic_greeting)
tl:AddTopics({ "explain", "explain%s+(.+)" }, topic_explain)
tl:AddTopics({ "(minor%s+healing)", "(death%s+sickness)", "(depletion)", "(restoration)", "(disease)", "(poison)", "(curse)", "(damnation)" }, topic_explain)
tl:AddTopics("teach%s+(.+)", topic_teach)
tl:AddTopics("cast%s+(.+)", topic_cast)
tl:AddTopics({ "share%s+food", "food" }, topic_sharefood)

ib:ShowInterface(event, tl:CheckMessage(event))
Logged

Syber
Giant slime
*


Karma: +0/-0
Posts: 17



View Profile
« Reply #1 on: April 23, 2007, 10:24:46 pm »

It does look cleaner
Logged
smacky
Administrator
*
*
*
*


Karma: +120/-101
Posts: 5380



View Profile
« Reply #2 on: April 23, 2007, 11:15:46 pm »

You made it here Syber. Wink

The main reason it looks cleaner is simply my coding style. I like my whitespace and non-cryptic variable names (sadly people don't find calling the player variable 'moron' nearly as funny as I do so as I refuse to use 'pl' I've gone with the bland 'activator' -- where is the love?)

But also SENTInce  handles the interface header, title, and buttons for you and opens/closes it automatically too. So instead of dealing with all that in each topic_foo function in your script you just call ib:ShowInterface() once at the end.

Finally, by using sensible natural language topics and pattern matching you can cut out a lot of duplicated code (reading this Grommit? Tongue), so my version is 3 or 4 functions shorter than the original.

It's better in play as well. Wink
Logged

grommit
Administrator
*
*
*
*


Karma: +28/-3
Posts: 2529



View Profile
« Reply #3 on: April 24, 2007, 09:49:23 am »

Quote from: "smacky"
you can cut out a lot of duplicated code (reading this Grommit? Tongue)


I hate duplicated code - don't know why you singled me out. If I need code in more than one place it goes in a function. If it's a repeated string it goes in a variable.
Logged
smacky
Administrator
*
*
*
*


Karma: +120/-101
Posts: 5380



View Profile
« Reply #4 on: April 24, 2007, 10:40:52 am »

You took the wrong half of my sentence:
Quote
by using sensible natural language topics and pattern matching ... (reading this Grommit? Razz)
Logged

Gecko
Administrator
*
*


Karma: +4/-0
Posts: 817



View Profile
« Reply #5 on: April 24, 2007, 11:30:02 am »

A few comments based on the example script:
- A very readable script. Congratulations! Now if only you felt like rewriting all the other scripts ;-)
- I think dropping "moron" for "activator" was a good idea. I hate opinionated source.
- The sensible use of patterns and captures in the topics make for really simple code. Unfortunately it is probably too complex for inexperienced scripters =(
- I see only two negative points:
  * the use of "{ unpack(foo),bar,baz }" is a bit annoying. AddTopics can be extended to accept nested tables so that it would be enough to write "{ foo, bar, baz }" and let AddTopics deal with the unpacking.
  * the use of return values from the topic functions is very non-self-explaining. I don't understand it (without reading the specs or source), so I believe other will have similar problems. I would prefer a slightly more verbose and self-explaining API. Something like "tl:SetFoo('services')" or even "ib:SetFoo('services')" instead of "return 'services'".
Logged

Daimonin developer, admin and moderator.
grommit
Administrator
*
*
*
*


Karma: +28/-3
Posts: 2529



View Profile
« Reply #6 on: April 24, 2007, 11:51:11 am »

Quote from: "smacky"
You took the wrong half of my sentence:
Quote
by using sensible natural language topics and pattern matching ... (reading this Grommit? Razz)

Did you look at my updated trade_safe.lua? They don't come much more pattern-matchy than that. Wink

Also using the form
Code:

local topic_greeting = function ()

just because you can, is imo just crypticising
Code:

local function topic_greeting()
Logged
Alderan
Administrator
*
*


Karma: +16/-2
Posts: 462



View Profile
« Reply #7 on: April 24, 2007, 03:01:17 pm »

Quote from: "smacky"

Oh, ignore any warnings you get about truncation (which doesn't seem to work ATM). I think that is still in development.


The warning will be thrown out by the client (in the development-version) when a title/link/button is too long to get fully shown. (So the scripter can rewrite that to fit in the interface)
the title: 'topic: cast remove death sickness' is too long, so this warning is right.
The truncation works, header-titles and buttons-text will be truncated in the string.
For message-titles and links the truncation will be automatically done by SDL_ClipRect.
See here the different truncations: http://daimonin.fapo.domainfactory-kunde.de/ib_bright.jpg
Logged

Leider habe ich keine Zeit, Ihr Buch zu lesen, schicken Sie mir bitte ein bereits gelesenes.

smacky
Administrator
*
*
*
*


Karma: +120/-101
Posts: 5380



View Profile
« Reply #8 on: April 24, 2007, 03:13:27 pm »

Quote from: "Gecko"

- A very readable script. Congratulations! Now if only you felt like rewriting all the other scripts ;-)

Actually that kind of is my intention (I am now doing scripts/smith.lua -- I want to make Stoneglow-specific versions of all the scripts in scripts/ at the very least). But rewriting scripts is a bit dull for me and might not make me too popular with the original authors. Wink

My hope is that with a few examples people will agree that doing things a la SENTInce is good for scripters and players (a little structure and consistency makes the end result so much better and contrary to popular belief structure does not stifle creativity; it enables it. So sticking to the style guide won't hinder anyone's conversation-writing).

Quote

- The sensible use of patterns and captures in the topics make for really simple code. Unfortunately it is probably too complex for inexperienced scripters =(

It probably depends more on how the individual's brain works rather than scripting experience, but yeah, patterns and captures probably will always be underused. Sad

Quote

  * the use of "{ unpack(foo),bar,baz }" is a bit annoying. AddTopics can be extended to accept nested tables so that it would be enough to write "{ foo, bar, baz }" and let AddTopics deal with the unpacking.

Ooh, I didn't realise this! So to take the actual example, instead of:
Code:
l:AddTopics({ unpack(tl.synonyms.greeting), "services?" }, topic_greeting)


I could use:
Code:
l:AddTopics({ tl.synonyms.greeting, "services?" }, topic_greeting)


In fact, to my eye, the former is clearer (especially with syntax highlighting in vim so unpack is light blue), but yeah, fair point.

Quote
 * the use of return values from the topic functions is very non-self-explaining. I don't understand it (without reading the specs or source), so I believe other will have similar problems. I would prefer a slightly more verbose and self-explaining API. Something like "tl:SetFoo('services')" or even "ib:SetFoo('services')" instead of "return 'services'".

Well yes it needs to be documented. After all, the whole ib/tl API is a bit unlear IMO without reading the docs.

The problem with doing it as you suggest is precisely the point you have made: is it a tl or an ib issue? I'd say tl because the topic_foo functions deal directly with tl:CheckMessage(). But then ultimately the returns are utilised by ib:ShowInterface() and ib:SetDecline(). I'd prefer not to have those lua/ scripts rely on each other's structures (ib:ShowInterface() uses the returns of tl:CheckMessage() but not the tl table itself).
Logged

grommit
Administrator
*
*
*
*


Karma: +28/-3
Posts: 2529



View Profile
« Reply #9 on: April 24, 2007, 03:13:40 pm »

Very nice, Alderan! But "truancated" needs fixing Wink
Logged
smacky
Administrator
*
*
*
*


Karma: +120/-101
Posts: 5380



View Profile
« Reply #10 on: April 24, 2007, 03:24:01 pm »

Quote from: "grommit"

Did you look at my updated trade_safe.lua? They don't come much more pattern-matchy than that. Wink

Actually no I haven't yet, but I remember you mentioned it. I'll have a look in a bit.

Quote

Also using the form
Code:

local topic_greeting = function ()

just because you can, is imo just crypticising
Code:

local function topic_greeting()

Ooh you meaney! It's not 'just because I can'. It's because that is correct and it makes more sense to me because it highlights the fact that Lua functions are normal variables, not special cases.

http://www.lua.org/manual/5.0/manual.html#2.5.8

So in fact you're just using syntactic sugar and I'm not crypticising at all. Tongue It's like using the cursed mouse instead of the keyboard is just interfacial sugar. Tongue
Logged

smacky
Administrator
*
*
*
*


Karma: +120/-101
Posts: 5380



View Profile
« Reply #11 on: April 24, 2007, 03:36:42 pm »

Quote from: "Alderan"

the title: 'topic: cast remove death sickness' is too long, so this warning is right.
The truncation works, header-titles and buttons-text will be truncated in the string.
For message-titles and links the truncation will be automatically done by SDL_ClipRect.

Yes, the thing that got me was that (is that) that title is just chopped halfway through the letter e of sickness IIRC. I think this is too precise. I think that truncation should be performed down to whole letters and you should replace the last letter of the truncated string with an ellipsis ('…') to indicate to the player that the string is longer but has been truncated. So eg, 'Topic cast remove death sickness' becomes 'Topic: cast remove death sick…'.

The problem is that the font bitmaps don't have an ellipsis defined (well, 11x15 does but in a character defined as a smiley in 7x4 *sigh*). Perhaps we should just say 255 is the Daimonin ellipsis character and add this to all the font bitmaps?
Logged

Alderan
Administrator
*
*


Karma: +16/-2
Posts: 462



View Profile
« Reply #12 on: April 24, 2007, 06:36:29 pm »

Ah i see what you mean. In the code was for the whole interface-body already with ClipRect some graphical truncation, so i didn't touch that. But that can be done. I think we even don't need an ellipsis, due to the proportional fonts.

Quote
But "truancated" needs fixing

Yeah, that's atm one of my favorite word for misspelling, but i try my best do improve my english;-)


EDIT: truncation with ellipsis done
Logged

Leider habe ich keine Zeit, Ihr Buch zu lesen, schicken Sie mir bitte ein bereits gelesenes.

Gecko
Administrator
*
*


Karma: +4/-0
Posts: 817



View Profile
« Reply #13 on: April 24, 2007, 09:05:50 pm »

Quote from: "smacky"

Quote

  * the use of "{ unpack(foo),bar,baz }" is a bit annoying. AddTopics can be extended to accept nested tables so that it would be enough to write "{ foo, bar, baz }" and let AddTopics deal with the unpacking.

Ooh, I didn't realise this! So to take the actual example, instead of:
Code:
l:AddTopics({ unpack(tl.synonyms.greeting), "services?" }, topic_greeting)


I could use:
Code:
l:AddTopics({ tl.synonyms.greeting, "services?" }, topic_greeting)


In fact, to my eye, the former is clearer (especially with syntax highlighting in vim so unpack is light blue), but yeah, fair point.


Well, I would prefer
Code:
l:AddTopics({ tl.greetings, "services?" }, topic_greeting)

But you get the point. This of course requires a little recursive coding of AddTopics()...

Quote from: "smacky"

Quote
 * the use of return values from the topic functions is very non-self-explaining. I don't understand it (without reading the specs or source), so I believe other will have similar problems. I would prefer a slightly more verbose and self-explaining API. Something like "tl:SetFoo('services')" or even "ib:SetFoo('services')" instead of "return 'services'".

Well yes it needs to be documented. After all, the whole ib/tl API is a bit unlear IMO without reading the docs.

The problem with doing it as you suggest is precisely the point you have made: is it a tl or an ib issue? I'd say tl because the topic_foo functions deal directly with tl:CheckMessage(). But then ultimately the returns are utilised by ib:ShowInterface() and ib:SetDecline(). I'd prefer not to have those lua/ scripts rely on each other's structures (ib:ShowInterface() uses the returns of tl:CheckMessage() but not the tl table itself).


If it is an interface function, it should go into ib. tl should not be tightly coupled to ib. I'm still not clear what the returned values are used for, which is precisely why I think an explicit method name would make it easier to understand. While the function return style is compact it is neither obvious nor extensible. Remember that readability is usually more important than writability.
Logged

Daimonin developer, admin and moderator.
smacky
Administrator
*
*
*
*


Karma: +120/-101
Posts: 5380



View Profile
« Reply #14 on: April 24, 2007, 10:28:46 pm »

Quote from: "Gecko"

Well, I would prefer
Code:
l:AddTopics({ tl.greetings, "services?" }, topic_greeting)

Well yes. The reason for including the synonyms subtable is to keep that second layer nice and clean, for readability and possible future expandability (to throw you adjectives back at you Tongue).
Quote
But you get the point. This of course requires a little recursive coding of AddTopics()...

Yes just had a go at this and got massively confused :lol: I'll try again later/tomorrow.

Re the magic returns, this is how it works:

A topic_foo function returns 1 or 2 values (counting the default implicit return nil).

These values, called txt and cmd, are then returned by tl:CheckMessage() via the internal _DoActions. If _DoActtions operates on a string (as often in the case of the default) txt is set to boolean true (and cmd is nil).

ib:ShowInterface() then takes txt and cmd as its second and third arguments, so the canonical way to call it is:
Code:
ib:ShowInterface(event, tl:CheckMessage(event))

(but of course this doesn't mean the one function relies on the other and importantly neither ib or tl structure has any direct contact with the other.)

So that is the journey of txt and cmd: they're born in topic_foo, educated in tl._DoActions(), graduate in tl:CheckMessage(), and work in ib:ShowInterface().

But what do they do? Well, cmd is only relevant if txt is a string. If txt is boolean true it tells ib:ShowInterface() to do an activator:Inteface(-1) -- close the interface immediately.

If txt is (currently) any other value but not a string (if I ever expand this the specific value is nil so this is the default behaviour) it tells ib:ShowInterface() to only show a Bye button (lhs).

If txt is a string it tells ib:ShowInterface() to also show a rhs button with txt as the text. In this case, if cmd is also a string it will be the command issued by selecting that button. If cmd is not a string then txt will be used as the command. Basically you usually want the text to be the same as the command so only one return is needed (eg, return "hello") but occasionally you will want different string (eg, where the command is more than about 12 characters, eg, return "explain", "explain services").

Quote
If it is an interface function, it should go into ib. tl should not be tightly coupled to ib.

Agreed. So which do you think is more appropriate?

Quote
I'm still not clear what the returned values are used for, which is precisely why I think an explicit method name would make it easier to understand. While the function return style is compact it is neither obvious nor extensible. Remember that readability is usually more important than writability.

Well hopefully my explanation has cleared things up. I can't argue that it is obvious but I do think the return system is in fact more extensible than tieing things down to an explicit method. But perhaps that would be more readable. But wbhat you gain in readbility perhaps you lose in flexibility?
Logged

Pages: [1] 2 3 ... 8 Go Up Print 
Daimonin Forum  |  Project News  |  Official announcements  |  Programmers' blogs  |  Topic: SENTInce « previous next »
Jump to:  

Page created in 0.208 seconds with 21 queries.
Powered by SMF 1.1.4 | SMF © 2006-2007, Simple Machines LLC
Seo4Smf v0.2 © Webmaster's Talks
Copyright 2008 Daimonin MMORPG  •  Terms of Service  •  XHTML  •  Daimonin sourceforge open source project